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 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");