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