2 * Copyright (c) 2022 Josh Rickmar <jrick@zettaport.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <sys/types.h>
20 #include <sys/socket.h>
31 #include "got_error.h"
33 #include "got_object.h"
34 #include "got_opentemp.h"
37 #include "got_compat.h"
41 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
45 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
48 #ifndef GOT_TAG_PATH_SSH_KEYGEN
49 #define GOT_TAG_PATH_SSH_KEYGEN "/usr/bin/ssh-keygen"
52 #ifndef GOT_TAG_PATH_SIGNIFY
53 #define GOT_TAG_PATH_SIGNIFY "/usr/bin/signify"
56 const struct got_error *
57 got_sigs_apply_unveil()
59 if (unveil(GOT_TAG_PATH_SSH_KEYGEN, "x") != 0) {
60 return got_error_from_errno2("unveil",
61 GOT_TAG_PATH_SSH_KEYGEN);
63 if (unveil(GOT_TAG_PATH_SIGNIFY, "x") != 0) {
64 return got_error_from_errno2("unveil",
65 GOT_TAG_PATH_SIGNIFY);
71 const struct got_error *
72 got_sigs_sign_tag_ssh(pid_t *newpid, int *in_fd, int *out_fd,
73 const char* key_file, int verbosity)
75 const struct got_error *error = NULL;
76 int pid, in_pfd[2], out_pfd[2];
84 argv[i++] = GOT_TAG_PATH_SSH_KEYGEN;
94 /* ssh(1) allows up to 3 "-v" options. */
95 for (j = 0; j < MIN(3, verbosity); j++)
99 assert(i <= nitems(argv));
101 if (pipe(in_pfd) == -1)
102 return got_error_from_errno("pipe");
103 if (pipe(out_pfd) == -1)
104 return got_error_from_errno("pipe");
108 error = got_error_from_errno("fork");
114 } else if (pid == 0) {
115 if (close(in_pfd[1]) == -1)
117 if (close(out_pfd[1]) == -1)
119 if (dup2(in_pfd[0], 0) == -1)
121 if (dup2(out_pfd[0], 1) == -1)
123 if (execv(GOT_TAG_PATH_SSH_KEYGEN, (char **const)argv) == -1)
125 abort(); /* not reached */
127 if (close(in_pfd[0]) == -1)
128 return got_error_from_errno("close");
129 if (close(out_pfd[0]) == -1)
130 return got_error_from_errno("close");
133 *out_fd = out_pfd[1];
138 signer_identity(const char *tagger)
142 lt = strstr(tagger, " <");
143 gt = strrchr(tagger, '>');
144 if (lt && gt && lt+1 < gt)
145 return strndup(lt+2, gt-lt-2);
149 static const char* BEGIN_SSH_SIG = "-----BEGIN SSH SIGNATURE-----\n";
150 static const char* END_SSH_SIG = "-----END SSH SIGNATURE-----\n";
153 got_sigs_get_tagmsg_ssh_signature(const char *tagmsg)
155 const char *s = tagmsg, *begin = NULL, *end = NULL;
157 while ((s = strstr(s, BEGIN_SSH_SIG)) != NULL) {
159 s += strlen(BEGIN_SSH_SIG);
162 end = strstr(begin+strlen(BEGIN_SSH_SIG), END_SSH_SIG);
165 return (end[strlen(END_SSH_SIG)] == '\0') ? begin : NULL;
168 static const struct got_error *
169 got_tag_write_signed_data(BUF *buf, struct got_tag_object *tag,
170 const char *start_sig)
172 const struct got_error *err = NULL;
173 struct got_object_id *id;
180 id = got_object_tag_get_object_id(tag);
181 err = got_object_id_str(&id_str, id);
185 const char *type_label = NULL;
186 switch (got_object_tag_get_object_type(tag)) {
187 case GOT_OBJ_TYPE_BLOB:
188 type_label = GOT_OBJ_LABEL_BLOB;
190 case GOT_OBJ_TYPE_TREE:
191 type_label = GOT_OBJ_LABEL_TREE;
193 case GOT_OBJ_TYPE_COMMIT:
194 type_label = GOT_OBJ_LABEL_COMMIT;
196 case GOT_OBJ_TYPE_TAG:
197 type_label = GOT_OBJ_LABEL_TAG;
202 got_date_format_gmtoff(gmtoff, sizeof(gmtoff),
203 got_object_tag_get_tagger_gmtoff(tag));
204 if (asprintf(&tagger, "%s %lld %s", got_object_tag_get_tagger(tag),
205 (long long)got_object_tag_get_tagger_time(tag), gmtoff) == -1) {
206 err = got_error_from_errno("asprintf");
210 err = buf_puts(&len, buf, GOT_TAG_LABEL_OBJECT);
213 err = buf_puts(&len, buf, id_str);
216 err = buf_putc(buf, '\n');
219 err = buf_puts(&len, buf, GOT_TAG_LABEL_TYPE);
222 err = buf_puts(&len, buf, type_label);
225 err = buf_putc(buf, '\n');
228 err = buf_puts(&len, buf, GOT_TAG_LABEL_TAG);
231 err = buf_puts(&len, buf, got_object_tag_get_name(tag));
234 err = buf_putc(buf, '\n');
237 err = buf_puts(&len, buf, GOT_TAG_LABEL_TAGGER);
240 err = buf_puts(&len, buf, tagger);
243 err = buf_puts(&len, buf, "\n");
246 tagmsg = got_object_tag_get_message(tag);
247 err = buf_append(&len, buf, tagmsg, start_sig-tagmsg);
257 const struct got_error *
258 got_sigs_verify_tag_ssh(char **msg, struct got_tag_object *tag,
259 const char *start_sig, const char* allowed_signers, const char* revoked,
262 const struct got_error *error = NULL;
263 const char* argv[17];
264 int pid, status, in_pfd[2], out_pfd[2];
265 char* parsed_identity = NULL;
266 const char *identity;
267 char* tmppath = NULL;
268 FILE *tmpsig, *out = NULL;
274 error = got_opentemp_named(&tmppath, &tmpsig,
275 GOT_TMPDIR_STR "/got-tagsig");
279 identity = got_object_tag_get_tagger(tag);
280 parsed_identity = signer_identity(identity);
281 if (parsed_identity != NULL)
282 identity = parsed_identity;
284 if (fputs(start_sig, tmpsig) == EOF) {
285 error = got_error_from_errno("fputs");
288 if (fflush(tmpsig) == EOF) {
289 error = got_error_from_errno("fflush");
293 error = buf_alloc(&buf, 0);
296 error = got_tag_write_signed_data(buf, tag, start_sig);
300 argv[i++] = GOT_TAG_PATH_SSH_KEYGEN;
302 argv[i++] = "verify";
304 argv[i++] = allowed_signers;
306 argv[i++] = identity;
316 /* ssh(1) allows up to 3 "-v" options. */
317 for (j = 0; j < MIN(3, verbosity); j++)
321 assert(i <= nitems(argv));
323 if (pipe(in_pfd) == -1) {
324 error = got_error_from_errno("pipe");
327 if (pipe(out_pfd) == -1) {
328 error = got_error_from_errno("pipe");
334 error = got_error_from_errno("fork");
340 } else if (pid == 0) {
341 if (close(in_pfd[1]) == -1)
343 if (close(out_pfd[1]) == -1)
345 if (dup2(in_pfd[0], 0) == -1)
347 if (dup2(out_pfd[0], 1) == -1)
349 if (execv(GOT_TAG_PATH_SSH_KEYGEN, (char **const)argv) == -1)
351 abort(); /* not reached */
353 if (close(in_pfd[0]) == -1) {
354 error = got_error_from_errno("close");
357 if (close(out_pfd[0]) == -1) {
358 error = got_error_from_errno("close");
361 if (buf_write_fd(buf, in_pfd[1]) == -1) {
362 error = got_error_from_errno("write");
365 if (close(in_pfd[1]) == -1) {
366 error = got_error_from_errno("close");
369 if (waitpid(pid, &status, 0) == -1) {
370 error = got_error_from_errno("waitpid");
373 if (!WIFEXITED(status)) {
374 error = got_error(GOT_ERR_BAD_TAG_SIGNATURE);
378 out = fdopen(out_pfd[1], "r");
380 error = got_error_from_errno("fdopen");
383 error = buf_load(&buf, out);
386 error = buf_putc(buf, '\0');
389 if (close(out_pfd[1]) == -1) {
390 error = got_error_from_errno("close");
395 if (WEXITSTATUS(status) != 0)
396 error = got_error(GOT_ERR_BAD_TAG_SIGNATURE);
399 free(parsed_identity);
401 if (tmpsig && fclose(tmpsig) == EOF && error == NULL)
402 error = got_error_from_errno("fclose");
403 if (out && fclose(out) == EOF && error == NULL)
404 error = got_error_from_errno("fclose");