From 00719770b7608af0c37dfd6c14a04eb7ebc4cad0 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 12 Jun 2008 13:31:13 -0400 Subject: reorganizing to prepare for transition to openpgp2ssh. --- src/keytrans/Makefile | 19 +++ src/keytrans/gnutls-helpers.c | 364 ++++++++++++++++++++++++++++++++++++++++++ src/keytrans/gnutls-helpers.h | 72 +++++++++ src/keytrans/gpg2ssh.c | 293 ++++++++++++++++++++++++++++++++++ src/keytrans/openpgp2ssh.c | 271 +++++++++++++++++++++++++++++++ src/keytrans/ssh2gpg.c | 171 ++++++++++++++++++++ 6 files changed, 1190 insertions(+) create mode 100644 src/keytrans/Makefile create mode 100644 src/keytrans/gnutls-helpers.c create mode 100644 src/keytrans/gnutls-helpers.h create mode 100644 src/keytrans/gpg2ssh.c create mode 100644 src/keytrans/openpgp2ssh.c create mode 100644 src/keytrans/ssh2gpg.c (limited to 'src/keytrans') diff --git a/src/keytrans/Makefile b/src/keytrans/Makefile new file mode 100644 index 0000000..fdd4ccd --- /dev/null +++ b/src/keytrans/Makefile @@ -0,0 +1,19 @@ +all: openpgp2ssh + +openpgp2ssh: openpgp2ssh.c gnutls-helpers.o + gcc -g -Wall --pedantic -o openpgp2ssh openpgp2ssh.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o + +%.o: %.c + gcc -g -Wall --pedantic -o $@ -c $< + +clean: + rm -f openpgp2ssh gpg2ssh ssh2gpg *.o + +# deprecated: +gpg2ssh: gpg2ssh.c gnutls-helpers.o + gcc -g -Wall --pedantic -o gpg2ssh gpg2ssh.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o + +ssh2gpg: ssh2gpg.c gnutls-helpers.o + gcc -g -Wall --pedantic -o ssh2gpg ssh2gpg.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o + +.PHONY: clean all diff --git a/src/keytrans/gnutls-helpers.c b/src/keytrans/gnutls-helpers.c new file mode 100644 index 0000000..6eae29e --- /dev/null +++ b/src/keytrans/gnutls-helpers.c @@ -0,0 +1,364 @@ +/* Author: Daniel Kahn Gillmor */ +/* Date: Fri, 04 Apr 2008 19:31:16 -0400 */ +/* License: GPL v3 or later */ + +#include "gnutls-helpers.h" +/* for htonl() */ +#include + +/* for setlocale() */ +#include + +/* for isalnum() */ +#include + +int loglevel = 0; + + +void err(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fflush(stderr); +} + +void logfunc(int level, const char* string) { + fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string); +} + +void init_keyid(gnutls_openpgp_keyid_t keyid) { + memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t)); +} + + + +void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid) +{ + static const char hex[16] = "0123456789ABCDEF"; + unsigned int kix = 0, outix = 0; + + while (kix < sizeof(gnutls_openpgp_keyid_t)) { + out[outix] = hex[(keyid[kix] >> 4) & 0x0f]; + out[outix + 1] = hex[keyid[kix] & 0x0f]; + kix++; + outix += 2; + } +} + + +int init_gnutls() { + const char* version = NULL; + const char* debug_string = NULL; + int ret; + + if (ret = gnutls_global_init(), ret) { + err("Failed to do gnutls_global_init() (error: %d)\n", ret); + return 1; + } + + version = gnutls_check_version(NULL); + + if (version) + err("gnutls version: %s\n", version); + else { + err("no version found!\n"); + return 1; + } + + if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) { + loglevel = atoi(debug_string); + gnutls_global_set_log_function(logfunc); + + gnutls_global_set_log_level(loglevel); + err("set log level to %d\n", loglevel); + } + return 0; +} + +void init_datum(gnutls_datum_t* d) { + d->data = NULL; + d->size = 0; +} +void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) { + dest->data = gnutls_realloc(dest->data, src->size); + dest->size = src->size; + memcpy(dest->data, src->data, src->size); +} +int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) { + if (a->size > b->size) { + err("a is larger\n"); + return 1; + } + if (a->size < b->size) { + err("b is larger\n"); + return -1; + } + return memcmp(a->data, b->data, a->size); +} +void free_datum(gnutls_datum_t* d) { + gnutls_free(d->data); + d->data = NULL; + d->size = 0; +} + +/* read the passed-in string, store in a single datum */ +int set_datum_string(gnutls_datum_t* d, const char* s) { + unsigned int x = strlen(s)+1; + unsigned char* c = NULL; + + c = gnutls_realloc(d->data, x); + if (NULL == c) + return -1; + d->data = c; + d->size = x; + memcpy(d->data, s, x); + return 0; +} + +/* read the passed-in file descriptor until EOF, store in a single + datum */ +int set_datum_fd(gnutls_datum_t* d, int fd) { + unsigned int bufsize = 1024; + unsigned int len = 0; + + FILE* f = fdopen(fd, "r"); + if (bufsize > d->size) { + bufsize = 1024; + d->data = gnutls_realloc(d->data, bufsize); + if (d->data == NULL) { + err("out of memory!\n"); + return -1; + } + d->size = bufsize; + } else { + bufsize = d->size; + } + f = fdopen(fd, "r"); + if (NULL == f) { + err("could not fdopen FD %d\n", fd); + } + clearerr(f); + while (!feof(f) && !ferror(f)) { + if (len == bufsize) { + /* allocate more space by doubling: */ + bufsize *= 2; + d->data = gnutls_realloc(d->data, bufsize); + if (d->data == NULL) { + err("out of memory!\n"); + return -1; + }; + d->size = bufsize; + } + len += fread(d->data + len, 1, bufsize - len, f); + /* err("read %d bytes\n", len); */ + } + if (ferror(f)) { + err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno)); + return -1; + } + + /* touch up buffer size to match reality: */ + d->data = gnutls_realloc(d->data, len); + d->size = len; + return 0; +} + +/* read the file indicated (by name) in the fname parameter. store + its entire contents in a single datum. */ +int set_datum_file(gnutls_datum_t* d, const char* fname) { + struct stat sbuf; + unsigned char* c = NULL; + FILE* file = NULL; + size_t x = 0; + + if (0 != stat(fname, &sbuf)) { + err("failed to stat '%s'\n", fname); + return -1; + } + + c = gnutls_realloc(d->data, sbuf.st_size); + if (NULL == c) { + err("failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname); + return -1; + } + + d->data = c; + d->size = sbuf.st_size; + file = fopen(fname, "r"); + if (NULL == file) { + err("failed to open '%s' for reading\n", fname); + return -1; + } + + x = fread(d->data, d->size, 1, file); + if (x != 1) { + err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname); + fclose(file); + return -1; + } + fclose(file); + return 0; +} + +int write_datum_fd(int fd, const gnutls_datum_t* d) { + if (d->size != write(fd, d->data, d->size)) { + err("failed to write body of datum.\n"); + return -1; + } + return 0; +} + + +int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) { + uint32_t len; + int looks_negative = (d->data[0] & 0x80); + unsigned char zero = 0; + + /* if the first bit is 1, then the datum will appear negative in the + MPI encoding style used by OpenSSH. In that case, we'll increase + the length by one, and dump out one more byte */ + + if (looks_negative) { + len = htonl(d->size + 1); + } else { + len = htonl(d->size); + } + if (write(fd, &len, sizeof(len)) != sizeof(len)) { + err("failed to write size of datum.\n"); + return -2; + } + if (looks_negative) { + if (write(fd, &zero, 1) != 1) { + err("failed to write padding byte for MPI.\n"); + return -2; + } + } + return write_datum_fd(fd, d); +} + +int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) { + unsigned int i; + int ret; + + for (i = 0; i < num; i++) + if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0) + return ret; + + return 0; +} + + +int datum_from_string(gnutls_datum_t* d, const char* str) { + d->size = strlen(str); + d->data = gnutls_realloc(d->data, d->size); + if (d->data == 0) + return ENOMEM; + memcpy(d->data, str, d->size); + return 0; +} + + +int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) { + int p[2]; + int ret; + + if (pid == NULL) { + err("bad pointer passed to create_writing_pipe()\n"); + return -1; + } + + if (ret = pipe(p), ret == -1) { + err("failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno)); + return -1; + } + + *pid = fork(); + if (*pid == -1) { + err("Failed to fork (error: %d \"%s\")\n", errno, strerror(errno)); + return -1; + } + if (*pid == 0) { /* this is the child */ + close(p[1]); /* close unused write end */ + + if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */ + err("Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno)); + exit(1); + } + execv(path, argv); + err("exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno)); + /* close the open file descriptors */ + close(p[0]); + close(0); + + exit(1); + } else { /* this is the parent */ + close(p[0]); /* close unused read end */ + return p[1]; + } +} + +int validate_ssh_host_userid(const char* userid) { + char* oldlocale = setlocale(LC_ALL, "C"); + + /* choke if userid does not match the expected format + ("ssh://fully.qualified.domain.name") */ + if (strncmp("ssh://", userid, strlen("ssh://")) != 0) { + err("The user ID should start with ssh:// for a host key\n"); + goto fail; + } + /* so that isalnum will work properly */ + userid += strlen("ssh://"); + while (0 != (*userid)) { + if (!isalnum(*userid)) { + err("label did not start with a letter or a digit! (%s)\n", userid); + goto fail; + } + userid++; + while (isalnum(*userid) || ('-' == (*userid))) + userid++; + if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label: + check last char + isalnum */ + if (!isalnum(*(userid - 1))) { + err("label did not end with a letter or a digit!\n"); + goto fail; + } + if ('.' == (*userid)) /* advance to the start of the next label */ + userid++; + } else { + err("invalid character in domain name: %c\n", *userid); + goto fail; + } + } + /* ensure that the last character is valid: */ + if (!isalnum(*(userid - 1))) { + err("hostname did not end with a letter or a digit!\n"); + goto fail; + } + /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we + make sure that we've got an OK string? */ + + return 0; + + fail: + setlocale(LC_ALL, oldlocale); + return 1; +} + +/* http://tools.ietf.org/html/rfc4880#section-5.5.2 */ +size_t get_openpgp_mpi_size(gnutls_datum_t* d) { + return 2 + d->size; +} + +int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d) { + uint16_t x; + + x = d->size * 8; + x = htons(x); + + write(fd, &x, sizeof(x)); + write(fd, d->data, d->size); + + return 0; +} diff --git a/src/keytrans/gnutls-helpers.h b/src/keytrans/gnutls-helpers.h new file mode 100644 index 0000000..9ea22a3 --- /dev/null +++ b/src/keytrans/gnutls-helpers.h @@ -0,0 +1,72 @@ +/* Author: Daniel Kahn Gillmor */ +/* Date: Fri, 04 Apr 2008 19:31:16 -0400 */ +/* License: GPL v3 or later */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Functions to help dealing with GnuTLS for monkeysphere key + translation projects: */ + +/* set everything up, including logging levels. Return 0 on + success */ +int init_gnutls(); + +/* logging and output functions: */ + +void err(const char* fmt, ...); +void logfunc(int level, const char* string); + +/* basic datum manipulations: */ + +void init_datum(gnutls_datum_t* d); +void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src); +int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b); +void free_datum(gnutls_datum_t* d); +int write_datum_fd(int fd, const gnutls_datum_t* d); +int write_datum_fd_with_length(int fd, const gnutls_datum_t* d); +int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num); + +/* set up a datum from a null-terminated string */ +int datum_from_string(gnutls_datum_t* d, const char* str); + +/* keyid manipulations: */ +typedef unsigned char printable_keyid[16]; + +void init_keyid(gnutls_openpgp_keyid_t keyid); +void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid); + +/* functions to get data into datum objects: */ + +/* read the passed-in string, store in a single datum */ +int set_datum_string(gnutls_datum_t* d, const char* s); + +/* read the passed-in file descriptor until EOF, store in a single + datum */ +int set_datum_fd(gnutls_datum_t* d, int fd); + +/* read the file indicated (by name) in the fname parameter. store + its entire contents in a single datum. */ +int set_datum_file(gnutls_datum_t* d, const char* fname); + +/* set up file descriptor pipe for writing (child process pid gets + stored in pid, fd is returned)*/ +int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]); + +/* return 0 if userid matches the monkeysphere spec for ssh host user IDs */ +int validate_ssh_host_userid(const char* userid); + +/* how many bytes will it take to write out this datum in OpenPGP MPI form? */ +size_t get_openpgp_mpi_size(gnutls_datum_t* d); + +/* write the MPI stored in gnutls_datum_t to file descriptor fd: */ +int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d); diff --git a/src/keytrans/gpg2ssh.c b/src/keytrans/gpg2ssh.c new file mode 100644 index 0000000..c99f03f --- /dev/null +++ b/src/keytrans/gpg2ssh.c @@ -0,0 +1,293 @@ +#include "gnutls-helpers.h" + +#include +#include + +/* for waitpid() */ +#include +#include + +/* + Author: Daniel Kahn Gillmor + Date: Tue, 08 Apr 2008 + License: GPL v3 or later + + monkeysphere public key translator: execute this with an GPG + certificate (public key(s) + userid(s)) on stdin. It currently + only works with RSA keys. + + It will spit out a version of the first key capable of being used + for authentication on stdout. The output format should be suitable + for appending a known_hosts file. + + Requirements: I've only built this so far with GnuTLS v2.3.4 -- + version 2.2.0 does not contain the appropriate pieces. + + */ + +int main(int argc, char* argv[]) { + gnutls_datum_t data; + int ret; + gnutls_openpgp_crt_t openpgp_crt; + gnutls_openpgp_keyid_t keyid; + printable_keyid p_keyid; + unsigned int keyidx; + unsigned int usage, bits; + gnutls_pk_algorithm_t algo; + + gnutls_datum_t m, e, p, q, g, y; + gnutls_datum_t algolabel; + + char output_data[10240]; + char userid[10240]; + size_t uidsz = sizeof(userid); + + const gnutls_datum_t* all[5]; + int pipefd; + pid_t child_pid; + char* const args[] = {"/usr/bin/base64", "--wrap=0", NULL}; + const char* algoname; + int mpicount; + int pipestatus; + + init_gnutls(); + + init_datum(&data); + + init_datum(&m); + init_datum(&e); + init_datum(&p); + init_datum(&q); + init_datum(&g); + init_datum(&y); + + init_datum(&algolabel); + + init_keyid(keyid); + + /* slurp in the private key from stdin */ + if (ret = set_datum_fd(&data, 0), ret) { + err("didn't read file descriptor 0\n"); + return 1; + } + + + if (ret = gnutls_openpgp_crt_init(&openpgp_crt), ret) { + err("Failed to initialize OpenPGP certificate (error: %d)\n", ret); + return 1; + } + + /* format could be either: GNUTLS_OPENPGP_FMT_RAW, + GNUTLS_OPENPGP_FMT_BASE64; if MONKEYSPHERE_RAW is set, use RAW, + otherwise, use BASE64: */ + + /* FIXME: we should be auto-detecting the input format, and + translating it as needed. */ + + if (getenv("MONKEYSPHERE_RAW")) { + err("assuming RAW formatted certificate\n"); + if (ret = gnutls_openpgp_crt_import(openpgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW), ret) { + err("failed to import the OpenPGP certificate in RAW format (error: %d)\n", ret); + return ret; + } + } else { + err("assuming BASE64 formatted certificate\n"); + if (ret = gnutls_openpgp_crt_import (openpgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64), ret) { + err("failed to import the OpenPGP certificate in BASE64 format (error: %d)\n", ret); + return ret; + } + } + + if (gnutls_openpgp_crt_get_revoked_status(openpgp_crt)) { + err("the primary key was revoked!\n"); + return 1; + } + + /* FIXME: We're currently looking at the primary key or maybe the + first authentication-capable subkey. + + Instead, we should be iterating through the primary key and all + subkeys: for each one with the authentication usage flag set of a + algorithm we can handle, we should output matching UserIDs and + the SSH version of the key. */ + + + if (ret = gnutls_openpgp_crt_get_key_usage(openpgp_crt, &usage), ret) { + err("failed to get the usage flags for the primary key (error: %d)\n", ret); + return ret; + } + if (usage & GNUTLS_KEY_KEY_AGREEMENT && + usage & GNUTLS_KEY_KEY_ENCIPHERMENT) { + err("the primary key can be used for authentication and communication encryption!\n"); + + algo = gnutls_openpgp_crt_get_pk_algorithm(openpgp_crt, &bits); + if (algo < 0) { + err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo); + return algo; + } else if (algo == GNUTLS_PK_RSA) { + + err("OpenPGP RSA certificate, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_pk_rsa_raw(openpgp_crt, &m, &e); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + } else if (algo == GNUTLS_PK_DSA) { + err("OpenPGP DSA Key, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_pk_dsa_raw(openpgp_crt, &p, &q, &g, &y); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export DSA key parameters (error: %d)\n", ret); + return 1; + } + } else { + err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo); + return 1; + } + + } else { + err("primary key is not good for authentication and communication encryption. Trying subkeys...\n"); + + if (ret = gnutls_openpgp_crt_get_auth_subkey(openpgp_crt, keyid, 0), ret) { + err("failed to find a subkey capable of authentication and communication encryption (error: %d)\n", ret); + return ret; + } + make_keyid_printable(p_keyid, keyid); + err("found authentication subkey %.16s\n", p_keyid); + + ret = gnutls_openpgp_crt_get_subkey_idx(openpgp_crt, keyid); + if (ret < 0) { + err("could not get the index of subkey %.16s (error: %d)\n", ret); + return ret; + } + keyidx = ret; + + if (gnutls_openpgp_crt_get_subkey_revoked_status(openpgp_crt, keyidx)) { + err("The authentication subkey was revoked!\n"); + return 1; + } + + if (ret = gnutls_openpgp_crt_get_subkey_usage(openpgp_crt, keyidx, &usage), ret) { + err("could not figure out usage of subkey %.16s (error: %d)\n", p_keyid, ret); + return ret; + } + if ((usage & GNUTLS_KEY_KEY_AGREEMENT) == 0 && + usage & GNUTLS_KEY_KEY_ENCIPHERMENT) { + err("could not find a subkey with authentication and communication encryption.\n"); + return 1; + } + + /* switch, based on the algorithm in question, to extract the MPI + components: */ + + algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(openpgp_crt, keyidx, &bits); + if (algo < 0) { + err("failed to get the algorithm of the authentication subkey (error: %d)\n", algo); + return algo; + } else if (algo == GNUTLS_PK_RSA) { + + err("OpenPGP RSA subkey, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(openpgp_crt, keyidx, &m, &e); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA subkey parameters (error: %d)\n", ret); + return 1; + } + } else if (algo == GNUTLS_PK_DSA) { + err("OpenPGP DSA subkey, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(openpgp_crt, keyidx, &p, &q, &g, &y); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export DSA subkey parameters (error: %d)\n", ret); + return 1; + } + } else { + err("OpenPGP subkey was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo); + return 1; + } + } + + /* make sure userid is NULL-terminated */ + userid[sizeof(userid) - 1] = 0; + uidsz--; + + /* FIXME: we're just choosing the first UserID from the certificate: + instead, we should be selecting every User ID that is adequately + signed and matches the spec, and aggregating them with commas for + known_hosts output */ + + if (ret = gnutls_openpgp_crt_get_name(openpgp_crt, 0, userid, &uidsz), ret) { + err("Failed to fetch the first UserID (error: %d)\n", ret); + return ret; + } + + if (ret = validate_ssh_host_userid(userid), ret) { + err("bad userid: not a valid ssh host.\n"); + return ret; + } + + /* remove ssh:// from the beginning of userid */ + memmove(userid, userid + strlen("ssh://"), 1 + strlen(userid) - strlen("ssh://")); + + + /* now we have algo, and the various MPI data are set. Can we + export them cleanly? */ + + /* for the moment, we'll just dump the info raw, and pipe it + externally through coreutils' /usr/bin/base64 */ + + if (algo == GNUTLS_PK_RSA) { + algoname = "ssh-rsa"; + mpicount = 3; + + all[0] = &algolabel; + all[1] = &e; + all[2] = &m; + } else if (algo == GNUTLS_PK_DSA) { + algoname = "ssh-dss"; + mpicount = 5; + + all[0] = &algolabel; + all[1] = &p; + all[2] = &q; + all[3] = &g; + all[4] = &y; + } else { + err("no idea what this algorithm is: %d\n", algo); + return 1; + } + + if (ret = datum_from_string(&algolabel, algoname), ret) { + err("couldn't label string (error: %d)\n", ret); + return ret; + } + + snprintf(output_data, sizeof(output_data), "%s %s ", userid, algoname); + + pipefd = create_writing_pipe(&child_pid, args[0], args); + if (pipefd < 0) { + err("failed to create a writing pipe (returned %d)\n", pipefd); + return pipefd; + } + + write(1, output_data, strlen(output_data)); + + if (0 != write_data_fd_with_length(pipefd, all, mpicount)) { + err("was not able to write out RSA key data\n"); + return 1; + } + close(pipefd); + if (child_pid != waitpid(child_pid, &pipestatus, 0)) { + err("could not wait for child process to return for some reason.\n"); + return 1; + } + if (pipestatus != 0) { + err("base64 pipe died with return code %d\n", pipestatus); + return pipestatus; + } + + write(1, "\n", 1); + + + + gnutls_openpgp_crt_deinit(openpgp_crt); + gnutls_global_deinit(); + return 0; +} diff --git a/src/keytrans/openpgp2ssh.c b/src/keytrans/openpgp2ssh.c new file mode 100644 index 0000000..d6bac68 --- /dev/null +++ b/src/keytrans/openpgp2ssh.c @@ -0,0 +1,271 @@ +#include "gnutls-helpers.h" + +#include +#include + +/* + Author: Daniel Kahn Gillmor + Date: Tue, 01 Apr 2008 + License: GPL v3 or later + + monkeysphere private key translator: execute this with an GPG + secret key on stdin (at the moment, only passphraseless RSA keys + work). + + It will spit out a PEM-encoded version of the key on stdout, which + can be fed into ssh-add like this: + + gpg --export-secret-keys $KEYID | monkeysphere | ssh-add -c /dev/stdin + + Requirements: I've only built this so far with GnuTLS v2.3.4 -- + version 2.2.0 does not contain the appropriate pieces. + + Notes: gpgkey2ssh doesn't seem to provide the same public + keys. Mighty weird! + +0 wt215@squeak:~/monkeysphere$ gpg --export-secret-keys 1DCDF89F | ~dkg/src/monkeysphere/monkeysphere | ssh-add -c /dev/stdin +gnutls version: 2.3.4 +OpenPGP RSA Key, with 1024 bits +Identity added: /dev/stdin (/dev/stdin) +The user has to confirm each use of the key +0 wt215@squeak:~/monkeysphere$ ssh-add -L +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9gWQqfrnhQKDQnND/3eOexpddE64J+1zp9fcyCje7H5LKclb6DBV2HS6WgW32PJhIzvP+fYZM3dzXea3fpv14y1SicXiRBDgF9SnsNA1qWn2RyzkLcKy7PmM0PDYtU1oiLTcQj/xkWcqW2sLKHT/WW+vZP5XP7RMGN/yWNMfE2Q== /dev/stdin +0 wt215@squeak:~/monkeysphere$ gpgkey2ssh 1DCDF89F +ssh-rsa AAAAB3NzaC1yc2EAAACBAL2BZCp+ueFAoNCc0P/d457Gl10Trgn7XOn19zIKN7sfkspyVvoMFXYdLpaBbfY8mEjO8/59hkzd3Nd5rd+m/XjLVKJxeJEEOAX1Kew0DWpafZHLOQtwrLs+YzQ8Ni1TWiItNxCP/GRZypbawsodP9Zb69k/lc/tEwY3/JY0x8TZAAAAAwEAAQ== COMMENT +0 wt215@squeak:~/monkeysphere$ + + */ + + +int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) { + gnutls_openpgp_privkey_t pgp_privkey; + gnutls_datum_t m, e, d, p, q, u, g, y, x; + gnutls_pk_algorithm_t pgp_algo; + unsigned int pgp_bits; + int ret; + + init_datum(&m); + init_datum(&e); + init_datum(&d); + init_datum(&p); + init_datum(&q); + init_datum(&u); + init_datum(&g); + init_datum(&y); + init_datum(&x); + + if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) { + err("Failed to initialized OpenPGP private key (error: %d)\n", ret); + return 1; + } + + + /* format could be either: GNUTLS_OPENPGP_FMT_RAW, + GNUTLS_OPENPGP_FMT_BASE64; if MONKEYSPHERE_RAW is set, use RAW, + otherwise, use BASE64: */ + + if (getenv("MONKEYSPHERE_RAW")) { + err("assuming RAW formatted private keys\n"); + if (ret = gnutls_openpgp_privkey_import(pgp_privkey, input, GNUTLS_OPENPGP_FMT_RAW, NULL, 0), ret) + err("failed to import the OpenPGP private key in RAW format (error: %d)\n", ret); + } else { + err("assuming BASE64 formatted private keys\n"); + if (ret = gnutls_openpgp_privkey_import (pgp_privkey, input, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0), ret) + err("failed to import the OpenPGP private key in BASE64 format (error: %d)\n", ret); + } + + pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(pgp_privkey, &pgp_bits); + if (pgp_algo < 0) { + err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo); + return 1; + } + if (pgp_algo == GNUTLS_PK_RSA) { + err("OpenPGP RSA Key, with %d bits\n", pgp_bits); + ret = gnutls_openpgp_privkey_export_rsa_raw(pgp_privkey, &m, &e, &d, &p, &q, &u); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + + ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to import RSA key parameters (error: %d)\n", ret); + return 1; + } + } else if (pgp_algo == GNUTLS_PK_DSA) { + err("OpenPGP DSA Key, with %d bits\n", pgp_bits); + ret = gnutls_openpgp_privkey_export_dsa_raw(pgp_privkey, &p, &q, &g, &y, &x); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export DSA key parameters (error: %d)\n", ret); + return 1; + } + + ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to import DSA key parameters (error: %d)\n", ret); + return 1; + } + } else { + err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo); + return 1; + } + + ret = gnutls_x509_privkey_fix(*output); + if (ret != 0) { + err("failed to fix up the private key in X.509 format (error: %d)\n", ret); + return 1; + } + + gnutls_openpgp_privkey_deinit(pgp_privkey); + return 0; +} + +int convert_x509_to_pgp(gnutls_openpgp_privkey_t* output, gnutls_datum_t* input) { + gnutls_x509_privkey_t x509_privkey; + gnutls_datum_t m, e, d, p, q, u, g, y, x; + gnutls_pk_algorithm_t x509_algo; + int ret; + + init_datum(&m); + init_datum(&e); + init_datum(&d); + init_datum(&p); + init_datum(&q); + init_datum(&u); + init_datum(&g); + init_datum(&y); + init_datum(&x); + + if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { + err("Failed to initialized X.509 private key (error: %d)\n", ret); + return 1; + } + + + /* format could be either: GNUTLS_X509_FMT_DER, + GNUTLS_X509_FMT_PEM; if MONKEYSPHERE_DER is set, use DER, + otherwise, use PEM: */ + + if (getenv("MONKEYSPHERE_DER")) { + err("assuming DER formatted private keys\n"); + if (ret = gnutls_x509_privkey_import(x509_privkey, input, GNUTLS_X509_FMT_DER), ret) + err("failed to import the X.509 private key in DER format (error: %d)\n", ret); + } else { + err("assuming PEM formatted private keys\n"); + if (ret = gnutls_x509_privkey_import (x509_privkey, input, GNUTLS_X509_FMT_PEM), ret) + err("failed to import the X.509 private key in PEM format (error: %d)\n", ret); + } + + x509_algo = gnutls_x509_privkey_get_pk_algorithm(x509_privkey); + if (x509_algo < 0) { + err("failed to get X.509 key algorithm (error: %d)\n", x509_algo); + return 1; + } + if (x509_algo == GNUTLS_PK_RSA) { + err("X.509 RSA Key\n"); + ret = gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + + /* ret = gnutls_openpgp_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u); */ + ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to import RSA key parameters (error: %d)\n", ret); + return 1; + } + } else if (x509_algo == GNUTLS_PK_DSA) { + err("X.509 DSA Key\n"); + ret = gnutls_x509_privkey_export_dsa_raw(x509_privkey, &p, &q, &g, &y, &x); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export DSA key parameters (error: %d)\n", ret); + return 1; + } + + /* ret = gnutls_openpgp_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x); */ + ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to import DSA key parameters (error: %d)\n", ret); + return 1; + } + } else { + err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", x509_algo); + return 1; + } + + gnutls_x509_privkey_deinit(x509_privkey); + return 0; +} + + +int main(int argc, char* argv[]) { + gnutls_datum_t data; + int ret; + gnutls_x509_privkey_t x509_privkey; + + char output_data[10240]; + size_t ods = sizeof(output_data); + + init_gnutls(); + + init_datum(&data); + + /* slurp in the private key from stdin */ + if (ret = set_datum_fd(&data, 0), ret) { + err("didn't read file descriptor 0\n"); + return 1; + } + + + + /* Or, instead, read in key from a file name: + if (ret = set_datum_file(&data, argv[1]), ret) { + err("didn't read file '%s'\n", argv[1]); + return 1; + } +*/ + + /* treat the passed file as an X.509 private key, and extract its + component values: */ + +/* if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { */ +/* err("Failed to import the X.509 key (error: %d)\n", ret); */ +/* return 1; */ +/* } */ +/* gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); */ + + /* try to print the PEM-encoded private key: */ +/* ret = gnutls_x509_privkey_export (x509_privkey, */ +/* GNUTLS_X509_FMT_PEM, */ +/* output_data, */ +/* &ods); */ +/* printf("ret: %u; ods: %u;\n", ret, ods); */ +/* if (ret == 0) { */ +/* write(0, output_data, ods); */ +/* } */ + + + if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { + err("Failed to initialize X.509 private key (error: %d)\n", ret); + return 1; + } + + if (ret = convert_pgp_to_x509(&x509_privkey, &data), ret) { + return ret; + } + + ret = gnutls_x509_privkey_export (x509_privkey, + GNUTLS_X509_FMT_PEM, + output_data, + &ods); + printf("ret: %u; ods: %u;\n", ret, ods); + if (ret == 0) { + write(1, output_data, ods); + } + + + gnutls_x509_privkey_deinit(x509_privkey); + gnutls_global_deinit(); + return 0; +} diff --git a/src/keytrans/ssh2gpg.c b/src/keytrans/ssh2gpg.c new file mode 100644 index 0000000..b14a540 --- /dev/null +++ b/src/keytrans/ssh2gpg.c @@ -0,0 +1,171 @@ +#include "gnutls-helpers.h" + +#include +#include + +/* for waitpid() */ +#include +#include + +/* for time() */ +#include + +/* for htons() */ +#include + + +/* + Author: Daniel Kahn Gillmor + Date: Sun, 2008-04-20 + License: GPL v3 or later + + monkeysphere public key translator: execute this with an ssh + private key on stdin. It currently only works with RSA keys. + + it should eventually work with OpenSSH-style public keys instead of + the full private key, but it was easier to do this way. + + It shoud spit out a version of the public key suitable for acting + as an OpenPGP public sub key packet. + + */ + +int main(int argc, char* argv[]) { + gnutls_datum_t data; + int ret; + gnutls_x509_privkey_t x509_privkey; + gnutls_openpgp_crt_t openpgp_crt; + gnutls_openpgp_keyid_t keyid; + printable_keyid p_keyid; + unsigned int keyidx; + unsigned int usage, bits; + gnutls_pk_algorithm_t algo; + + unsigned char packettag; + unsigned char openpgpversion; + time_t timestamp; + uint32_t clunkytime; + unsigned char openpgpalgo; + unsigned int packetlen; + uint16_t plen; + + gnutls_datum_t m, e, d, p, q, u, g, y; + gnutls_datum_t algolabel; + + char output_data[10240]; + char userid[10240]; + size_t uidsz = sizeof(userid); + + const gnutls_datum_t* all[5]; + int pipefd; + pid_t child_pid; + char* const args[] = {"/usr/bin/base64", "--wrap=0", NULL}; + const char* algoname; + int mpicount; + int pipestatus; + + init_gnutls(); + + init_datum(&data); + + init_datum(&m); + init_datum(&e); + init_datum(&d); + init_datum(&p); + init_datum(&q); + init_datum(&u); + init_datum(&g); + init_datum(&y); + + init_datum(&algolabel); + + init_keyid(keyid); + + /* slurp in the private key from stdin */ + if (ret = set_datum_fd(&data, 0), ret) { + err("didn't read file descriptor 0\n"); + return 1; + } + + + if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { + err("Failed to initialize private key structure (error: %d)\n", ret); + return 1; + } + + err("assuming PEM formatted private key\n"); + if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { + err("failed to import the PEM-encoded private key (error: %d)\n", ret); + return ret; + } + + algo = gnutls_x509_privkey_get_pk_algorithm(x509_privkey); + if (algo < 0) { + err("failed to get the algorithm of the PEM-encoded public key (error: %d)\n", algo); + return algo; + } else if (algo == GNUTLS_PK_RSA) { + err("RSA private key\n"); + ret = gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + err("Modulus size %d, exponent size %d\n", m.size, e.size); + } else if (algo == GNUTLS_PK_DSA) { + err("DSA Key, not implemented!!\n", bits); + return 1; + } else { + err("Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo); + return 1; + } + + /* now we have algo, and the various MPI data are set. Can we + export them as a public subkey packet? */ + + /* this packet should be tagged 14, and should contain: + + 1 octet: version (4) + 4 octets: time of generation (seconds since 1970) + 1 octet: algo (http://tools.ietf.org/html/rfc4880#section-5.5.2 implies 1 for RSA) + + MPI: modulus + MPI: exponent + */ + + packetlen = 1 + 4 + 1; + /* FIXME: this is RSA only. for DSA, there'll be more: */ + packetlen += get_openpgp_mpi_size(&m) + get_openpgp_mpi_size(&e); + + /* FIXME: we should generate this bound more cleanly -- i just + happen to know that 65535 is 2^16-1: */ + if (packetlen > 65535) { + err("packet length is too long (%d)\n", packetlen); + return 1; + } + + /* we're going to emit an old-style packet, with tag 14 (public + subkey), with a two-octet packet length */ + packettag = 0x80 | (14 << 2) | 1; + + write(1, &packettag, sizeof(packettag)); + plen = htons(packetlen); + write(1, &plen, sizeof(plen)); + + openpgpversion = 4; + write(1, &openpgpversion, 1); + + timestamp = time(NULL); + clunkytime = htonl(timestamp); + write(1, &clunkytime, 4); + + /* FIXME: handle things other than RSA */ + openpgpalgo = 1; + write(1, &openpgpalgo, 1); + + write_openpgp_mpi_to_fd(1, &m); + write_openpgp_mpi_to_fd(1, &e); + + gnutls_x509_privkey_deinit(x509_privkey); + gnutls_global_deinit(); + return 0; +} -- cgit v1.2.3 From 6f2d6f78cd11231d6f7ffd6361812b1bd49a4c34 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 12 Jun 2008 17:25:55 -0400 Subject: Major openpgp2ssh overhaul. It's an unforgiving and brittle tool, but it should do what we expect it to do, and its major limitations should be documented in the man page. --- man/man1/openpgp2ssh.1 | 47 ++++-- src/keytrans/gnutls-helpers.c | 68 ++++++++ src/keytrans/gnutls-helpers.h | 4 + src/keytrans/openpgp2ssh.c | 357 +++++++++++++++++++++++++++++++----------- 4 files changed, 371 insertions(+), 105 deletions(-) (limited to 'src/keytrans') diff --git a/man/man1/openpgp2ssh.1 b/man/man1/openpgp2ssh.1 index 6267141..1a02b38 100644 --- a/man/man1/openpgp2ssh.1 +++ b/man/man1/openpgp2ssh.1 @@ -12,16 +12,16 @@ openpgp2ssh .Nm gpg --export-secret-key $KEYID | openpgp2ssh $KEYID .Sh DESCRIPTION -openpgp2ssh takes OpenPGP-formatted RSA and DSA keys on standard -input, and spits out the requested equivalent SSH-style key on -standard output. +openpgp2ssh takes an OpenPGP-formatted primary key and associated +subkeys on standard input, and spits out the requested equivalent +SSH-style key on standard output. -If the data on standard input contains only a single key, you can -invoke openpgp2ssh without arguments. If the data on standard input -contains multiple keys (e.g. a primary key and associated subkeys), -you must specify a specific OpenPGP keyid (e.g. CCD2ED94D21739E9) or +If the data on standard input contains no subkeys, you can invoke +openpgp2ssh without arguments. If the data on standard input contains +multiple keys (e.g. a primary key and associated subkeys), you must +specify a specific OpenPGP keyid (e.g. CCD2ED94D21739E9) or fingerprint as the first argument to indicate which key to export. -The keyid must be at least 8 hex characters. +The keyid must be exactly 16 hex characters. If the input contains an OpenPGP RSA or DSA public key, it will be converted to the OpenSSH-style single-line keystring, prefixed with @@ -31,20 +31,29 @@ insertion into known_hosts files and authorized_keys files. If the input contains an OpenPGP RSA or DSA secret key, it will be converted to the equivalent PEM-encoded private key. -Note that the keys produced by this process are stripped of all -identifying information, including certifications, self-signatures, -etc. - openpgp2ssh is part of the .Xr monkeysphere 1 framework for providing a PKI for SSH. +.Sh CAVEATS +The keys produced by this process are stripped of all identifying +information, including certifications, self-signatures, etc. This is +intentional, since ssh attaches no inherent significance to these +features. + +openpgp2ssh only works with RSA or DSA keys, because those are the +only ones which work with ssh. + +Assuming a valid key type, though, openpgp2ssh will produce output for +any requested key. This means, among other things, that it will +happily export revoked keys, unverifiable keys, expired keys, etc. +Make sure you do your own key validation before using this tool! .Sh EXAMPLES .Nm gpg --export-secret-key $KEYID | openpgp2ssh $KEYID | ssh-add -c /dev/stdin This pushes the secret key into the active .Xr ssh-agent 1 . -Tools (such as -.Xr ssh 1 ) +Tools such as +.Xr ssh 1 which know how to talk to the .Xr ssh-agent 1 can now rely on the key. @@ -58,8 +67,14 @@ lsh(1) and putty(1). Secret key output is currently not passphrase-protected. -This program is not yet implemented, and this man page currently only -describes expected functionality. +openpgp2ssh currently cannot handle passphrase-protected secret keys on input. + +It would be nice to be able to use keyids shorter or longer than 16 +hex characters. + +openpgp2ssh only acts on keys associated with the first primary key +passed in. If you send it more than one primary key, it will silently +ignore later ones. .Sh SEE ALSO .Xr monkeysphere 1 , .Xr ssh 1 , diff --git a/src/keytrans/gnutls-helpers.c b/src/keytrans/gnutls-helpers.c index 6eae29e..7c342bb 100644 --- a/src/keytrans/gnutls-helpers.c +++ b/src/keytrans/gnutls-helpers.c @@ -12,6 +12,9 @@ /* for isalnum() */ #include +/* for exit() */ +#include + int loglevel = 0; @@ -46,6 +49,71 @@ void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid) } } +unsigned char hex2bin(unsigned char x) { + if ((x >= '0') && (x <= '9')) + return x - '0'; + if ((x >= 'A') && (x <= 'F')) + return 10 + x - 'A'; + if ((x >= 'a') && (x <= 'f')) + return 10 + x - 'a'; + return 0xff; +} + +void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) { + unsigned int pkix = 0, outkix = 0; + + while (pkix < sizeof(printable_keyid)) { + unsigned hi = hex2bin(in[pkix]); + unsigned lo = hex2bin(in[pkix + 1]); + if (hi == 0xff) { + err("character '%c' is not a hex char\n", in[pkix]); + exit(1); + } + if (lo == 0xff) { + err("character '%c' is not a hex char\n", in[pkix + 1]); + exit(1); + } + out[outkix] = lo | (hi << 4); + + pkix += 2; + outkix++; + } +} + +int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) { + printable_keyid p; + int ret; + + ret = convert_string_to_printable_keyid(p, str); + if (ret == 0) + collapse_printable_keyid(out, p); + return ret; +} +int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) { + int arglen, x; + arglen = 0; + x = 0; + while ((arglen <= sizeof(printable_keyid)) && + (str[x] != '\0')) { + if (isxdigit(str[x])) { + if (arglen == sizeof(printable_keyid)) { + err("There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str); + return 1; + } + pkeyid[arglen] = str[x]; + arglen++; + } + x++; + } + + if (arglen != sizeof(printable_keyid)) { + err("keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid)); + return 1; + } + return 0; +} + + int init_gnutls() { const char* version = NULL; diff --git a/src/keytrans/gnutls-helpers.h b/src/keytrans/gnutls-helpers.h index 9ea22a3..00cdec7 100644 --- a/src/keytrans/gnutls-helpers.h +++ b/src/keytrans/gnutls-helpers.h @@ -44,6 +44,10 @@ typedef unsigned char printable_keyid[16]; void init_keyid(gnutls_openpgp_keyid_t keyid); void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid); +void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in); +int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str); +int convert_string_to_printable_keyid(printable_keyid out, const char* str); + /* functions to get data into datum objects: */ diff --git a/src/keytrans/openpgp2ssh.c b/src/keytrans/openpgp2ssh.c index d6bac68..30e19d5 100644 --- a/src/keytrans/openpgp2ssh.c +++ b/src/keytrans/openpgp2ssh.c @@ -3,47 +3,47 @@ #include #include +/* for waitpid() */ +#include +#include + /* Author: Daniel Kahn Gillmor - Date: Tue, 01 Apr 2008 + Date: 2008-06-12 13:47:41-0400 License: GPL v3 or later - monkeysphere private key translator: execute this with an GPG - secret key on stdin (at the moment, only passphraseless RSA keys - work). + monkeysphere key translator: execute this with an OpenPGP key on + stdin, (please indicate the specific keyid that you want as the + first argument if there are subkeys). At the moment, only public + keys and passphraseless secret keys work. - It will spit out a PEM-encoded version of the key on stdout, which - can be fed into ssh-add like this: + For secret keys, it will spit out a PEM-encoded version of the key + on stdout, which can be fed into ssh-add like this: - gpg --export-secret-keys $KEYID | monkeysphere | ssh-add -c /dev/stdin + gpg --export-secret-keys $KEYID | openpgp2ssh $KEYID | ssh-add -c /dev/stdin - Requirements: I've only built this so far with GnuTLS v2.3.4 -- - version 2.2.0 does not contain the appropriate pieces. + For public keys, it will spit out a single line of text that can + (with some massaging) be used in an openssh known_hosts or + authorized_keys file. For example: - Notes: gpgkey2ssh doesn't seem to provide the same public - keys. Mighty weird! + echo server.example.org $(gpg --export $KEYID | openpgp2ssh $KEYID) >> ~/.ssh/known_hosts -0 wt215@squeak:~/monkeysphere$ gpg --export-secret-keys 1DCDF89F | ~dkg/src/monkeysphere/monkeysphere | ssh-add -c /dev/stdin -gnutls version: 2.3.4 -OpenPGP RSA Key, with 1024 bits -Identity added: /dev/stdin (/dev/stdin) -The user has to confirm each use of the key -0 wt215@squeak:~/monkeysphere$ ssh-add -L -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9gWQqfrnhQKDQnND/3eOexpddE64J+1zp9fcyCje7H5LKclb6DBV2HS6WgW32PJhIzvP+fYZM3dzXea3fpv14y1SicXiRBDgF9SnsNA1qWn2RyzkLcKy7PmM0PDYtU1oiLTcQj/xkWcqW2sLKHT/WW+vZP5XP7RMGN/yWNMfE2Q== /dev/stdin -0 wt215@squeak:~/monkeysphere$ gpgkey2ssh 1DCDF89F -ssh-rsa AAAAB3NzaC1yc2EAAACBAL2BZCp+ueFAoNCc0P/d457Gl10Trgn7XOn19zIKN7sfkspyVvoMFXYdLpaBbfY8mEjO8/59hkzd3Nd5rd+m/XjLVKJxeJEEOAX1Kew0DWpafZHLOQtwrLs+YzQ8Ni1TWiItNxCP/GRZypbawsodP9Zb69k/lc/tEwY3/JY0x8TZAAAAAwEAAQ== COMMENT -0 wt215@squeak:~/monkeysphere$ + Requirements: I've only built this so far with GnuTLS v2.3.x. + GnuTLS 2.2.x does not contain the appropriate functionality. */ -int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) { - gnutls_openpgp_privkey_t pgp_privkey; +/* FIXME: keyid should be const as well */ +int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_openpgp_privkey_t* pgp_privkey, gnutls_openpgp_keyid_t* keyid) { gnutls_datum_t m, e, d, p, q, u, g, y, x; gnutls_pk_algorithm_t pgp_algo; unsigned int pgp_bits; int ret; +/* FIXME: actually respect keyid argument. At the moment, we just + emit the primary key. */ + init_datum(&m); init_datum(&e); init_datum(&d); @@ -54,34 +54,14 @@ int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) { init_datum(&y); init_datum(&x); - if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) { - err("Failed to initialized OpenPGP private key (error: %d)\n", ret); - return 1; - } - - - /* format could be either: GNUTLS_OPENPGP_FMT_RAW, - GNUTLS_OPENPGP_FMT_BASE64; if MONKEYSPHERE_RAW is set, use RAW, - otherwise, use BASE64: */ - - if (getenv("MONKEYSPHERE_RAW")) { - err("assuming RAW formatted private keys\n"); - if (ret = gnutls_openpgp_privkey_import(pgp_privkey, input, GNUTLS_OPENPGP_FMT_RAW, NULL, 0), ret) - err("failed to import the OpenPGP private key in RAW format (error: %d)\n", ret); - } else { - err("assuming BASE64 formatted private keys\n"); - if (ret = gnutls_openpgp_privkey_import (pgp_privkey, input, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0), ret) - err("failed to import the OpenPGP private key in BASE64 format (error: %d)\n", ret); - } - - pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(pgp_privkey, &pgp_bits); + pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits); if (pgp_algo < 0) { err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo); return 1; } if (pgp_algo == GNUTLS_PK_RSA) { err("OpenPGP RSA Key, with %d bits\n", pgp_bits); - ret = gnutls_openpgp_privkey_export_rsa_raw(pgp_privkey, &m, &e, &d, &p, &q, &u); + ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u); if (GNUTLS_E_SUCCESS != ret) { err ("failed to export RSA key parameters (error: %d)\n", ret); return 1; @@ -94,7 +74,7 @@ int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) { } } else if (pgp_algo == GNUTLS_PK_DSA) { err("OpenPGP DSA Key, with %d bits\n", pgp_bits); - ret = gnutls_openpgp_privkey_export_dsa_raw(pgp_privkey, &p, &q, &g, &y, &x); + ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x); if (GNUTLS_E_SUCCESS != ret) { err ("failed to export DSA key parameters (error: %d)\n", ret); return 1; @@ -116,10 +96,190 @@ int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) { return 1; } - gnutls_openpgp_privkey_deinit(pgp_privkey); return 0; } +/* FIXME: keyid should be const also */ +int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_openpgp_keyid_t* keyid) { + gnutls_openpgp_keyid_t curkeyid; + int ret; + int subkeyidx; + int subkeycount; + int found = 0; + gnutls_datum_t m, e, p, q, g, y, algolabel; + unsigned int bits; + gnutls_pk_algorithm_t algo; + const gnutls_datum_t* all[5]; + const char* algoname; + int mpicount; + /* output_data must be at least 2 chars longer than the maximum possible + algorithm name: */ + char output_data[20]; + + /* variables for the output conversion: */ + int pipestatus; + int pipefd, child_pid; + char* const b64args[] = {"/usr/bin/base64", "--wrap=0", NULL}; + + init_datum(&m); + init_datum(&e); + init_datum(&p); + init_datum(&q); + init_datum(&g); + init_datum(&algolabel); + + + /* figure out if we've got the right thing: */ + subkeycount = gnutls_openpgp_crt_get_subkey_count(*pgp_crt); + if (subkeycount < 0) { + err("Could not determine subkey count (got value %d)\n", subkeycount); + return 1; + } + + if ((keyid == NULL) && + (subkeycount > 0)) { + err("No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1); + return 1; + } + + if (keyid != NULL) { + ret = gnutls_openpgp_crt_get_key_id(*pgp_crt, curkeyid); + if (ret) { + err("Could not get keyid (error: %d)\n", ret); + return 1; + } + } + if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) { + /* we want to export the primary key: */ + err("exporting primary key\n"); + + /* FIXME: this is almost identical to the block below for subkeys. + This clumsiness seems inherent in the gnutls OpenPGP API, + though. ugh. */ + algo = gnutls_openpgp_crt_get_pk_algorithm(*pgp_crt, &bits); + if (algo < 0) { + err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo); + return algo; + } else if (algo == GNUTLS_PK_RSA) { + err("OpenPGP RSA certificate, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + } else if (algo == GNUTLS_PK_DSA) { + err("OpenPGP DSA Key, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export DSA key parameters (error: %d)\n", ret); + return 1; + } + } + found = 1; + + } else { + /* lets trawl through the subkeys until we find the one we want: */ + for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) { + ret = gnutls_openpgp_crt_get_subkey_id(*pgp_crt, subkeyidx, curkeyid); + if (ret) { + err("Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret); + return 1; + } + if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) { + err("exporting subkey index %d\n", subkeyidx); + + /* FIXME: this is almost identical to the block above for the + primary key. */ + algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(*pgp_crt, subkeyidx, &bits); + if (algo < 0) { + err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo); + return algo; + } else if (algo == GNUTLS_PK_RSA) { + err("OpenPGP RSA certificate, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + } else if (algo == GNUTLS_PK_DSA) { + err("OpenPGP DSA Key, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export DSA key parameters (error: %d)\n", ret); + return 1; + } + } + found = 1; + + } + } + } + + if (!found) { + err("Could not find key in input\n"); + return 1; + } + + /* if we made it this far, we've got MPIs, and we've got the + algorithm, so we just need to emit the info */ + if (algo == GNUTLS_PK_RSA) { + algoname = "ssh-rsa"; + mpicount = 3; + + all[0] = &algolabel; + all[1] = &e; + all[2] = &m; + } else if (algo == GNUTLS_PK_DSA) { + algoname = "ssh-dss"; + mpicount = 5; + + all[0] = &algolabel; + all[1] = &p; + all[2] = &q; + all[3] = &g; + all[4] = &y; + } else { + err("Key algorithm was neither DSA nor RSA (it was %d). Can't deal. Sorry!\n", algo); + return 1; + } + + if (ret = datum_from_string(&algolabel, algoname), ret) { + err("couldn't label string (error: %d)\n", ret); + return ret; + } + + snprintf(output_data, sizeof(output_data), "%s ", algoname); + + pipefd = create_writing_pipe(&child_pid, b64args[0], b64args); + if (pipefd < 0) { + err("failed to create a writing pipe (returned %d)\n", pipefd); + return pipefd; + } + + write(1, output_data, strlen(output_data)); + + if (0 != write_data_fd_with_length(pipefd, all, mpicount)) { + err("was not able to write out RSA key data\n"); + return 1; + } + close(pipefd); + if (child_pid != waitpid(child_pid, &pipestatus, 0)) { + err("could not wait for child process to return for some reason.\n"); + return 1; + } + if (pipestatus != 0) { + err("base64 pipe died with return code %d\n", pipestatus); + return pipestatus; + } + + write(1, "\n", 1); + + + return 0; +} + + + int convert_x509_to_pgp(gnutls_openpgp_privkey_t* output, gnutls_datum_t* input) { gnutls_x509_privkey_t x509_privkey; gnutls_datum_t m, e, d, p, q, u, g, y, x; @@ -203,69 +363,88 @@ int main(int argc, char* argv[]) { gnutls_datum_t data; int ret; gnutls_x509_privkey_t x509_privkey; + gnutls_openpgp_privkey_t pgp_privkey; + gnutls_openpgp_crt_t pgp_crt; char output_data[10240]; size_t ods = sizeof(output_data); + + gnutls_openpgp_keyid_t keyid; + gnutls_openpgp_keyid_t* use_keyid; init_gnutls(); + + /* figure out what keyid we should be looking for: */ + use_keyid = NULL; + if (argv[1] != NULL) { + ret = convert_string_to_keyid(keyid, argv[1]); + if (ret != 0) + return ret; + use_keyid = &keyid; + } + init_datum(&data); - /* slurp in the private key from stdin */ + /* slurp in the key from stdin */ if (ret = set_datum_fd(&data, 0), ret) { err("didn't read file descriptor 0\n"); return 1; } - - /* Or, instead, read in key from a file name: - if (ret = set_datum_file(&data, argv[1]), ret) { - err("didn't read file '%s'\n", argv[1]); + if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) { + err("Failed to initialized OpenPGP private key (error: %d)\n", ret); return 1; } -*/ - - /* treat the passed file as an X.509 private key, and extract its - component values: */ - -/* if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { */ -/* err("Failed to import the X.509 key (error: %d)\n", ret); */ -/* return 1; */ -/* } */ -/* gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); */ - - /* try to print the PEM-encoded private key: */ -/* ret = gnutls_x509_privkey_export (x509_privkey, */ -/* GNUTLS_X509_FMT_PEM, */ -/* output_data, */ -/* &ods); */ -/* printf("ret: %u; ods: %u;\n", ret, ods); */ -/* if (ret == 0) { */ -/* write(0, output_data, ods); */ -/* } */ - - - if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { - err("Failed to initialize X.509 private key (error: %d)\n", ret); - return 1; + /* check whether it's a private key or a public key, by trying them: */ + if ((gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0) == 0) || + (gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0) == 0)) { + /* we're dealing with a private key */ + err("Translating private key\n"); + if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { + err("Failed to initialize X.509 private key for output (error: %d)\n", ret); + return 1; + } + + ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, use_keyid); + + gnutls_openpgp_privkey_deinit(pgp_privkey); + if (ret) + return ret; + + ret = gnutls_x509_privkey_export (x509_privkey, + GNUTLS_X509_FMT_PEM, + output_data, + &ods); + if (ret == 0) { + write(1, output_data, ods); + } + gnutls_x509_privkey_deinit(x509_privkey); + + } else { + if (ret = gnutls_openpgp_crt_init(&pgp_crt), ret) { + err("Failed to initialized OpenPGP certificate (error: %d)\n", ret); + return 1; + } + + if ((gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW) == 0) || + (gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64) == 0)) { + /* we're dealing with a public key */ + err("Translating public key\n"); + + ret = emit_public_openssh_from_pgp(&pgp_crt, use_keyid); + + } else { + /* we have no idea what kind of key this is at all anyway! */ + err("Input does contain any form of OpenPGP key I recognize."); + return 1; + } } - if (ret = convert_pgp_to_x509(&x509_privkey, &data), ret) { - return ret; - } - ret = gnutls_x509_privkey_export (x509_privkey, - GNUTLS_X509_FMT_PEM, - output_data, - &ods); - printf("ret: %u; ods: %u;\n", ret, ods); - if (ret == 0) { - write(1, output_data, ods); - } - gnutls_x509_privkey_deinit(x509_privkey); gnutls_global_deinit(); return 0; } -- cgit v1.2.3 From 8042f93c695f373542356739b0812e628f7b8b1d Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 13 Jun 2008 09:34:16 -0400 Subject: keytrans cleanup: getting rid of unimplemented/useless code. --- src/keytrans/Makefile | 9 +- src/keytrans/gpg2ssh.c | 293 --------------------------------------------- src/keytrans/openpgp2ssh.c | 81 ------------- src/keytrans/ssh2gpg.c | 171 -------------------------- 4 files changed, 1 insertion(+), 553 deletions(-) delete mode 100644 src/keytrans/gpg2ssh.c delete mode 100644 src/keytrans/ssh2gpg.c (limited to 'src/keytrans') diff --git a/src/keytrans/Makefile b/src/keytrans/Makefile index fdd4ccd..53fa5dc 100644 --- a/src/keytrans/Makefile +++ b/src/keytrans/Makefile @@ -7,13 +7,6 @@ openpgp2ssh: openpgp2ssh.c gnutls-helpers.o gcc -g -Wall --pedantic -o $@ -c $< clean: - rm -f openpgp2ssh gpg2ssh ssh2gpg *.o - -# deprecated: -gpg2ssh: gpg2ssh.c gnutls-helpers.o - gcc -g -Wall --pedantic -o gpg2ssh gpg2ssh.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o - -ssh2gpg: ssh2gpg.c gnutls-helpers.o - gcc -g -Wall --pedantic -o ssh2gpg ssh2gpg.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o + rm -f openpgp2ssh *.o .PHONY: clean all diff --git a/src/keytrans/gpg2ssh.c b/src/keytrans/gpg2ssh.c deleted file mode 100644 index c99f03f..0000000 --- a/src/keytrans/gpg2ssh.c +++ /dev/null @@ -1,293 +0,0 @@ -#include "gnutls-helpers.h" - -#include -#include - -/* for waitpid() */ -#include -#include - -/* - Author: Daniel Kahn Gillmor - Date: Tue, 08 Apr 2008 - License: GPL v3 or later - - monkeysphere public key translator: execute this with an GPG - certificate (public key(s) + userid(s)) on stdin. It currently - only works with RSA keys. - - It will spit out a version of the first key capable of being used - for authentication on stdout. The output format should be suitable - for appending a known_hosts file. - - Requirements: I've only built this so far with GnuTLS v2.3.4 -- - version 2.2.0 does not contain the appropriate pieces. - - */ - -int main(int argc, char* argv[]) { - gnutls_datum_t data; - int ret; - gnutls_openpgp_crt_t openpgp_crt; - gnutls_openpgp_keyid_t keyid; - printable_keyid p_keyid; - unsigned int keyidx; - unsigned int usage, bits; - gnutls_pk_algorithm_t algo; - - gnutls_datum_t m, e, p, q, g, y; - gnutls_datum_t algolabel; - - char output_data[10240]; - char userid[10240]; - size_t uidsz = sizeof(userid); - - const gnutls_datum_t* all[5]; - int pipefd; - pid_t child_pid; - char* const args[] = {"/usr/bin/base64", "--wrap=0", NULL}; - const char* algoname; - int mpicount; - int pipestatus; - - init_gnutls(); - - init_datum(&data); - - init_datum(&m); - init_datum(&e); - init_datum(&p); - init_datum(&q); - init_datum(&g); - init_datum(&y); - - init_datum(&algolabel); - - init_keyid(keyid); - - /* slurp in the private key from stdin */ - if (ret = set_datum_fd(&data, 0), ret) { - err("didn't read file descriptor 0\n"); - return 1; - } - - - if (ret = gnutls_openpgp_crt_init(&openpgp_crt), ret) { - err("Failed to initialize OpenPGP certificate (error: %d)\n", ret); - return 1; - } - - /* format could be either: GNUTLS_OPENPGP_FMT_RAW, - GNUTLS_OPENPGP_FMT_BASE64; if MONKEYSPHERE_RAW is set, use RAW, - otherwise, use BASE64: */ - - /* FIXME: we should be auto-detecting the input format, and - translating it as needed. */ - - if (getenv("MONKEYSPHERE_RAW")) { - err("assuming RAW formatted certificate\n"); - if (ret = gnutls_openpgp_crt_import(openpgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW), ret) { - err("failed to import the OpenPGP certificate in RAW format (error: %d)\n", ret); - return ret; - } - } else { - err("assuming BASE64 formatted certificate\n"); - if (ret = gnutls_openpgp_crt_import (openpgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64), ret) { - err("failed to import the OpenPGP certificate in BASE64 format (error: %d)\n", ret); - return ret; - } - } - - if (gnutls_openpgp_crt_get_revoked_status(openpgp_crt)) { - err("the primary key was revoked!\n"); - return 1; - } - - /* FIXME: We're currently looking at the primary key or maybe the - first authentication-capable subkey. - - Instead, we should be iterating through the primary key and all - subkeys: for each one with the authentication usage flag set of a - algorithm we can handle, we should output matching UserIDs and - the SSH version of the key. */ - - - if (ret = gnutls_openpgp_crt_get_key_usage(openpgp_crt, &usage), ret) { - err("failed to get the usage flags for the primary key (error: %d)\n", ret); - return ret; - } - if (usage & GNUTLS_KEY_KEY_AGREEMENT && - usage & GNUTLS_KEY_KEY_ENCIPHERMENT) { - err("the primary key can be used for authentication and communication encryption!\n"); - - algo = gnutls_openpgp_crt_get_pk_algorithm(openpgp_crt, &bits); - if (algo < 0) { - err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo); - return algo; - } else if (algo == GNUTLS_PK_RSA) { - - err("OpenPGP RSA certificate, with %d bits\n", bits); - ret = gnutls_openpgp_crt_get_pk_rsa_raw(openpgp_crt, &m, &e); - if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export RSA key parameters (error: %d)\n", ret); - return 1; - } - } else if (algo == GNUTLS_PK_DSA) { - err("OpenPGP DSA Key, with %d bits\n", bits); - ret = gnutls_openpgp_crt_get_pk_dsa_raw(openpgp_crt, &p, &q, &g, &y); - if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export DSA key parameters (error: %d)\n", ret); - return 1; - } - } else { - err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo); - return 1; - } - - } else { - err("primary key is not good for authentication and communication encryption. Trying subkeys...\n"); - - if (ret = gnutls_openpgp_crt_get_auth_subkey(openpgp_crt, keyid, 0), ret) { - err("failed to find a subkey capable of authentication and communication encryption (error: %d)\n", ret); - return ret; - } - make_keyid_printable(p_keyid, keyid); - err("found authentication subkey %.16s\n", p_keyid); - - ret = gnutls_openpgp_crt_get_subkey_idx(openpgp_crt, keyid); - if (ret < 0) { - err("could not get the index of subkey %.16s (error: %d)\n", ret); - return ret; - } - keyidx = ret; - - if (gnutls_openpgp_crt_get_subkey_revoked_status(openpgp_crt, keyidx)) { - err("The authentication subkey was revoked!\n"); - return 1; - } - - if (ret = gnutls_openpgp_crt_get_subkey_usage(openpgp_crt, keyidx, &usage), ret) { - err("could not figure out usage of subkey %.16s (error: %d)\n", p_keyid, ret); - return ret; - } - if ((usage & GNUTLS_KEY_KEY_AGREEMENT) == 0 && - usage & GNUTLS_KEY_KEY_ENCIPHERMENT) { - err("could not find a subkey with authentication and communication encryption.\n"); - return 1; - } - - /* switch, based on the algorithm in question, to extract the MPI - components: */ - - algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(openpgp_crt, keyidx, &bits); - if (algo < 0) { - err("failed to get the algorithm of the authentication subkey (error: %d)\n", algo); - return algo; - } else if (algo == GNUTLS_PK_RSA) { - - err("OpenPGP RSA subkey, with %d bits\n", bits); - ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(openpgp_crt, keyidx, &m, &e); - if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export RSA subkey parameters (error: %d)\n", ret); - return 1; - } - } else if (algo == GNUTLS_PK_DSA) { - err("OpenPGP DSA subkey, with %d bits\n", bits); - ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(openpgp_crt, keyidx, &p, &q, &g, &y); - if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export DSA subkey parameters (error: %d)\n", ret); - return 1; - } - } else { - err("OpenPGP subkey was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo); - return 1; - } - } - - /* make sure userid is NULL-terminated */ - userid[sizeof(userid) - 1] = 0; - uidsz--; - - /* FIXME: we're just choosing the first UserID from the certificate: - instead, we should be selecting every User ID that is adequately - signed and matches the spec, and aggregating them with commas for - known_hosts output */ - - if (ret = gnutls_openpgp_crt_get_name(openpgp_crt, 0, userid, &uidsz), ret) { - err("Failed to fetch the first UserID (error: %d)\n", ret); - return ret; - } - - if (ret = validate_ssh_host_userid(userid), ret) { - err("bad userid: not a valid ssh host.\n"); - return ret; - } - - /* remove ssh:// from the beginning of userid */ - memmove(userid, userid + strlen("ssh://"), 1 + strlen(userid) - strlen("ssh://")); - - - /* now we have algo, and the various MPI data are set. Can we - export them cleanly? */ - - /* for the moment, we'll just dump the info raw, and pipe it - externally through coreutils' /usr/bin/base64 */ - - if (algo == GNUTLS_PK_RSA) { - algoname = "ssh-rsa"; - mpicount = 3; - - all[0] = &algolabel; - all[1] = &e; - all[2] = &m; - } else if (algo == GNUTLS_PK_DSA) { - algoname = "ssh-dss"; - mpicount = 5; - - all[0] = &algolabel; - all[1] = &p; - all[2] = &q; - all[3] = &g; - all[4] = &y; - } else { - err("no idea what this algorithm is: %d\n", algo); - return 1; - } - - if (ret = datum_from_string(&algolabel, algoname), ret) { - err("couldn't label string (error: %d)\n", ret); - return ret; - } - - snprintf(output_data, sizeof(output_data), "%s %s ", userid, algoname); - - pipefd = create_writing_pipe(&child_pid, args[0], args); - if (pipefd < 0) { - err("failed to create a writing pipe (returned %d)\n", pipefd); - return pipefd; - } - - write(1, output_data, strlen(output_data)); - - if (0 != write_data_fd_with_length(pipefd, all, mpicount)) { - err("was not able to write out RSA key data\n"); - return 1; - } - close(pipefd); - if (child_pid != waitpid(child_pid, &pipestatus, 0)) { - err("could not wait for child process to return for some reason.\n"); - return 1; - } - if (pipestatus != 0) { - err("base64 pipe died with return code %d\n", pipestatus); - return pipestatus; - } - - write(1, "\n", 1); - - - - gnutls_openpgp_crt_deinit(openpgp_crt); - gnutls_global_deinit(); - return 0; -} diff --git a/src/keytrans/openpgp2ssh.c b/src/keytrans/openpgp2ssh.c index 30e19d5..10351e6 100644 --- a/src/keytrans/openpgp2ssh.c +++ b/src/keytrans/openpgp2ssh.c @@ -278,87 +278,6 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope return 0; } - - -int convert_x509_to_pgp(gnutls_openpgp_privkey_t* output, gnutls_datum_t* input) { - gnutls_x509_privkey_t x509_privkey; - gnutls_datum_t m, e, d, p, q, u, g, y, x; - gnutls_pk_algorithm_t x509_algo; - int ret; - - init_datum(&m); - init_datum(&e); - init_datum(&d); - init_datum(&p); - init_datum(&q); - init_datum(&u); - init_datum(&g); - init_datum(&y); - init_datum(&x); - - if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { - err("Failed to initialized X.509 private key (error: %d)\n", ret); - return 1; - } - - - /* format could be either: GNUTLS_X509_FMT_DER, - GNUTLS_X509_FMT_PEM; if MONKEYSPHERE_DER is set, use DER, - otherwise, use PEM: */ - - if (getenv("MONKEYSPHERE_DER")) { - err("assuming DER formatted private keys\n"); - if (ret = gnutls_x509_privkey_import(x509_privkey, input, GNUTLS_X509_FMT_DER), ret) - err("failed to import the X.509 private key in DER format (error: %d)\n", ret); - } else { - err("assuming PEM formatted private keys\n"); - if (ret = gnutls_x509_privkey_import (x509_privkey, input, GNUTLS_X509_FMT_PEM), ret) - err("failed to import the X.509 private key in PEM format (error: %d)\n", ret); - } - - x509_algo = gnutls_x509_privkey_get_pk_algorithm(x509_privkey); - if (x509_algo < 0) { - err("failed to get X.509 key algorithm (error: %d)\n", x509_algo); - return 1; - } - if (x509_algo == GNUTLS_PK_RSA) { - err("X.509 RSA Key\n"); - ret = gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); - if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export RSA key parameters (error: %d)\n", ret); - return 1; - } - - /* ret = gnutls_openpgp_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u); */ - ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; - if (GNUTLS_E_SUCCESS != ret) { - err ("failed to import RSA key parameters (error: %d)\n", ret); - return 1; - } - } else if (x509_algo == GNUTLS_PK_DSA) { - err("X.509 DSA Key\n"); - ret = gnutls_x509_privkey_export_dsa_raw(x509_privkey, &p, &q, &g, &y, &x); - if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export DSA key parameters (error: %d)\n", ret); - return 1; - } - - /* ret = gnutls_openpgp_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x); */ - ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; - if (GNUTLS_E_SUCCESS != ret) { - err ("failed to import DSA key parameters (error: %d)\n", ret); - return 1; - } - } else { - err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", x509_algo); - return 1; - } - - gnutls_x509_privkey_deinit(x509_privkey); - return 0; -} - - int main(int argc, char* argv[]) { gnutls_datum_t data; int ret; diff --git a/src/keytrans/ssh2gpg.c b/src/keytrans/ssh2gpg.c deleted file mode 100644 index b14a540..0000000 --- a/src/keytrans/ssh2gpg.c +++ /dev/null @@ -1,171 +0,0 @@ -#include "gnutls-helpers.h" - -#include -#include - -/* for waitpid() */ -#include -#include - -/* for time() */ -#include - -/* for htons() */ -#include - - -/* - Author: Daniel Kahn Gillmor - Date: Sun, 2008-04-20 - License: GPL v3 or later - - monkeysphere public key translator: execute this with an ssh - private key on stdin. It currently only works with RSA keys. - - it should eventually work with OpenSSH-style public keys instead of - the full private key, but it was easier to do this way. - - It shoud spit out a version of the public key suitable for acting - as an OpenPGP public sub key packet. - - */ - -int main(int argc, char* argv[]) { - gnutls_datum_t data; - int ret; - gnutls_x509_privkey_t x509_privkey; - gnutls_openpgp_crt_t openpgp_crt; - gnutls_openpgp_keyid_t keyid; - printable_keyid p_keyid; - unsigned int keyidx; - unsigned int usage, bits; - gnutls_pk_algorithm_t algo; - - unsigned char packettag; - unsigned char openpgpversion; - time_t timestamp; - uint32_t clunkytime; - unsigned char openpgpalgo; - unsigned int packetlen; - uint16_t plen; - - gnutls_datum_t m, e, d, p, q, u, g, y; - gnutls_datum_t algolabel; - - char output_data[10240]; - char userid[10240]; - size_t uidsz = sizeof(userid); - - const gnutls_datum_t* all[5]; - int pipefd; - pid_t child_pid; - char* const args[] = {"/usr/bin/base64", "--wrap=0", NULL}; - const char* algoname; - int mpicount; - int pipestatus; - - init_gnutls(); - - init_datum(&data); - - init_datum(&m); - init_datum(&e); - init_datum(&d); - init_datum(&p); - init_datum(&q); - init_datum(&u); - init_datum(&g); - init_datum(&y); - - init_datum(&algolabel); - - init_keyid(keyid); - - /* slurp in the private key from stdin */ - if (ret = set_datum_fd(&data, 0), ret) { - err("didn't read file descriptor 0\n"); - return 1; - } - - - if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { - err("Failed to initialize private key structure (error: %d)\n", ret); - return 1; - } - - err("assuming PEM formatted private key\n"); - if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { - err("failed to import the PEM-encoded private key (error: %d)\n", ret); - return ret; - } - - algo = gnutls_x509_privkey_get_pk_algorithm(x509_privkey); - if (algo < 0) { - err("failed to get the algorithm of the PEM-encoded public key (error: %d)\n", algo); - return algo; - } else if (algo == GNUTLS_PK_RSA) { - err("RSA private key\n"); - ret = gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); - if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export RSA key parameters (error: %d)\n", ret); - return 1; - } - err("Modulus size %d, exponent size %d\n", m.size, e.size); - } else if (algo == GNUTLS_PK_DSA) { - err("DSA Key, not implemented!!\n", bits); - return 1; - } else { - err("Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo); - return 1; - } - - /* now we have algo, and the various MPI data are set. Can we - export them as a public subkey packet? */ - - /* this packet should be tagged 14, and should contain: - - 1 octet: version (4) - 4 octets: time of generation (seconds since 1970) - 1 octet: algo (http://tools.ietf.org/html/rfc4880#section-5.5.2 implies 1 for RSA) - - MPI: modulus - MPI: exponent - */ - - packetlen = 1 + 4 + 1; - /* FIXME: this is RSA only. for DSA, there'll be more: */ - packetlen += get_openpgp_mpi_size(&m) + get_openpgp_mpi_size(&e); - - /* FIXME: we should generate this bound more cleanly -- i just - happen to know that 65535 is 2^16-1: */ - if (packetlen > 65535) { - err("packet length is too long (%d)\n", packetlen); - return 1; - } - - /* we're going to emit an old-style packet, with tag 14 (public - subkey), with a two-octet packet length */ - packettag = 0x80 | (14 << 2) | 1; - - write(1, &packettag, sizeof(packettag)); - plen = htons(packetlen); - write(1, &plen, sizeof(plen)); - - openpgpversion = 4; - write(1, &openpgpversion, 1); - - timestamp = time(NULL); - clunkytime = htonl(timestamp); - write(1, &clunkytime, 4); - - /* FIXME: handle things other than RSA */ - openpgpalgo = 1; - write(1, &openpgpalgo, 1); - - write_openpgp_mpi_to_fd(1, &m); - write_openpgp_mpi_to_fd(1, &e); - - gnutls_x509_privkey_deinit(x509_privkey); - gnutls_global_deinit(); - return 0; -} -- cgit v1.2.3 From e983d61545f58db81429d82a356dfadafd491dd0 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 13 Jun 2008 09:41:16 -0400 Subject: added top-level Makefile for ease of building with debhelper. stupid whitespace cleanup in openpgp2ssh.c --- Makefile | 9 +++++++++ src/keytrans/openpgp2ssh.c | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Makefile (limited to 'src/keytrans') diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b28e54e --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +all: keytrans + +keytrans: + $(MAKE) -C src/keytrans + +clean: + $(MAKE) -C src/keytrans clean + +.PHONY: all clean diff --git a/src/keytrans/openpgp2ssh.c b/src/keytrans/openpgp2ssh.c index 10351e6..4593b33 100644 --- a/src/keytrans/openpgp2ssh.c +++ b/src/keytrans/openpgp2ssh.c @@ -273,7 +273,6 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope } write(1, "\n", 1); - return 0; } -- cgit v1.2.3 From 03cc847e6fc901ab4c1920324910126158655e37 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 13 Jun 2008 11:18:00 -0400 Subject: monkeysphere debianization. Package can now be cleanly built with minimal lintian warnings. --- COPYING | 6 +++--- Makefile | 5 ++++- debian/changelog | 5 +++-- debian/control | 6 +++--- debian/copyright | 9 +++++++-- man/man1/openpgp2ssh.1 | 54 ++++++++++++++++++++++++++++++-------------------- src/keytrans/Makefile | 2 +- test.key | 27 ------------------------- 8 files changed, 53 insertions(+), 61 deletions(-) delete mode 100644 test.key (limited to 'src/keytrans') diff --git a/COPYING b/COPYING index ab8788d..c920a0e 100644 --- a/COPYING +++ b/COPYING @@ -1,13 +1,13 @@ MonkeySphere is a system to use the OpenPGP web-of-trust to authenticate and encrypt ssh connections. -It is free software, written by: +It is free software, developed by: Jameson Rollins - Daniel Kahn Gillmor -with much help from: + Daniel Kahn Gillmor Jamie McClelland Micah Anderson Matthew Goins + Mike Castleman MonkeySphere is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/Makefile b/Makefile index b28e54e..64e6cbe 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,10 @@ all: keytrans keytrans: $(MAKE) -C src/keytrans +release: clean + tar c COPYING doc etc Makefile man src | gzip -n > ../monkeysphere_`head -n1 debian/changelog | sed 's/.*(\([^-]*\)-.*/\1/'`.orig.tar.gz + clean: $(MAKE) -C src/keytrans clean -.PHONY: all clean +.PHONY: all clean release diff --git a/debian/changelog b/debian/changelog index 2b68de6..ec744e1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,7 @@ monkeysphere (0.1-1) unstable; urgency=low - * to be first release... + * First release of debian package for monkeysphere. + * This is experimental -- please report bugs! - -- Jameson Graef Rollins Tue, 10 Jun 2008 17:20:16 -0400 + -- Daniel Kahn Gillmor Fri, 13 Jun 2008 10:53:43 -0400 diff --git a/debian/control b/debian/control index e190ae0..afd5bfa 100644 --- a/debian/control +++ b/debian/control @@ -1,18 +1,18 @@ Source: monkeysphere Section: net Priority: extra -Maintainer: Daniel Kahn Gillmor +Maintainer: Daniel Kahn Gillmor Uploaders: Jameson Rollins Build-Depends: debhelper (>= 7.0), libgnutls-dev (>= 2.3.14) Standards-Version: 3.8.0.1 Homepage: http://cmrg.fifthhorseman.net/wiki/OpenPGPandSSH -Enhances: openssh-client, openssh-server Dm-Upload-Allowed: yes Package: monkeysphere Architecture: any -Depends: openssh-client, gnupg | gnupg2, coreutils (>= 6) +Depends: openssh-client, gnupg | gnupg2, coreutils (>= 6), ${shlibs:Depends} Recommends: netcat +Enhances: openssh-client, openssh-server Description: use the OpenPGP web of trust to verify ssh connections SSH key-based authentication is tried-and-true, but it lacks a true Public Key Infrastructure for key certification, revocation and diff --git a/debian/copyright b/debian/copyright index 413f60f..11abe8b 100644 --- a/debian/copyright +++ b/debian/copyright @@ -4,8 +4,13 @@ Debianized-Date: Fri Jun 13 10:19:16 EDT 2008 Original-Source: http://lair.fifthhorseman.net/~dkg/git/monkeysphere.git/ Files: * -Copyright: Jameson Rollins , - Daniel Kahn Gillmor +Copyright: 2008 Jameson Rollins , + Daniel Kahn Gillmor , + Jamie McClelland , + Micah Anderson , + Matthew Goins , + Mike Castleman + License: GPL-3+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/man/man1/openpgp2ssh.1 b/man/man1/openpgp2ssh.1 index 1a02b38..83b6154 100644 --- a/man/man1/openpgp2ssh.1 +++ b/man/man1/openpgp2ssh.1 @@ -7,31 +7,34 @@ openpgp2ssh .Nd translate OpenPGP keys to SSH keys .Sh SYNOPSIS .Nm openpgp2ssh < mykey.gpg - +.Pp .Nm gpg --export $KEYID | openpgp2ssh $KEYID - +.Pp .Nm gpg --export-secret-key $KEYID | openpgp2ssh $KEYID .Sh DESCRIPTION -openpgp2ssh takes an OpenPGP-formatted primary key and associated +.Nm +takes an OpenPGP-formatted primary key and associated subkeys on standard input, and spits out the requested equivalent SSH-style key on standard output. - +.Pp If the data on standard input contains no subkeys, you can invoke -openpgp2ssh without arguments. If the data on standard input contains +.Nm +without arguments. If the data on standard input contains multiple keys (e.g. a primary key and associated subkeys), you must specify a specific OpenPGP keyid (e.g. CCD2ED94D21739E9) or fingerprint as the first argument to indicate which key to export. The keyid must be exactly 16 hex characters. - +.Pp If the input contains an OpenPGP RSA or DSA public key, it will be converted to the OpenSSH-style single-line keystring, prefixed with the key type. This format is suitable (with minor alterations) for insertion into known_hosts files and authorized_keys files. - +.Pp If the input contains an OpenPGP RSA or DSA secret key, it will be converted to the equivalent PEM-encoded private key. - -openpgp2ssh is part of the +.Pp +.Nm +is part of the .Xr monkeysphere 1 framework for providing a PKI for SSH. .Sh CAVEATS @@ -39,17 +42,20 @@ The keys produced by this process are stripped of all identifying information, including certifications, self-signatures, etc. This is intentional, since ssh attaches no inherent significance to these features. - -openpgp2ssh only works with RSA or DSA keys, because those are the +.Pp +.Nm +only works with RSA or DSA keys, because those are the only ones which work with ssh. - -Assuming a valid key type, though, openpgp2ssh will produce output for +.Pp +Assuming a valid key type, though, +.Nm +will produce output for any requested key. This means, among other things, that it will happily export revoked keys, unverifiable keys, expired keys, etc. Make sure you do your own key validation before using this tool! .Sh EXAMPLES .Nm gpg --export-secret-key $KEYID | openpgp2ssh $KEYID | ssh-add -c /dev/stdin - +.Pp This pushes the secret key into the active .Xr ssh-agent 1 . Tools such as @@ -58,21 +64,25 @@ which know how to talk to the .Xr ssh-agent 1 can now rely on the key. .Sh AUTHOR -openpgp2ssh and this man page were written by Daniel Kahn Gillmor +.Nm +and this man page were written by Daniel Kahn Gillmor . .Sh BUGS -openpgp2ssh currently only exports into formats used by the OpenSSH. +.Nm +currently only exports into formats used by the OpenSSH. It should support other key output formats, such as those used by lsh(1) and putty(1). - +.Pp Secret key output is currently not passphrase-protected. - -openpgp2ssh currently cannot handle passphrase-protected secret keys on input. - +.Pp +.Nm +currently cannot handle passphrase-protected secret keys on input. +.Pp It would be nice to be able to use keyids shorter or longer than 16 hex characters. - -openpgp2ssh only acts on keys associated with the first primary key +.Pp +.Nm +only acts on keys associated with the first primary key passed in. If you send it more than one primary key, it will silently ignore later ones. .Sh SEE ALSO diff --git a/src/keytrans/Makefile b/src/keytrans/Makefile index 53fa5dc..79602ef 100644 --- a/src/keytrans/Makefile +++ b/src/keytrans/Makefile @@ -1,7 +1,7 @@ all: openpgp2ssh openpgp2ssh: openpgp2ssh.c gnutls-helpers.o - gcc -g -Wall --pedantic -o openpgp2ssh openpgp2ssh.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o + gcc -g -Wall --pedantic -o openpgp2ssh openpgp2ssh.c `libgnutls-config --libs --cflags` gnutls-helpers.o %.o: %.c gcc -g -Wall --pedantic -o $@ -c $< diff --git a/test.key b/test.key deleted file mode 100644 index 4e05880..0000000 --- a/test.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAxMv33LvWBZnKtahorHGYdBZqVxrNUQcVNrgxp4bf/FvgvSLG -kBrw6wHFdVYvWWViD5efrJugqA4+pKp16LEWlc7JZICrou4vEJGkvoqBIJC/4cVN -xcwV1a8jo9ZOYjt0JIyuHrEDGW/edQYWI41XO/H+QdMDsdI+oOmfPV/V4eMyjGKH -vRJ+xDae5izhUb3Lb00YnxpP2n/zhvHpn7weu+bzvwb3pMMo9336Ft7m5ulGPJzN -+3l595LW+lUSDUlUJbACp4Nyn+i9ODPV6xzghzirsh7rnD8jD2kaqIVkcvEhusoB -JN3daPXt9t6m5cfsCWu31BXdbpTWiLIZRUxDzQIBIwKCAQEAl9CMAg0+s90KFxuD -8r4H5IZSCK5GnZe/6GI07vMEj3oTxRrTsP7XG7DoyDsr1z+UyjMjZ+XFE+27S9P0 -ju8Cy1Zg2ICEZ78OXT0nUSkEhtYQXbV2gqTAYwNzQ9/WEUPOn9o9LZ5+u9n0wKzs -gdNvLj5WbUsC2aIwUD8xswDJkP5cA4RfKo8Mz40aXbK6b+S/bOKEkXRFvOor46pl -A8GHxUVcUPUG7LAXCm1FWrDob6FTlv3yW8DeVTCYwt6HdrTmc9b+yOinwMR6ZvUz -R6AESGG7czCvA6rpkCcprfCPx0gfntuzLiGRtl54GvbYWWtPDlxnPwcw1zcSALvM -pJNpawKBgQD/zze04kYZBNDTxolBrZltpPXtPpOrG2Otp8CHreOKn0TifCFPDnCb -ewUhxuDRA+L9KPLT311DtHfIzXJ8/RD6K/QE72ny39h2X2Pn2hWSgb9+iysHBDNc -jb136QFoKQcpqUpLEfTvA71Yqvuk6gsYiuWnIN5KJwy/AhwFQnK/WQKBgQDE8X87 -C+0JSg2ybUopOQVSrvildJEa8CWbM1SAL1j3E24U2fPh+zVmIxqa2m4X/PxFBBTv -WVGayzFkmJK2Dgt7F7hBqi5HelP0B38dXtkPlK6idTALNHoS/7HCDXISgHmDOhcQ -LHGQUuQMkTq6H4cOMwTNO5aM2zc5E9uF/hptlQKBgEHHkftQIKdZAn+Zc8Bud+j+ -iGGTv5JmIPIj0mwIJJFcJ6f0CJCr8RIJsNzMvXeTSP9FCz3LuOWGLW4mM2H37mw3 -MB6GtNgNrLC5cXYiIs3m2XhPq/p9bEr/4ENnzSlposGR7ohVExjjtFig/uFDfzIy -WE+MG+cunOCoxWBwLCKTAoGBALQP/0vtpYTV/eT2NS0A7uyCt3Kzt94dZDYgTUH/ -Z0hMR2OFcUOj2Qzs5R/dpnxVA+dUMGXOAXeVNHk7CcsFhtbxHX3dbCQYEj4yvVyu -fVAS6M8MDqsoqh//uHbnuMB1dmlZrq+zmwecPjdgNbF76TGNuz9MbGOGmOO6Yk6f -LhsLAoGAJoK+yRDaEFDwrjdvGazEy/d1CtknkGY2r4vb8giEodFJcQQhtVtjnYPl -gDIpbcpeT0GDiZd0pxAxpibbKM63pYz8PKtlq0B/qXgArRgJnbku01Jc4iLVWPqK -qitRgsz1HdN2tIqa8oQE0iuvyoq+r6+pqcQJd7sc6lKlk0gO0Mo= ------END RSA PRIVATE KEY----- -- cgit v1.2.3 From 1fe3ced39fb67b32b1cce11cd692e492221da930 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 13 Jun 2008 13:05:42 -0400 Subject: making openpgp2ssh less verbose. --- src/keytrans/gnutls-helpers.c | 85 ++++++++++++++++++++++--------------------- src/keytrans/gnutls-helpers.h | 2 +- src/keytrans/openpgp2ssh.c | 78 +++++++++++++++++++-------------------- 3 files changed, 84 insertions(+), 81 deletions(-) (limited to 'src/keytrans') diff --git a/src/keytrans/gnutls-helpers.c b/src/keytrans/gnutls-helpers.c index 7c342bb..c6e4979 100644 --- a/src/keytrans/gnutls-helpers.c +++ b/src/keytrans/gnutls-helpers.c @@ -15,11 +15,12 @@ /* for exit() */ #include -int loglevel = 0; +static int loglevel = 0; - -void err(const char* fmt, ...) { +void err(int level, const char* fmt, ...) { va_list ap; + if (level < loglevel) + return; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); @@ -66,11 +67,11 @@ void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) { unsigned hi = hex2bin(in[pkix]); unsigned lo = hex2bin(in[pkix + 1]); if (hi == 0xff) { - err("character '%c' is not a hex char\n", in[pkix]); + err(0, "character '%c' is not a hex char\n", in[pkix]); exit(1); } if (lo == 0xff) { - err("character '%c' is not a hex char\n", in[pkix + 1]); + err(0, "character '%c' is not a hex char\n", in[pkix + 1]); exit(1); } out[outkix] = lo | (hi << 4); @@ -97,7 +98,7 @@ int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) { (str[x] != '\0')) { if (isxdigit(str[x])) { if (arglen == sizeof(printable_keyid)) { - err("There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str); + err(0, "There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str); return 1; } pkeyid[arglen] = str[x]; @@ -107,7 +108,7 @@ int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) { } if (arglen != sizeof(printable_keyid)) { - err("keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid)); + err(0, "Keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid)); return 1; } return 0; @@ -120,27 +121,29 @@ int init_gnutls() { const char* debug_string = NULL; int ret; + if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) { + loglevel = atoi(debug_string); + } + if (ret = gnutls_global_init(), ret) { - err("Failed to do gnutls_global_init() (error: %d)\n", ret); + err(0, "Failed to do gnutls_global_init() (error: %d)\n", ret); return 1; } version = gnutls_check_version(NULL); if (version) - err("gnutls version: %s\n", version); + err(1, "gnutls version: %s\n", version); else { - err("no version found!\n"); + err(0, "no gnutls version found!\n"); return 1; } - if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) { - loglevel = atoi(debug_string); - gnutls_global_set_log_function(logfunc); - - gnutls_global_set_log_level(loglevel); - err("set log level to %d\n", loglevel); - } + gnutls_global_set_log_function(logfunc); + + gnutls_global_set_log_level(loglevel); + err(1, "set log level to %d\n", loglevel); + return 0; } @@ -155,11 +158,11 @@ void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) { } int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) { if (a->size > b->size) { - err("a is larger\n"); + err(0,"a is larger\n"); return 1; } if (a->size < b->size) { - err("b is larger\n"); + err(0,"b is larger\n"); return -1; } return memcmp(a->data, b->data, a->size); @@ -195,7 +198,7 @@ int set_datum_fd(gnutls_datum_t* d, int fd) { bufsize = 1024; d->data = gnutls_realloc(d->data, bufsize); if (d->data == NULL) { - err("out of memory!\n"); + err(0,"out of memory!\n"); return -1; } d->size = bufsize; @@ -204,7 +207,7 @@ int set_datum_fd(gnutls_datum_t* d, int fd) { } f = fdopen(fd, "r"); if (NULL == f) { - err("could not fdopen FD %d\n", fd); + err(0,"could not fdopen FD %d\n", fd); } clearerr(f); while (!feof(f) && !ferror(f)) { @@ -213,16 +216,16 @@ int set_datum_fd(gnutls_datum_t* d, int fd) { bufsize *= 2; d->data = gnutls_realloc(d->data, bufsize); if (d->data == NULL) { - err("out of memory!\n"); + err(0,"out of memory!\n"); return -1; }; d->size = bufsize; } len += fread(d->data + len, 1, bufsize - len, f); - /* err("read %d bytes\n", len); */ + /* err(0,"read %d bytes\n", len); */ } if (ferror(f)) { - err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno)); + err(0,"Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno)); return -1; } @@ -241,13 +244,13 @@ int set_datum_file(gnutls_datum_t* d, const char* fname) { size_t x = 0; if (0 != stat(fname, &sbuf)) { - err("failed to stat '%s'\n", fname); + err(0,"failed to stat '%s'\n", fname); return -1; } c = gnutls_realloc(d->data, sbuf.st_size); if (NULL == c) { - err("failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname); + err(0,"failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname); return -1; } @@ -255,13 +258,13 @@ int set_datum_file(gnutls_datum_t* d, const char* fname) { d->size = sbuf.st_size; file = fopen(fname, "r"); if (NULL == file) { - err("failed to open '%s' for reading\n", fname); + err(0,"failed to open '%s' for reading\n", fname); return -1; } x = fread(d->data, d->size, 1, file); if (x != 1) { - err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname); + err(0,"tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname); fclose(file); return -1; } @@ -271,7 +274,7 @@ int set_datum_file(gnutls_datum_t* d, const char* fname) { int write_datum_fd(int fd, const gnutls_datum_t* d) { if (d->size != write(fd, d->data, d->size)) { - err("failed to write body of datum.\n"); + err(0,"failed to write body of datum.\n"); return -1; } return 0; @@ -293,12 +296,12 @@ int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) { len = htonl(d->size); } if (write(fd, &len, sizeof(len)) != sizeof(len)) { - err("failed to write size of datum.\n"); + err(0,"failed to write size of datum.\n"); return -2; } if (looks_negative) { if (write(fd, &zero, 1) != 1) { - err("failed to write padding byte for MPI.\n"); + err(0,"failed to write padding byte for MPI.\n"); return -2; } } @@ -332,29 +335,29 @@ int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) { int ret; if (pid == NULL) { - err("bad pointer passed to create_writing_pipe()\n"); + err(0,"bad pointer passed to create_writing_pipe()\n"); return -1; } if (ret = pipe(p), ret == -1) { - err("failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno)); + err(0,"failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno)); return -1; } *pid = fork(); if (*pid == -1) { - err("Failed to fork (error: %d \"%s\")\n", errno, strerror(errno)); + err(0,"Failed to fork (error: %d \"%s\")\n", errno, strerror(errno)); return -1; } if (*pid == 0) { /* this is the child */ close(p[1]); /* close unused write end */ if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */ - err("Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno)); + err(0,"Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno)); exit(1); } execv(path, argv); - err("exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno)); + err(0,"exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno)); /* close the open file descriptors */ close(p[0]); close(0); @@ -372,14 +375,14 @@ int validate_ssh_host_userid(const char* userid) { /* choke if userid does not match the expected format ("ssh://fully.qualified.domain.name") */ if (strncmp("ssh://", userid, strlen("ssh://")) != 0) { - err("The user ID should start with ssh:// for a host key\n"); + err(0,"The user ID should start with ssh:// for a host key\n"); goto fail; } /* so that isalnum will work properly */ userid += strlen("ssh://"); while (0 != (*userid)) { if (!isalnum(*userid)) { - err("label did not start with a letter or a digit! (%s)\n", userid); + err(0,"label did not start with a letter or a digit! (%s)\n", userid); goto fail; } userid++; @@ -389,19 +392,19 @@ int validate_ssh_host_userid(const char* userid) { check last char isalnum */ if (!isalnum(*(userid - 1))) { - err("label did not end with a letter or a digit!\n"); + err(0,"label did not end with a letter or a digit!\n"); goto fail; } if ('.' == (*userid)) /* advance to the start of the next label */ userid++; } else { - err("invalid character in domain name: %c\n", *userid); + err(0,"invalid character in domain name: %c\n", *userid); goto fail; } } /* ensure that the last character is valid: */ if (!isalnum(*(userid - 1))) { - err("hostname did not end with a letter or a digit!\n"); + err(0,"hostname did not end with a letter or a digit!\n"); goto fail; } /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we diff --git a/src/keytrans/gnutls-helpers.h b/src/keytrans/gnutls-helpers.h index 00cdec7..670d5ff 100644 --- a/src/keytrans/gnutls-helpers.h +++ b/src/keytrans/gnutls-helpers.h @@ -23,7 +23,7 @@ int init_gnutls(); /* logging and output functions: */ -void err(const char* fmt, ...); +void err(int level, const char* fmt, ...); void logfunc(int level, const char* string); /* basic datum manipulations: */ diff --git a/src/keytrans/openpgp2ssh.c b/src/keytrans/openpgp2ssh.c index 4593b33..9e356e7 100644 --- a/src/keytrans/openpgp2ssh.c +++ b/src/keytrans/openpgp2ssh.c @@ -56,43 +56,43 @@ int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_open pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits); if (pgp_algo < 0) { - err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo); + err(0, "failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo); return 1; } if (pgp_algo == GNUTLS_PK_RSA) { - err("OpenPGP RSA Key, with %d bits\n", pgp_bits); + err(0,"OpenPGP RSA Key, with %d bits\n", pgp_bits); ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u); if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export RSA key parameters (error: %d)\n", ret); + err(1, "failed to export RSA key parameters (error: %d)\n", ret); return 1; } ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u); if (GNUTLS_E_SUCCESS != ret) { - err ("failed to import RSA key parameters (error: %d)\n", ret); + err(1, "failed to import RSA key parameters (error: %d)\n", ret); return 1; } } else if (pgp_algo == GNUTLS_PK_DSA) { - err("OpenPGP DSA Key, with %d bits\n", pgp_bits); + err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits); ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x); if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export DSA key parameters (error: %d)\n", ret); + err(0,"failed to export DSA key parameters (error: %d)\n", ret); return 1; } ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x); if (GNUTLS_E_SUCCESS != ret) { - err ("failed to import DSA key parameters (error: %d)\n", ret); + err(0,"failed to import DSA key parameters (error: %d)\n", ret); return 1; } } else { - err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo); + err(0,"OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo); return 1; } ret = gnutls_x509_privkey_fix(*output); if (ret != 0) { - err("failed to fix up the private key in X.509 format (error: %d)\n", ret); + err(0,"failed to fix up the private key in X.509 format (error: %d)\n", ret); return 1; } @@ -132,46 +132,46 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope /* figure out if we've got the right thing: */ subkeycount = gnutls_openpgp_crt_get_subkey_count(*pgp_crt); if (subkeycount < 0) { - err("Could not determine subkey count (got value %d)\n", subkeycount); + err(0,"Could not determine subkey count (got value %d)\n", subkeycount); return 1; } if ((keyid == NULL) && (subkeycount > 0)) { - err("No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1); + err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1); return 1; } if (keyid != NULL) { ret = gnutls_openpgp_crt_get_key_id(*pgp_crt, curkeyid); if (ret) { - err("Could not get keyid (error: %d)\n", ret); + err(0,"Could not get keyid (error: %d)\n", ret); return 1; } } if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) { /* we want to export the primary key: */ - err("exporting primary key\n"); + err(0,"exporting primary key\n"); /* FIXME: this is almost identical to the block below for subkeys. This clumsiness seems inherent in the gnutls OpenPGP API, though. ugh. */ algo = gnutls_openpgp_crt_get_pk_algorithm(*pgp_crt, &bits); if (algo < 0) { - err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo); + err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo); return algo; } else if (algo == GNUTLS_PK_RSA) { - err("OpenPGP RSA certificate, with %d bits\n", bits); + err(0,"OpenPGP RSA certificate, with %d bits\n", bits); ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e); if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export RSA key parameters (error: %d)\n", ret); + err(0,"failed to export RSA key parameters (error: %d)\n", ret); return 1; } } else if (algo == GNUTLS_PK_DSA) { - err("OpenPGP DSA Key, with %d bits\n", bits); + err(0,"OpenPGP DSA Key, with %d bits\n", bits); ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y); if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export DSA key parameters (error: %d)\n", ret); + err(0,"failed to export DSA key parameters (error: %d)\n", ret); return 1; } } @@ -182,30 +182,30 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) { ret = gnutls_openpgp_crt_get_subkey_id(*pgp_crt, subkeyidx, curkeyid); if (ret) { - err("Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret); + err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret); return 1; } if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) { - err("exporting subkey index %d\n", subkeyidx); + err(0,"exporting subkey index %d\n", subkeyidx); /* FIXME: this is almost identical to the block above for the primary key. */ algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(*pgp_crt, subkeyidx, &bits); if (algo < 0) { - err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo); + err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo); return algo; } else if (algo == GNUTLS_PK_RSA) { - err("OpenPGP RSA certificate, with %d bits\n", bits); + err(0,"OpenPGP RSA certificate, with %d bits\n", bits); ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e); if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export RSA key parameters (error: %d)\n", ret); + err(0,"failed to export RSA key parameters (error: %d)\n", ret); return 1; } } else if (algo == GNUTLS_PK_DSA) { - err("OpenPGP DSA Key, with %d bits\n", bits); + err(0,"OpenPGP DSA Key, with %d bits\n", bits); ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y); if (GNUTLS_E_SUCCESS != ret) { - err ("failed to export DSA key parameters (error: %d)\n", ret); + err(0,"failed to export DSA key parameters (error: %d)\n", ret); return 1; } } @@ -216,7 +216,7 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope } if (!found) { - err("Could not find key in input\n"); + err(0,"Could not find key in input\n"); return 1; } @@ -239,12 +239,12 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope all[3] = &g; all[4] = &y; } else { - err("Key algorithm was neither DSA nor RSA (it was %d). Can't deal. Sorry!\n", algo); + err(0,"Key algorithm was neither DSA nor RSA (it was %d). Can't deal. Sorry!\n", algo); return 1; } if (ret = datum_from_string(&algolabel, algoname), ret) { - err("couldn't label string (error: %d)\n", ret); + err(0,"couldn't label string (error: %d)\n", ret); return ret; } @@ -252,23 +252,23 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope pipefd = create_writing_pipe(&child_pid, b64args[0], b64args); if (pipefd < 0) { - err("failed to create a writing pipe (returned %d)\n", pipefd); + err(0,"failed to create a writing pipe (returned %d)\n", pipefd); return pipefd; } write(1, output_data, strlen(output_data)); if (0 != write_data_fd_with_length(pipefd, all, mpicount)) { - err("was not able to write out RSA key data\n"); + err(0,"was not able to write out RSA key data\n"); return 1; } close(pipefd); if (child_pid != waitpid(child_pid, &pipestatus, 0)) { - err("could not wait for child process to return for some reason.\n"); + err(0,"could not wait for child process to return for some reason.\n"); return 1; } if (pipestatus != 0) { - err("base64 pipe died with return code %d\n", pipestatus); + err(0,"base64 pipe died with return code %d\n", pipestatus); return pipestatus; } @@ -306,22 +306,22 @@ int main(int argc, char* argv[]) { /* slurp in the key from stdin */ if (ret = set_datum_fd(&data, 0), ret) { - err("didn't read file descriptor 0\n"); + err(0,"didn't read file descriptor 0\n"); return 1; } if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) { - err("Failed to initialized OpenPGP private key (error: %d)\n", ret); + err(0,"Failed to initialized OpenPGP private key (error: %d)\n", ret); return 1; } /* check whether it's a private key or a public key, by trying them: */ if ((gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0) == 0) || (gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0) == 0)) { /* we're dealing with a private key */ - err("Translating private key\n"); + err(0,"Translating private key\n"); if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { - err("Failed to initialize X.509 private key for output (error: %d)\n", ret); + err(0,"Failed to initialize X.509 private key for output (error: %d)\n", ret); return 1; } @@ -342,20 +342,20 @@ int main(int argc, char* argv[]) { } else { if (ret = gnutls_openpgp_crt_init(&pgp_crt), ret) { - err("Failed to initialized OpenPGP certificate (error: %d)\n", ret); + err(0,"Failed to initialized OpenPGP certificate (error: %d)\n", ret); return 1; } if ((gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW) == 0) || (gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64) == 0)) { /* we're dealing with a public key */ - err("Translating public key\n"); + err(0,"Translating public key\n"); ret = emit_public_openssh_from_pgp(&pgp_crt, use_keyid); } else { /* we have no idea what kind of key this is at all anyway! */ - err("Input does contain any form of OpenPGP key I recognize."); + err(0,"Input does contain any form of OpenPGP key I recognize."); return 1; } } -- cgit v1.2.3 From 2fa88e2fde0f56774c76e5cbdc8ea2c67849f3c0 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 13 Jun 2008 13:11:54 -0400 Subject: invert the sense of the loglevel test. duh. --- src/keytrans/gnutls-helpers.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/keytrans') diff --git a/src/keytrans/gnutls-helpers.c b/src/keytrans/gnutls-helpers.c index c6e4979..d5f3719 100644 --- a/src/keytrans/gnutls-helpers.c +++ b/src/keytrans/gnutls-helpers.c @@ -15,11 +15,13 @@ /* for exit() */ #include +/* higher levels allow more frivolous error messages through. + this is set with the MONKEYSPHERE_DEBUG variable */ static int loglevel = 0; void err(int level, const char* fmt, ...) { va_list ap; - if (level < loglevel) + if (level > loglevel) return; va_start(ap, fmt); vfprintf(stderr, fmt, ap); -- cgit v1.2.3 From 3141ed26d4d5af1886166f5d91a041dc60d381eb Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 14 Jun 2008 15:06:48 -0400 Subject: cleaning up error output. --- src/keytrans/openpgp2ssh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/keytrans') diff --git a/src/keytrans/openpgp2ssh.c b/src/keytrans/openpgp2ssh.c index 9e356e7..58f569e 100644 --- a/src/keytrans/openpgp2ssh.c +++ b/src/keytrans/openpgp2ssh.c @@ -355,7 +355,7 @@ int main(int argc, char* argv[]) { } else { /* we have no idea what kind of key this is at all anyway! */ - err(0,"Input does contain any form of OpenPGP key I recognize."); + err(0,"Input does contain any form of OpenPGP key I recognize.\n"); return 1; } } -- cgit v1.2.3 From b13286dc0e549347f86b64049d4eb2bd2891e1ab Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Mon, 16 Jun 2008 00:48:13 -0400 Subject: openpgp2ssh now handles private key export for subkeys, not just public keys. --- src/keytrans/openpgp2ssh.c | 120 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 98 insertions(+), 22 deletions(-) (limited to 'src/keytrans') diff --git a/src/keytrans/openpgp2ssh.c b/src/keytrans/openpgp2ssh.c index 58f569e..ce4d13d 100644 --- a/src/keytrans/openpgp2ssh.c +++ b/src/keytrans/openpgp2ssh.c @@ -40,6 +40,10 @@ int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_open gnutls_pk_algorithm_t pgp_algo; unsigned int pgp_bits; int ret; + gnutls_openpgp_keyid_t curkeyid; + int subkeyidx; + int subkeycount; + int found = 0; /* FIXME: actually respect keyid argument. At the moment, we just emit the primary key. */ @@ -54,32 +58,104 @@ int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_open init_datum(&y); init_datum(&x); - pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits); - if (pgp_algo < 0) { - err(0, "failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo); + + subkeycount = gnutls_openpgp_privkey_get_subkey_count(*pgp_privkey); + if (subkeycount < 0) { + err(0,"Could not determine subkey count (got value %d)\n", subkeycount); return 1; } - if (pgp_algo == GNUTLS_PK_RSA) { - err(0,"OpenPGP RSA Key, with %d bits\n", pgp_bits); - ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u); - if (GNUTLS_E_SUCCESS != ret) { - err(1, "failed to export RSA key parameters (error: %d)\n", ret); + + if ((keyid == NULL) && + (subkeycount > 0)) { + err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1); + return 1; + } + + if (keyid != NULL) { + ret = gnutls_openpgp_privkey_get_key_id(*pgp_privkey, curkeyid); + if (ret) { + err(0,"Could not get keyid (error: %d)\n", ret); return 1; } + } + if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) { + /* we want to export the primary key: */ + err(0,"exporting primary key\n"); - ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u); - if (GNUTLS_E_SUCCESS != ret) { - err(1, "failed to import RSA key parameters (error: %d)\n", ret); + /* FIXME: this is almost identical to the block below for subkeys. + This clumsiness seems inherent in the gnutls OpenPGP API, + though. ugh. */ + pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits); + if (pgp_algo < 0) { + err(0, "failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo); return 1; } - } else if (pgp_algo == GNUTLS_PK_DSA) { - err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits); - ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x); + if (pgp_algo == GNUTLS_PK_RSA) { + err(0,"OpenPGP RSA Key, with %d bits\n", pgp_bits); + ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u); + if (GNUTLS_E_SUCCESS != ret) { + err(0, "failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + + } else if (pgp_algo == GNUTLS_PK_DSA) { + err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits); + ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x); + if (GNUTLS_E_SUCCESS != ret) { + err(0,"failed to export DSA key parameters (error: %d)\n", ret); + return 1; + } + } + found = 1; + } else { + /* lets trawl through the subkeys until we find the one we want: */ + for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) { + ret = gnutls_openpgp_privkey_get_subkey_id(*pgp_privkey, subkeyidx, curkeyid); + if (ret) { + err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret); + return 1; + } + if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) { + err(0,"exporting subkey index %d\n", subkeyidx); + + /* FIXME: this is almost identical to the block above for the + primary key. */ + pgp_algo = gnutls_openpgp_privkey_get_subkey_pk_algorithm(*pgp_privkey, subkeyidx, &pgp_bits); + if (pgp_algo < 0) { + err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", pgp_algo); + return pgp_algo; + } else if (pgp_algo == GNUTLS_PK_RSA) { + err(0,"OpenPGP RSA key, with %d bits\n", pgp_bits); + ret = gnutls_openpgp_privkey_export_subkey_rsa_raw(*pgp_privkey, subkeyidx, &m, &e, &d, &p, &q, &u); + if (GNUTLS_E_SUCCESS != ret) { + err(0,"failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + } else if (pgp_algo == GNUTLS_PK_DSA) { + err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits); + ret = gnutls_openpgp_privkey_export_subkey_dsa_raw(*pgp_privkey, subkeyidx, &p, &q, &g, &y, &x); + if (GNUTLS_E_SUCCESS != ret) { + err(0,"failed to export DSA key parameters (error: %d)\n", ret); + return 1; + } + } + found = 1; + } + } + } + + if (!found) { + err(0,"Could not find key in input\n"); + return 1; + } + + if (pgp_algo == GNUTLS_PK_RSA) { + ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u); if (GNUTLS_E_SUCCESS != ret) { - err(0,"failed to export DSA key parameters (error: %d)\n", ret); + err(0, "failed to import RSA key parameters (error: %d)\n", ret); return 1; } - + } else if (pgp_algo == GNUTLS_PK_DSA) { ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x); if (GNUTLS_E_SUCCESS != ret) { err(0,"failed to import DSA key parameters (error: %d)\n", ret); @@ -164,14 +240,14 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope err(0,"OpenPGP RSA certificate, with %d bits\n", bits); ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e); if (GNUTLS_E_SUCCESS != ret) { - err(0,"failed to export RSA key parameters (error: %d)\n", ret); + err(0,"failed to export RSA certificate parameters (error: %d)\n", ret); return 1; } } else if (algo == GNUTLS_PK_DSA) { - err(0,"OpenPGP DSA Key, with %d bits\n", bits); + err(0,"OpenPGP DSA certificate, with %d bits\n", bits); ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y); if (GNUTLS_E_SUCCESS != ret) { - err(0,"failed to export DSA key parameters (error: %d)\n", ret); + err(0,"failed to export DSA certificate parameters (error: %d)\n", ret); return 1; } } @@ -198,14 +274,14 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope err(0,"OpenPGP RSA certificate, with %d bits\n", bits); ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e); if (GNUTLS_E_SUCCESS != ret) { - err(0,"failed to export RSA key parameters (error: %d)\n", ret); + err(0,"failed to export RSA certificate parameters (error: %d)\n", ret); return 1; } } else if (algo == GNUTLS_PK_DSA) { - err(0,"OpenPGP DSA Key, with %d bits\n", bits); + err(0,"OpenPGP DSA certificate, with %d bits\n", bits); ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y); if (GNUTLS_E_SUCCESS != ret) { - err(0,"failed to export DSA key parameters (error: %d)\n", ret); + err(0,"failed to export DSA certificate parameters (error: %d)\n", ret); return 1; } } -- cgit v1.2.3 From 785736d891f6c61eb5d7f4f10687ef9a0d920c3b Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Mon, 16 Jun 2008 01:05:12 -0400 Subject: openpgp2ssh whitespace and comment cleanup. --- src/keytrans/openpgp2ssh.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src/keytrans') diff --git a/src/keytrans/openpgp2ssh.c b/src/keytrans/openpgp2ssh.c index ce4d13d..511af71 100644 --- a/src/keytrans/openpgp2ssh.c +++ b/src/keytrans/openpgp2ssh.c @@ -45,9 +45,6 @@ int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_open int subkeycount; int found = 0; -/* FIXME: actually respect keyid argument. At the moment, we just - emit the primary key. */ - init_datum(&m); init_datum(&e); init_datum(&d); @@ -58,7 +55,6 @@ int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_open init_datum(&y); init_datum(&x); - subkeycount = gnutls_openpgp_privkey_get_subkey_count(*pgp_privkey); if (subkeycount < 0) { err(0,"Could not determine subkey count (got value %d)\n", subkeycount); @@ -436,9 +432,6 @@ int main(int argc, char* argv[]) { } } - - - gnutls_global_deinit(); return 0; } -- cgit v1.2.3 From 9715df03ccc1620db97d3fea50e5031f6ed36fde Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Mon, 16 Jun 2008 10:24:39 -0400 Subject: genericized the hex printing capabilities. --- src/keytrans/gnutls-helpers.c | 19 ++++++++++++++----- src/keytrans/gnutls-helpers.h | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'src/keytrans') diff --git a/src/keytrans/gnutls-helpers.c b/src/keytrans/gnutls-helpers.c index d5f3719..5b4c46a 100644 --- a/src/keytrans/gnutls-helpers.c +++ b/src/keytrans/gnutls-helpers.c @@ -15,6 +15,8 @@ /* for exit() */ #include +#include + /* higher levels allow more frivolous error messages through. this is set with the MONKEYSPHERE_DEBUG variable */ static int loglevel = 0; @@ -40,14 +42,21 @@ void init_keyid(gnutls_openpgp_keyid_t keyid) { void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid) +{ + assert(sizeof(out) >= 2*sizeof(keyid)); + hex_print_data((char*)out, (const char*)keyid, sizeof(keyid)); +} + +/* you must have twice as many bytes in the out buffer as in the in buffer */ +void hex_print_data(char* out, const char* in, size_t incount) { static const char hex[16] = "0123456789ABCDEF"; - unsigned int kix = 0, outix = 0; + unsigned int inix = 0, outix = 0; - while (kix < sizeof(gnutls_openpgp_keyid_t)) { - out[outix] = hex[(keyid[kix] >> 4) & 0x0f]; - out[outix + 1] = hex[keyid[kix] & 0x0f]; - kix++; + while (inix < incount) { + out[outix] = hex[(in[inix] >> 4) & 0x0f]; + out[outix + 1] = hex[in[inix] & 0x0f]; + inix++; outix += 2; } } diff --git a/src/keytrans/gnutls-helpers.h b/src/keytrans/gnutls-helpers.h index 670d5ff..f196456 100644 --- a/src/keytrans/gnutls-helpers.h +++ b/src/keytrans/gnutls-helpers.h @@ -48,6 +48,8 @@ void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in); int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str); int convert_string_to_printable_keyid(printable_keyid out, const char* str); +/* you must have twice as many bytes in the out buffer as in the in buffer */ +void hex_print_data(char* out, const char* in, size_t incount); /* functions to get data into datum objects: */ -- cgit v1.2.3