1 /* Copyright 2011-2014 Red Hat, Inc.
2  * All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Author(s): Peter Jones <pjones@redhat.com>
17  */
18 
19 #include <err.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <popt.h>
23 #include <pwd.h>
24 #include <stddef.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <sys/un.h>
29 
30 #include "pesign.h"
31 
32 #define NO_FLAGS		0x00
33 #define UNLOCK_TOKEN		0x01
34 #define KILL_DAEMON		0x02
35 #define SIGN_BINARY		0x04
36 #define IS_TOKEN_UNLOCKED	0x08
37 #define FLAG_LIST_END		0x10
38 
39 static struct {
40 	int flag;
41 	const char *name;
42 } flag_names[] = {
43 	{UNLOCK_TOKEN, "unlock"},
44 	{KILL_DAEMON, "kill"},
45 	{SIGN_BINARY, "sign"},
46 	{IS_TOKEN_UNLOCKED, "is-unlocked"},
47 	{FLAG_LIST_END, NULL},
48 };
49 
50 static void
print_flag_name(FILE * f,int flag)51 print_flag_name(FILE *f, int flag)
52 {
53 	for (int i = 0; flag_names[i].flag != FLAG_LIST_END; i++) {
54 		if (flag_names[i].flag == flag)
55 			fprintf(f, "%s ", flag_names[i].name);
56 	}
57 }
58 
59 static int
connect_to_server(void)60 connect_to_server(void)
61 {
62 	int rc = access(SOCKPATH, R_OK);
63 	if (rc != 0) {
64 		fprintf(stderr, "pesign-client: could not connect to server: "
65 			"%m\n");
66 		exit(1);
67 	}
68 
69 	struct sockaddr_un addr_un = {
70 		.sun_family = AF_UNIX,
71 		.sun_path = SOCKPATH,
72 	};
73 
74 	int sd = socket(AF_UNIX, SOCK_STREAM, 0);
75 	if (sd < 0) {
76 		fprintf(stderr, "pesign-client: could not open socket: %m\n");
77 		exit(1);
78 	}
79 
80 	socklen_t len = strlen(addr_un.sun_path) +
81 			sizeof(addr_un.sun_family);
82 
83 	rc = connect(sd, (struct sockaddr *)&addr_un, len);
84 	if (rc < 0) {
85 		fprintf(stderr, "pesign-client: could not connect to daemon: "
86 			"%m\n");
87 		exit(1);
88 	}
89 
90 	return sd;
91 }
92 
93 static int32_t
94 check_response(int sd, char **srvmsg);
95 
96 static void
check_cmd_version(int sd,uint32_t command,char * name,int32_t version)97 check_cmd_version(int sd, uint32_t command, char *name, int32_t version)
98 {
99 	struct msghdr msg;
100 	struct iovec iov[1];
101 	pesignd_msghdr pm;
102 
103 	pm.version = PESIGND_VERSION;
104 	pm.command = CMD_GET_CMD_VERSION;
105 	pm.size = sizeof(command);
106 	iov[0].iov_base = &pm;
107 	iov[0].iov_len = sizeof(pm);
108 
109 	memset(&msg, '\0', sizeof(msg));
110 	msg.msg_iov = iov;
111 	msg.msg_iovlen = 1;
112 
113 	ssize_t n;
114 	n = sendmsg(sd, &msg, 0);
115 	if (n < 0) {
116 		fprintf(stderr, "check-cmd-version: kill daemon failed: %m\n");
117 		exit(1);
118 	}
119 
120 	iov[0].iov_base = &command;
121 	iov[0].iov_len = sizeof(command);
122 
123 	msg.msg_iov = iov;
124 	msg.msg_iovlen = 1;
125 
126 	n = sendmsg(sd, &msg, 0);
127 	if (n < 0)
128 		err(1, "check-cmd-version: sendmsg failed");
129 
130 	char *srvmsg = NULL;
131 	int32_t rc = check_response(sd, &srvmsg);
132 	if (rc < 0)
133 		errx(1, "command \"%s\" not known by server", name);
134 	if (rc != version)
135 		errx(1, "command \"%s\": client version %d, server version %d",
136 			name, version, rc);
137 }
138 
139 static void
send_kill_daemon(int sd)140 send_kill_daemon(int sd)
141 {
142 	struct msghdr msg;
143 	struct iovec iov;
144 	pesignd_msghdr pm;
145 
146 	check_cmd_version(sd, CMD_KILL_DAEMON, "kill-daemon", 0);
147 
148 	pm.version = PESIGND_VERSION;
149 	pm.command = CMD_KILL_DAEMON;
150 	pm.size = 0;
151 
152 	iov.iov_base = &pm;
153 	iov.iov_len = sizeof(pm);
154 
155 	memset(&msg, '\0', sizeof(msg));
156 	msg.msg_iov = &iov;
157 	msg.msg_iovlen = 1;
158 
159 	ssize_t n;
160 
161 	n = sendmsg(sd, &msg, 0);
162 	if (n < 0) {
163 		fprintf(stderr, "pesign-client: kill daemon failed: %m\n");
164 		exit(1);
165 	}
166 }
167 
168 static int32_t
check_response(int sd,char ** srvmsg)169 check_response(int sd, char **srvmsg)
170 {
171 	ssize_t n;
172 	struct msghdr msg;
173 	struct iovec iov;
174 	char buffer[1024];
175 
176 	pesignd_msghdr *pm;
177 
178 	msg.msg_name = NULL;
179 	msg.msg_namelen = 0;
180 
181 	memset(&msg, '\0', sizeof(msg));
182 	memset(buffer, '\0', sizeof(buffer));
183 
184 	iov.iov_base = buffer;
185 	iov.iov_len = 1023;
186 	msg.msg_iov = &iov;
187 	msg.msg_iovlen = 1;
188 
189 	n = recvmsg(sd, &msg, 0);
190 	if (n < 0) {
191 		fprintf(stderr, "pesign-client: could not get response from "
192 			"server: %m\n");
193 		exit(1);
194 	}
195 
196 	pm = (pesignd_msghdr *)buffer;
197 
198 	if (pm->version != PESIGND_VERSION) {
199 		fprintf(stderr, "pesign-client: got version %d, "
200 			"expected version %d\n", pm->version, PESIGND_VERSION);
201 		exit(1);
202 	}
203 
204 	if (pm->command != CMD_RESPONSE) {
205 		fprintf(stderr, "pesign-client: got unexpected response: %d\n",
206 			pm->command);
207 		exit(1);
208 	}
209 
210 	pesignd_cmd_response *resp = (pesignd_cmd_response *)((uint8_t *)pm +
211 					offsetof(pesignd_msghdr, size) +
212 					sizeof(pm->size));
213 
214 	if (resp->rc == 0)
215 		return 0;
216 
217 	*srvmsg = strdup((char *)resp->errmsg);
218 	return resp->rc;
219 }
220 
221 static char *
get_token_pin(int pinfd,char * pinfile,char * envname)222 get_token_pin(int pinfd, char *pinfile, char *envname)
223 {
224 	char *pin = NULL;
225 	FILE *pinf = NULL;
226 
227 	errno = 0;
228 	/* validate that the fd we got is real... */
229 	if (pinfd >= 0) {
230 		pinf = fdopen(pinfd, "r");
231 		if (!pinf) {
232 			if (errno != EBADF)
233 				close(pinfd);
234 			return NULL;
235 		}
236 
237 		ssize_t n = getline(&pin, 0, pinf);
238 		if (n < 0 || !pin) {
239 			fclose(pinf);
240 			close(pinfd);
241 			return NULL;
242 		}
243 
244 		char *c = strchrnul(pin, '\n');
245 		*c = '\0';
246 
247 		fclose(pinf);
248 		close(pinfd);
249 		return pin;
250 	} else if (pinfile) {
251 		pinf = fopen(pinfile, "r");
252 		if (!pinf)
253 			return NULL;
254 
255 		size_t len;
256 		ssize_t n = getline(&pin, &len, pinf);
257 		if (n < 0 || !pin) {
258 			fclose(pinf);
259 			return NULL;
260 		}
261 
262 		char *c = strchrnul(pin, '\n');
263 		*c = '\0';
264 
265 		fclose(pinf);
266 		return pin;
267 	} else {
268 		pin = getenv(envname);
269 		if (pin)
270 			return strdup(pin);
271 	}
272 
273 	pin = readpw(NULL, PR_FALSE, NULL);
274 	return pin;
275 }
276 
277 static void
unlock_token(int sd,char * tokenname,char * pin)278 unlock_token(int sd, char *tokenname, char *pin)
279 {
280 	struct msghdr msg;
281 	struct iovec iov[2];
282 	pesignd_msghdr pm;
283 
284 	uint32_t size0 = pesignd_string_size(tokenname);
285 
286 	uint32_t size1 = pesignd_string_size(pin);
287 
288 	check_cmd_version(sd, CMD_UNLOCK_TOKEN, "unlock-token", 0);
289 
290 	pm.version = PESIGND_VERSION;
291 	pm.command = CMD_UNLOCK_TOKEN;
292 	pm.size = size0 + size1;
293 	iov[0].iov_base = &pm;
294 	iov[0].iov_len = sizeof (pm);
295 
296 	memset(&msg, '\0', sizeof(msg));
297 	msg.msg_iov = iov;
298 	msg.msg_iovlen = 1;
299 
300 	ssize_t n;
301 	n = sendmsg(sd, &msg, 0);
302 	if (n < 0) {
303 		fprintf(stderr, "pesign-client: unlock token: sendmsg failed: "
304 			"%m\n");
305 		exit(1);
306 	}
307 
308 	uint8_t *buffer = NULL;
309 	buffer = calloc(1, size0 + size1);
310 	if (!buffer) {
311 		fprintf(stderr, "pesign-client: could not allocate memory: "
312 			"%m\n");
313 		exit(1);
314 	}
315 
316 	pesignd_string *tn = (pesignd_string *)buffer;
317 	pesignd_string_set(tn, tokenname);
318 	iov[0].iov_base = tn;
319 	iov[0].iov_len = size0;
320 
321 	pesignd_string *tp = pesignd_string_next(tn);
322 	pesignd_string_set(tp, pin);
323 
324 	iov[1].iov_base = tp;
325 	iov[1].iov_len = size1;
326 
327 	msg.msg_iov = iov;
328 	msg.msg_iovlen = 2;
329 
330 	n = sendmsg(sd, &msg, 0);
331 	if (n < 0) {
332 		fprintf(stderr, "pesign-client: unlock token: sendmsg failed: "
333 			"%m\n");
334 		exit(1);
335 	}
336 
337 	char *srvmsg = NULL;
338 	int rc = check_response(sd, &srvmsg);
339 	if (rc < 0) {
340 		fprintf(stderr, "pesign-client: %s\n",
341 			srvmsg);
342 		exit(1);
343 	}
344 
345 	free(buffer);
346 }
347 
348 static void
is_token_unlocked(int sd,char * tokenname)349 is_token_unlocked(int sd, char *tokenname)
350 {
351 	struct msghdr msg;
352 	struct iovec iov[1];
353 	pesignd_msghdr pm;
354 
355 	uint32_t size0 = pesignd_string_size(tokenname);
356 
357 	check_cmd_version(sd, CMD_IS_TOKEN_UNLOCKED, "is-token-unlocked", 0);
358 
359 	pm.version = PESIGND_VERSION;
360 	pm.command = CMD_IS_TOKEN_UNLOCKED;
361 	pm.size = size0;
362 	iov[0].iov_base = &pm;
363 	iov[0].iov_len = sizeof (pm);
364 
365 	memset(&msg, '\0', sizeof(msg));
366 	msg.msg_iov = iov;
367 	msg.msg_iovlen = 1;
368 
369 	ssize_t n;
370 	n = sendmsg(sd, &msg, 0);
371 	if (n < 0)
372 		err(1, "is_token_unlocked: sendmsg failed");
373 
374 	uint8_t *buffer = NULL;
375 	buffer = calloc(1, size0);
376 	if (!buffer)
377 		err(1, "is_token_unlocked: Could not allocate memory");
378 
379 	pesignd_string *tn = (pesignd_string *)buffer;
380 	pesignd_string_set(tn, tokenname);
381 	iov[0].iov_base = tn;
382 	iov[0].iov_len = size0;
383 
384 	msg.msg_iov = iov;
385 	msg.msg_iovlen = 1;
386 
387 	n = sendmsg(sd, &msg, 0);
388 	if (n < 0)
389 		err(1, "is_token_unlocked: sendmsg failed");
390 
391 	char *srvmsg = NULL;
392 	int rc = check_response(sd, &srvmsg);
393 	if (rc < 0)
394 		errx(1, "%s", srvmsg);
395 	printf("token \"%s\" is %slocked\n", tokenname, rc == 1 ? "" : "un");
396 
397 	free(buffer);
398 }
399 
400 static void
send_fd(int sd,int fd)401 send_fd(int sd, int fd)
402 {
403 	struct msghdr msg;
404 	struct iovec iov;
405 	char buf[2] = "\0";
406 
407 	memset(&msg, '\0', sizeof(msg));
408 
409 	iov.iov_base = buf;
410 	iov.iov_len = sizeof(buf);
411 
412 	msg.msg_iov = &iov;
413 	msg.msg_iovlen = 1;
414 
415 	size_t controllen = CMSG_SPACE(sizeof(int));
416 	struct cmsghdr *cm = malloc(controllen);
417 	if (!cm) {
418 		fprintf(stderr, "pesign-client: could not allocate memory: "
419 			"%m\n");
420 		exit(1);
421 	}
422 
423 	msg.msg_control = cm;
424 	msg.msg_controllen = controllen;
425 
426 	struct cmsghdr *cme;
427 
428 	cme = CMSG_FIRSTHDR(&msg);
429 	cme->cmsg_len = CMSG_LEN(sizeof(int));
430 	cme->cmsg_level = SOL_SOCKET;
431 	cme->cmsg_type = SCM_RIGHTS;
432 	*(int *)CMSG_DATA(cme) = fd;
433 
434 	ssize_t n;
435 	n = sendmsg(sd, &msg, 0);
436 	if (n < 0) {
437 		fprintf(stderr, "pesign-client: sign: sendmsg failed: "
438 			"%m\n");
439 		exit(1);
440 	}
441 }
442 
443 static void
sign(int sd,char * infile,char * outfile,char * tokenname,char * certname,int attached)444 sign(int sd, char *infile, char *outfile, char *tokenname, char *certname,
445 	int attached)
446 {
447 	int infd = open(infile, O_RDONLY);
448 	if (infd < 0) {
449 		fprintf(stderr, "pesign-client: could not open input file "
450 			"\"%s\": %m\n", infile);
451 		exit(1);
452 	}
453 
454 	int outfd = open(outfile, O_RDWR|O_CREAT, 0600);
455 	if (outfd < 0) {
456 		fprintf(stderr, "pesign-client: could not open output file "
457 			"\"%s\": %m\n", outfile);
458 		exit(1);
459 	}
460 
461 	struct msghdr msg;
462 	struct iovec iov[2];
463 
464 	uint32_t size0 = pesignd_string_size(tokenname);
465 	uint32_t size1 = pesignd_string_size(certname);
466 
467 	pesignd_msghdr *pm;
468 	pm = calloc(1, sizeof(*pm));
469 	if (!pm) {
470 oom:
471 		fprintf(stderr, "pesign-client: could not allocate memory: "
472 			"%m\n");
473 		exit(1);
474 	}
475 
476 	check_cmd_version(sd, attached ? CMD_SIGN_ATTACHED : CMD_SIGN_DETACHED,
477 			attached ? "sign-attached" : "sign-detached", 0);
478 
479 	pm->version = PESIGND_VERSION;
480 	pm->command = attached ? CMD_SIGN_ATTACHED : CMD_SIGN_DETACHED;
481 	pm->size = size0 + size1;
482 	iov[0].iov_base = pm;
483 	iov[0].iov_len = sizeof (*pm);
484 
485 	memset(&msg, '\0', sizeof(msg));
486 	msg.msg_iov = iov;
487 	msg.msg_iovlen = 1;
488 
489 	ssize_t n;
490 	n = sendmsg(sd, &msg, 0);
491 	if (n < 0) {
492 		fprintf(stderr, "pesign-client: sign: sendmsg failed: "
493 			"%m\n");
494 		exit(1);
495 	}
496 
497 	char *buffer;
498 	buffer = malloc(size0 + size1);
499 	if (!buffer)
500 		goto oom;
501 
502 	pesignd_string *tn = (pesignd_string *)buffer;
503 	pesignd_string_set(tn, tokenname);
504 	iov[0].iov_base = tn;
505 	iov[0].iov_len = size0;
506 
507 	pesignd_string *cn = pesignd_string_next(tn);
508 	pesignd_string_set(cn, certname);
509 	iov[1].iov_base = cn;
510 	iov[1].iov_len = size1;
511 
512 	msg.msg_iov = iov;
513 	msg.msg_iovlen = 2;
514 
515 	n = sendmsg(sd, &msg, 0);
516 	if (n < 0) {
517 		fprintf(stderr, "pesign-client: sign: sendmsg failed: "
518 			"%m\n");
519 		exit(1);
520 	}
521 	free(buffer);
522 
523 	send_fd(sd, infd);
524 	send_fd(sd, outfd);
525 
526 	char *srvmsg = NULL;
527 	int rc = check_response(sd, &srvmsg);
528 	if (rc < 0) {
529 		fprintf(stderr, "pesign-client: signing failed: \"%s\"\n",
530 			srvmsg);
531 		exit(1);
532 	}
533 
534 	close(infd);
535 	close(outfd);
536 
537 	return;
538 }
539 
540 int
main(int argc,char * argv[])541 main(int argc, char *argv[])
542 {
543 	char *tokenname = "NSS Certificate DB";
544 	char *certname = NULL;
545 	poptContext optCon;
546 	int rc;
547 	int action = NO_FLAGS;
548 	char *infile = NULL;
549 	char *outfile = NULL;
550 	char *exportfile = NULL;
551 	int attached = 1;
552 	int pinfd = -1;
553 	char *pinfile = NULL;
554 	char *tokenpin = NULL;
555 
556 	struct poptOption options[] = {
557 		{NULL, '\0', POPT_ARG_INTL_DOMAIN, "pesign" },
558 		{"token", 't', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT,
559 			&tokenname, 0, "NSS token holding signing key",
560 			"<token>" },
561 		{"certificate", 'c', POPT_ARG_STRING,
562 			&certname, 0, "NSS certificate name", "<nickname>" },
563 		{"unlock", 'u', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action,
564 			UNLOCK_TOKEN, "unlock nss token", NULL },
565 		{"is-unlocked", 'q', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action,
566 			IS_TOKEN_UNLOCKED, "query if an nss token is unlocked",
567 			NULL},
568 		{"kill", 'k', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action,
569 			KILL_DAEMON, "kill running daemon", NULL },
570 		{"sign", 's', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action,
571 			SIGN_BINARY, "sign binary", NULL },
572 		{"infile", 'i', POPT_ARG_STRING,
573 			&infile, 0, "input filename", "<infile>" },
574 		{"outfile", 'o', POPT_ARG_STRING,
575 			&outfile, 0, "output filename", "<outfile>" },
576 		{"export", 'e', POPT_ARG_STRING,
577 			&exportfile, 0, "create detached signature",
578 			"<outfile>" },
579 		{"pinfd", 'f', POPT_ARG_INT, &pinfd, -1,
580 			"read file descriptor for pin information",
581 			"<file descriptor>" },
582 		{"pinfile", 'F', POPT_ARG_STRING, &pinfile, 0,
583 			"read named file for pin information",
584 			"<pin file name>" },
585 		POPT_AUTOALIAS
586 		POPT_AUTOHELP
587 		POPT_TABLEEND
588 	};
589 
590 	optCon = poptGetContext("pesign", argc, (const char **)argv, options,0);
591 
592 	rc = poptReadDefaultConfig(optCon, 0);
593 	if (rc < 0 && !(rc == POPT_ERROR_ERRNO && errno == ENOENT)) {
594 		fprintf(stderr,
595 			"pesign-client: poptReadDefaultConfig failed: %s\n",
596 			poptStrerror(rc));
597 		exit(1);
598 	}
599 
600 	while ((rc = poptGetNextOpt(optCon)) > 0)
601 		;
602 
603 	if (rc < -1) {
604 		fprintf(stderr, "pesign-client: Invalid argument: %s: %s\n",
605 			poptBadOption(optCon, 0), poptStrerror(rc));
606 		exit(1);
607 	}
608 
609 	if (poptPeekArg(optCon)) {
610 		fprintf(stderr, "pesign-client: Invalid Argument: \"%s\"\n",
611 			poptPeekArg(optCon));
612 		exit(1);
613 	}
614 
615 	if (action == NO_FLAGS) {
616 		poptPrintUsage(optCon, stdout, 0);
617 		poptFreeContext(optCon);
618 		exit(0);
619 	}
620 
621 	if (action & SIGN_BINARY && (!outfile && !exportfile)) {
622 		fprintf(stderr, "pesign-client: neither --outfile nor --export "
623 			"specified\n");
624 		exit(1);
625 	}
626 
627 	if (outfile && exportfile) {
628 		fprintf(stderr, "pesign-client: both --outfile and --export "
629 			"specified\n");
630 		exit(1);
631 	}
632 	if (exportfile) {
633 		outfile = exportfile;
634 		attached = 0;
635 	}
636 
637 	poptFreeContext(optCon);
638 
639 	int sd = -1;
640 
641 	switch (action) {
642 	case UNLOCK_TOKEN:
643 		tokenpin = get_token_pin(pinfd, pinfile, "PESIGN_TOKEN_PIN");
644 		if (tokenpin == NULL) {
645 			if (errno)
646 				fprintf(stderr, "pesign-client: could not "
647 					"get token pin: %m\n");
648 			else
649 				fprintf(stderr, "pesign-client: no token pin "
650 					"specified");
651 			exit(1);
652 		}
653 		sd = connect_to_server();
654 		unlock_token(sd, tokenname, tokenpin);
655 		free(tokenpin);
656 		break;
657 	case IS_TOKEN_UNLOCKED:
658 		sd = connect_to_server();
659 		is_token_unlocked(sd, tokenname);
660 		break;
661 	case KILL_DAEMON:
662 		sd = connect_to_server();
663 		send_kill_daemon(sd);
664 		break;
665 	case SIGN_BINARY:
666 		if (!infile) {
667 			fprintf(stderr, "pesign-client: no input file "
668 				"specified\n");
669 			exit(1);
670 		}
671 		if (!outfile) {
672 			fprintf(stderr, "pesign-client: no output file "
673 				"specified\n");
674 			exit(1);
675 		}
676 		if (!certname) {
677 			fprintf(stderr, "pesign-client: no certificate name "
678 				"spefified\n");
679 			exit(1);
680 		}
681 		sd = connect_to_server();
682 		sign(sd, infile, outfile, tokenname, certname, attached);
683 		break;
684 	default:
685 		fprintf(stderr, "Incompatible flags (0x%08x): ", action);
686 		for (int i = 1; i < FLAG_LIST_END; i <<= 1) {
687 			if (action & i)
688 				print_flag_name(stderr, i);
689 		}
690 		fprintf(stderr, "\n");
691 		exit(1);
692 	}
693 
694 	return 0;
695 }
696