diff options
Diffstat (limited to 'sm/import.c')
-rw-r--r-- | sm/import.c | 725 |
1 files changed, 0 insertions, 725 deletions
diff --git a/sm/import.c b/sm/import.c deleted file mode 100644 index c5581eb64..000000000 --- a/sm/import.c +++ /dev/null @@ -1,725 +0,0 @@ -/* import.c - Import certificates - * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * GnuPG is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * GnuPG is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <time.h> -#include <assert.h> -#include <signal.h> -#include <fcntl.h> -#include <sys/wait.h> - -#include "gpgsm.h" -#include <gcrypt.h> -#include <ksba.h> - -#include "keydb.h" -#include "i18n.h" - -#ifdef _POSIX_OPEN_MAX -#define MAX_OPEN_FDS _POSIX_OPEN_MAX -#else -#define MAX_OPEN_FDS 20 -#endif - - -struct stats_s { - unsigned long count; - unsigned long imported; - unsigned long unchanged; - unsigned long not_imported; - unsigned long secret_read; - unsigned long secret_imported; - unsigned long secret_dups; - }; - - -static gpg_error_t parse_p12 (ksba_reader_t reader, FILE **retfp, - struct stats_s *stats); - - - -static void -print_imported_status (CTRL ctrl, ksba_cert_t cert, int new_cert) -{ - char *fpr; - - fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); - if (new_cert) - gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL); - - gpgsm_status2 (ctrl, STATUS_IMPORT_OK, - new_cert? "1":"0", fpr, NULL); - - xfree (fpr); -} - - -/* Print an IMPORT_PROBLEM status. REASON is one of: - 0 := "No specific reason given". - 1 := "Invalid Certificate". - 2 := "Issuer Certificate missing". - 3 := "Certificate Chain too long". - 4 := "Error storing certificate". -*/ -static void -print_import_problem (CTRL ctrl, ksba_cert_t cert, int reason) -{ - char *fpr = NULL; - char buf[25]; - int i; - - sprintf (buf, "%d", reason); - if (cert) - { - fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); - /* detetect an error (all high) value */ - for (i=0; fpr[i] == 'F'; i++) - ; - if (!fpr[i]) - { - xfree (fpr); - fpr = NULL; - } - } - gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL); - xfree (fpr); -} - - -void -print_imported_summary (CTRL ctrl, struct stats_s *stats) -{ - char buf[14*25]; - - if (!opt.quiet) - { - log_info (_("total number processed: %lu\n"), stats->count); - if (stats->imported) - { - log_info (_(" imported: %lu"), stats->imported ); - log_printf ("\n"); - } - if (stats->unchanged) - log_info (_(" unchanged: %lu\n"), stats->unchanged); - if (stats->secret_read) - log_info (_(" secret keys read: %lu\n"), stats->secret_read ); - if (stats->secret_imported) - log_info (_(" secret keys imported: %lu\n"), stats->secret_imported ); - if (stats->secret_dups) - log_info (_(" secret keys unchanged: %lu\n"), stats->secret_dups ); - if (stats->not_imported) - log_info (_(" not imported: %lu\n"), stats->not_imported); - } - - sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", - stats->count, - 0l /*stats->no_user_id*/, - stats->imported, - 0l /*stats->imported_rsa*/, - stats->unchanged, - 0l /*stats->n_uids*/, - 0l /*stats->n_subk*/, - 0l /*stats->n_sigs*/, - 0l /*stats->n_revoc*/, - stats->secret_read, - stats->secret_imported, - stats->secret_dups, - 0l /*stats->skipped_new_keys*/, - stats->not_imported - ); - gpgsm_status (ctrl, STATUS_IMPORT_RES, buf); -} - - - -static void -check_and_store (CTRL ctrl, struct stats_s *stats, ksba_cert_t cert, int depth) -{ - int rc; - - if (stats) - stats->count++; - if ( depth >= 50 ) - { - log_error (_("certificate chain too long\n")); - if (stats) - stats->not_imported++; - print_import_problem (ctrl, cert, 3); - return; - } - - /* Some basic checks, but don't care about missing certificates; - this is so that we are able to import entire certificate chains - w/o requirening a special order (i.e. root-CA first). This used - to be different but because gpgsm_verify even imports - certificates without any checks, it doesn't matter much and the - code gets much cleaner. A housekeeping function to remove - certificates w/o an anchor would be nice, though. */ - rc = gpgsm_basic_cert_check (cert); - if (!rc || gpg_err_code (rc) == GPG_ERR_MISSING_CERT) - { - int existed; - - if (!keydb_store_cert (cert, 0, &existed)) - { - ksba_cert_t next = NULL; - - if (!existed) - { - print_imported_status (ctrl, cert, 1); - if (stats) - stats->imported++; - } - else - { - print_imported_status (ctrl, cert, 0); - if (stats) - stats->unchanged++; - } - - if (opt.verbose > 1 && existed) - { - if (depth) - log_info ("issuer certificate already in DB\n"); - else - log_info ("certificate already in DB\n"); - } - else if (opt.verbose && !existed) - { - if (depth) - log_info ("issuer certificate imported\n"); - else - log_info ("certificate imported\n"); - } - - /* Now lets walk up the chain and import all certificates up - the chain. This is required in case we already stored - parent certificates in the ephemeral keybox. Do not - update the statistics, though. */ - if (!gpgsm_walk_cert_chain (cert, &next)) - { - check_and_store (ctrl, NULL, next, depth+1); - ksba_cert_release (next); - } - } - else - { - log_error (_("error storing certificate\n")); - if (stats) - stats->not_imported++; - print_import_problem (ctrl, cert, 4); - } - } - else - { - log_error (_("basic certificate checks failed - not imported\n")); - if (stats) - stats->not_imported++; - print_import_problem (ctrl, cert, - gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 : - gpg_err_code (rc) == GPG_ERR_BAD_CERT? 1 : 0); - } -} - - - - -static int -import_one (CTRL ctrl, struct stats_s *stats, int in_fd) -{ - int rc; - Base64Context b64reader = NULL; - ksba_reader_t reader; - ksba_cert_t cert = NULL; - ksba_cms_t cms = NULL; - FILE *fp = NULL; - ksba_content_type_t ct; - int any = 0; - - fp = fdopen ( dup (in_fd), "rb"); - if (!fp) - { - rc = gpg_error (gpg_err_code_from_errno (errno)); - log_error ("fdopen() failed: %s\n", strerror (errno)); - goto leave; - } - - rc = gpgsm_create_reader (&b64reader, ctrl, fp, 1, &reader); - if (rc) - { - log_error ("can't create reader: %s\n", gpg_strerror (rc)); - goto leave; - } - - - /* We need to loop here to handle multiple PEM objects in one - file. */ - do - { - ksba_cms_release (cms); cms = NULL; - ksba_cert_release (cert); cert = NULL; - - ct = ksba_cms_identify (reader); - if (ct == KSBA_CT_SIGNED_DATA) - { /* This is probably a signed-only message - import the certs */ - ksba_stop_reason_t stopreason; - int i; - - rc = ksba_cms_new (&cms); - if (rc) - goto leave; - - rc = ksba_cms_set_reader_writer (cms, reader, NULL); - if (rc) - { - log_error ("ksba_cms_set_reader_writer failed: %s\n", - gpg_strerror (rc)); - goto leave; - } - - do - { - rc = ksba_cms_parse (cms, &stopreason); - if (rc) - { - log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); - goto leave; - } - - if (stopreason == KSBA_SR_BEGIN_DATA) - log_info ("not a certs-only message\n"); - } - while (stopreason != KSBA_SR_READY); - - for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) - { - check_and_store (ctrl, stats, cert, 0); - ksba_cert_release (cert); - cert = NULL; - } - if (!i) - log_error ("no certificate found\n"); - else - any = 1; - } - else if (ct == KSBA_CT_PKCS12) - { /* This seems to be a pkcs12 message. We use an external - tool to parse the message and to store the private keys. - We need to use a another reader here to parse the - certificate we included in the p12 file; then we continue - to look for other pkcs12 files (works only if they are in - PEM format. */ - FILE *certfp; - Base64Context b64p12rdr; - ksba_reader_t p12rdr; - - rc = parse_p12 (reader, &certfp, stats); - if (!rc) - { - any = 1; - - rewind (certfp); - rc = gpgsm_create_reader (&b64p12rdr, ctrl, certfp, 1, &p12rdr); - if (rc) - { - log_error ("can't create reader: %s\n", gpg_strerror (rc)); - fclose (certfp); - goto leave; - } - - do - { - ksba_cert_release (cert); cert = NULL; - rc = ksba_cert_new (&cert); - if (!rc) - { - rc = ksba_cert_read_der (cert, p12rdr); - if (!rc) - check_and_store (ctrl, stats, cert, 0); - } - ksba_reader_clear (p12rdr, NULL, NULL); - } - while (!rc && !gpgsm_reader_eof_seen (b64p12rdr)); - - if (gpg_err_code (rc) == GPG_ERR_EOF) - rc = 0; - gpgsm_destroy_reader (b64p12rdr); - fclose (certfp); - if (rc) - goto leave; - } - } - else if (ct == KSBA_CT_NONE) - { /* Failed to identify this message - assume a certificate */ - - rc = ksba_cert_new (&cert); - if (rc) - goto leave; - - rc = ksba_cert_read_der (cert, reader); - if (rc) - goto leave; - - check_and_store (ctrl, stats, cert, 0); - any = 1; - } - else - { - log_error ("can't extract certificates from input\n"); - rc = gpg_error (GPG_ERR_NO_DATA); - } - - ksba_reader_clear (reader, NULL, NULL); - } - while (!gpgsm_reader_eof_seen (b64reader)); - - leave: - if (any && gpg_err_code (rc) == GPG_ERR_EOF) - rc = 0; - ksba_cms_release (cms); - ksba_cert_release (cert); - gpgsm_destroy_reader (b64reader); - if (fp) - fclose (fp); - return rc; -} - - -int -gpgsm_import (CTRL ctrl, int in_fd) -{ - int rc; - struct stats_s stats; - - memset (&stats, 0, sizeof stats); - rc = import_one (ctrl, &stats, in_fd); - print_imported_summary (ctrl, &stats); - /* If we never printed an error message do it now so that a command - line invocation will return with an error (log_error keeps a - global errorcount) */ - if (rc && !log_get_errorcount (0)) - log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); - return rc; -} - - -int -gpgsm_import_files (CTRL ctrl, int nfiles, char **files, - int (*of)(const char *fname)) -{ - int rc = 0; - struct stats_s stats; - - memset (&stats, 0, sizeof stats); - - if (!nfiles) - rc = import_one (ctrl, &stats, 0); - else - { - for (; nfiles && !rc ; nfiles--, files++) - { - int fd = of (*files); - rc = import_one (ctrl, &stats, fd); - close (fd); - if (rc == -1) - rc = 0; - } - } - print_imported_summary (ctrl, &stats); - /* If we never printed an error message do it now so that a command - line invocation will return with an error (log_error keeps a - global errorcount) */ - if (rc && !log_get_errorcount (0)) - log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); - return rc; -} - - -/* Fork and exec the protecttool, connect the file descriptor of - INFILE to stdin, return a new stream in STATUSFILE, write the - output to OUTFILE and the pid of the process in PID. Returns 0 on - success or an error code. */ -static gpg_error_t -popen_protect_tool (const char *pgmname, - FILE *infile, FILE *outfile, FILE **statusfile, pid_t *pid) -{ - gpg_error_t err; - int fd, fdout, rp[2]; - int n, i; - - fflush (infile); - rewind (infile); - fd = fileno (infile); - fdout = fileno (outfile); - if (fd == -1 || fdout == -1) - log_fatal ("no file descriptor for temporary file: %s\n", - strerror (errno)); - - /* Now start the protect-tool. */ - if (pipe (rp) == -1) - { - err = gpg_error_from_errno (errno); - log_error (_("error creating a pipe: %s\n"), strerror (errno)); - return err; - } - - *pid = fork (); - if (*pid == -1) - { - err = gpg_error_from_errno (errno); - log_error (_("error forking process: %s\n"), strerror (errno)); - close (rp[0]); - close (rp[1]); - return err; - } - - if (!*pid) - { /* Child. */ - const char *arg0; - - arg0 = strrchr (pgmname, '/'); - if (arg0) - arg0++; - else - arg0 = pgmname; - - /* Connect the infile to stdin. */ - if (fd != 0 && dup2 (fd, 0) == -1) - log_fatal ("dup2 stdin failed: %s\n", strerror (errno)); - - /* Connect the outfile to stdout. */ - if (fdout != 1 && dup2 (fdout, 1) == -1) - log_fatal ("dup2 stdout failed: %s\n", strerror (errno)); - - /* Connect stderr to our pipe. */ - if (rp[1] != 2 && dup2 (rp[1], 2) == -1) - log_fatal ("dup2 stderr failed: %s\n", strerror (errno)); - - /* Close all other files. */ - n = sysconf (_SC_OPEN_MAX); - if (n < 0) - n = MAX_OPEN_FDS; - for (i=3; i < n; i++) - close(i); - errno = 0; - - setup_pinentry_env (); - - execlp (pgmname, arg0, - "--homedir", opt.homedir, - "--p12-import", - "--store", - "--no-fail-on-exist", - "--enable-status-msg", - "--", - NULL); - /* No way to print anything, as we have closed all streams. */ - _exit (31); - } - - /* Parent. */ - close (rp[1]); - *statusfile = fdopen (rp[0], "r"); - if (!*statusfile) - { - err = gpg_error_from_errno (errno); - log_error ("can't fdopen pipe for reading: %s", strerror (errno)); - kill (*pid, SIGTERM); - return err; - } - - return 0; -} - - -/* Assume that the reader is at a pkcs#12 message and try to import - certificates from that stupid format. We will alos store secret - keys. All of the pkcs#12 parsing and key storing is handled by the - gpg-protect-tool, we merely have to take care of receiving the - certificates. On success RETFP returns a temporary file with - certificates. */ -static gpg_error_t -parse_p12 (ksba_reader_t reader, FILE **retfp, struct stats_s *stats) -{ - const char *pgmname; - gpg_error_t err = 0, child_err = 0; - int i, c, cont_line; - unsigned int pos; - FILE *tmpfp, *certfp = NULL, *fp = NULL; - char buffer[1024]; - size_t nread; - pid_t pid = -1; - - if (!opt.protect_tool_program || !*opt.protect_tool_program) - pgmname = GNUPG_DEFAULT_PROTECT_TOOL; - else - pgmname = opt.protect_tool_program; - - *retfp = NULL; - - /* To avoid an extra feeder process or doing selects and because - gpg-protect-tool will anyway parse the entire pkcs#12 message in - memory, we simply use tempfiles here and pass them to - the gpg-protect-tool. */ - tmpfp = tmpfile (); - if (!tmpfp) - { - err = gpg_error_from_errno (errno); - log_error (_("error creating temporary file: %s\n"), strerror (errno)); - goto cleanup; - } - while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread))) - { - if (nread && fwrite (buffer, nread, 1, tmpfp) != 1) - { - err = gpg_error_from_errno (errno); - log_error (_("error writing to temporary file: %s\n"), - strerror (errno)); - goto cleanup; - } - } - if (gpg_err_code (err) == GPG_ERR_EOF) - err = 0; - if (err) - { - log_error (_("error reading input: %s\n"), gpg_strerror (err)); - goto cleanup; - } - - certfp = tmpfile (); - if (!certfp) - { - err = gpg_error_from_errno (errno); - log_error (_("error creating temporary file: %s\n"), strerror (errno)); - goto cleanup; - } - - err = popen_protect_tool (pgmname, tmpfp, certfp, &fp, &pid); - if (err) - { - pid = -1; - goto cleanup; - } - fclose (tmpfp); - tmpfp = NULL; - - /* Read stderr of the protect tool. */ - pos = 0; - cont_line = 0; - while ((c=getc (fp)) != EOF) - { - /* fixme: We could here grep for status information of the - protect tool to figure out better error codes for - CHILD_ERR. */ - buffer[pos++] = c; - if (pos >= sizeof buffer - 5 || c == '\n') - { - buffer[pos - (c == '\n')] = 0; - if (cont_line) - log_printf ("%s", buffer); - else - { - if (!strncmp (buffer, "gpg-protect-tool: [PROTECT-TOOL:] ",34)) - { - char *p, *pend; - - p = buffer + 34; - pend = strchr (p, ' '); - if (pend) - *pend = 0; - if ( !strcmp (p, "secretkey-stored")) - { - stats->count++; - stats->secret_read++; - stats->secret_imported++; - } - else if ( !strcmp (p, "secretkey-exists")) - { - stats->count++; - stats->secret_read++; - stats->secret_dups++; - } - else if ( !strcmp (p, "bad-passphrase")) - ; - } - else - log_info ("%s", buffer); - } - pos = 0; - cont_line = (c != '\n'); - } - } - - if (pos) - { - buffer[pos] = 0; - if (cont_line) - log_printf ("%s\n", buffer); - else - log_info ("%s\n", buffer); - } - - /* If we found no error in the output of the cild, setup a suitable - error code, which will later be reset if the exit status of the - child is 0. */ - if (!child_err) - child_err = gpg_error (GPG_ERR_DECRYPT_FAILED); - - - cleanup: - if (tmpfp) - fclose (tmpfp); - if (fp) - fclose (fp); - if (pid != -1) - { - int status; - - while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR) - ; - if (i == -1) - log_error (_("waiting for protect-tool to terminate failed: %s\n"), - strerror (errno)); - else if (WIFEXITED (status) && WEXITSTATUS (status) == 31) - log_error (_("error running `%s': probably not installed\n"), pgmname); - else if (WIFEXITED (status) && WEXITSTATUS (status)) - log_error (_("error running `%s': exit status %d\n"), pgmname, - WEXITSTATUS (status)); - else if (!WIFEXITED (status)) - log_error (_("error running `%s': terminated\n"), pgmname); - else - child_err = 0; - } - if (!err) - err = child_err; - if (err) - { - if (certfp) - fclose (certfp); - } - else - *retfp = certfp; - return err; -} |