git
/
gpg-interface.c
1113 строк · 29.3 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "commit.h"5#include "config.h"6#include "date.h"7#include "gettext.h"8#include "run-command.h"9#include "strbuf.h"10#include "dir.h"11#include "ident.h"12#include "gpg-interface.h"13#include "path.h"14#include "sigchain.h"15#include "tempfile.h"16#include "alias.h"17
18static int git_gpg_config(const char *, const char *,19const struct config_context *, void *);20
21static void gpg_interface_lazy_init(void)22{
23static int done;24
25if (done)26return;27done = 1;28git_config(git_gpg_config, NULL);29}
30
31static char *configured_signing_key;32static char *ssh_default_key_command;33static char *ssh_allowed_signers;34static char *ssh_revocation_file;35static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;36
37struct gpg_format {38const char *name;39const char *program;40const char **verify_args;41const char **sigs;42int (*verify_signed_buffer)(struct signature_check *sigc,43struct gpg_format *fmt,44const char *signature,45size_t signature_size);46int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature,47const char *signing_key);48const char *(*get_default_key)(void);49const char *(*get_key_id)(void);50};51
52static const char *openpgp_verify_args[] = {53"--keyid-format=long",54NULL55};56static const char *openpgp_sigs[] = {57"-----BEGIN PGP SIGNATURE-----",58"-----BEGIN PGP MESSAGE-----",59NULL60};61
62static const char *x509_verify_args[] = {63NULL64};65static const char *x509_sigs[] = {66"-----BEGIN SIGNED MESSAGE-----",67NULL68};69
70static const char *ssh_verify_args[] = { NULL };71static const char *ssh_sigs[] = {72"-----BEGIN SSH SIGNATURE-----",73NULL74};75
76static int verify_gpg_signed_buffer(struct signature_check *sigc,77struct gpg_format *fmt,78const char *signature,79size_t signature_size);80static int verify_ssh_signed_buffer(struct signature_check *sigc,81struct gpg_format *fmt,82const char *signature,83size_t signature_size);84static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,85const char *signing_key);86static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,87const char *signing_key);88
89static const char *get_default_ssh_signing_key(void);90
91static const char *get_ssh_key_id(void);92
93static struct gpg_format gpg_format[] = {94{95.name = "openpgp",96.program = "gpg",97.verify_args = openpgp_verify_args,98.sigs = openpgp_sigs,99.verify_signed_buffer = verify_gpg_signed_buffer,100.sign_buffer = sign_buffer_gpg,101.get_default_key = NULL,102.get_key_id = NULL,103},104{105.name = "x509",106.program = "gpgsm",107.verify_args = x509_verify_args,108.sigs = x509_sigs,109.verify_signed_buffer = verify_gpg_signed_buffer,110.sign_buffer = sign_buffer_gpg,111.get_default_key = NULL,112.get_key_id = NULL,113},114{115.name = "ssh",116.program = "ssh-keygen",117.verify_args = ssh_verify_args,118.sigs = ssh_sigs,119.verify_signed_buffer = verify_ssh_signed_buffer,120.sign_buffer = sign_buffer_ssh,121.get_default_key = get_default_ssh_signing_key,122.get_key_id = get_ssh_key_id,123},124};125
126static struct gpg_format *use_format = &gpg_format[0];127
128static struct gpg_format *get_format_by_name(const char *str)129{
130int i;131
132for (i = 0; i < ARRAY_SIZE(gpg_format); i++)133if (!strcmp(gpg_format[i].name, str))134return gpg_format + i;135return NULL;136}
137
138static struct gpg_format *get_format_by_sig(const char *sig)139{
140int i, j;141
142for (i = 0; i < ARRAY_SIZE(gpg_format); i++)143for (j = 0; gpg_format[i].sigs[j]; j++)144if (starts_with(sig, gpg_format[i].sigs[j]))145return gpg_format + i;146return NULL;147}
148
149void signature_check_clear(struct signature_check *sigc)150{
151FREE_AND_NULL(sigc->payload);152FREE_AND_NULL(sigc->output);153FREE_AND_NULL(sigc->gpg_status);154FREE_AND_NULL(sigc->signer);155FREE_AND_NULL(sigc->key);156FREE_AND_NULL(sigc->fingerprint);157FREE_AND_NULL(sigc->primary_key_fingerprint);158}
159
160/* An exclusive status -- only one of them can appear in output */
161#define GPG_STATUS_EXCLUSIVE (1<<0)162/* The status includes key identifier */
163#define GPG_STATUS_KEYID (1<<1)164/* The status includes user identifier */
165#define GPG_STATUS_UID (1<<2)166/* The status includes key fingerprints */
167#define GPG_STATUS_FINGERPRINT (1<<3)168/* The status includes trust level */
169#define GPG_STATUS_TRUST_LEVEL (1<<4)170
171/* Short-hand for standard exclusive *SIG status with keyid & UID */
172#define GPG_STATUS_STDSIG (GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID|GPG_STATUS_UID)173
174static struct {175char result;176const char *check;177unsigned int flags;178} sigcheck_gpg_status[] = {179{ 'G', "GOODSIG ", GPG_STATUS_STDSIG },180{ 'B', "BADSIG ", GPG_STATUS_STDSIG },181{ 'E', "ERRSIG ", GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID },182{ 'X', "EXPSIG ", GPG_STATUS_STDSIG },183{ 'Y', "EXPKEYSIG ", GPG_STATUS_STDSIG },184{ 'R', "REVKEYSIG ", GPG_STATUS_STDSIG },185{ 0, "VALIDSIG ", GPG_STATUS_FINGERPRINT },186{ 0, "TRUST_", GPG_STATUS_TRUST_LEVEL },187};188
189/* Keep the order same as enum signature_trust_level */
190static struct sigcheck_gpg_trust_level {191const char *key;192const char *display_key;193enum signature_trust_level value;194} sigcheck_gpg_trust_level[] = {195{ "UNDEFINED", "undefined", TRUST_UNDEFINED },196{ "NEVER", "never", TRUST_NEVER },197{ "MARGINAL", "marginal", TRUST_MARGINAL },198{ "FULLY", "fully", TRUST_FULLY },199{ "ULTIMATE", "ultimate", TRUST_ULTIMATE },200};201
202static void replace_cstring(char **field, const char *line, const char *next)203{
204free(*field);205
206if (line && next)207*field = xmemdupz(line, next - line);208else209*field = NULL;210}
211
212static int parse_gpg_trust_level(const char *level,213enum signature_trust_level *res)214{
215size_t i;216
217for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_trust_level); i++) {218if (!strcmp(sigcheck_gpg_trust_level[i].key, level)) {219*res = sigcheck_gpg_trust_level[i].value;220return 0;221}222}223return 1;224}
225
226static void parse_gpg_output(struct signature_check *sigc)227{
228const char *buf = sigc->gpg_status;229const char *line, *next;230int i, j;231int seen_exclusive_status = 0;232
233/* Iterate over all lines */234for (line = buf; *line; line = strchrnul(line+1, '\n')) {235while (*line == '\n')236line++;237if (!*line)238break;239
240/* Skip lines that don't start with GNUPG status */241if (!skip_prefix(line, "[GNUPG:] ", &line))242continue;243
244/* Iterate over all search strings */245for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {246if (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {247/*248* GOODSIG, BADSIG etc. can occur only once for
249* each signature. Therefore, if we had more
250* than one then we're dealing with multiple
251* signatures. We don't support them
252* currently, and they're rather hard to
253* create, so something is likely fishy and we
254* should reject them altogether.
255*/
256if (sigcheck_gpg_status[i].flags & GPG_STATUS_EXCLUSIVE) {257if (seen_exclusive_status++)258goto error;259}260
261if (sigcheck_gpg_status[i].result)262sigc->result = sigcheck_gpg_status[i].result;263/* Do we have key information? */264if (sigcheck_gpg_status[i].flags & GPG_STATUS_KEYID) {265next = strchrnul(line, ' ');266replace_cstring(&sigc->key, line, next);267/* Do we have signer information? */268if (*next && (sigcheck_gpg_status[i].flags & GPG_STATUS_UID)) {269line = next + 1;270next = strchrnul(line, '\n');271replace_cstring(&sigc->signer, line, next);272}273}274
275/* Do we have trust level? */276if (sigcheck_gpg_status[i].flags & GPG_STATUS_TRUST_LEVEL) {277/*278* GPG v1 and v2 differs in how the
279* TRUST_ lines are written. Some
280* trust lines contain no additional
281* space-separated information for v1.
282*/
283size_t trust_size = strcspn(line, " \n");284char *trust = xmemdupz(line, trust_size);285
286if (parse_gpg_trust_level(trust, &sigc->trust_level)) {287free(trust);288goto error;289}290free(trust);291}292
293/* Do we have fingerprint? */294if (sigcheck_gpg_status[i].flags & GPG_STATUS_FINGERPRINT) {295const char *limit;296char **field;297
298next = strchrnul(line, ' ');299replace_cstring(&sigc->fingerprint, line, next);300
301/*302* Skip interim fields. The search is
303* limited to the same line since only
304* OpenPGP signatures has a field with
305* the primary fingerprint.
306*/
307limit = strchrnul(line, '\n');308for (j = 9; j > 0; j--) {309if (!*next || limit <= next)310break;311line = next + 1;312next = strchrnul(line, ' ');313}314
315field = &sigc->primary_key_fingerprint;316if (!j) {317next = strchrnul(line, '\n');318replace_cstring(field, line, next);319} else {320replace_cstring(field, NULL, NULL);321}322}323
324break;325}326}327}328return;329
330error:331sigc->result = 'E';332/* Clear partial data to avoid confusion */333FREE_AND_NULL(sigc->primary_key_fingerprint);334FREE_AND_NULL(sigc->fingerprint);335FREE_AND_NULL(sigc->signer);336FREE_AND_NULL(sigc->key);337}
338
339static int verify_gpg_signed_buffer(struct signature_check *sigc,340struct gpg_format *fmt,341const char *signature,342size_t signature_size)343{
344struct child_process gpg = CHILD_PROCESS_INIT;345struct tempfile *temp;346int ret;347struct strbuf gpg_stdout = STRBUF_INIT;348struct strbuf gpg_stderr = STRBUF_INIT;349
350temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");351if (!temp)352return error_errno(_("could not create temporary file"));353if (write_in_full(temp->fd, signature, signature_size) < 0 ||354close_tempfile_gently(temp) < 0) {355error_errno(_("failed writing detached signature to '%s'"),356temp->filename.buf);357delete_tempfile(&temp);358return -1;359}360
361strvec_push(&gpg.args, fmt->program);362strvec_pushv(&gpg.args, fmt->verify_args);363strvec_pushl(&gpg.args,364"--status-fd=1",365"--verify", temp->filename.buf, "-",366NULL);367
368sigchain_push(SIGPIPE, SIG_IGN);369ret = pipe_command(&gpg, sigc->payload, sigc->payload_len, &gpg_stdout, 0,370&gpg_stderr, 0);371sigchain_pop(SIGPIPE);372
373delete_tempfile(&temp);374
375ret |= !strstr(gpg_stdout.buf, "\n[GNUPG:] GOODSIG ");376sigc->output = strbuf_detach(&gpg_stderr, NULL);377sigc->gpg_status = strbuf_detach(&gpg_stdout, NULL);378
379parse_gpg_output(sigc);380
381strbuf_release(&gpg_stdout);382strbuf_release(&gpg_stderr);383
384return ret;385}
386
387static void parse_ssh_output(struct signature_check *sigc)388{
389const char *line, *principal, *search;390char *to_free;391char *key = NULL;392
393/*394* ssh-keygen output should be:
395* Good "git" signature for PRINCIPAL with RSA key SHA256:FINGERPRINT
396*
397* or for valid but unknown keys:
398* Good "git" signature with RSA key SHA256:FINGERPRINT
399*
400* Note that "PRINCIPAL" can contain whitespace, "RSA" and
401* "SHA256" part could be a different token that names of
402* the algorithms used, and "FINGERPRINT" is a hexadecimal
403* string. By finding the last occurence of " with ", we can
404* reliably parse out the PRINCIPAL.
405*/
406sigc->result = 'B';407sigc->trust_level = TRUST_NEVER;408
409line = to_free = xmemdupz(sigc->output, strcspn(sigc->output, "\n"));410
411if (skip_prefix(line, "Good \"git\" signature for ", &line)) {412/* Search for the last "with" to get the full principal */413principal = line;414do {415search = strstr(line, " with ");416if (search)417line = search + 1;418} while (search != NULL);419if (line == principal)420goto cleanup;421
422/* Valid signature and known principal */423sigc->result = 'G';424sigc->trust_level = TRUST_FULLY;425sigc->signer = xmemdupz(principal, line - principal - 1);426} else if (skip_prefix(line, "Good \"git\" signature with ", &line)) {427/* Valid signature, but key unknown */428sigc->result = 'G';429sigc->trust_level = TRUST_UNDEFINED;430} else {431goto cleanup;432}433
434key = strstr(line, "key ");435if (key) {436sigc->fingerprint = xstrdup(strstr(line, "key ") + 4);437sigc->key = xstrdup(sigc->fingerprint);438} else {439/*440* Output did not match what we expected
441* Treat the signature as bad
442*/
443sigc->result = 'B';444}445
446cleanup:447free(to_free);448}
449
450static int verify_ssh_signed_buffer(struct signature_check *sigc,451struct gpg_format *fmt,452const char *signature,453size_t signature_size)454{
455struct child_process ssh_keygen = CHILD_PROCESS_INIT;456struct tempfile *buffer_file;457int ret = -1;458const char *line;459char *principal;460struct strbuf ssh_principals_out = STRBUF_INIT;461struct strbuf ssh_principals_err = STRBUF_INIT;462struct strbuf ssh_keygen_out = STRBUF_INIT;463struct strbuf ssh_keygen_err = STRBUF_INIT;464struct strbuf verify_time = STRBUF_INIT;465const struct date_mode verify_date_mode = {466.type = DATE_STRFTIME,467.strftime_fmt = "%Y%m%d%H%M%S",468/* SSH signing key validity has no timezone information - Use the local timezone */469.local = 1,470};471
472if (!ssh_allowed_signers) {473error(_("gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification"));474return -1;475}476
477buffer_file = mks_tempfile_t(".git_vtag_tmpXXXXXX");478if (!buffer_file)479return error_errno(_("could not create temporary file"));480if (write_in_full(buffer_file->fd, signature, signature_size) < 0 ||481close_tempfile_gently(buffer_file) < 0) {482error_errno(_("failed writing detached signature to '%s'"),483buffer_file->filename.buf);484delete_tempfile(&buffer_file);485return -1;486}487
488if (sigc->payload_timestamp)489strbuf_addf(&verify_time, "-Overify-time=%s",490show_date(sigc->payload_timestamp, 0, verify_date_mode));491
492/* Find the principal from the signers */493strvec_pushl(&ssh_keygen.args, fmt->program,494"-Y", "find-principals",495"-f", ssh_allowed_signers,496"-s", buffer_file->filename.buf,497verify_time.buf,498NULL);499ret = pipe_command(&ssh_keygen, NULL, 0, &ssh_principals_out, 0,500&ssh_principals_err, 0);501if (ret && strstr(ssh_principals_err.buf, "usage:")) {502error(_("ssh-keygen -Y find-principals/verify is needed for ssh signature verification (available in openssh version 8.2p1+)"));503goto out;504}505if (ret || !ssh_principals_out.len) {506/*507* We did not find a matching principal in the allowedSigners
508* Check without validation
509*/
510child_process_init(&ssh_keygen);511strvec_pushl(&ssh_keygen.args, fmt->program,512"-Y", "check-novalidate",513"-n", "git",514"-s", buffer_file->filename.buf,515verify_time.buf,516NULL);517pipe_command(&ssh_keygen, sigc->payload, sigc->payload_len,518&ssh_keygen_out, 0, &ssh_keygen_err, 0);519
520/*521* Fail on unknown keys
522* we still call check-novalidate to display the signature info
523*/
524ret = -1;525} else {526/* Check every principal we found (one per line) */527const char *next;528for (line = ssh_principals_out.buf;529*line;530line = next) {531const char *end_of_text;532
533next = end_of_text = strchrnul(line, '\n');534
535/* Did we find a LF, and did we have CR before it? */536if (*end_of_text &&537line < end_of_text &&538end_of_text[-1] == '\r')539end_of_text--;540
541/* Unless we hit NUL, skip over the LF we found */542if (*next)543next++;544
545/* Not all lines are data. Skip empty ones */546if (line == end_of_text)547continue;548
549/* We now know we have an non-empty line. Process it */550principal = xmemdupz(line, end_of_text - line);551
552child_process_init(&ssh_keygen);553strbuf_release(&ssh_keygen_out);554strbuf_release(&ssh_keygen_err);555strvec_push(&ssh_keygen.args, fmt->program);556/*557* We found principals
558* Try with each until we find a match
559*/
560strvec_pushl(&ssh_keygen.args, "-Y", "verify",561"-n", "git",562"-f", ssh_allowed_signers,563"-I", principal,564"-s", buffer_file->filename.buf,565verify_time.buf,566NULL);567
568if (ssh_revocation_file) {569if (file_exists(ssh_revocation_file)) {570strvec_pushl(&ssh_keygen.args, "-r",571ssh_revocation_file, NULL);572} else {573warning(_("ssh signing revocation file configured but not found: %s"),574ssh_revocation_file);575}576}577
578sigchain_push(SIGPIPE, SIG_IGN);579ret = pipe_command(&ssh_keygen, sigc->payload, sigc->payload_len,580&ssh_keygen_out, 0, &ssh_keygen_err, 0);581sigchain_pop(SIGPIPE);582
583FREE_AND_NULL(principal);584
585if (!ret)586ret = !starts_with(ssh_keygen_out.buf, "Good");587
588if (!ret)589break;590}591}592
593strbuf_stripspace(&ssh_keygen_out, NULL);594strbuf_stripspace(&ssh_keygen_err, NULL);595/* Add stderr outputs to show the user actual ssh-keygen errors */596strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len);597strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len);598sigc->output = strbuf_detach(&ssh_keygen_out, NULL);599sigc->gpg_status = xstrdup(sigc->output);600
601parse_ssh_output(sigc);602
603out:604if (buffer_file)605delete_tempfile(&buffer_file);606strbuf_release(&ssh_principals_out);607strbuf_release(&ssh_principals_err);608strbuf_release(&ssh_keygen_out);609strbuf_release(&ssh_keygen_err);610strbuf_release(&verify_time);611
612return ret;613}
614
615static int parse_payload_metadata(struct signature_check *sigc)616{
617const char *ident_line = NULL;618size_t ident_len;619struct ident_split ident;620const char *signer_header;621
622switch (sigc->payload_type) {623case SIGNATURE_PAYLOAD_COMMIT:624signer_header = "committer";625break;626case SIGNATURE_PAYLOAD_TAG:627signer_header = "tagger";628break;629case SIGNATURE_PAYLOAD_UNDEFINED:630case SIGNATURE_PAYLOAD_PUSH_CERT:631/* Ignore payloads we don't want to parse */632return 0;633default:634BUG("invalid value for sigc->payload_type");635}636
637ident_line = find_commit_header(sigc->payload, signer_header, &ident_len);638if (!ident_line || !ident_len)639return 1;640
641if (split_ident_line(&ident, ident_line, ident_len))642return 1;643
644if (!sigc->payload_timestamp && ident.date_begin && ident.date_end)645sigc->payload_timestamp = parse_timestamp(ident.date_begin, NULL, 10);646
647return 0;648}
649
650int check_signature(struct signature_check *sigc,651const char *signature, size_t slen)652{
653struct gpg_format *fmt;654int status;655
656gpg_interface_lazy_init();657
658sigc->result = 'N';659sigc->trust_level = TRUST_UNDEFINED;660
661fmt = get_format_by_sig(signature);662if (!fmt)663die(_("bad/incompatible signature '%s'"), signature);664
665if (parse_payload_metadata(sigc))666return 1;667
668status = fmt->verify_signed_buffer(sigc, fmt, signature, slen);669
670if (status && !sigc->output)671return !!status;672
673status |= sigc->result != 'G';674status |= sigc->trust_level < configured_min_trust_level;675
676return !!status;677}
678
679void print_signature_buffer(const struct signature_check *sigc, unsigned flags)680{
681const char *output = flags & GPG_VERIFY_RAW ? sigc->gpg_status :682sigc->output;683
684if (flags & GPG_VERIFY_VERBOSE && sigc->payload)685fwrite(sigc->payload, 1, sigc->payload_len, stdout);686
687if (output)688fputs(output, stderr);689}
690
691size_t parse_signed_buffer(const char *buf, size_t size)692{
693size_t len = 0;694size_t match = size;695while (len < size) {696const char *eol;697
698if (get_format_by_sig(buf + len))699match = len;700
701eol = memchr(buf + len, '\n', size - len);702len += eol ? eol - (buf + len) + 1 : size - len;703}704return match;705}
706
707int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct strbuf *signature)708{
709size_t match = parse_signed_buffer(buf, size);710if (match != size) {711strbuf_add(payload, buf, match);712remove_signature(payload);713strbuf_add(signature, buf + match, size - match);714return 1;715}716return 0;717}
718
719void set_signing_key(const char *key)720{
721gpg_interface_lazy_init();722
723free(configured_signing_key);724configured_signing_key = xstrdup(key);725}
726
727static int git_gpg_config(const char *var, const char *value,728const struct config_context *ctx UNUSED,729void *cb UNUSED)730{
731struct gpg_format *fmt = NULL;732const char *fmtname = NULL;733char *trust;734int ret;735
736if (!strcmp(var, "user.signingkey")) {737if (!value)738return config_error_nonbool(var);739set_signing_key(value);740return 0;741}742
743if (!strcmp(var, "gpg.format")) {744if (!value)745return config_error_nonbool(var);746fmt = get_format_by_name(value);747if (!fmt)748return error(_("invalid value for '%s': '%s'"),749var, value);750use_format = fmt;751return 0;752}753
754if (!strcmp(var, "gpg.mintrustlevel")) {755if (!value)756return config_error_nonbool(var);757
758trust = xstrdup_toupper(value);759ret = parse_gpg_trust_level(trust, &configured_min_trust_level);760free(trust);761
762if (ret)763return error(_("invalid value for '%s': '%s'"),764var, value);765return 0;766}767
768if (!strcmp(var, "gpg.ssh.defaultkeycommand"))769return git_config_string(&ssh_default_key_command, var, value);770
771if (!strcmp(var, "gpg.ssh.allowedsignersfile"))772return git_config_pathname(&ssh_allowed_signers, var, value);773
774if (!strcmp(var, "gpg.ssh.revocationfile"))775return git_config_pathname(&ssh_revocation_file, var, value);776
777if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))778fmtname = "openpgp";779
780if (!strcmp(var, "gpg.x509.program"))781fmtname = "x509";782
783if (!strcmp(var, "gpg.ssh.program"))784fmtname = "ssh";785
786if (fmtname) {787fmt = get_format_by_name(fmtname);788return git_config_string((char **) &fmt->program, var, value);789}790
791return 0;792}
793
794/*
795* Returns 1 if `string` contains a literal ssh key, 0 otherwise
796* `key` will be set to the start of the actual key if a prefix is present.
797*/
798static int is_literal_ssh_key(const char *string, const char **key)799{
800if (skip_prefix(string, "key::", key))801return 1;802if (starts_with(string, "ssh-")) {803*key = string;804return 1;805}806return 0;807}
808
809static char *get_ssh_key_fingerprint(const char *signing_key)810{
811struct child_process ssh_keygen = CHILD_PROCESS_INIT;812int ret = -1;813struct strbuf fingerprint_stdout = STRBUF_INIT;814struct strbuf **fingerprint;815char *fingerprint_ret;816const char *literal_key = NULL;817
818/*819* With SSH Signing this can contain a filename or a public key
820* For textual representation we usually want a fingerprint
821*/
822if (is_literal_ssh_key(signing_key, &literal_key)) {823strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf", "-", NULL);824ret = pipe_command(&ssh_keygen, literal_key,825strlen(literal_key), &fingerprint_stdout, 0,826NULL, 0);827} else {828strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf",829configured_signing_key, NULL);830ret = pipe_command(&ssh_keygen, NULL, 0, &fingerprint_stdout, 0,831NULL, 0);832}833
834if (!!ret)835die_errno(_("failed to get the ssh fingerprint for key '%s'"),836signing_key);837
838fingerprint = strbuf_split_max(&fingerprint_stdout, ' ', 3);839if (!fingerprint[1])840die_errno(_("failed to get the ssh fingerprint for key '%s'"),841signing_key);842
843fingerprint_ret = strbuf_detach(fingerprint[1], NULL);844strbuf_list_free(fingerprint);845strbuf_release(&fingerprint_stdout);846return fingerprint_ret;847}
848
849/* Returns the first public key from an ssh-agent to use for signing */
850static const char *get_default_ssh_signing_key(void)851{
852struct child_process ssh_default_key = CHILD_PROCESS_INIT;853int ret = -1;854struct strbuf key_stdout = STRBUF_INIT, key_stderr = STRBUF_INIT;855struct strbuf **keys;856char *key_command = NULL;857const char **argv;858int n;859char *default_key = NULL;860const char *literal_key = NULL;861
862if (!ssh_default_key_command)863die(_("either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"));864
865key_command = xstrdup(ssh_default_key_command);866n = split_cmdline(key_command, &argv);867
868if (n < 0)869die("malformed build-time gpg.ssh.defaultKeyCommand: %s",870split_cmdline_strerror(n));871
872strvec_pushv(&ssh_default_key.args, argv);873ret = pipe_command(&ssh_default_key, NULL, 0, &key_stdout, 0,874&key_stderr, 0);875
876if (!ret) {877keys = strbuf_split_max(&key_stdout, '\n', 2);878if (keys[0] && is_literal_ssh_key(keys[0]->buf, &literal_key)) {879/*880* We only use `is_literal_ssh_key` here to check validity
881* The prefix will be stripped when the key is used.
882*/
883default_key = strbuf_detach(keys[0], NULL);884} else {885warning(_("gpg.ssh.defaultKeyCommand succeeded but returned no keys: %s %s"),886key_stderr.buf, key_stdout.buf);887}888
889strbuf_list_free(keys);890} else {891warning(_("gpg.ssh.defaultKeyCommand failed: %s %s"),892key_stderr.buf, key_stdout.buf);893}894
895free(key_command);896free(argv);897strbuf_release(&key_stdout);898
899return default_key;900}
901
902static const char *get_ssh_key_id(void) {903return get_ssh_key_fingerprint(get_signing_key());904}
905
906/* Returns a textual but unique representation of the signing key */
907const char *get_signing_key_id(void)908{
909gpg_interface_lazy_init();910
911if (use_format->get_key_id) {912return use_format->get_key_id();913}914
915/* GPG/GPGSM only store a key id on this variable */916return get_signing_key();917}
918
919const char *get_signing_key(void)920{
921gpg_interface_lazy_init();922
923if (configured_signing_key)924return configured_signing_key;925if (use_format->get_default_key) {926return use_format->get_default_key();927}928
929return git_committer_info(IDENT_STRICT | IDENT_NO_DATE);930}
931
932const char *gpg_trust_level_to_str(enum signature_trust_level level)933{
934struct sigcheck_gpg_trust_level *trust;935
936if (level < 0 || level >= ARRAY_SIZE(sigcheck_gpg_trust_level))937BUG("invalid trust level requested %d", level);938
939trust = &sigcheck_gpg_trust_level[level];940if (trust->value != level)941BUG("sigcheck_gpg_trust_level[] unsorted");942
943return sigcheck_gpg_trust_level[level].display_key;944}
945
946int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)947{
948gpg_interface_lazy_init();949
950return use_format->sign_buffer(buffer, signature, signing_key);951}
952
953/*
954* Strip CR from the line endings, in case we are on Windows.
955* NEEDSWORK: make it trim only CRs before LFs and rename
956*/
957static void remove_cr_after(struct strbuf *buffer, size_t offset)958{
959size_t i, j;960
961for (i = j = offset; i < buffer->len; i++) {962if (buffer->buf[i] != '\r') {963if (i != j)964buffer->buf[j] = buffer->buf[i];965j++;966}967}968strbuf_setlen(buffer, j);969}
970
971static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,972const char *signing_key)973{
974struct child_process gpg = CHILD_PROCESS_INIT;975int ret;976size_t bottom;977const char *cp;978struct strbuf gpg_status = STRBUF_INIT;979
980strvec_pushl(&gpg.args,981use_format->program,982"--status-fd=2",983"-bsau", signing_key,984NULL);985
986bottom = signature->len;987
988/*989* When the username signingkey is bad, program could be terminated
990* because gpg exits without reading and then write gets SIGPIPE.
991*/
992sigchain_push(SIGPIPE, SIG_IGN);993ret = pipe_command(&gpg, buffer->buf, buffer->len,994signature, 1024, &gpg_status, 0);995sigchain_pop(SIGPIPE);996
997for (cp = gpg_status.buf;998cp && (cp = strstr(cp, "[GNUPG:] SIG_CREATED "));999cp++) {1000if (cp == gpg_status.buf || cp[-1] == '\n')1001break; /* found */1002}1003ret |= !cp;1004if (ret) {1005error(_("gpg failed to sign the data:\n%s"),1006gpg_status.len ? gpg_status.buf : "(no gpg output)");1007strbuf_release(&gpg_status);1008return -1;1009}1010strbuf_release(&gpg_status);1011
1012/* Strip CR from the line endings, in case we are on Windows. */1013remove_cr_after(signature, bottom);1014
1015return 0;1016}
1017
1018static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,1019const char *signing_key)1020{
1021struct child_process signer = CHILD_PROCESS_INIT;1022int ret = -1;1023size_t bottom, keylen;1024struct strbuf signer_stderr = STRBUF_INIT;1025struct tempfile *key_file = NULL, *buffer_file = NULL;1026char *ssh_signing_key_file = NULL;1027struct strbuf ssh_signature_filename = STRBUF_INIT;1028const char *literal_key = NULL;1029int literal_ssh_key = 0;1030
1031if (!signing_key || signing_key[0] == '\0')1032return error(1033_("user.signingKey needs to be set for ssh signing"));1034
1035if (is_literal_ssh_key(signing_key, &literal_key)) {1036/* A literal ssh key */1037literal_ssh_key = 1;1038key_file = mks_tempfile_t(".git_signing_key_tmpXXXXXX");1039if (!key_file)1040return error_errno(1041_("could not create temporary file"));1042keylen = strlen(literal_key);1043if (write_in_full(key_file->fd, literal_key, keylen) < 0 ||1044close_tempfile_gently(key_file) < 0) {1045error_errno(_("failed writing ssh signing key to '%s'"),1046key_file->filename.buf);1047goto out;1048}1049ssh_signing_key_file = strbuf_detach(&key_file->filename, NULL);1050} else {1051/* We assume a file */1052ssh_signing_key_file = interpolate_path(signing_key, 1);1053}1054
1055buffer_file = mks_tempfile_t(".git_signing_buffer_tmpXXXXXX");1056if (!buffer_file) {1057error_errno(_("could not create temporary file"));1058goto out;1059}1060
1061if (write_in_full(buffer_file->fd, buffer->buf, buffer->len) < 0 ||1062close_tempfile_gently(buffer_file) < 0) {1063error_errno(_("failed writing ssh signing key buffer to '%s'"),1064buffer_file->filename.buf);1065goto out;1066}1067
1068strvec_pushl(&signer.args, use_format->program,1069"-Y", "sign",1070"-n", "git",1071"-f", ssh_signing_key_file,1072NULL);1073if (literal_ssh_key)1074strvec_push(&signer.args, "-U");1075strvec_push(&signer.args, buffer_file->filename.buf);1076
1077sigchain_push(SIGPIPE, SIG_IGN);1078ret = pipe_command(&signer, NULL, 0, NULL, 0, &signer_stderr, 0);1079sigchain_pop(SIGPIPE);1080
1081if (ret) {1082if (strstr(signer_stderr.buf, "usage:"))1083error(_("ssh-keygen -Y sign is needed for ssh signing (available in openssh version 8.2p1+)"));1084
1085ret = error("%s", signer_stderr.buf);1086goto out;1087}1088
1089bottom = signature->len;1090
1091strbuf_addbuf(&ssh_signature_filename, &buffer_file->filename);1092strbuf_addstr(&ssh_signature_filename, ".sig");1093if (strbuf_read_file(signature, ssh_signature_filename.buf, 0) < 0) {1094ret = error_errno(1095_("failed reading ssh signing data buffer from '%s'"),1096ssh_signature_filename.buf);1097goto out;1098}1099/* Strip CR from the line endings, in case we are on Windows. */1100remove_cr_after(signature, bottom);1101
1102out:1103if (key_file)1104delete_tempfile(&key_file);1105if (buffer_file)1106delete_tempfile(&buffer_file);1107if (ssh_signature_filename.len)1108unlink_or_warn(ssh_signature_filename.buf);1109strbuf_release(&signer_stderr);1110strbuf_release(&ssh_signature_filename);1111FREE_AND_NULL(ssh_signing_key_file);1112return ret;1113}
1114