/ src / key.c
key.c
  1  /*-
  2   * Copyright (c) 2021 Kyle Evans <kevans@FreeBSD.org>
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer
  9   *    in this position and unchanged.
 10   * 2. Redistributions in binary form must reproduce the above copyright
 11   *    notice, this list of conditions and the following disclaimer in the
 12   *    documentation and/or other materials provided with the distribution.
 13   *
 14   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 15   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 16   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 17   * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 18   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 19   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 20   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 21   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 23   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 24   */
 25  
 26  #include <sys/uio.h>
 27  
 28  #include <bsd_compat.h>
 29  #include <assert.h>
 30  #include <err.h>
 31  #include <getopt.h>
 32  #include <signal.h>
 33  #include <stdio.h>
 34  #include <string.h>
 35  
 36  #if __has_include(<readpassphrase.h>)
 37  #include <readpassphrase.h>
 38  #elif __has_include(<bsd/readpassphrase.h>)
 39  #include <bsd/readpassphrase.h>
 40  #else
 41  #include "readpassphrase_compat.h"
 42  #endif
 43  
 44  #include <unistd.h>
 45  
 46  #include <pkg.h>
 47  #include "pkgcli.h"
 48  
 49  enum {
 50  	ARG_CREATE = CHAR_MAX + 1,
 51  	ARG_PUBLIC,
 52  	ARG_SIGN,
 53  };
 54  
 55  typedef enum {
 56  	MODE_UNSPECIFIED = 0,
 57  	MODE_CREATE,
 58  	MODE_PUBLIC,
 59  	MODE_SIGN,
 60  } key_mode_t;
 61  
 62  void
 63  usage_key(void)
 64  {
 65  	fprintf(stderr, "Usage: pkg key [--create | --public | --sign] [-t <type>] "
 66  	    "<key-path>\n");
 67  	fprintf(stderr, "For more information see 'pkg help key'.\n");
 68  }
 69  
 70  static int
 71  key_create(struct pkg_key *key, int argc __unused, char *argv[] __unused)
 72  {
 73  	/* No arguments to setup for now. */
 74  	return (pkg_key_create(key, NULL, 0));
 75  }
 76  
 77  static int
 78  key_pubout(struct pkg_key *key)
 79  {
 80  	char *keybuf = NULL;
 81  	size_t keylen;
 82  	int ret;
 83  
 84  	ret = pkg_key_pubkey(key, &keybuf, &keylen);
 85  	if (ret != EPKG_OK)
 86  		return (ret);
 87  
 88  	fwrite(keybuf, keylen, 1, stdout);
 89  	free(keybuf);
 90  	return (0);
 91  }
 92  
 93  static int
 94  key_sign_data(struct pkg_key *key, const char *name)
 95  {
 96  	char buf[BUFSIZ];
 97  	xstring *datastr;
 98  	char *data;
 99  	unsigned char *sig;
100  	size_t datasz, readsz, siglen;
101  	FILE *datafile;
102  	int rc;
103  
104  	datafile = NULL;
105  	datastr = NULL;
106  	rc = EPKG_FATAL;
107  	if (STREQ(name, "-")) {
108  		datafile = stdin;	/* XXX Make it configurable? */
109  		name = "stdin";
110  	} else {
111  		datafile = fopen(name, "rb");
112  		if (datafile == NULL)
113  			err(EXIT_FAILURE, "fopen");
114  	}
115  
116  	datastr = xstring_new();
117  	while (!feof(datafile)) {
118  		readsz = fread(&buf[0], 1, sizeof(buf), datafile);
119  		if (readsz == 0 && ferror(datafile)) {
120  			fprintf(stderr, "%s: I/O error\n", name);
121  			goto out;
122  		}
123  
124  		fwrite(buf, readsz, 1, datastr->fp);
125  	}
126  
127  	data = xstring_get_binary(datastr, &datasz);
128  	datastr = NULL;
129  
130  	sig = NULL;
131  	rc = pkg_key_sign_data(key, (unsigned char *)data, datasz, &sig, &siglen);
132  	free(data);
133  
134  #if 0
135  	fprintf(stderr, "SIGNED: %s\n", data);
136  #endif
137  /*
138  +SIGNED: 64628d55add8b281b9868aea00c4829a3ad260cfc4262e9d1244a1ab67584935
139  +SIGNED: a2eb46d60cd26657b273ec55a0909e642ef522f35074a9c62c3c4b42608e55e1
140  */
141  
142  	if (rc == EPKG_OK) {
143  		size_t writesz;
144  
145  		if ((writesz = fwrite(sig, 1, siglen, stdout)) < siglen) {
146  			fprintf(stderr, "Failed to write signature out [%zu/%zu]\n",
147  			    writesz, siglen);
148  			rc = EPKG_FATAL;
149  		}
150  	}
151  	free(sig);
152  
153  out:
154  	xstring_free(datastr);
155  	if (datafile != stdin)
156  		fclose(datafile);
157  	return rc;
158  }
159  
160  static int
161  key_info(struct pkg_key *key, const char *file, const char *type)
162  {
163  	struct iovec *iov;
164  	int niov, rc;
165  
166  	iov = NULL;
167  	rc = pkg_key_info(key, &iov, &niov);
168  	if (rc != EPKG_OK)
169  		return (rc);
170  
171  	assert((niov % 2) == 0);
172  
173  	printf("Key file '%s' (type %s)\n", file, type);
174  	for (int i = 0; i < niov; i += 2) {
175  		const char *kv_name = iov[i].iov_base;
176  		const char *kv_val = iov[i + 1].iov_base;
177  		printf("  - %s: %s\n", kv_name, kv_val);
178  
179  		free(iov[i + 1].iov_base);
180  	}
181  
182  	free(iov);
183  	return (EPKG_OK);
184  }
185  
186  int
187  password_cb(char *buf, int size, int rwflag, void *key)
188  {
189  	int len = 0;
190  	char pass[BUFSIZ];
191  	sigset_t sig, oldsig;
192  
193  	(void)rwflag;
194  	(void)key;
195  
196  	/* Block sigalarm temporary */
197  	sigemptyset(&sig);
198  	sigaddset(&sig, SIGALRM);
199  	sigprocmask(SIG_BLOCK, &sig, &oldsig);
200  
201  	if (readpassphrase("\nEnter passphrase: ", pass, BUFSIZ, RPP_ECHO_OFF) == NULL)
202  		return 0;
203  
204  	len = strlen(pass);
205  
206  	if (len <= 0)  return 0;
207  	if (len > size) len = size;
208  
209  	memset(buf, '\0', size);
210  	memcpy(buf, pass, len);
211  	memset(pass, 0, BUFSIZ);
212  
213  	sigprocmask(SIG_SETMASK, &oldsig, NULL);
214  
215  	return (len);
216  }
217  
218  int
219  exec_key(int argc, char **argv)
220  {
221  	int	 ret;
222  	int	 ch;
223  	struct pkg_key *key = NULL;
224  	const char *keypath, *keytype = NULL;
225  	key_mode_t keymode;
226  
227  	struct option longopts[] = {
228  		{ "create",	no_argument,		NULL,	ARG_CREATE },
229  		{ "public", no_argument,		NULL,	ARG_PUBLIC },
230  		{ "sign", no_argument,		NULL,		ARG_SIGN },
231  		{ NULL,		0,			NULL,	0 },
232  	};
233  
234  	keymode = MODE_UNSPECIFIED;
235  
236  	/* XXX maybe eventually we can just derive the key type. */
237  	while ((ch = getopt_long(argc, argv, "t:", longopts, NULL)) != -1) {
238  		switch (ch) {
239  		case ARG_CREATE:
240  			if (keymode != MODE_UNSPECIFIED) {
241  				usage_key();
242  				return (EXIT_FAILURE);
243  			}
244  			keymode = MODE_CREATE;
245  			break;
246  		case ARG_PUBLIC:
247  			if (keymode != MODE_UNSPECIFIED) {
248  				usage_key();
249  				return (EXIT_FAILURE);
250  			}
251  			keymode = MODE_PUBLIC;
252  			break;
253  		case ARG_SIGN:
254  			if (keymode != MODE_UNSPECIFIED) {
255  				usage_key();
256  				return (EXIT_FAILURE);
257  			}
258  			keymode = MODE_SIGN;
259  			break;
260  		case 't':
261  			keytype = optarg;
262  			break;
263  		default:
264  			usage_key();
265  			return (EXIT_FAILURE);
266  		}
267  	}
268  	argc -= optind;
269  	argv += optind;
270  
271  	if (argc != 1) {
272  		usage_key();
273  		return (EXIT_FAILURE);
274  	}
275  
276  	if (keytype == NULL)
277  		keytype = "rsa";
278  
279  	keypath = argv[0];
280  	if (*keypath == '\0') {
281  		fprintf(stderr, "keypath must not be empty.\n");
282  		usage_key();
283  		return (EXIT_FAILURE);
284  	}
285  
286  	ret = pkg_key_new(&key, keytype, keypath, password_cb);
287  	if (ret != EPKG_OK) {
288  		fprintf(stderr, "Failed to create key context.\n");
289  		return (EXIT_FAILURE);
290  	}
291  
292  	switch (keymode) {
293  	case MODE_CREATE:
294  		ret = key_create(key, argc, argv);
295  		if (ret != EPKG_OK) {
296  			switch (ret) {
297  			case EPKG_OPNOTSUPP:
298  				fprintf(stderr, "Type '%s' does not support generation.\n",
299  				    keytype);
300  				break;
301  			default:
302  				fprintf(stderr, "Failed to generate the key.\n");
303  				break;
304  			}
305  
306  			goto out;
307  		}
308  
309  		fprintf(stderr, "Created '%s' private key at %s\n", keytype, keypath);
310  		/* FALLTHROUGH */
311  	case MODE_PUBLIC:
312  		ret = key_pubout(key);
313  		if (ret != EPKG_OK) {
314  			switch (ret) {
315  			case EPKG_OPNOTSUPP:
316  				fprintf(stderr, "Type '%s' does not support pubout.\n",
317  				    keytype);
318  				break;
319  			default:
320  				fprintf(stderr, "Failed to get keyinfo.\n");
321  				break;
322  			}
323  
324  			goto out;
325  		}
326  
327  		break;
328  	case MODE_SIGN:
329  		ret = key_sign_data(key, "-");
330  		if (ret != EPKG_OK) {
331  			switch (ret) {
332  			case EPKG_OPNOTSUPP:
333  				fprintf(stderr, "Type '%s' does not support signing.\n",
334  				    keytype);
335  				break;
336  			default:
337  				fprintf(stderr, "Failed to sign.\n");
338  				break;
339  			}
340  
341  			goto out;
342  		}
343  		break;
344  	case MODE_UNSPECIFIED:
345  		ret = key_info(key, keypath, keytype);
346  		if (ret != EPKG_OK) {
347  			switch (ret) {
348  			case EPKG_OPNOTSUPP:
349  				printf("Type '%s' does not support keyinfo.\n",
350  				    keytype);
351  				break;
352  			default:
353  				printf("Failed to get keyinfo.\n");
354  				break;
355  			}
356  
357  			goto out;
358  		}
359  
360  		break;
361  	}
362  
363  out:
364  	pkg_key_free(key);
365  	return (ret == EPKG_OK ? EXIT_SUCCESS : EXIT_FAILURE);
366  }