loudmouth/src/lm-ssl-gnutls.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
       
     2 /*
       
     3  * Copyright (C) 2003-2006 Imendio AB
       
     4  *
       
     5  * This program is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Lesser General Public License as
       
     7  * published by the Free Software Foundation; either version 2 of the
       
     8  * License, or (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 GNU
       
    13  * Lesser General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Lesser General Public
       
    16  * License along with this program; if not, write to the
       
    17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    18  * Boston, MA 02111-1307, USA.
       
    19  */
       
    20 
       
    21 #include <config.h>
       
    22 
       
    23 #include <string.h>
       
    24 #include <glib.h>
       
    25 
       
    26 #include "lm-debug.h"
       
    27 #include "lm-error.h"
       
    28 #include "lm-ssl-base.h"
       
    29 #include "lm-ssl-internals.h"
       
    30 
       
    31 #ifdef HAVE_GNUTLS
       
    32 
       
    33 #include <gnutls/x509.h>
       
    34 
       
    35 #define CA_PEM_FILE "/etc/ssl/certs/ca-certificates.crt"
       
    36 
       
    37 struct _LmSSL {
       
    38 	LmSSLBase base;
       
    39 
       
    40 	gnutls_session                 gnutls_session;
       
    41 	gnutls_certificate_credentials gnutls_xcred;
       
    42 	gboolean                       started;
       
    43 };
       
    44 
       
    45 static gboolean       ssl_verify_certificate    (LmSSL       *ssl,
       
    46 						 const gchar *server);
       
    47 
       
    48 static gboolean
       
    49 ssl_verify_certificate (LmSSL *ssl, const gchar *server)
       
    50 {
       
    51 	LmSSLBase *base;
       
    52 	unsigned int        status;
       
    53 	int rc;
       
    54 
       
    55 	base = LM_SSL_BASE (ssl);
       
    56 
       
    57 	/* This verification function uses the trusted CAs in the credentials
       
    58 	 * structure. So you must have installed one or more CA certificates.
       
    59 	 */
       
    60 	rc = gnutls_certificate_verify_peers2 (ssl->gnutls_session, &status);
       
    61 
       
    62 	if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND) {
       
    63 		if (base->func (ssl,
       
    64 			       LM_SSL_STATUS_NO_CERT_FOUND,
       
    65 			       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
    66 			return FALSE;
       
    67 		}
       
    68 	}
       
    69 
       
    70 	if (rc != 0) {
       
    71 		if (base->func (ssl,
       
    72 			       LM_SSL_STATUS_GENERIC_ERROR,
       
    73 			       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
    74 			return FALSE;
       
    75 		}
       
    76 	}
       
    77 
       
    78 	if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND) {
       
    79 		if (base->func (ssl,
       
    80 			       LM_SSL_STATUS_NO_CERT_FOUND,
       
    81 			       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
    82 			return FALSE;
       
    83 		}
       
    84 	}
       
    85 	
       
    86 	if (status & GNUTLS_CERT_INVALID
       
    87 	    || status & GNUTLS_CERT_REVOKED) {
       
    88 		if (base->func (ssl, LM_SSL_STATUS_UNTRUSTED_CERT,
       
    89 			       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
    90 			return FALSE;
       
    91 		}
       
    92 	}
       
    93 	
       
    94 	if (gnutls_certificate_expiration_time_peers (ssl->gnutls_session) < time (0)) {
       
    95 		if (base->func (ssl, LM_SSL_STATUS_CERT_EXPIRED,
       
    96 			       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
    97 			return FALSE;
       
    98 		}
       
    99 	}
       
   100 	
       
   101 	if (gnutls_certificate_activation_time_peers (ssl->gnutls_session) > time (0)) {
       
   102 		if (base->func (ssl, LM_SSL_STATUS_CERT_NOT_ACTIVATED,
       
   103 			       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
   104 			return FALSE;
       
   105 		}
       
   106 	}
       
   107 	
       
   108 	if (gnutls_certificate_type_get (ssl->gnutls_session) == GNUTLS_CRT_X509) {
       
   109 		const gnutls_datum* cert_list;
       
   110 		guint cert_list_size;
       
   111 		size_t digest_size;
       
   112 		gnutls_x509_crt cert;
       
   113 		
       
   114 		cert_list = gnutls_certificate_get_peers (ssl->gnutls_session, &cert_list_size);
       
   115 		if (cert_list == NULL) {
       
   116 			if (base->func (ssl, LM_SSL_STATUS_NO_CERT_FOUND,
       
   117 				       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
   118 				return FALSE;
       
   119 			}
       
   120 		}
       
   121 
       
   122 		gnutls_x509_crt_init (&cert);
       
   123 
       
   124 		if (gnutls_x509_crt_import (cert, &cert_list[0],
       
   125 					     GNUTLS_X509_FMT_DER) != 0) {
       
   126 			if (base->func (ssl, LM_SSL_STATUS_NO_CERT_FOUND, 
       
   127 					base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
   128 				return FALSE;
       
   129 			}
       
   130 		}
       
   131 
       
   132 		if (!gnutls_x509_crt_check_hostname (cert, server)) {
       
   133 			if (base->func (ssl, LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
       
   134 				       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
   135 				return FALSE;
       
   136 			}
       
   137 		}
       
   138 
       
   139 		gnutls_x509_crt_deinit (cert);
       
   140 
       
   141 		digest_size = sizeof (base->fingerprint);
       
   142 
       
   143 		if (gnutls_fingerprint (GNUTLS_DIG_MD5, &cert_list[0],
       
   144 					base->fingerprint,
       
   145 					&digest_size) >= 0) {
       
   146 			if (base->expected_fingerprint &&
       
   147 			    memcmp (base->expected_fingerprint, 
       
   148 				    base->fingerprint,
       
   149 				    digest_size) &&
       
   150 			    base->func (ssl,
       
   151 				       LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,
       
   152 				       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
   153 				return FALSE;
       
   154 			}
       
   155 		} 
       
   156 		else if (base->func (ssl, LM_SSL_STATUS_GENERIC_ERROR,
       
   157 				     base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
   158 			return FALSE; 
       
   159 		} 
       
   160 	}
       
   161 
       
   162 	return TRUE;
       
   163 }
       
   164 
       
   165 /* From lm-ssl-protected.h */
       
   166 
       
   167 LmSSL *
       
   168 _lm_ssl_new (const gchar    *expected_fingerprint,
       
   169 	    LmSSLFunction   ssl_function,
       
   170 	    gpointer        user_data,
       
   171 	    GDestroyNotify  notify)
       
   172 {
       
   173 	LmSSL *ssl;
       
   174 
       
   175 	ssl = g_new0 (LmSSL, 1);
       
   176 
       
   177 	_lm_ssl_base_init ((LmSSLBase *) ssl,
       
   178 			   expected_fingerprint,
       
   179 			   ssl_function, user_data, notify);
       
   180 
       
   181 	return ssl;
       
   182 }
       
   183 
       
   184 void
       
   185 _lm_ssl_initialize (LmSSL *ssl) 
       
   186 {
       
   187 	gnutls_global_init ();
       
   188 	gnutls_certificate_allocate_credentials (&ssl->gnutls_xcred);
       
   189 	gnutls_certificate_set_x509_trust_file(ssl->gnutls_xcred,
       
   190 					       CA_PEM_FILE,
       
   191 					       GNUTLS_X509_FMT_PEM);
       
   192 }
       
   193 
       
   194 gboolean
       
   195 _lm_ssl_begin (LmSSL *ssl, gint fd, const gchar *server, GError **error)
       
   196 {
       
   197 	int ret;
       
   198 	gboolean auth_ok = TRUE;
       
   199 	const int cert_type_priority[] =
       
   200 		{ GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 };
       
   201 	const int compression_priority[] =
       
   202 		{ GNUTLS_COMP_DEFLATE, GNUTLS_COMP_NULL, 0 };
       
   203 
       
   204 	gnutls_init (&ssl->gnutls_session, GNUTLS_CLIENT);
       
   205 	gnutls_set_default_priority (ssl->gnutls_session);
       
   206 	gnutls_certificate_type_set_priority (ssl->gnutls_session,
       
   207 					      cert_type_priority);
       
   208 	gnutls_compression_set_priority (ssl->gnutls_session,
       
   209 					 compression_priority);
       
   210 	gnutls_credentials_set (ssl->gnutls_session,
       
   211 				GNUTLS_CRD_CERTIFICATE,
       
   212 				ssl->gnutls_xcred);
       
   213 
       
   214 	gnutls_transport_set_ptr (ssl->gnutls_session,
       
   215 				  (gnutls_transport_ptr_t) fd);
       
   216 
       
   217 	ret = gnutls_handshake (ssl->gnutls_session);
       
   218 
       
   219 	if (ret >= 0) {
       
   220 		auth_ok = ssl_verify_certificate (ssl, server);
       
   221 	}
       
   222 
       
   223 	if (ret < 0 || !auth_ok) {
       
   224 		char *errmsg;
       
   225 
       
   226 		gnutls_perror (ret);
       
   227 	
       
   228 		if (!auth_ok) {
       
   229 			errmsg = "*** GNUTLS authentication error";
       
   230 		} else {
       
   231 			errmsg = "*** GNUTLS handshake failed";
       
   232 		}
       
   233 
       
   234 		g_set_error (error, 
       
   235 			     LM_ERROR, LM_ERROR_CONNECTION_OPEN,
       
   236 			     errmsg);			
       
   237 
       
   238 		return FALSE;
       
   239 	}
       
   240 	lm_verbose ("GNUTLS negotiated compression: %s",
       
   241 		    gnutls_compression_get_name (gnutls_compression_get
       
   242 			(ssl->gnutls_session)));
       
   243 
       
   244 	ssl->started = TRUE;
       
   245 
       
   246 	return TRUE;
       
   247 }
       
   248 
       
   249 GIOStatus
       
   250 _lm_ssl_read (LmSSL *ssl, gchar *buf, gint len, gsize *bytes_read)
       
   251 {
       
   252 	GIOStatus status;
       
   253 	gint      b_read;
       
   254 
       
   255 	*bytes_read = 0;
       
   256 	b_read = gnutls_record_recv (ssl->gnutls_session, buf, len);
       
   257 
       
   258 	if (b_read == GNUTLS_E_AGAIN) {
       
   259 		status = G_IO_STATUS_AGAIN;
       
   260 	}
       
   261 	else if (b_read == 0) {
       
   262 		status = G_IO_STATUS_EOF;
       
   263 	}
       
   264 	else if (b_read < 0) {
       
   265 		status = G_IO_STATUS_ERROR;
       
   266 	} else {
       
   267 		*bytes_read = (guint) b_read;
       
   268 		status = G_IO_STATUS_NORMAL;
       
   269 	}
       
   270 
       
   271 	return status;
       
   272 }
       
   273 
       
   274 gint
       
   275 _lm_ssl_send (LmSSL *ssl, const gchar *str, gint len)
       
   276 {
       
   277 	gint bytes_written;
       
   278 
       
   279 	bytes_written = gnutls_record_send (ssl->gnutls_session, str, len);
       
   280 
       
   281 	while (bytes_written < 0) {
       
   282 		if (bytes_written != GNUTLS_E_INTERRUPTED &&
       
   283 		    bytes_written != GNUTLS_E_AGAIN) {
       
   284 			return -1;
       
   285 		}
       
   286 	
       
   287 		bytes_written = gnutls_record_send (ssl->gnutls_session, 
       
   288 						    str, len);
       
   289 	}
       
   290 
       
   291 	return bytes_written;
       
   292 }
       
   293 
       
   294 void 
       
   295 _lm_ssl_close (LmSSL *ssl)
       
   296 {
       
   297 	if (!ssl->started)
       
   298 		return;
       
   299 
       
   300 	gnutls_deinit (ssl->gnutls_session);
       
   301 	gnutls_certificate_free_credentials (ssl->gnutls_xcred);
       
   302 	gnutls_global_deinit ();
       
   303 }
       
   304 
       
   305 void
       
   306 _lm_ssl_free (LmSSL *ssl)
       
   307 {
       
   308 	_lm_ssl_base_free_fields (LM_SSL_BASE (ssl));
       
   309 	g_free (ssl);
       
   310 }
       
   311 
       
   312 #endif /* HAVE_GNUTLS */