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.
17 #include "got_compat.h"
20 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/queue.h>
34 #include "got_error.h"
36 #include "got_object.h"
37 #include "got_opentemp.h"
43 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
47 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
50 #ifndef GOT_TAG_PATH_SSH_KEYGEN
51 #define GOT_TAG_PATH_SSH_KEYGEN "/usr/bin/ssh-keygen"
54 #ifndef GOT_TAG_PATH_SIGNIFY
55 #define GOT_TAG_PATH_SIGNIFY "/usr/bin/signify"
58 const struct got_error
*
59 got_sigs_apply_unveil(void)
61 if (unveil(GOT_TAG_PATH_SSH_KEYGEN
, "x") != 0) {
62 return got_error_from_errno2("unveil",
63 GOT_TAG_PATH_SSH_KEYGEN
);
65 if (unveil(GOT_TAG_PATH_SIGNIFY
, "x") != 0) {
66 return got_error_from_errno2("unveil",
67 GOT_TAG_PATH_SIGNIFY
);
73 const struct got_error
*
74 got_sigs_sign_tag_ssh(pid_t
*newpid
, int *in_fd
, int *out_fd
,
75 const char* key_file
, int verbosity
)
77 const struct got_error
*error
= NULL
;
78 int pid
, in_pfd
[2], out_pfd
[2];
86 argv
[i
++] = GOT_TAG_PATH_SSH_KEYGEN
;
96 /* ssh(1) allows up to 3 "-v" options. */
97 for (j
= 0; j
< MIN(3, verbosity
); j
++)
101 assert(i
<= nitems(argv
));
103 if (pipe(in_pfd
) == -1)
104 return got_error_from_errno("pipe");
105 if (pipe(out_pfd
) == -1)
106 return got_error_from_errno("pipe");
110 error
= got_error_from_errno("fork");
116 } else if (pid
== 0) {
117 if (close(in_pfd
[1]) == -1)
119 if (close(out_pfd
[0]) == -1)
121 if (dup2(in_pfd
[0], 0) == -1)
123 if (dup2(out_pfd
[1], 1) == -1)
125 if (execv(GOT_TAG_PATH_SSH_KEYGEN
, (char **const)argv
) == -1)
127 abort(); /* not reached */
129 if (close(in_pfd
[0]) == -1)
130 return got_error_from_errno("close");
131 if (close(out_pfd
[1]) == -1)
132 return got_error_from_errno("close");
135 *out_fd
= out_pfd
[0];
140 signer_identity(const char *tagger
)
144 lt
= strstr(tagger
, " <");
145 gt
= strrchr(tagger
, '>');
146 if (lt
&& gt
&& lt
+1 < gt
)
147 return strndup(lt
+2, gt
-lt
-2);
151 static const char* BEGIN_SSH_SIG
= "-----BEGIN SSH SIGNATURE-----\n";
152 static const char* END_SSH_SIG
= "-----END SSH SIGNATURE-----\n";
155 got_sigs_get_tagmsg_ssh_signature(const char *tagmsg
)
157 const char *s
= tagmsg
, *begin
= NULL
, *end
= NULL
;
159 while ((s
= strstr(s
, BEGIN_SSH_SIG
)) != NULL
) {
161 s
+= strlen(BEGIN_SSH_SIG
);
164 end
= strstr(begin
+strlen(BEGIN_SSH_SIG
), END_SSH_SIG
);
167 return (end
[strlen(END_SSH_SIG
)] == '\0') ? begin
: NULL
;
170 static const struct got_error
*
171 got_tag_write_signed_data(BUF
*buf
, struct got_tag_object
*tag
,
172 const char *start_sig
)
174 const struct got_error
*err
= NULL
;
175 struct got_object_id
*id
;
182 id
= got_object_tag_get_object_id(tag
);
183 err
= got_object_id_str(&id_str
, id
);
187 const char *type_label
= NULL
;
188 switch (got_object_tag_get_object_type(tag
)) {
189 case GOT_OBJ_TYPE_BLOB
:
190 type_label
= GOT_OBJ_LABEL_BLOB
;
192 case GOT_OBJ_TYPE_TREE
:
193 type_label
= GOT_OBJ_LABEL_TREE
;
195 case GOT_OBJ_TYPE_COMMIT
:
196 type_label
= GOT_OBJ_LABEL_COMMIT
;
198 case GOT_OBJ_TYPE_TAG
:
199 type_label
= GOT_OBJ_LABEL_TAG
;
204 got_date_format_gmtoff(gmtoff
, sizeof(gmtoff
),
205 got_object_tag_get_tagger_gmtoff(tag
));
206 if (asprintf(&tagger
, "%s %lld %s", got_object_tag_get_tagger(tag
),
207 (long long)got_object_tag_get_tagger_time(tag
), gmtoff
) == -1) {
208 err
= got_error_from_errno("asprintf");
212 err
= buf_puts(&len
, buf
, GOT_TAG_LABEL_OBJECT
);
215 err
= buf_puts(&len
, buf
, id_str
);
218 err
= buf_putc(buf
, '\n');
221 err
= buf_puts(&len
, buf
, GOT_TAG_LABEL_TYPE
);
224 err
= buf_puts(&len
, buf
, type_label
);
227 err
= buf_putc(buf
, '\n');
230 err
= buf_puts(&len
, buf
, GOT_TAG_LABEL_TAG
);
233 err
= buf_puts(&len
, buf
, got_object_tag_get_name(tag
));
236 err
= buf_putc(buf
, '\n');
239 err
= buf_puts(&len
, buf
, GOT_TAG_LABEL_TAGGER
);
242 err
= buf_puts(&len
, buf
, tagger
);
245 err
= buf_puts(&len
, buf
, "\n");
248 tagmsg
= got_object_tag_get_message(tag
);
249 err
= buf_append(&len
, buf
, tagmsg
, start_sig
-tagmsg
);
259 const struct got_error
*
260 got_sigs_verify_tag_ssh(char **msg
, struct got_tag_object
*tag
,
261 const char *start_sig
, const char* allowed_signers
, const char* revoked
,
264 const struct got_error
*error
= NULL
;
265 const char* argv
[17];
266 int pid
, status
, in_pfd
[2], out_pfd
[2];
267 char* parsed_identity
= NULL
;
268 const char *identity
;
269 char *tmppath
= NULL
;
276 error
= got_opentemp_named(&tmppath
, &tmpsig
,
277 GOT_TMPDIR_STR
"/got-tagsig", "");
281 identity
= got_object_tag_get_tagger(tag
);
282 parsed_identity
= signer_identity(identity
);
283 if (parsed_identity
!= NULL
)
284 identity
= parsed_identity
;
286 if (fputs(start_sig
, tmpsig
) == EOF
) {
287 error
= got_error_from_errno("fputs");
290 if (fflush(tmpsig
) == EOF
) {
291 error
= got_error_from_errno("fflush");
295 error
= buf_alloc(&buf
, 0);
298 error
= got_tag_write_signed_data(buf
, tag
, start_sig
);
302 argv
[i
++] = GOT_TAG_PATH_SSH_KEYGEN
;
304 argv
[i
++] = "verify";
306 argv
[i
++] = allowed_signers
;
308 argv
[i
++] = identity
;
318 /* ssh(1) allows up to 3 "-v" options. */
319 for (j
= 0; j
< MIN(3, verbosity
); j
++)
323 assert(i
<= nitems(argv
));
325 if (pipe(in_pfd
) == -1) {
326 error
= got_error_from_errno("pipe");
329 if (pipe(out_pfd
) == -1) {
330 error
= got_error_from_errno("pipe");
336 error
= got_error_from_errno("fork");
342 } else if (pid
== 0) {
343 if (close(in_pfd
[1]) == -1)
345 if (close(out_pfd
[0]) == -1)
347 if (dup2(in_pfd
[0], 0) == -1)
349 if (dup2(out_pfd
[1], 1) == -1)
351 if (execv(GOT_TAG_PATH_SSH_KEYGEN
, (char **const)argv
) == -1)
353 abort(); /* not reached */
355 if (close(in_pfd
[0]) == -1) {
356 error
= got_error_from_errno("close");
359 if (close(out_pfd
[1]) == -1) {
360 error
= got_error_from_errno("close");
363 if (buf_write_fd(buf
, in_pfd
[1]) == -1) {
364 error
= got_error_from_errno("write");
367 if (close(in_pfd
[1]) == -1) {
368 error
= got_error_from_errno("close");
371 if (waitpid(pid
, &status
, 0) == -1) {
372 error
= got_error_from_errno("waitpid");
375 if (!WIFEXITED(status
)) {
376 error
= got_error(GOT_ERR_BAD_TAG_SIGNATURE
);
380 error
= buf_load_fd(&buf
, out_pfd
[0]);
383 error
= buf_putc(buf
, '\0');
386 if (close(out_pfd
[0]) == -1) {
387 error
= got_error_from_errno("close");
391 if (WEXITSTATUS(status
) != 0)
392 error
= got_error(GOT_ERR_BAD_TAG_SIGNATURE
);
395 free(parsed_identity
);
396 if (tmppath
&& unlink(tmppath
) == -1 && error
== NULL
)
397 error
= got_error_from_errno("unlink");
400 if (tmpsig
&& fclose(tmpsig
) == EOF
&& error
== NULL
)
401 error
= got_error_from_errno("fclose");