1/* SSL support via GnuTLS library. 2 Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, 3 Inc. 4 5This file is part of GNU Wget. 6 7GNU Wget is free software; you can redistribute it and/or modify 8it under the terms of the GNU General Public License as published by 9the Free Software Foundation; either version 3 of the License, or 10(at your option) any later version. 11 12GNU Wget is distributed in the hope that it will be useful, 13but WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15GNU General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along with Wget. If not, see <http://www.gnu.org/licenses/>. 19 20Additional permission under GNU GPL version 3 section 7 21 22If you modify this program, or any covered work, by linking or 23combining it with the OpenSSL project's OpenSSL library (or a 24modified version of that library), containing parts covered by the 25terms of the OpenSSL or SSLeay licenses, the Free Software Foundation 26grants you additional permission to convey the resulting work. 27Corresponding Source for a non-source form of such a combination 28shall include the source code for the parts of OpenSSL used as well 29as that of the covered work. */ 30 31#include "wget.h" 32 33#include <assert.h> 34#include <errno.h> 35#ifdef HAVE_UNISTD_H 36# include <unistd.h> 37#endif 38#include <string.h> 39#include <stdio.h> 40 41#include <gnutls/gnutls.h> 42#include <gnutls/x509.h> 43 44#include "utils.h" 45#include "connect.h" 46#include "url.h" 47#include "ssl.h" 48 49/* Note: some of the functions private to this file have names that 50 begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be 51 confused with actual gnutls functions -- such as the gnutls_read 52 preprocessor macro. */ 53 54static gnutls_certificate_credentials credentials; 55 56bool 57ssl_init () 58{ 59 gnutls_global_init (); 60 gnutls_certificate_allocate_credentials (&credentials); 61 if (opt.ca_cert) 62 gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert, 63 GNUTLS_X509_FMT_PEM); 64 return true; 65} 66 67struct wgnutls_transport_context { 68 gnutls_session session; /* GnuTLS session handle */ 69 int last_error; /* last error returned by read/write/... */ 70 71 /* Since GnuTLS doesn't support the equivalent to recv(..., 72 MSG_PEEK) or SSL_peek(), we have to do it ourselves. Peeked data 73 is stored to PEEKBUF, and wgnutls_read checks that buffer before 74 actually reading. */ 75 char peekbuf[512]; 76 int peekstart, peeklen; 77}; 78 79#ifndef MIN 80# define MIN(i, j) ((i) <= (j) ? (i) : (j)) 81#endif 82 83static int 84wgnutls_read (int fd, char *buf, int bufsize, void *arg) 85{ 86 int ret; 87 struct wgnutls_transport_context *ctx = arg; 88 89 if (ctx->peeklen) 90 { 91 /* If we have any peek data, simply return that. */ 92 int copysize = MIN (bufsize, ctx->peeklen); 93 memcpy (buf, ctx->peekbuf + ctx->peekstart, copysize); 94 ctx->peeklen -= copysize; 95 if (ctx->peeklen != 0) 96 ctx->peekstart += copysize; 97 else 98 ctx->peekstart = 0; 99 return copysize; 100 } 101 102 do 103 ret = gnutls_record_recv (ctx->session, buf, bufsize); 104 while (ret == GNUTLS_E_INTERRUPTED); 105 106 if (ret < 0) 107 ctx->last_error = ret; 108 return ret; 109} 110 111static int 112wgnutls_write (int fd, char *buf, int bufsize, void *arg) 113{ 114 int ret; 115 struct wgnutls_transport_context *ctx = arg; 116 do 117 ret = gnutls_record_send (ctx->session, buf, bufsize); 118 while (ret == GNUTLS_E_INTERRUPTED); 119 if (ret < 0) 120 ctx->last_error = ret; 121 return ret; 122} 123 124static int 125wgnutls_poll (int fd, double timeout, int wait_for, void *arg) 126{ 127 return 1; 128} 129 130static int 131wgnutls_peek (int fd, char *buf, int bufsize, void *arg) 132{ 133 int ret; 134 struct wgnutls_transport_context *ctx = arg; 135 136 /* We don't support peeks following peeks: the reader must drain all 137 peeked data before the next peek. */ 138 assert (ctx->peeklen == 0); 139 if (bufsize > sizeof ctx->peekbuf) 140 bufsize = sizeof ctx->peekbuf; 141 142 do 143 ret = gnutls_record_recv (ctx->session, buf, bufsize); 144 while (ret == GNUTLS_E_INTERRUPTED); 145 146 if (ret >= 0) 147 { 148 memcpy (ctx->peekbuf, buf, ret); 149 ctx->peeklen = ret; 150 } 151 return ret; 152} 153 154static const char * 155wgnutls_errstr (int fd, void *arg) 156{ 157 struct wgnutls_transport_context *ctx = arg; 158 return gnutls_strerror (ctx->last_error); 159} 160 161static void 162wgnutls_close (int fd, void *arg) 163{ 164 struct wgnutls_transport_context *ctx = arg; 165 /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/ 166 gnutls_deinit (ctx->session); 167 xfree (ctx); 168#ifndef WINDOWS 169 close (fd); 170#else 171 closesocket (fd); 172#endif 173} 174 175/* gnutls_transport is the singleton that describes the SSL transport 176 methods provided by this file. */ 177 178static struct transport_implementation wgnutls_transport = { 179 wgnutls_read, wgnutls_write, wgnutls_poll, 180 wgnutls_peek, wgnutls_errstr, wgnutls_close 181}; 182 183bool 184ssl_connect (int fd) 185{ 186 static const int cert_type_priority[] = { 187 GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 188 }; 189 struct wgnutls_transport_context *ctx; 190 gnutls_session session; 191 int err; 192 gnutls_init (&session, GNUTLS_CLIENT); 193 gnutls_set_default_priority (session); 194 gnutls_certificate_type_set_priority (session, cert_type_priority); 195 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials); 196 gnutls_transport_set_ptr (session, (gnutls_transport_ptr) fd); 197 err = gnutls_handshake (session); 198 if (err < 0) 199 { 200 logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err)); 201 gnutls_deinit (session); 202 return false; 203 } 204 ctx = xnew0 (struct wgnutls_transport_context); 205 ctx->session = session; 206 fd_register_transport (fd, &wgnutls_transport, ctx); 207 return true; 208} 209 210bool 211ssl_check_certificate (int fd, const char *host) 212{ 213 struct wgnutls_transport_context *ctx = fd_transport_context (fd); 214 215 unsigned int status; 216 int err; 217 218 /* If the user has specified --no-check-cert, we still want to warn 219 him about problems with the server's certificate. */ 220 const char *severity = opt.check_cert ? _("ERROR") : _("WARNING"); 221 bool success = true; 222 223 err = gnutls_certificate_verify_peers2 (ctx->session, &status); 224 if (err < 0) 225 { 226 logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"), 227 severity, quotearg_style (escape_quoting_style, host)); 228 success = false; 229 goto out; 230 } 231 232 if (status & GNUTLS_CERT_INVALID) 233 { 234 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"), 235 severity, quote (host)); 236 success = false; 237 } 238 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) 239 { 240 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"), 241 severity, quote (host)); 242 success = false; 243 } 244 if (status & GNUTLS_CERT_REVOKED) 245 { 246 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"), 247 severity, quote (host)); 248 success = false; 249 } 250 251 if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509) 252 { 253 time_t now = time (NULL); 254 gnutls_x509_crt cert; 255 const gnutls_datum *cert_list; 256 unsigned int cert_list_size; 257 258 if ((err = gnutls_x509_crt_init (&cert)) < 0) 259 { 260 logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"), 261 gnutls_strerror (err)); 262 success = false; 263 goto out; 264 } 265 266 cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size); 267 if (!cert_list) 268 { 269 logprintf (LOG_NOTQUIET, _("No certificate found\n")); 270 success = false; 271 goto out; 272 } 273 err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER); 274 if (err < 0) 275 { 276 logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"), 277 gnutls_strerror (err)); 278 success = false; 279 goto out; 280 } 281 if (now < gnutls_x509_crt_get_activation_time (cert)) 282 { 283 logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n")); 284 success = false; 285 } 286 if (now >= gnutls_x509_crt_get_expiration_time (cert)) 287 { 288 logprintf (LOG_NOTQUIET, _("The certificate has expired\n")); 289 success = false; 290 } 291 if (!gnutls_x509_crt_check_hostname (cert, host)) 292 { 293 logprintf (LOG_NOTQUIET, 294 _("The certificate's owner does not match hostname %s\n"), 295 quote (host)); 296 success = false; 297 } 298 gnutls_x509_crt_deinit (cert); 299 } 300 301 out: 302 return opt.check_cert ? success : true; 303} 304