mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

src/mn-ssl.c (4221B) - raw

      1 /*
      2  * Mail Notification
      3  * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License along
     16  * with this program; if not, write to the Free Software Foundation, Inc.,
     17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     18  */
     19 
     20 #include <errno.h>
     21 #include <glib.h>
     22 #include <glib/gi18n.h>
     23 
     24 typedef GMutex CRYPTO_dynlock_value;
     25 
     26 #include <openssl/ssl.h>
     27 #include <openssl/crypto.h>
     28 #include <openssl/err.h>
     29 #include "mn-ssl.h"
     30 
     31 static gboolean attempted = FALSE;
     32 static SSL_CTX *ctx = NULL;
     33 static char *init_error = NULL;
     34 
     35 static int num_locks;
     36 static GMutex **locks;
     37 
     38 /* a general purpose global SSL lock */
     39 G_LOCK_DEFINE(mn_ssl);
     40 
     41 static void
     42 locking_cb (int mode, int n, const char *file, int line)
     43 {
     44   g_assert(n >= 0 && n < num_locks);
     45 
     46   if ((mode & CRYPTO_LOCK) != 0)
     47     g_mutex_lock(locks[n]);
     48   else
     49     g_mutex_unlock(locks[n]);
     50 }
     51 
     52 static unsigned long
     53 id_cb (void)
     54 {
     55   return (unsigned long) g_thread_self();
     56 }
     57 
     58 static struct CRYPTO_dynlock_value *
     59 dynlock_create_cb (const char *file, int line)
     60 {
     61   return (struct CRYPTO_dynlock_value *) g_mutex_new();
     62 }
     63 
     64 static void
     65 dynlock_locking_cb (int mode,
     66 		    struct CRYPTO_dynlock_value *lock,
     67 		    const char *file,
     68 		    int line)
     69 {
     70   if ((mode & CRYPTO_LOCK) != 0)
     71     g_mutex_lock((GMutex *) lock);
     72   else
     73     g_mutex_unlock((GMutex *) lock);
     74 }
     75 
     76 static void
     77 dynlock_destroy_cb (struct CRYPTO_dynlock_value *lock,
     78 		    const char *file,
     79 		    int line)
     80 {
     81   g_mutex_free((GMutex *) lock);
     82 }
     83 
     84 static void
     85 init_threading (void)
     86 {
     87   int i;
     88 
     89   num_locks = CRYPTO_num_locks();
     90 
     91   locks = g_new(GMutex *, num_locks);
     92   for (i = 0; i < num_locks; i++)
     93     locks[i] = g_mutex_new();
     94 
     95   CRYPTO_set_locking_callback(locking_cb);
     96   CRYPTO_set_id_callback(id_cb);
     97 
     98   CRYPTO_set_dynlock_create_callback(dynlock_create_cb);
     99   CRYPTO_set_dynlock_lock_callback(dynlock_locking_cb);
    100   CRYPTO_set_dynlock_destroy_callback(dynlock_destroy_cb);
    101 }
    102 
    103 SSL_CTX *
    104 mn_ssl_init (GError **err)
    105 {
    106   /*
    107    * SSL_CTX_new() will fail the second time it is called, so we just
    108    * keep the same context for the whole application lifetime.
    109    */
    110 
    111   G_LOCK(mn_ssl);
    112   if (! attempted)
    113     {
    114       SSL_library_init();
    115       SSL_load_error_strings();
    116 
    117       init_threading();
    118 
    119       ctx = SSL_CTX_new(SSLv23_client_method());
    120       if (ctx)
    121 	{
    122 	  SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
    123 	  SSL_CTX_set_default_verify_paths(ctx); /* #19578 */
    124 	}
    125       else
    126 	init_error = g_strdup(mn_ssl_get_error());
    127 
    128       attempted = TRUE;
    129     }
    130 
    131   if (! ctx)
    132     {
    133       g_assert(init_error != NULL);
    134       g_set_error(err, 0, 0, "%s", init_error);
    135     }
    136   G_UNLOCK(mn_ssl);
    137 
    138   return ctx;
    139 }
    140 
    141 const char *
    142 mn_ssl_get_error (void)
    143 {
    144   static GStaticPrivate buf_key = G_STATIC_PRIVATE_INIT;
    145   char *buf;
    146 
    147   /*
    148    * We use a per-thread buffer so that the caller does not have to
    149    * free the returned string.
    150    */
    151 
    152   buf = g_static_private_get(&buf_key);
    153   if (! buf)
    154     {
    155       buf = g_new(char, 120);	/* the size is specified by the manpage */
    156       g_static_private_set(&buf_key, buf, g_free);
    157     }
    158 
    159   return ERR_error_string(ERR_get_error(), buf);
    160 }
    161 
    162 const char *
    163 mn_ssl_get_io_error (const SSL *ssl, int ret)
    164 {
    165   g_return_val_if_fail(ssl != NULL, NULL);
    166 
    167   switch (SSL_get_error(ssl, ret))
    168     {
    169     case SSL_ERROR_SYSCALL:
    170       if (ERR_peek_error() == 0)
    171 	switch (ret)
    172 	  {
    173 	  case 0:
    174 	    return "EOF";
    175 
    176 	  case -1:
    177 	    /*
    178 	     * This assumes that a UNIX BIO is in use (it is always
    179 	     * the case in MN).
    180 	     */
    181 	    return g_strerror(errno);
    182 	  }
    183       else
    184 	return mn_ssl_get_error();
    185       break;
    186 
    187     case SSL_ERROR_SSL:
    188       return mn_ssl_get_error();
    189     }
    190 
    191   return _("unknown SSL/TLS error");
    192 }