/* crlfetch.c - LDAP access * Copyright (C) 2002 Klarälvdalens Datakonsult AB * Copyright (C) 2003, 2004, 2005, 2006 g10 Code GmbH * * This file is part of DirMngr. * * DirMngr 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. * * DirMngr 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 #include #include #include #include "crlfetch.h" #include "dirmngr.h" #include "misc.h" #include "http.h" /* We need to associate a reader object with file streams. This table is used for it. */ struct file_reader_map_s { ksba_reader_t reader; FILE *fp; }; #define MAX_FILE_READER 50 static struct file_reader_map_s file_reader_map[MAX_FILE_READER]; /* Associate FP with READER. If the table is full wait until another thread has removed an entry. */ static void register_file_reader (ksba_reader_t reader, FILE *fp) { int i; for (;;) { for (i=0; i < MAX_FILE_READER; i++) if (!file_reader_map[i].reader) { file_reader_map[i].reader = reader; file_reader_map[i].fp = fp; return; } log_info (_("reader to file mapping table full - waiting\n")); pth_sleep (2); } } /* Scan the table for an entry matching READER, emove tha entry and return the associated file pointer. */ static FILE * get_file_reader (ksba_reader_t reader) { FILE *fp = NULL; int i; for (i=0; i < MAX_FILE_READER; i++) if (file_reader_map[i].reader == reader) { fp = file_reader_map[i].fp; file_reader_map[i].reader = NULL; file_reader_map[i].fp = NULL; break; } return fp; } /* Fetch CRL from URL and return the entire CRL using new ksba reader object in READER. Note that this reader object should be closed only using ldap_close_reader. */ gpg_error_t crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) { gpg_error_t err; parsed_uri_t uri; char *free_this = NULL; int redirects_left = 2; /* We allow for 2 redirect levels. */ *reader = NULL; once_more: err = http_parse_uri (&uri, url); http_release_parsed_uri (uri); if (err && url && !strncmp (url, "https:", 6)) { /* Our HTTP code does not support TLS, thus we can't use this scheme and it is frankly not useful for CRL retrieval anyway. We resort to using http, assuming that the server also provides plain http access. */ free_this = xtrymalloc (strlen (url) + 1); if (free_this) { strcpy (stpcpy (free_this,"http:"), url+6); err = http_parse_uri (&uri, free_this); http_release_parsed_uri (uri); if (!err) { log_info (_("using \"http\" instead of \"https\"\n")); url = free_this; } } } if (!err) /* Yes, our HTTP code groks that. */ { http_t hd; if (opt.disable_http) { log_error (_("CRL access not possible due to disabled %s\n"), "HTTP"); err = gpg_error (GPG_ERR_NOT_SUPPORTED); } else err = http_open_document (&hd, url, NULL, (opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) |HTTP_FLAG_NO_SHUTDOWN |HTTP_FLAG_NEED_HEADER, opt.http_proxy, NULL); switch ( err? 99999 : http_get_status_code (hd) ) { case 200: { FILE *fp = http_get_read_ptr (hd); err = ksba_reader_new (reader); if (!err) err = ksba_reader_set_file (*reader, fp); if (err) { log_error (_("error initializing reader object: %s\n"), gpg_strerror (err)); ksba_reader_release (*reader); *reader = NULL; http_close (hd, 0); } else { register_file_reader (*reader, fp); http_close (hd, 1); } } break; case 301: /* Redirection (perm.). */ case 302: /* Redirection (temp.). */ { const char *s = http_get_header (hd, "Location"); log_info (_("URL `%s' redirected to `%s' (%u)\n"), url, s?s:"[none]", http_get_status_code (hd)); if (s && *s && redirects_left-- ) { xfree (free_this); url = NULL; free_this = xtrystrdup (s); if (!free_this) err = gpg_error_from_errno (errno); else { url = free_this; http_close (hd, 0); /* Note, that our implementation of redirection actually handles a redirect to LDAP. */ goto once_more; } } else err = gpg_error (GPG_ERR_NO_DATA); log_error (_("too many redirections\n")); /* Or no "Location". */ http_close (hd, 0); } break; case 99999: /* Made up status code foer error reporting. */ log_error (_("error retrieving `%s': %s\n"), url, gpg_strerror (err)); break; default: log_error (_("error retrieving `%s': http status %u\n"), url, http_get_status_code (hd)); err = gpg_error (GPG_ERR_NO_DATA); http_close (hd, 0); } } else /* Let the LDAP code try other schemes. */ { if (opt.disable_ldap) { log_error (_("CRL access not possible due to disabled %s\n"), "LDAP"); err = gpg_error (GPG_ERR_NOT_SUPPORTED); } else err = url_fetch_ldap (ctrl, url, NULL, 0, reader); } xfree (free_this); return err; } /* Fetch CRL for ISSUER using a default server. Return the entire CRL as a newly opened stream returned in R_FP. */ gpg_error_t crl_fetch_default (ctrl_t ctrl, const char *issuer, ksba_reader_t *reader) { if (opt.disable_ldap) { log_error (_("CRL access not possible due to disabled %s\n"), "LDAP"); return gpg_error (GPG_ERR_NOT_SUPPORTED); } return attr_fetch_ldap (ctrl, issuer, "certificateRevocationList", reader); } /* Fetch a CA certificate for DN using the default server. This function only initiates the fetch; fetch_next_cert must be used to actually read the certificate; end_cert_fetch to end the operation. */ gpg_error_t ca_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, const char *dn) { if (opt.disable_ldap) { log_error (_("CRL access not possible due to disabled %s\n"), "LDAP"); return gpg_error (GPG_ERR_NOT_SUPPORTED); } return start_default_fetch_ldap (ctrl, context, dn, "cACertificate"); } gpg_error_t start_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, strlist_t patterns, const ldap_server_t server) { if (opt.disable_ldap) { log_error (_("certificate search not possible due to disabled %s\n"), "LDAP"); return gpg_error (GPG_ERR_NOT_SUPPORTED); } return start_cert_fetch_ldap (ctrl, context, patterns, server); } gpg_error_t fetch_next_cert (cert_fetch_context_t context, unsigned char **value, size_t * valuelen) { return fetch_next_cert_ldap (context, value, valuelen); } /* Fetch the next data from CONTEXT, assuming it is a certificate and return it as a cert object in R_CERT. */ gpg_error_t fetch_next_ksba_cert (cert_fetch_context_t context, ksba_cert_t *r_cert) { gpg_error_t err; unsigned char *value; size_t valuelen; ksba_cert_t cert; *r_cert = NULL; err = fetch_next_cert_ldap (context, &value, &valuelen); if (!err && !value) err = gpg_error (GPG_ERR_BUG); if (err) return err; err = ksba_cert_new (&cert); if (err) { xfree (value); return err; } err = ksba_cert_init_from_mem (cert, value, valuelen); xfree (value); if (err) { ksba_cert_release (cert); return err; } *r_cert = cert; return 0; } void end_cert_fetch (cert_fetch_context_t context) { return end_cert_fetch_ldap (context); } /* This function is to be used to close the reader object. In addition to running ksba_reader_release it also releases the LDAP or HTTP contexts associated with that reader. */ void crl_close_reader (ksba_reader_t reader) { FILE *fp; if (!reader) return; /* Check whether this is a HTTP one. */ fp = get_file_reader (reader); if (fp) /* This is an HTTP context. */ fclose (fp); else /* This is an ldap wrapper context. */ ldap_wrapper_release_context (reader); /* Now get rid of the reader object. */ ksba_reader_release (reader); }