--- configure.ac.~2~ Tue Jul 29 21:17:55 2003 +++ configure.ac Wed Jul 30 00:01:02 2003 @@ -783,6 +783,14 @@ AC_CHECK_LIB(crypt, crypt) fi +GPG_MSG=no +AC_ARG_WITH(gpg, [ --with-gpg Support GPG host keys], [ + if [ x"$with_gpg" != xno ]; then + AC_DEFINE(WITH_GPG, 1, [Support gpg host keys.]) + GPG_MSG=yes + fi +], [GPG_MSG=yes; AC_DEFINE(WITH_GPG, 1, [Support gpg host keys.])]) + # Start of GSI/Globus 2.0 mods # Check whether the user wants GSI (Globus 2.0) support # if we are using GSI, we will also use the @@ -2599,6 +2607,7 @@ echo " PAM support: ${PAM_MSG}" echo " KerberosIV support: $KRB4_MSG" echo " KerberosV support: $KRB5_MSG" +echo " GPG support: $GPG_MSG" echo " Smartcard support: $SCARD_MSG" echo " AFS support: $AFS_MSG" echo " S/KEY support: $SKEY_MSG" --- Makefile.in.~2~ Tue Jul 29 21:17:55 2003 +++ Makefile.in Tue Jul 29 21:18:39 2003 @@ -67,7 +67,7 @@ key.o dispatch.o kex.o mac.o uuencode.o misc.o \ rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o kexgex.o \ kexdhc.o kexgexc.o scard.o msg.o progressmeter.o \ - entropy.o kexgssc.o gss-genr.o + entropy.o kexgssc.o gss-genr.o ssh-gpg.o SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ sshconnect.o sshconnect1.o sshconnect2.o --- sshd.c.~2~ Tue Jul 29 21:17:55 2003 +++ sshd.c Tue Jul 29 21:18:40 2003 @@ -39,6 +39,31 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * GPG modifications: + * + * Copyright (c) 2002 Joel N. Weber II. + * + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * */ #include "includes.h" @@ -89,6 +114,10 @@ #include "ssh-gss.h" #endif +#ifdef WITH_GPG +#include "ssh-gpg.h" +#endif + #ifdef LIBWRAP #include #include @@ -181,6 +210,10 @@ u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; } sensitive_data; +/* The value of options.num_host_key_files, plus the number of OpenPGP + * host keys. */ +static int num_host_keys; + /* * Flag indicating whether the RSA server key needs to be regenerated. * Is set in the SIGALRM handler and cleared when the key is regenerated. @@ -497,7 +530,7 @@ key_free(sensitive_data.server_key); sensitive_data.server_key = NULL; } - for (i = 0; i < options.num_host_key_files; i++) { + for (i = 0; i < num_host_keys; i++) { if (sensitive_data.host_keys[i]) { key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = NULL; @@ -520,6 +553,8 @@ sensitive_data.server_key = tmp; } + /* We use the number of host key files here, because OpenPGP keys + don't need to be demoted, and they will appear at the end. */ for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { tmp = key_demote(sensitive_data.host_keys[i]); @@ -692,13 +727,17 @@ int i; buffer_init(&b); - for (i = 0; i < options.num_host_key_files; i++) { + for (i = 0; i < num_host_keys; i++) { Key *key = sensitive_data.host_keys[i]; if (key == NULL) continue; switch (key->type) { case KEY_RSA: case KEY_DSA: +#ifdef WITH_GPG + case KEY_PGP_RSA: + case KEY_PGP_DSA: +#endif if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); @@ -718,7 +757,7 @@ { int i; - for (i = 0; i < options.num_host_key_files; i++) { + for (i = 0; i < num_host_keys; i++) { Key *key = sensitive_data.host_keys[i]; if (key != NULL && key->type == type) return key; @@ -729,7 +768,7 @@ Key * get_hostkey_by_index(int ind) { - if (ind < 0 || ind >= options.num_host_key_files) + if (ind < 0 || ind >= num_host_keys) return (NULL); return (sensitive_data.host_keys[ind]); } @@ -739,7 +778,7 @@ { int i; - for (i = 0; i < options.num_host_key_files; i++) { + for (i = 0; i < num_host_keys; i++) { if (key == sensitive_data.host_keys[i]) return (i); } @@ -824,6 +863,9 @@ Authctxt *authctxt; Key *key; int ret, key_used = 0; +#ifdef WITH_GPG + int status; +#endif #ifdef HAVE_SECUREWARE (void)set_auth_parameters(ac, av); @@ -982,8 +1024,26 @@ debug("sshd version %.100s", SSH_VERSION); /* load private host keys */ - sensitive_data.host_keys = xmalloc(options.num_host_key_files * - sizeof(Key *)); + num_host_keys = options.num_host_key_files; +#ifdef WITH_GPG + if (options.rsa_pgp_key_name) { + num_host_keys++; + } + if (options.dsa_pgp_key_name) { + num_host_keys++; + } + if (options.rsa_pgp_key_name || options.dsa_pgp_key_name) { + if (options.gpg_absolute_filename) + gpg_absolute_filename = options.gpg_absolute_filename; + else + fatal ("PGP host key specified, but GPGAbsoluteFilename not set."); + if (options.gpg_homedir) + gpg_homedir = options.gpg_homedir; + else + fatal ("PGP host key specified, but GPGHomedir not set."); + } +#endif + sensitive_data.host_keys = xmalloc(num_host_keys*sizeof(Key*)); for (i = 0; i < options.num_host_key_files; i++) sensitive_data.host_keys[i] = NULL; sensitive_data.server_key = NULL; @@ -1013,6 +1073,36 @@ debug("private host key: #%d type %d %s", i, key->type, key_type(key)); } +#ifdef WITH_GPG + if (options.rsa_pgp_key_name) { + key = key_new(KEY_PGP_RSA); + key->pgp_key_name = xstrdup(options.rsa_pgp_key_name); + status = ssh_gpg_export (options.rsa_pgp_key_name, + &(key->pgp_key_data), + &(key->pgp_key_data_size)); + if (status) { + fatal ("ssh_gpg_export failed on RSA key"); + } + sensitive_data.host_keys[i] = key; + debug("private host key: #%d type %d %s", i, key->type, + key_type(key)); + i++; + } + if (options.dsa_pgp_key_name) { + key = key_new(KEY_PGP_DSA); + key->pgp_key_name = xstrdup(options.dsa_pgp_key_name); + status = ssh_gpg_export (options.dsa_pgp_key_name, + &(key->pgp_key_data), + &(key->pgp_key_data_size)); + if (status) { + fatal ("ssh_gpg_export failed on DSA key"); + } + sensitive_data.host_keys[i] = key; + debug("private host key: #%d type %d %s", i, key->type, + key_type(key)); + i++; + } +#endif if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { log("Disabling protocol version 1. Could not load host key"); options.protocol &= ~SSH_PROTO_1; --- key.h.~2~ Tue Jul 29 21:17:55 2003 +++ key.h Tue Jul 29 21:18:40 2003 @@ -34,6 +34,8 @@ KEY_RSA1, KEY_RSA, KEY_DSA, + KEY_PGP_RSA, + KEY_PGP_DSA, KEY_NULL, KEY_UNSPEC }; @@ -47,6 +49,7 @@ }; /* key is stored in external hardware */ +/* or otherwise not directly used by ssh (as in GPG) */ #define KEY_FLAG_EXT 0x0001 struct Key { @@ -54,6 +57,15 @@ int flags; RSA *rsa; DSA *dsa; +#ifdef WITH_GPG + char *pgp_key_name; + u_char *pgp_key_data; + size_t pgp_key_data_size; + u_char *pgp_key_fingerprint; + Key *pgp_subkey; + int pgp_key_type; + char pgp_key_trust; +#endif }; Key *key_new(int); --- key.c.~2~ Tue Jul 29 21:17:55 2003 +++ key.c Tue Jul 29 21:22:05 2003 @@ -30,6 +30,32 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * GPG modifications: + * + * Copyright (c) 2002, 2003 Joel N. Weber II. + * + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * */ #include "includes.h" RCSID("$OpenBSD: key.c,v 1.51 2003/02/12 09:33:04 markus Exp $"); @@ -39,6 +65,9 @@ #include "xmalloc.h" #include "key.h" #include "rsa.h" +#ifdef WITH_GPG +#include "ssh-gpg.h" +#endif #include "uuencode.h" #include "buffer.h" #include "bufaux.h" @@ -52,9 +81,22 @@ DSA *dsa; k = xmalloc(sizeof(*k)); k->type = type; - k->flags = 0; + if ((type == KEY_PGP_RSA) || (type == KEY_PGP_DSA)) { + k->flags = KEY_FLAG_EXT; + } else { + k->flags = 0; + } k->dsa = NULL; k->rsa = NULL; +#ifdef WITH_GPG + k->pgp_key_name = NULL; + k->pgp_key_data = NULL; + k->pgp_key_data_size = 0; + k->pgp_key_fingerprint = NULL; + k->pgp_subkey = NULL; + k->pgp_key_type = 0xdeadbeef; + k->pgp_key_trust = 0; +#endif switch (k->type) { case KEY_RSA1: case KEY_RSA: @@ -79,6 +121,10 @@ fatal("key_new: BN_new failed"); k->dsa = dsa; break; +#ifdef WITH_GPG + case KEY_PGP_RSA: + case KEY_PGP_DSA: +#endif case KEY_UNSPEC: break; default: @@ -112,6 +158,12 @@ if ((k->dsa->priv_key = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); break; +#ifdef WITH_GPG + case KEY_PGP_RSA: + case KEY_PGP_DSA: + fatal("key_new_private: PGP not supported"); + break; +#endif case KEY_UNSPEC: break; default: @@ -123,6 +175,9 @@ void key_free(Key *k) { +#ifdef WITH_GPG + Key *sub, *sub2; +#endif switch (k->type) { case KEY_RSA1: case KEY_RSA: @@ -135,6 +190,26 @@ DSA_free(k->dsa); k->dsa = NULL; break; +#ifdef WITH_GPG + case KEY_PGP_RSA: + case KEY_PGP_DSA: + if (k->pgp_key_name != NULL) + xfree(k->pgp_key_name); + if (k->pgp_key_data != NULL) + xfree(k->pgp_key_data); + if (k->pgp_key_fingerprint != NULL) + xfree(k->pgp_key_fingerprint); + if (k->pgp_subkey != NULL) { + sub = k->pgp_subkey; + while (sub) { + sub2 = sub->pgp_subkey; + sub->pgp_subkey = 0; + key_free(sub); + sub = sub2; + } + } + break; +#endif case KEY_UNSPEC: break; default: @@ -162,6 +237,24 @@ BN_cmp(a->dsa->g, b->dsa->g) == 0 && BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; break; +#ifdef WITH_GPG + case KEY_PGP_RSA: + case KEY_PGP_DSA: + if (a->pgp_key_name != NULL + && b->pgp_key_name != NULL) { + return !strcmp (a->pgp_key_name, b->pgp_key_name); + } else if (a->pgp_key_data != NULL + && b->pgp_key_data != NULL) { + return a->pgp_key_data_size == b->pgp_key_data_size + && !memcmp (a->pgp_key_data, b->pgp_key_data, + a->pgp_key_data_size); + } else { + return a->pgp_key_fingerprint != NULL + && b->pgp_key_fingerprint != NULL + && !strcmp (a->pgp_key_fingerprint, + b->pgp_key_fingerprint); + } +#endif default: fatal("key_equal: bad key type %d", a->type); break; @@ -296,6 +389,15 @@ u_char *dgst_raw; u_int dgst_raw_len; +#ifdef WITH_GPG + if ((k->type == KEY_PGP_RSA) || (k->type == KEY_PGP_DSA)) { + if (!k->pgp_key_fingerprint) { + debug("key_fingerprint: null k->pgp_key_fingerprint"); + return xstrdup("NO FINGERPRINT"); + } + return xstrdup (k->pgp_key_fingerprint); + } +#endif dgst_raw = key_fingerprint_raw(k, dgst_type, &dgst_raw_len); if (!dgst_raw) fatal("key_fingerprint: null from key_fingerprint_raw()"); @@ -406,6 +508,10 @@ case KEY_UNSPEC: case KEY_RSA: case KEY_DSA: +#ifdef WITH_GPG + case KEY_PGP_RSA: + case KEY_PGP_DSA: +#endif space = strchr(cp, ' '); if (space == NULL) { debug3("key_read: missing whitespace"); @@ -430,6 +536,25 @@ debug3("key_read: type mismatch"); return -1; } +#ifdef WITH_GPG + if ((ret->type == KEY_PGP_RSA) || (ret->type == KEY_PGP_DSA)) { + space = strchr (cp, ' '); + /* If there's no space found after the key, find + the newline instead, so that a fingerprint without + a comment will be accepted. */ + if (space == NULL) + space = strchr (cp, '\n'); + if (space != NULL) { + *space = 0; + } + ret->pgp_key_fingerprint = xstrdup (cp); + if (space != NULL) { + *space = ' '; + } + success = 1; + goto key_read_advance; + } +#endif len = 2*strlen(cp); blob = xmalloc(len); n = uudecode(cp, blob, len); @@ -474,6 +599,9 @@ if (success != 1) break; /* advance cp: skip whitespace and data */ +#ifdef WITH_GPG +key_read_advance: +#endif while (*cp == ' ' || *cp == '\t') cp++; while (*cp != '\0' && *cp != ' ' && *cp != '\t') @@ -533,6 +661,14 @@ case KEY_DSA: return "DSA"; break; +#ifdef WITH_GPG + case KEY_PGP_RSA: + return "OpenPGP RSA"; + break; + case KEY_PGP_DSA: + return "OpenPGP DSA"; + break; +#endif } return "unknown"; } @@ -547,6 +683,14 @@ case KEY_DSA: return "ssh-dss"; break; +#ifdef WITH_GPG + case KEY_PGP_RSA: + return "pgp-sign-rsa"; + break; + case KEY_PGP_DSA: + return "pgp-sign-dss"; + break; +#endif } return "ssh-unknown"; } @@ -601,6 +745,8 @@ case KEY_RSA1: k->rsa = rsa_generate_private_key(bits); break; + /* I don't think we need key_generate to do anything useful for the pgp + case, hence I'm allowing this to fall through to this error. */ default: fatal("key_generate: unknown type %d", type); } @@ -626,6 +772,9 @@ BN_copy(n->rsa->n, k->rsa->n); BN_copy(n->rsa->e, k->rsa->e); break; + /* I don't think we need key_from_private to do anything useful for + the pgp case, hence I'm allowing this to fall through to this + error. */ default: fatal("key_from_private: unknown type %d", k->type); break; @@ -646,6 +795,12 @@ return KEY_RSA; } else if (strcmp(name, "ssh-dss") == 0) { return KEY_DSA; +#ifdef WITH_GPG + } else if (strcmp(name, "pgp-sign-rsa") == 0) { + return KEY_PGP_RSA; + } else if (strcmp(name, "pgp-sign-dss") == 0) { + return KEY_PGP_DSA; +#endif } else if (strcmp(name, "null") == 0){ return KEY_NULL; } @@ -710,6 +865,16 @@ DSA_print_fp(stderr, key->dsa, 8); #endif break; +#ifdef WITH_GPG + case KEY_PGP_RSA: + case KEY_PGP_DSA: + key = key_new(type); + key->pgp_key_data_size = buffer_len (&b); + key->pgp_key_data = xmalloc (key->pgp_key_data_size); + buffer_get (&b, key->pgp_key_data, + key->pgp_key_data_size); + break; +#endif case KEY_UNSPEC: key = key_new(type); break; @@ -749,6 +914,18 @@ buffer_put_bignum2(&b, key->rsa->e); buffer_put_bignum2(&b, key->rsa->n); break; +#ifdef WITH_GPG + case KEY_PGP_DSA: + case KEY_PGP_RSA: + if (key->pgp_key_data) { + buffer_put_cstring(&b, key_ssh_name(key)); + buffer_append(&b, key->pgp_key_data, + key->pgp_key_data_size); + } else { + fatal ("key_to_blob: PGP key structure lacks exported data"); + } + break; +#endif default: error("key_to_blob: unsupported key type %d", key->type); buffer_free(&b); @@ -763,6 +940,7 @@ } memset(buffer_ptr(&b), 0, len); buffer_free(&b); + debug2("key_to_blob: returning %d bytes", len); return len; } @@ -779,6 +957,12 @@ case KEY_RSA: return ssh_rsa_sign(key, sigp, lenp, data, datalen); break; +#ifdef WITH_GPG + case KEY_PGP_RSA: + case KEY_PGP_DSA: + return ssh_gpg_sign(key, sigp, lenp, data, datalen); + break; +#endif default: error("key_sign: illegal key type %d", key->type); return -1; @@ -806,6 +990,12 @@ case KEY_RSA: return ssh_rsa_verify(key, signature, signaturelen, data, datalen); break; +#ifdef WITH_GPG + case KEY_PGP_RSA: + case KEY_PGP_DSA: + return ssh_gpg_verify_sig(key, signature, signaturelen, data, datalen); + break; +#endif default: error("key_verify: illegal key type %d", key->type); return -1; @@ -847,8 +1037,11 @@ if ((pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) fatal("key_demote: BN_dup failed"); break; + /* I don't think we need key_demote to do anything useful for + the pgp case, hence I'm allowing this to fall through to this + error. */ default: - fatal("key_free: bad key type %d", k->type); + fatal("key_demote: bad key type %d", k->type); break; } --- ssh.c.~1~ Sun Feb 23 19:57:32 2003 +++ ssh.c Tue Jul 29 21:18:40 2003 @@ -73,6 +73,10 @@ #include "scard.h" #endif +#ifdef WITH_GPG +#include "ssh-gpg.h" +#endif + #ifdef HAVE___PROGNAME extern char *__progname; #else @@ -684,6 +688,16 @@ if (mkdir(buf, 0700) < 0) error("Could not create directory '%.200s'.", buf); +#ifdef WITH_GPG + if (options.gpg_absolute_filename != NULL) { + gpg_absolute_filename = options.gpg_absolute_filename; + if (options.gpg_homedir == NULL) + gpg_homedir = getenv("HOME"); + else + gpg_homedir = options.gpg_homedir; + } +#endif + /* load options.identity_files */ load_public_identity_files(); @@ -1169,6 +1183,9 @@ char *filename; int i = 0; Key *public; +#ifdef WITH_GPG + int status; +#endif #ifdef SMARTCARD Key **keys; @@ -1202,4 +1219,34 @@ options.identity_files[i] = filename; options.identity_keys[i] = public; } +#ifdef WITH_GPG + if (options.rsa_pgp_key_name) { + options.identity_keys[i] = key_new(KEY_PGP_RSA); + options.identity_keys[i]->pgp_key_name = xstrdup(options.rsa_pgp_key_name); + status = ssh_gpg_export (options.rsa_pgp_key_name, + &(options.identity_keys[i]->pgp_key_data), + &(options.identity_keys[i]->pgp_key_data_size)); + if (status) { + fatal ("ssh_gpg_export failed on RSA key"); + } + options.identity_files[i] = xstrdup("external gpg key"); + i++; + options.num_identity_files++; + debug ("RSA GPG identity %s", options.rsa_pgp_key_name); + } + if (options.dsa_pgp_key_name) { + options.identity_keys[i] = key_new(KEY_PGP_DSA); + options.identity_keys[i]->pgp_key_name = xstrdup(options.dsa_pgp_key_name); + status = ssh_gpg_export (options.dsa_pgp_key_name, + &(options.identity_keys[i]->pgp_key_data), + &(options.identity_keys[i]->pgp_key_data_size)); + if (status) { + fatal ("ssh_gpg_export failed on DSA key"); + } + options.identity_files[i] = xstrdup("external gpg key"); + i++; + options.num_identity_files++; + debug ("DSA GPG identity %s", options.dsa_pgp_key_name); + } +#endif } --- sshconnect.c.~1~ Sun Dec 22 21:06:20 2002 +++ sshconnect.c Tue Jul 29 21:18:40 2003 @@ -10,6 +10,31 @@ * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". + * + * GPG modifications: + * + * Copyright (c) 2002, 2003 Joel N. Weber II. + * + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * */ #include "includes.h" @@ -33,6 +58,10 @@ #include "misc.h" #include "readpass.h" +#ifdef WITH_GPG +#include "ssh-gpg.h" +#endif + char *client_version_string = NULL; char *server_version_string = NULL; @@ -800,6 +829,16 @@ { struct stat st; +#ifdef WITH_GPG + char *email; + + if ((host_key->type == KEY_PGP_RSA) + || (host_key->type == KEY_PGP_DSA)) { + email = malloc (strlen (host) + 5); + sprintf (email, "ssh@%s", host); + return ssh_gpg_get_key_info (host_key, email, 0); + } +#endif /* return ok if the key can be found in an old keyfile */ if (stat(options.system_hostfile2, &st) == 0 || stat(options.user_hostfile2, &st) == 0) { --- sshconnect2.c.~2~ Tue Jul 29 21:17:55 2003 +++ sshconnect2.c Tue Jul 29 21:18:40 2003 @@ -20,6 +20,32 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * GPG modifications: + * + * Copyright (c) 2002, 2003 Joel N. Weber II. + * + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * */ #include "includes.h" @@ -52,6 +78,11 @@ #include "ssh-gss.h" #endif +#ifdef WITH_GPG +#include "ssh-gpg.h" +#endif + + /* import */ extern char *client_version_string; extern char *server_version_string; @@ -85,6 +116,11 @@ char *orig, *gss; int len; #endif +#ifdef WITH_GPG + char *gpg_orig; + int gpg_len; +#endif + xxx_host = host; xxx_hostaddr = hostaddr; @@ -127,6 +163,15 @@ if (options.hostkeyalgorithms != NULL) myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = options.hostkeyalgorithms; +#ifdef WITH_GPG + /* If we have GPG, then add the algorithms that GPG supports. */ + if (options.gpg_absolute_filename != NULL) { + gpg_orig=myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; + gpg_len = strlen(gpg_orig)+sizeof("pgp-sign-rsa,pgp-sign-dss,"); + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]=xmalloc(gpg_len); + snprintf(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],gpg_len,"pgp-sign-rsa,pgp-sign-dss,%s",gpg_orig); + } +#endif #ifdef GSSAPI /* If we've got GSSAPI algorithms, then we also support the --- auth2-pubkey.c.~1~ Thu Jun 6 16:27:56 2002 +++ auth2-pubkey.c Tue Jul 29 21:18:40 2003 @@ -41,6 +41,10 @@ #include "canohost.h" #include "monitor_wrap.h" +#ifdef WITH_GPG +#include "ssh-gpg.h" +#endif + /* import */ extern ServerOptions options; extern u_char *session_id2; @@ -180,6 +184,14 @@ /* Temporarily use the user's uid. */ temporarily_use_uid(pw); + +#ifdef WITH_GPG + if ((key->type == KEY_PGP_RSA) || (key->type == KEY_PGP_DSA)) { + gpg_homedir = pw->pw_dir; + if (!key->pgp_key_fingerprint) + ssh_gpg_get_key_info (key, 0, 1); + } +#endif debug("trying public key file %s", file); --- monitor.c.~2~ Tue Jul 29 21:17:55 2003 +++ monitor.c Tue Jul 29 21:18:40 2003 @@ -22,6 +22,32 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * GPG modifications: + * + * Copyright (c) 2002 Joel N. Weber II. + * + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * */ #include "includes.h" @@ -64,6 +90,11 @@ static Gssctxt *gsscontext = NULL; #endif +#ifdef WITH_GPG +#include "uidswap.h" +#include "ssh-gpg.h" +#endif + /* Imports */ extern ServerOptions options; extern u_int utmp_len; @@ -1034,7 +1065,24 @@ if (!valid_data) fatal("%s: bad signature data blob", __func__); +#ifdef WITH_GPG + if ((key->type == KEY_PGP_RSA) || (key->type == KEY_PGP_DSA)) { + temporarily_use_uid(authctxt->pw); + gpg_homedir = authctxt->pw->pw_dir; + /* FIXME: calling ssh_gpg_get_key_info here to get the + fingerprint, and the key structure which may contain + subkeys to get the key type information, adds extra + overhead which is somewhat annoying. It would be nice + to cache the key structure. */ + ssh_gpg_get_key_info (key, 0, 1); + } +#endif verified = key_verify(key, signature, signaturelen, data, datalen); +#ifdef WITH_GPG + if ((key->type == KEY_PGP_RSA) || (key->type == KEY_PGP_DSA)) { + restore_uid(); + } +#endif debug3("%s: key %p signature %s", __func__, key, verified ? "verified" : "unverified"); --- servconf.c.~2~ Tue Jul 29 21:17:55 2003 +++ servconf.c Tue Jul 29 21:18:40 2003 @@ -7,6 +7,31 @@ * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". + * + * GPG modifications: + * + * Copyright (c) 2002 Joel N. Weber II. + * + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * */ #include "includes.h" @@ -103,6 +128,10 @@ #ifdef AFS options->afs_token_passing = -1; #endif +#ifdef WITH_GPG + options->rsa_pgp_key_name = NULL; + options->dsa_pgp_key_name = NULL; +#endif options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->challenge_response_authentication = -1; @@ -308,6 +337,12 @@ #ifdef AFS sAFSTokenPassing, #endif +#ifdef WITH_GPG + sRSAPGPKeyName, + sDSAPGPKeyName, + sGPGAbsoluteFilename, + sGPGHomedir, +#endif sChallengeResponseAuthentication, sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, sPrintMotd, sPrintLastLog, sIgnoreRhosts, @@ -367,6 +402,12 @@ #ifdef AFS { "afstokenpassing", sAFSTokenPassing }, #endif +#ifdef WITH_GPG + { "rsapgpkeyname", sRSAPGPKeyName }, + { "dsapgpkeyname", sDSAPGPKeyName }, + { "gpgabsolutefilename", sGPGAbsoluteFilename }, + { "gpghomedir", sGPGHomedir }, +#endif { "passwordauthentication", sPasswordAuthentication }, { "kbdinteractiveauthentication", sKbdInteractiveAuthentication }, { "challengeresponseauthentication", sChallengeResponseAuthentication }, @@ -708,7 +749,48 @@ intptr = &options->afs_token_passing; goto parse_flag; #endif - +#ifdef WITH_GPG + case sRSAPGPKeyName: + charptr = &options->rsa_pgp_key_name; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing key name.", + filename, linenum); + if (*charptr == NULL) { + *charptr = xstrdup(arg); + } + break; + case sDSAPGPKeyName: + charptr = &options->dsa_pgp_key_name; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing key name.", + filename, linenum); + if (*charptr == NULL) { + *charptr = xstrdup(arg); + } + break; + case sGPGAbsoluteFilename: + charptr = &options->gpg_absolute_filename; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing file name.", + filename, linenum); + if (*charptr == NULL) { + *charptr = xstrdup(arg); + } + break; + case sGPGHomedir: + charptr = &options->gpg_homedir; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing file name.", + filename, linenum); + if (*charptr == NULL) { + *charptr = xstrdup(arg); + } + break; +#endif case sPasswordAuthentication: intptr = &options->password_authentication; goto parse_flag; --- servconf.h.~2~ Tue Jul 29 21:17:55 2003 +++ servconf.h Tue Jul 29 21:18:40 2003 @@ -98,6 +98,12 @@ #ifdef AFS int afs_token_passing; /* If true, permit AFS token passing. */ #endif +#ifdef WITH_GPG + char *rsa_pgp_key_name; /* Name of RSA PGP key to use as host key. */ + char *dsa_pgp_key_name; /* Name of DSA PGP key to use as host key. */ + char *gpg_absolute_filename; /* Absolute filename of gpg binary. */ + char *gpg_homedir; /* Value to pass as $HOME to gpg binary. */ +#endif int password_authentication; /* If true, permit password * authentication. */ int kbd_interactive_authentication; /* If true, permit */ --- readconf.c.~2~ Tue Jul 29 21:17:55 2003 +++ readconf.c Tue Jul 29 21:18:40 2003 @@ -9,6 +9,32 @@ * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". + * + * + * GPG modifications: + * + * Copyright (c) 2002 Joel N. Weber II. + * + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * */ #include "includes.h" @@ -94,6 +120,12 @@ oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication, oPasswordAuthentication, oRSAAuthentication, oChallengeResponseAuthentication, oXAuthLocation, +#ifdef WITH_GPG + oGPGAbsoluteFilename, + oGPGHomedir, + oRSAPGPKeyName, + oDSAPGPKeyName, +#endif #if defined(KRB4) || defined(KRB5) oKerberosAuthentication, #endif @@ -147,6 +179,12 @@ { "challengeresponseauthentication", oChallengeResponseAuthentication }, { "skeyauthentication", oChallengeResponseAuthentication }, /* alias */ { "tisauthentication", oChallengeResponseAuthentication }, /* alias */ +#ifdef WITH_GPG + { "gpgabsolutefilename", oGPGAbsoluteFilename }, + { "gpghomedir", oGPGHomedir }, + { "rsapgpkeyname", oRSAPGPKeyName }, + { "dsapgpkeyname", oDSAPGPKeyName }, +#endif #if defined(KRB4) || defined(KRB5) { "kerberosauthentication", oKerberosAuthentication }, #endif @@ -520,6 +558,21 @@ charptr = &options->smartcard_device; goto parse_string; +#ifdef WITH_GPG + case oGPGAbsoluteFilename: + charptr = &options->gpg_absolute_filename; + goto parse_string; + case oGPGHomedir: + charptr = &options->gpg_homedir; + goto parse_string; + case oRSAPGPKeyName: + charptr = &options->rsa_pgp_key_name; + goto parse_string; + case oDSAPGPKeyName: + charptr = &options->dsa_pgp_key_name; + goto parse_string; +#endif + case oProxyCommand: charptr = &options->proxy_command; len = strspn(s, WHITESPACE "="); @@ -796,6 +849,12 @@ #endif #ifdef AFS options->afs_token_passing = -1; +#endif +#ifdef WITH_GPG + options->gpg_absolute_filename = NULL; + options->gpg_homedir = NULL; + options->rsa_pgp_key_name = NULL; + options->dsa_pgp_key_name = NULL; #endif options->password_authentication = -1; options->kbd_interactive_authentication = -1; --- readconf.h.~2~ Tue Jul 29 21:17:55 2003 +++ readconf.h Tue Jul 29 21:18:40 2003 @@ -41,6 +41,12 @@ int hostbased_authentication; /* ssh2's rhosts_rsa */ int challenge_response_authentication; /* Try S/Key or TIS, authentication. */ +#ifdef WITH_GPG + char *gpg_absolute_filename; + char *gpg_homedir; + char *rsa_pgp_key_name; + char *dsa_pgp_key_name; +#endif #if defined(KRB4) || defined(KRB5) int kerberos_authentication; /* Try Kerberos authentication. */ #endif --- /dev/null Wed Jul 30 17:55:31 2003 +++ ssh-gpg.c Tue Jul 29 21:20:05 2003 @@ -0,0 +1,994 @@ +/* + * Copyright (c) 2002, 2003 Joel N. Weber II. + * + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef WITH_GPG + +#include "xmalloc.h" +#include "key.h" +#include "ssh-gpg.h" +#include "log.h" +#include "misc.h" +#include "buffer.h" +#include "bufaux.h" + +#define SSH_GPG_BUFSIZ 16384 +#define SHORT_LBSIZE 100 +#define LONG_LBSIZE 10000 + +char *gpg_homedir, *gpg_absolute_filename; + +/* Split the line s on the character sep, changing the sep characters + in s to '\0' in the process. malloc an array of char * to return + as a, which consists of one pointer to each string, followed by a + null pointer. also return the number of fields extracted in + count. */ +static void +split_separated_list (char *s, char ***a, int *count, char sep) +{ + char *x, *y; + char **b; + + *count = 1; + x = s; + while (*x) { + if ((*x) == sep) + (*count)++; + x++; + } + *a = xmalloc (((*count) + 1) * sizeof (char *)); + x = s; + b = *a; + while (1) { + y = x; + while ((*y) && ((*y) != sep)) + y++; + if (*y) { + *y = 0; + *b = x; + b++; + x = y + 1; + } else { + *b = x; + b++; + *b = 0; + return; + } + } +} + +/* Read a line from f, and pass that line to split_separated_list, and + pass the results from split_separated_list back to the caller. */ +static char ** +read_and_split_line (FILE *f, char ***a, int *count, char sep, char *dbg) +{ + char buf[LONG_LBSIZE]; + int i; + + if (fgets (buf, LONG_LBSIZE, f)) { + i = 0; + while (buf[i]) { + if (buf[i] == '\n') + buf[i] = 0; + else + i++; + } + if (dbg) { + debug2 (dbg, buf); + } + split_separated_list (buf, a, count, sep); + } else { + *a = 0; + } + return *a; +} + +/* Call debug on each line in the file referenced by fd. func is a string + containing the name of the calling function, and str is a string containing + a description of which pipe this is. */ +static void +fd_to_debug (int fd, char *func, char *str) +{ + char buf[SHORT_LBSIZE]; + int i; + FILE *f; + + f = fdopen (fd, "r"); + if (!f) + fatal ("%s: error fdopening %s", func, str); + while (fgets (buf, SHORT_LBSIZE, f)) { + i = 0; + while (buf[i]) { + if (buf[i] == '\n') + buf[i] = 0; + else + i++; + } + debug ("%s: %s: %s", func, str, buf); + } + if (ferror (f)) + fatal ("%s: error reading %s", func, str); + if (fclose (f)) + fatal ("%s: error closing %s: %s", func, str, strerror(errno)); +} + + +/* Return 1 if email exactly matches the email section of uid, + else return 0. Call fatal if there is a problem with the syntax + of either argument. */ +static int +ssh_gpg_email_value_matches (char *uid, char *email) +{ + char *startbracket, *endbracket; + char *extrastartbracket, *extraendbracket, *firstendbracket; + char *at, *dot; + + /* Deliberately prevent people from using unqualified hostnames, as I + have some concern that namespace collision between different + hosts with the same name in different domains may be a security + risk. It is not entirely clear that there is no solution to this + problem, but for now, this seems like a reasonable way to make sure + that this is not a risk. */ + at = strchr (email, '@'); + if (!at) + fatal ("ssh_gpg_email_value_matches: no at character found"); + dot = strchr (at, '.'); + if (!dot) + fatal ("ssh_gpg_email_value_matches: host name is not fully qualified"); + + startbracket = strchr (uid, '<'); + if (!startbracket) + fatal ("ssh_gpg_email_value_matches: No < character found in uid"); + + extrastartbracket = strchr (startbracket + 1, '<'); + if (extrastartbracket) + fatal ("ssh_gpg_email_value_matches: More than one < character found in uid"); + + endbracket = strchr (startbracket, '>'); + if (!endbracket) + fatal ("ssh_gpg_email_value_matches: No > character found in uid"); + + extraendbracket = strchr (endbracket + 1, '>'); + if (extraendbracket) + fatal ("ssh_gpg_email_value_matches: More than one > character found in uid"); + + firstendbracket = strchr (uid, '>'); + if (endbracket != firstendbracket) + fatal ("ssh_gpg_email_value_matches: More than one > character found in uid"); + + if (!strncasecmp (startbracket + 1, email, + (endbracket - startbracket) - 1)) + return 1; + return 0; +} + +/* Return 1 if the key is unbogus. By ``unbogus'', we mean + we don't care whether the web of trust tells us we should trust the + key, but we do at least care that it's got proper self-signatures, + it's not expired or revoked, etc. ssh_gpg_get_key_info does the web + of trust checks. */ +static int +unbogus (Key *key) +{ + switch (key->pgp_key_trust) { + case '-': + case 'q': + case 'n': + case 'm': + case 'f': + case 'u': + return 1; + default: + return 0; + } +} + +/* Return a string explaining why the key is not trusted. */ +static char * +bad_trust_reason (Key *key) +{ + switch (key->pgp_key_trust) { + case 'e': + return "expired"; + case 'r': + return "revoked"; + case 'i': + return "invalid"; + case 'd': + return "disabled"; + default: + return "not adaquately trusted"; + } +} + +/* return 1 if key types match, else return 0. */ +int +ssh_key_type_matches_pgp_key_type (Key *key, int pgp_keytype) +{ + switch (key->type) { + case KEY_PGP_RSA: + if (pgp_keytype == 1) + return 1; + break; + case KEY_PGP_DSA: + if (pgp_keytype == 17) + return 1; + break; + default: + fatal ("ssh_key_type_matches_pgp_key_type: impossible ssh key type value"); + } + return 0; +} + +int +ssh_gpg_export(char *pgp_key_name, u_char **data, u_int *data_count) +{ + int chin[2], chout[2], cherr[2], status; + pid_t pid, pid2; + char *env[2]; + mysig_t old_sigchld; + ssize_t count; + u_int data_size; + + status = pipe (chin); + if (status) + fatal("ssh_gpg_export: pipe: %s", strerror(errno)); + + status = pipe (chout); + if (status) + fatal("ssh_gpg_export: pipe: %s", strerror(errno)); + + status = pipe (cherr); + if (status) + fatal("ssh_gpg_export: pipe: %s", strerror(errno)); + + old_sigchld = mysignal(SIGCHLD, SIG_DFL); + pid = fork (); + if (pid == 0) { + env[0] = malloc (strlen (gpg_homedir) + 6); + sprintf (env[0], "HOME=%s", gpg_homedir); + env[1] = 0; + close (chin[1]); + close (0); + dup (chin[0]); + close (chin[0]); + close (chout[0]); + close (1); + dup (chout[1]); + close (chout[1]); + close (cherr[0]); + close (2); + dup (cherr[1]); + close (cherr[1]); + execle (gpg_absolute_filename, "gpg", "--export", pgp_key_name, (char *) 0, + env); + perror ("ssh_gpg_export: execle"); + exit (-1); + } + + if (pid < 0) + fatal("ssh_gpg_export: fork: %s", strerror(errno)); + + close (chin[0]); + close (chin[1]); + close (chout[1]); + close (cherr[1]); + + pid2 = waitpid(pid, &status, 0); + mysignal(SIGCHLD, old_sigchld); + + if (pid2 < 0) + fatal("ssh_gpg_export: waitpid: %s", strerror(errno)); + + /* In theory, this could lose if data is getting written both to + stdout and stderr, and a pipe isn't large enough to contain all of the + data written to one stream and they get out of sequence. In pratice, + I'm not really convinced this is an issue. */ + + /* Collect data from stderr before checking the exit status, so that if + gpg printed an error, we pass it along appropriately. */ + + /* This will contain a spurious newline, for reasons I haven't bothered + to chase down. */ + fd_to_debug (cherr[0], "ssh_gpg_export", "gpg stderr"); + + if (WIFSIGNALED(status)) + fatal("ssh_gpg_export: child died by signal %d", WTERMSIG(status)); + + if (WEXITSTATUS(status)) + fatal("ssh_gpg_export: child exited with return code %d", WEXITSTATUS(status)); + + data_size = SSH_GPG_BUFSIZ; + *data_count = 0; + *data = xmalloc ((data_size) + 1); + + do { + count = read (chout[0], (*data) + (*data_count), + (data_size) - (*data_count)); + (*data_count) += count; + if ((*data_count) == data_size) { + data_size += SSH_GPG_BUFSIZ; + (*data) = xrealloc ((*data), data_size + 1); + if (!(*data)) + fatal ("ssh_gpg_export: xrealloc"); + } + } while (count > 0); + + close (chout[0]); + + if (!(*data_count)) { + fatal ("ssh_gpg_export: failed to read any data from gpg"); + } + + return 0; +} + +int +ssh_gpg_sign(Key *key, u_char **sigp, u_int *lenp, + u_char *data, u_int datalen) +{ + int chin[2], chout[2], cherr[2], chstatus[2], status; + pid_t pid, pid2; + char *env[2]; + mysig_t old_sigchld; + ssize_t count; + u_int len; + FILE *statusf; + char status_fd_string[64]; + int keytype = 0; + char **fields; + int fieldcount, sig_read = 0; + Buffer b; + char buf[SSH_GPG_BUFSIZ]; + + status = pipe (chin); + if (status) + fatal("ssh_gpg_sign: pipe: %s", strerror(errno)); + + status = pipe (chout); + if (status) + fatal("ssh_gpg_sign: pipe: %s", strerror(errno)); + + status = pipe (cherr); + if (status) + fatal("ssh_gpg_sign: pipe: %s", strerror(errno)); + + status = pipe (chstatus); + if (status) + fatal("ssh_gpg_sign: pipe: %s", strerror(errno)); + + old_sigchld = mysignal(SIGCHLD, SIG_DFL); + pid = fork (); + if (pid == 0) { + env[0] = malloc (strlen (gpg_homedir) + 6); + sprintf (env[0], "HOME=%s", gpg_homedir); + env[1] = 0; + close (chin[1]); + close (0); + dup (chin[0]); + close (chin[0]); + close (chout[0]); + close (1); + dup (chout[1]); + close (chout[1]); + close (cherr[0]); + close (2); + dup (cherr[1]); + close (cherr[1]); + close (chstatus[0]); + sprintf (status_fd_string, "%d", chstatus[1]); + execle (gpg_absolute_filename, "gpg", "--status-fd", + status_fd_string, "-u", key->pgp_key_name, + "--detach-sign", (char *) 0, env); + perror ("ssh_gpg_sign: execle"); + exit (-1); + } + + if (pid < 0) + fatal("ssh_gpg_sign: fork: %s", strerror(errno)); + + count = write (chin[1], data, datalen); + if (count < 0) + fatal ("ssh_gpg_sign: write failed writing to child's input: %s", + strerror(errno)); + if (count != datalen) + fatal ("ssh_gpg_sign: write failed writing to child's input"); + + close (chin[0]); + close (chin[1]); + close (chout[1]); + close (cherr[1]); + close (chstatus[1]); + + pid2 = waitpid(pid, &status, 0); + mysignal(SIGCHLD, old_sigchld); + + if (pid2 < 0) + fatal("ssh_gpg_sign: waitpid: %s", strerror(errno)); + + /* In theory, this could lose if data is getting written both to + stdout and stderr, and a pipe isn't large enough to contain all of the + data written to one stream and they get out of sequence. In pratice, + I'm not really convinced this is an issue. */ + + /* Collect data from stderr before checking the exit status, so that if + gpg printed an error, we pass it along appropriately. */ + + fd_to_debug (cherr[0], "ssh_gpg_sign", "gpg stderr"); + + if (WIFSIGNALED(status)) + fatal("ssh_gpg_sign: child died by signal %d", WTERMSIG(status)); + + if (WEXITSTATUS(status)) + fatal("ssh_gpg_sign: child exited with return code %d", WEXITSTATUS(status)); + + statusf = fdopen (chstatus[0], "r"); + while (read_and_split_line (statusf, &fields, &fieldcount, ' ', + "ssh_gpg_sign: gpg statusfd: %s")) { + if (fieldcount < 2) + fatal ("ssh_gpg_sign: not enough fields in status line"); + if ((!strcmp ("[GNUPG:]", fields[0])) + && (!strcmp ("SIG_CREATED", fields[1]))) { + if (keytype) + fatal ("ssh_gpg_sign: more than one SIG_CREATED line found; can't happen"); + if (fieldcount < 4) + fatal ("ssh_gpg_sign: not enough fields in SIG_CREATED line"); + keytype = atoi (fields[3]); + if (!keytype) + fatal ("ssh_gpg_sign: failed to parse keytype from SIG_CREATED line; can't happen"); + debug2 ("key is of OpenPGP type %d", keytype); + } + xfree (fields); + } + if (ferror (statusf)) + fatal ("ssh_gpg_sign: error reading gpg statusfd"); + fclose (statusf); + + if (!keytype) + fatal ("ssh_gpg_sign: no SIG_CREATED line found."); + + /* fascistly force people to configure their computers correctly. */ + if (!ssh_key_type_matches_pgp_key_type (key, keytype)) + fatal ("ssh expects %s key, but GPG key type is %d", + key_ssh_name (key), keytype); + + buffer_init(&b); + buffer_put_cstring(&b, key_ssh_name (key)); + + do { + count = read (chout[0], buf, SSH_GPG_BUFSIZ); + buffer_append (&b, buf, count); + if (count) + sig_read = 1; + } while (count > 0); + + close (chout[0]); + + if (!sig_read) { + fatal ("ssh_gpg_sign: failed to read any signature from gpg"); + } + len = buffer_len(&b); + if (lenp != NULL) + *lenp = len; + if (sigp != NULL) { + *sigp = xmalloc(len); + memcpy(*sigp, buffer_ptr(&b), len); + } + buffer_free(&b); + + return 0; +} + +static void +ssh_gpg_import(Key *key, u_char *data, u_int data_count, int dry_run) +{ + int chin[2], chout[2], cherr[2], chstatus[2], status; + pid_t pid, pid2; + char *env[2]; + mysig_t old_sigchld; + char status_fd_string[64]; + int count; + FILE *statusf; + char **fields; + int fieldcount; + + debug2 ("entering ssh_gpg_import"); + + if (!data_count) + fatal ("ssh_gpg_import: data_count is 0"); + + if (!data) + fatal ("ssh_gpg_import: data_count is NULL"); + + status = pipe (chin); + if (status) + fatal("ssh_gpg_import: pipe: %s", strerror(errno)); + + status = pipe (chout); + if (status) + fatal("ssh_gpg_import: pipe: %s", strerror(errno)); + + status = pipe (cherr); + if (status) + fatal("ssh_gpg_import: pipe: %s", strerror(errno)); + + status = pipe (chstatus); + if (status) + fatal("ssh_gpg_import: pipe: %s", strerror(errno)); + + old_sigchld = mysignal(SIGCHLD, SIG_DFL); + pid = fork (); + if (pid == 0) { + env[0] = malloc (strlen (gpg_homedir) + 6); + sprintf (env[0], "HOME=%s", gpg_homedir); + env[1] = 0; + close (chin[1]); + close (0); + dup (chin[0]); + close (chin[0]); + close (chout[0]); + close (1); + dup (chout[1]); + close (chout[1]); + close (cherr[0]); + close (2); + dup (cherr[1]); + close (cherr[1]); + close (chstatus[0]); + sprintf (status_fd_string, "%d", chstatus[1]); + if (dry_run) + execle (gpg_absolute_filename, "gpg", "--quiet", "--status-fd", + status_fd_string, "--dry-run", "--import", (char *) 0, env); + else + execle (gpg_absolute_filename, "gpg", "--quiet", "--status-fd", + status_fd_string, "--import", (char *) 0, env); + perror ("ssh_gpg_import: execle"); + exit (-1); + } + + if (pid < 0) + fatal("ssh_gpg_import: fork: %s", strerror(errno)); + + close (chin[0]); + /* We don't use stdout at all. */ + close (chout[0]); + close (chout[1]); + close (cherr[1]); + close (chstatus[1]); + + do { + count = write (chin[1], data, data_count); + if (count < 0) + fatal("ssh_gpg_import: writing data to gpg: %s", strerror(errno)); + debug2 ("ssh_gpg_import: wrote %d bytes to gpg child", count); + data += count, data_count -= count; + } while (data_count); + + close (chin[1]); + + fd_to_debug (cherr[0], "ssh_gpg_import", "gpg stderr"); + + statusf = fdopen (chstatus[0], "r"); + while (read_and_split_line (statusf, &fields, &fieldcount, ' ', + "ssh_gpg_import: gpg statusfd: %s")) { + if ((!strcmp ("[GNUPG:]", fields[0])) + && (!strcmp ("IMPORT_OK", fields[1]))) { + if (key->pgp_key_fingerprint) + fatal ("ssh_gpg_import: pgp_key_fingerprint is not NULL before adding fingerprint"); + key->pgp_key_fingerprint = xstrdup (fields [3]); + debug2 ("ssh_gpg_import: key->pgp_key_fingerprint is '%s'", key->pgp_key_fingerprint); + } + } + if (ferror (statusf)) + fatal ("ssh_gpg_import: error reading gpg statusfd"); + fclose (statusf); + + pid2 = waitpid(pid, &status, 0); + mysignal(SIGCHLD, old_sigchld); + + if (pid2 < 0) + fatal("ssh_gpg_import: waitpid: %s", strerror(errno)); + + if (WIFSIGNALED(status)) + fatal("ssh_gpg_import: child died by signal %d", WTERMSIG(status)); + + if (WEXITSTATUS(status)) + fatal("ssh_gpg_import: child exited with return code %d", WEXITSTATUS(status)); + + debug2 ("about to exit ssh_gpg_import"); +} + + +int +ssh_gpg_verify_sig(Key *key, u_char *signature, u_int signaturelen, + u_char *data, u_int data_count) +{ + int chin[2], chout[2], cherr[2], chstatus[2], chsig[2], status; + pid_t pid, pid2; + char *env[2]; + mysig_t old_sigchld; + char status_fd_string[64], sig_fd_string[64]; + int count; + FILE *statusf; + int retval = 0; + Key *subkey; + char **fields; + int fieldcount; + char *type_string; + Buffer b; + + debug2 ("entering ssh_gpg_verify_sig, signaturelen == %d, data_count == %d", + signaturelen, data_count); + + if (!key->pgp_key_fingerprint) + ssh_gpg_import (key, key->pgp_key_data, key->pgp_key_data_size, 1); + + if (!unbogus (key)) + fatal ("ssh_gpg_verify_sig: primary public key is %s", bad_trust_reason (key)); + + buffer_init(&b); + buffer_append(&b, signature, signaturelen); + type_string = buffer_get_string(&b, NULL); + + if ((strcmp("pgp-sign-rsa", type_string) != 0) + && (strcmp("pgp-sign-dss", type_string) != 0)) + fatal ("ssh_gpg_verify_sig: unrecognized signature type %s", type_string); + + xfree (type_string); + + status = pipe (chin); + if (status) + fatal("ssh_gpg_verify_sig: pipe: %s", strerror(errno)); + + status = pipe (chout); + if (status) + fatal("ssh_gpg_verify_sig: pipe: %s", strerror(errno)); + + status = pipe (cherr); + if (status) + fatal("ssh_gpg_verify_sig: pipe: %s", strerror(errno)); + + status = pipe (chstatus); + if (status) + fatal("ssh_gpg_verify_sig: pipe: %s", strerror(errno)); + + status = pipe (chsig); + if (status) + fatal("ssh_gpg_verify_sig: pipe: %s", strerror(errno)); + + old_sigchld = mysignal(SIGCHLD, SIG_DFL); + pid = fork (); + if (pid == 0) { + env[0] = malloc (strlen (gpg_homedir) + 6); + sprintf (env[0], "HOME=%s", gpg_homedir); + env[1] = 0; + close (chin[1]); + close (0); + dup (chin[0]); + close (chin[0]); + close (chout[0]); + close (1); + dup (chout[1]); + close (chout[1]); + close (cherr[0]); + close (2); + dup (cherr[1]); + close (cherr[1]); + close (chstatus[0]); + close (chsig[1]); + sprintf (status_fd_string, "%d", chstatus[1]); + sprintf (sig_fd_string, "-&%d", chsig[0]); + execle (gpg_absolute_filename, "gpg", "--quiet", "--status-fd", + status_fd_string, "--enable-special-filenames", "--verify", + "--", sig_fd_string, "-", (char *) 0, env); + perror ("ssh_gpg_verify_sig: execle"); + exit (-1); + } + + if (pid < 0) + fatal("ssh_gpg_verify_sig: fork: %s", strerror(errno)); + + close (chin[0]); + close (chout[1]); + close (cherr[1]); + close (chstatus[1]); + close (chsig[0]); + + /* We ignore anything sent to stdout. */ + close (chout[0]); + + do { + count = write (chsig[1], buffer_ptr (&b), buffer_len (&b)); + if (count < 0) + fatal("ssh_gpg_verify_sig: writing signature data to gpg: %s", + strerror(errno)); + debug2 ("ssh_gpg_verify_sig: wrote %d bytes of signature to gpg child", count); + buffer_consume (&b, count); + } while (buffer_len (&b)); + + close (chsig[1]); + buffer_free(&b); + + do { + count = write (chin[1], data, data_count); + if (count < 0) + fatal("ssh_gpg_verify_sig: writing data to gpg: %s", strerror(errno)); + debug2 ("ssh_gpg_verify_sig: wrote %d bytes to gpg child", count); + data += count, data_count -= count; + } while (data_count); + + close (chin[1]); + + /* This will normally print a message saying that verify succeeded. + It might be that we should use --logger-fd and redirect that to + debug, and then make stderr cause ``error'' to be called, but that + would be effort, and everyone knows to try again ssh -v when things + fail, anyway. */ + fd_to_debug (cherr[0], "ssh_gpg_verify_sig", "gpg stderr"); + + statusf = fdopen (chstatus[0], "r"); + while (read_and_split_line (statusf, &fields, &fieldcount, ' ', + "ssh_gpg_verify_sig: gpg statusfd: %s")) { + if (fieldcount < 2) + fatal ("ssh_gpg_verify_sig: not enough fields in status line"); + if ((!strcmp ("[GNUPG:]", fields[0])) + && (!strcmp ("VALIDSIG", fields[1]))) { + subkey = key; + while (subkey && !retval) { + debug2 ("ssh_gpg_verify_sig: Comparing key used to make signature against %s", + subkey->pgp_key_fingerprint); + if (fieldcount < 3) + fatal ("ssh_gpg_verify_sig: not enough fields in VALIDSIG line"); + if (!strcmp(subkey->pgp_key_fingerprint, fields[2])) { + debug2 ("ssh_gpg_verify_sig: Match found."); + if (!unbogus (subkey)) + fatal ("ssh_gpg_verify_sig: subkey is %s", bad_trust_reason (subkey)); + retval = 1; + /* fascistly force people to configure their computers correctly. + draft-ietf-secsh-transport-15.txt says that pgp-sign-rsa means + that ``the key'' is in RSA format. It's not clear what + ``the key'' means when the primary public key is one type, and + the subkey used for the sig another, and sending mail asking + for claification didn't yield responses, so this code simply + verifies that the key type used for the signature matches the + key type that the ssh protocol said would be used. */ + if (!ssh_key_type_matches_pgp_key_type (subkey, + subkey->pgp_key_type)) + fatal ("ssh expects %s key, but GPG key type is %d", + key_ssh_name (subkey), subkey->pgp_key_type); + } else { + subkey = subkey->pgp_subkey; + } + } + if (!retval) + fatal ("ssh_gpg_verify_sig: pgp_key_fingerprint doesn't match fingerprint of imported key"); + } + xfree (fields); + } + if (ferror (statusf)) + fatal ("ssh_gpg_verify_sig: error reading gpg statusfd"); + fclose (statusf); + + pid2 = waitpid(pid, &status, 0); + mysignal(SIGCHLD, old_sigchld); + + if (pid2 < 0) + fatal("ssh_gpg_verify_sig: waitpid: %s", strerror(errno)); + + if (WIFSIGNALED(status)) + fatal("ssh_gpg_verify_sig: child died by signal %d", WTERMSIG(status)); + + if (WEXITSTATUS(status)) + fatal("ssh_gpg_verify_sig: child exited with return code %d", WEXITSTATUS(status)); + + debug2 ("about to exit ssh_gpg_verify_sig; retval == %d", retval); + + return retval; +} + + +typedef enum { + KL_START, + KL_PUB, + KL_PUB_FPR, + KL_UID, + KL_SUB, + KL_SUB_FPR +} keylist_state; + +/* Call gpg --list-keys with appropriate args, to fill in subkey info, + key type and trust info. + + If email is non-null, return -1 if key doesn't have a trusted + uid that matches email. Else, return 0. + + Add key to keyring unless dry_run is non-null. + + This tries to call fatal rather agressively if gpg's output doesn't + match what it expects, in order to try to make sure that if gpg + evolves or I misunderstood what its output should be, that is as + unlikely to cause security problems as possible. */ +int +ssh_gpg_get_key_info(Key *key, char *email, int dry_run) +{ + int chin[2], chout[2], cherr[2], status; + pid_t pid, pid2; + char *env[2]; + mysig_t old_sigchld; + FILE *outf; + int trusted_email_match = 0; + keylist_state kl_state = KL_START; + Key *subkey; + char **fields; + int fieldcount; + + debug2 ("entering ssh_gpg_get_key_info, email == %s", email); + + if (!key->pgp_key_fingerprint) { + if (!key->pgp_key_data) + fatal ("ssh_gpg_get_key_info: no key->pgp_key_fingerprint, and no key->pgp_key_data"); + ssh_gpg_import (key, key->pgp_key_data, key->pgp_key_data_size, dry_run); + if (!key->pgp_key_fingerprint) + fatal ("ssh_gpg_get_key_info: key->pgp_key_fingerprint is NULL after ssh_gpg_import; perhaps your version of gnupg is too old?"); + } + + subkey = key; + + status = pipe (chin); + if (status) + fatal("ssh_gpg_get_key_info: pipe: %s", strerror(errno)); + + status = pipe (chout); + if (status) + fatal("ssh_gpg_get_key_info: pipe: %s", strerror(errno)); + + status = pipe (cherr); + if (status) + fatal("ssh_gpg_get_key_info: pipe: %s", strerror(errno)); + + old_sigchld = mysignal(SIGCHLD, SIG_DFL); + pid = fork (); + if (pid == 0) { + env[0] = malloc (strlen (gpg_homedir) + 6); + sprintf (env[0], "HOME=%s", gpg_homedir); + env[1] = 0; + close (chin[1]); + close (0); + dup (chin[0]); + close (chin[0]); + close (chout[0]); + close (1); + dup (chout[1]); + close (chout[1]); + close (cherr[0]); + close (2); + dup (cherr[1]); + close (cherr[1]); + execle (gpg_absolute_filename, "gpg", "--fixed-list-mode", + "--with-colons", "--fingerprint", "--fingerprint", + key->pgp_key_fingerprint, (char *) 0, env); + perror ("ssh_gpg_get_key_info: execle"); + exit (-1); + } + + if (pid < 0) + fatal("ssh_gpg_get_key_info: fork: %s", strerror(errno)); + + close (chin[0]); + close (chout[1]); + close (cherr[1]); + + /* Nothing to send to gpg. */ + close (chin[1]); + + fd_to_debug (cherr[0], "ssh_gpg_get_key_info", "gpg stderr"); + + outf = fdopen (chout[0], "r"); + while (read_and_split_line (outf, &fields, &fieldcount, ':', + "ssh_gpg_get_key_info: gpg stdout: %s")) { + if (!fields[0]) + fatal ("ssh_gpg_get_key_info: empty first field of gpg output, can't happen."); + if (!strcmp ("pub", fields[0])) { + if (kl_state != KL_START) + fatal ("ssh_gpg_get_key_info: pub seen, not at begining of key info."); + kl_state = KL_PUB; + if (fieldcount < 4) + fatal ("ssh_gpg_get_key_info: not enough fields in pub line"); + key->pgp_key_trust = fields[1][0]; + key->pgp_key_type = atoi (fields[3]); + debug2 ("key->pgp_key_type is %d", key->pgp_key_type); + } else if (!strcmp ("uid", fields[0])) { + if ((kl_state != KL_PUB_FPR) + && (kl_state != KL_UID)) { + fatal ("ssh_gpg_get_key_info: uid seen, in wrong place; can't happen."); + } + kl_state = KL_UID; + if (fieldcount < 10) + fatal ("ssh_gpg_get_key_info: not enough fields in uid line"); + /* We skip over untrusted uids, because there might in theory be a good + reason why some uids would get more sigs than others, or something. */ + if ((fields[1][0] == 'f') || (fields[1][0] == 'u')) { + debug2 ("ssh_gpg_get_key_info: uid is fully trusted, checking identity value"); + if (email) { + if (ssh_gpg_email_value_matches (fields[9], email)) { + debug2 ("ssh_gpg_get_key_info: key is trusted and has correct uid"); + trusted_email_match = 1; + } + } + } + } else if (!strcmp ("fpr", fields[0])) { + if (kl_state == KL_PUB) { + kl_state = KL_PUB_FPR; + if (strcmp (key->pgp_key_fingerprint, fields[9])) + fatal ("ssh_gpg_get_key_info: fingerprint doesn't match fingerprint we have"); + } else if (kl_state == KL_SUB) { + kl_state = KL_SUB_FPR; + subkey->pgp_key_fingerprint = xmalloc (strlen (fields[9]) + 1); + strcpy (subkey->pgp_key_fingerprint, fields[9]); + debug2 ("ssh_gpg_get_key_info: subkey->pgp_key_fingerprint is %s", subkey->pgp_key_fingerprint); + } else { + fatal ("ssh_gpg_get_key_info: fpr found in unexpected state."); + } + } else if (!strcmp ("sub", fields[0])) { + if ((kl_state == KL_UID) || (kl_state == KL_SUB_FPR)) { + subkey->pgp_subkey = key_new (key->type); + subkey = subkey->pgp_subkey; + kl_state = KL_SUB; + } else { + fatal ("ssh_gpg_get_key_info: sub found in unexpected state."); + } + subkey->pgp_key_trust = fields[1][0]; + subkey->pgp_key_type = atoi (fields[3]); + debug2 ("ssh_gpg_get_key_info: subkey type is %d", subkey->pgp_key_type); + } else { + fatal ("ssh_gpg_get_key_info: unrecognized line type seen."); + } + xfree (fields); + } + if (ferror (outf)) + fatal ("ssh_gpg_get_key_info: error reading gpg outfd"); + fclose (outf); + + pid2 = waitpid(pid, &status, 0); + mysignal(SIGCHLD, old_sigchld); + + if (pid2 < 0) + fatal("ssh_gpg_get_key_info: waitpid: %s", strerror(errno)); + + if (WIFSIGNALED(status)) + fatal("ssh_gpg_get_key_info: child died by signal %d", WTERMSIG(status)); + + if (WEXITSTATUS(status)) + fatal("ssh_gpg_get_key_info: child exited with return code %d", WEXITSTATUS(status)); + + if (email && !trusted_email_match) { + error ("ssh_gpg_get_key_info: no trusted match for email value of uid."); + return -1; + } + + return 1; +} + +#endif /* WITH_GPG */ --- /dev/null Wed Jul 30 17:55:31 2003 +++ ssh-gpg.h Tue Jul 29 21:18:40 2003 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2002 Joel N. Weber II. + * + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +int +ssh_gpg_export(char *pgp_key_name, u_char **data, u_int *data_count); + +int +ssh_gpg_sign(Key *key, u_char **sigp, u_int *lenp, + u_char *data, u_int datalen); + +int +ssh_gpg_verify_sig(Key *key, u_char *signature, u_int signaturelen, + u_char *data, u_int data_count); + +int +ssh_gpg_get_key_info(Key *key, char *hostname, int dry_run); + + +extern char *gpg_homedir, *gpg_absolute_filename; --- LICENCE.~1~ Thu Jun 20 21:19:12 2002 +++ LICENCE Tue Jul 29 21:18:40 2003 @@ -180,6 +180,30 @@ * SUCH DAMAGE. 7) + The code to use gpg was contributed by Joel N. Weber II under + the following license: + + * Redistribution, copying, and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +8) Remaining components of the software are provided under a standard 2-term BSD licence with the following names as copyright holders: