]> sjero.net Git - wget/blob - src/openssl.c
[svn] Check for the server's identity after the SSL handshake.
[wget] / src / openssl.c
1 /* SSL support via OpenSSL library.
2    Copyright (C) 2000-2005 Free Software Foundation, Inc.
3    Originally contributed by Christian Fraenkel.
4
5 This file is part of GNU Wget.
6
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Wget; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 In addition, as a special exception, the Free Software Foundation
22 gives permission to link the code of its release of Wget with the
23 OpenSSL project's "OpenSSL" library (or with modified versions of it
24 that use the same license as the "OpenSSL" library), and distribute
25 the linked executables.  You must obey the GNU General Public License
26 in all respects for all of the code used other than "OpenSSL".  If you
27 modify this file, you may extend this exception to your version of the
28 file, but you are not obligated to do so.  If you do not wish to do
29 so, delete this exception statement from your version.  */
30
31 #include <config.h>
32
33 #include <assert.h>
34 #include <errno.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #ifdef HAVE_STRING_H
39 # include <string.h>
40 #else
41 # include <strings.h>
42 #endif
43
44 #include <openssl/bio.h>
45 #include <openssl/crypto.h>
46 #include <openssl/x509.h>
47 #include <openssl/ssl.h>
48 #include <openssl/err.h>
49 #include <openssl/pem.h>
50 #include <openssl/rand.h>
51
52 #include "wget.h"
53 #include "utils.h"
54 #include "connect.h"
55 #include "url.h"
56 #include "ssl.h"
57
58 #ifndef errno
59 extern int errno;
60 #endif
61
62 /* Application-wide SSL context.  This is common to all SSL
63    connections.  */
64 SSL_CTX *ssl_ctx;
65
66 /* Initialize the SSL's PRNG using various methods. */
67
68 static void
69 init_prng (void)
70 {
71   char namebuf[256];
72   const char *random_file;
73
74   if (RAND_status ())
75     /* The PRNG has been seeded; no further action is necessary. */
76     return;
77
78   /* Seed from a file specified by the user.  This will be the file
79      specified with --random-file, $RANDFILE, if set, or ~/.rnd, if it
80      exists.  */
81   if (opt.random_file)
82     random_file = opt.random_file;
83   else
84     {
85       /* Get the random file name using RAND_file_name. */
86       namebuf[0] = '\0';
87       random_file = RAND_file_name (namebuf, sizeof (namebuf));
88     }
89
90   if (random_file && *random_file)
91     /* Seed at most 16k (apparently arbitrary value borrowed from
92        curl) from random file. */
93     RAND_load_file (random_file, 16384);
94
95   if (RAND_status ())
96     return;
97
98   /* Get random data from EGD if opt.egd_file was used.  */
99   if (opt.egd_file && *opt.egd_file)
100     RAND_egd (opt.egd_file);
101
102   if (RAND_status ())
103     return;
104
105 #ifdef WINDOWS
106   /* Under Windows, we can try to seed the PRNG using screen content.
107      This may or may not work, depending on whether we'll calling Wget
108      interactively.  */
109
110   RAND_screen ();
111   if (RAND_status ())
112     return;
113 #endif
114
115 #if 0 /* don't do this by default */
116   {
117     int maxrand = 500;
118
119     /* Still not random enough, presumably because neither /dev/random
120        nor EGD were available.  Try to seed OpenSSL's PRNG with libc
121        PRNG.  This is cryptographically weak and defeats the purpose
122        of using OpenSSL, which is why it is highly discouraged.  */
123
124     logprintf (LOG_NOTQUIET, _("WARNING: using a weak random seed.\n"));
125
126     while (RAND_status () == 0 && maxrand-- > 0)
127       {
128         unsigned char rnd = random_number (256);
129         RAND_seed (&rnd, sizeof (rnd));
130       }
131   }
132 #endif
133 }
134
135 /* Print errors in the OpenSSL error stack. */
136
137 static void
138 print_errors (void) 
139 {
140   unsigned long curerr = 0;
141   while ((curerr = ERR_get_error ()) != 0)
142     logprintf (LOG_NOTQUIET, "OpenSSL: %s\n", ERR_error_string (curerr, NULL));
143 }
144
145 /* Convert keyfile type as used by options.h to a type as accepted by
146    SSL_CTX_use_certificate_file and SSL_CTX_use_PrivateKey_file.
147
148    (options.h intentionally doesn't use values from openssl/ssl.h so
149    it doesn't depend specifically on OpenSSL for SSL functionality.)  */
150
151 static int
152 key_type_to_ssl_type (enum keyfile_type type)
153 {
154   switch (type)
155     {
156     case keyfile_pem:
157       return SSL_FILETYPE_PEM;
158     case keyfile_asn1:
159       return SSL_FILETYPE_ASN1;
160     default:
161       abort ();
162     }
163 }
164
165 /* Create an SSL Context and set default paths etc.  Called the first
166    time an HTTP download is attempted.
167
168    Returns 1 on success, 0 otherwise.  */
169
170 int
171 ssl_init ()
172 {
173   SSL_METHOD *meth;
174
175   if (ssl_ctx)
176     /* The SSL has already been initialized. */
177     return 1;
178
179   /* Init the PRNG.  If that fails, bail out.  */
180   init_prng ();
181   if (RAND_status () != 1)
182     {
183       logprintf (LOG_NOTQUIET,
184                  _("Could not seed PRNG; consider using --random-file.\n"));
185       goto error;
186     }
187
188   SSL_library_init ();
189   SSL_load_error_strings ();
190   SSLeay_add_all_algorithms ();
191   SSLeay_add_ssl_algorithms ();
192
193   switch (opt.secure_protocol)
194     {
195     case secure_protocol_auto:
196       meth = SSLv23_client_method ();
197       break;
198     case secure_protocol_sslv2:
199       meth = SSLv2_client_method ();
200       break;
201     case secure_protocol_sslv3:
202       meth = SSLv3_client_method ();
203       break;
204     case secure_protocol_tlsv1:
205       meth = TLSv1_client_method ();
206       break;
207     default:
208       abort ();
209     }
210
211   ssl_ctx = SSL_CTX_new (meth);
212   if (!ssl_ctx)
213     goto error;
214
215   SSL_CTX_set_default_verify_paths (ssl_ctx);
216   SSL_CTX_load_verify_locations (ssl_ctx, opt.ca_cert, opt.ca_directory);
217
218   /* Specify whether the connect should fail if the verification of
219      the peer fails or if it should go ahead.  */
220   SSL_CTX_set_verify (ssl_ctx,
221                       opt.check_cert ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
222
223   if (opt.cert_file)
224     if (SSL_CTX_use_certificate_file (ssl_ctx, opt.cert_file,
225                                       key_type_to_ssl_type (opt.cert_type))
226         != 1)
227       goto error;
228   if (opt.private_key)
229     if (SSL_CTX_use_PrivateKey_file (ssl_ctx, opt.private_key,
230                                      key_type_to_ssl_type (opt.private_key_type))
231         != 1)
232       goto error;
233
234   /* Since fd_write unconditionally assumes partial writes (and
235      handles them correctly), allow them in OpenSSL.  */
236   SSL_CTX_set_mode (ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
237
238   return 1;
239
240  error:
241   if (ssl_ctx)
242     SSL_CTX_free (ssl_ctx);
243   print_errors ();
244   return 0;
245 }
246
247 static int
248 openssl_read (int fd, char *buf, int bufsize, void *ctx)
249 {
250   int ret;
251   SSL *ssl = (SSL *) ctx;
252   do
253     ret = SSL_read (ssl, buf, bufsize);
254   while (ret == -1
255          && SSL_get_error (ssl, ret) == SSL_ERROR_SYSCALL
256          && errno == EINTR);
257   return ret;
258 }
259
260 static int
261 openssl_write (int fd, char *buf, int bufsize, void *ctx)
262 {
263   int ret = 0;
264   SSL *ssl = (SSL *) ctx;
265   do
266     ret = SSL_write (ssl, buf, bufsize);
267   while (ret == -1
268          && SSL_get_error (ssl, ret) == SSL_ERROR_SYSCALL
269          && errno == EINTR);
270   return ret;
271 }
272
273 static int
274 openssl_poll (int fd, double timeout, int wait_for, void *ctx)
275 {
276   SSL *ssl = (SSL *) ctx;
277   if (timeout == 0)
278     return 1;
279   if (SSL_pending (ssl))
280     return 1;
281   return select_fd (fd, timeout, wait_for);
282 }
283
284 static int
285 openssl_peek (int fd, char *buf, int bufsize, void *ctx)
286 {
287   int ret;
288   SSL *ssl = (SSL *) ctx;
289   do
290     ret = SSL_peek (ssl, buf, bufsize);
291   while (ret == -1
292          && SSL_get_error (ssl, ret) == SSL_ERROR_SYSCALL
293          && errno == EINTR);
294   return ret;
295 }
296
297 static void
298 openssl_close (int fd, void *ctx)
299 {
300   SSL *ssl = (SSL *) ctx;
301   SSL_shutdown (ssl);
302   SSL_free (ssl);
303
304 #ifdef WINDOWS
305   closesocket (fd);
306 #else
307   close (fd);
308 #endif
309
310   DEBUGP (("Closed %d/SSL 0x%0lx\n", fd, (unsigned long) ssl));
311 }
312
313 /* Sets up a SSL structure and performs the handshake on fd.  The
314    resulting SSL structure is registered with the file descriptor FD
315    using fd_register_transport.  That way subsequent calls to xread,
316    xwrite, etc., will use the appropriate SSL functions.
317
318    Returns 1 on success, 0 on failure.  */
319
320 int
321 ssl_connect (int fd) 
322 {
323   SSL *ssl;
324
325   assert (ssl_ctx != NULL);
326   ssl = SSL_new (ssl_ctx);
327   if (!ssl)
328     goto error;
329   if (!SSL_set_fd (ssl, fd))
330     goto error;
331   SSL_set_connect_state (ssl);
332   if (SSL_connect (ssl) <= 0 || ssl->state != SSL_ST_OK)
333     goto error;
334
335   /* Register FD with Wget's transport layer, i.e. arrange that
336      SSL-enabled functions are used for reading, writing, and polling.
337      That way the rest of Wget can keep using fd_read, fd_write, and
338      friends and not care what happens underneath.  */
339   fd_register_transport (fd, openssl_read, openssl_write, openssl_poll,
340                          openssl_peek, openssl_close, ssl);
341   DEBUGP (("Connected %d to SSL 0x%0*lx\n", fd, 2 * sizeof (void *),
342            (unsigned long) ssl));
343   return 1;
344
345  error:
346   print_errors ();
347   if (ssl)
348     SSL_free (ssl);
349   return 0;
350 }
351
352 /* Check that the identity of the remote host, as presented by its
353    server certificate, corresponds to HOST, which is the host name the
354    user thinks he's connecting to.  This assumes that FD has been
355    connected to an SSL context using ssl_connect.  Return 1 if the
356    identity checks out, 0 otherwise.
357
358    If opt.check_cert is 0, this always returns 1, but still warns the
359    user about the mismatches, if any.  */
360
361 int
362 ssl_check_server_identity (int fd, const char *host)
363 {
364   X509 *peer = NULL;
365   char peer_CN[256];
366   long vresult;
367   int retval;
368
369   /* If the user has specified --no-check-cert, we still want to warn
370      him about problems with the server's certificate.  */
371   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
372
373   SSL *ssl = (SSL *) fd_transport_context (fd);
374   assert (ssl != NULL);
375
376   peer = SSL_get_peer_certificate (ssl);
377   if (!peer)
378     {
379       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
380                  severity, escnonprint (host));
381       retval = 0;
382       goto out;
383     }
384
385   vresult = SSL_get_verify_result (ssl);
386   if (vresult != X509_V_OK)
387     {
388       logprintf (LOG_NOTQUIET,
389                  _("%s: Certificate verification error for %s: %s\n"),
390                  severity, escnonprint (host),
391                  X509_verify_cert_error_string (vresult));
392       retval = 0;
393       goto out;
394     }
395
396   /* Check that the common name matches HOST.
397
398      #### This should use dNSName if available; according to rfc2818:
399      "If a subjectAltName extension of type dNSName is present, that
400      MUST be used as the identity."  */
401
402   peer_CN[0] = '\0';
403   X509_NAME_get_text_by_NID (X509_get_subject_name (peer),
404                              NID_commonName, peer_CN, sizeof (peer_CN));
405   if (0 != strcasecmp (peer_CN, host))
406     {
407       logprintf (LOG_NOTQUIET, _("\
408 %s: certificate common name `%s' doesn't match requested host name `%s'.\n"),
409                  severity, escnonprint (peer_CN), escnonprint (host));
410       retval = 0;
411       goto out;
412     }
413
414   /* The certificate was found, verified, and matched HOST. */
415   retval = 1;
416
417  out:
418   if (peer)
419     X509_free (peer);
420
421   /* Allow --no-check-cert to disable certificate checking. */
422   return opt.check_cert ? retval : 1;
423 }
424