Imported Upstream version 2.72.alpha
[platform/upstream/glib-networking.git] / tls / gnutls / gtlsserverconnection-gnutls.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  * GIO - GLib Input, Output and Streaming Library
4  *
5  * Copyright 2010 Red Hat, Inc
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, see
19  * <http://www.gnu.org/licenses/>.
20  *
21  * In addition, when the library is used with OpenSSL, a special
22  * exception applies. Refer to the LICENSE_EXCEPTION file for details.
23  */
24
25 #include "config.h"
26 #include "glib.h"
27
28 #include <errno.h>
29 #include <gnutls/gnutls.h>
30 #include <gnutls/x509.h>
31
32 #include "gtlsserverconnection-gnutls.h"
33 #include "gtlsbackend-gnutls.h"
34 #include "gtlscertificate-gnutls.h"
35 #include <glib/gi18n-lib.h>
36
37 enum
38 {
39   PROP_0,
40   PROP_AUTHENTICATION_MODE
41 };
42
43 struct _GTlsServerConnectionGnutls
44 {
45   GTlsConnectionGnutls parent_instance;
46
47   GTlsAuthenticationMode authentication_mode;
48
49   gnutls_pcert_st *pcert;
50   unsigned int pcert_length;
51   gnutls_privkey_t pkey;
52 };
53
54 static void     g_tls_server_connection_gnutls_initable_interface_init (GInitableIface  *iface);
55
56 static void g_tls_server_connection_gnutls_server_connection_interface_init (GTlsServerConnectionInterface *iface);
57
58 static int g_tls_server_connection_gnutls_handshake_thread_retrieve_function (gnutls_session_t              session,
59                                                                               const gnutls_datum_t         *req_ca_rdn,
60                                                                               int                           nreqs,
61                                                                               const gnutls_pk_algorithm_t  *pk_algos,
62                                                                               int                           pk_algos_length,
63                                                                               gnutls_pcert_st             **pcert,
64                                                                               unsigned int                 *pcert_length,
65                                                                               gnutls_privkey_t             *pkey);
66
67 static GInitableIface *g_tls_server_connection_gnutls_parent_initable_iface;
68
69 G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionGnutls, g_tls_server_connection_gnutls, G_TYPE_TLS_CONNECTION_GNUTLS,
70                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
71                                                 g_tls_server_connection_gnutls_initable_interface_init)
72                          G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION,
73                                                 g_tls_server_connection_gnutls_server_connection_interface_init)
74                          G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_SERVER_CONNECTION,
75                                                 NULL)
76 )
77
78 static void
79 clear_gnutls_certificate_copy (GTlsServerConnectionGnutls *gnutls)
80 {
81   g_tls_certificate_gnutls_copy_free (gnutls->pcert, gnutls->pcert_length, gnutls->pkey);
82
83   gnutls->pcert = NULL;
84   gnutls->pcert_length = 0;
85   gnutls->pkey = NULL;
86 }
87
88 static void
89 g_tls_server_connection_gnutls_init (GTlsServerConnectionGnutls *gnutls)
90 {
91 }
92
93 static void
94 g_tls_server_connection_gnutls_finalize (GObject *object)
95 {
96   GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
97
98   clear_gnutls_certificate_copy (gnutls);
99
100   G_OBJECT_CLASS (g_tls_server_connection_gnutls_parent_class)->finalize (object);
101 }
102
103 static gboolean
104 g_tls_server_connection_gnutls_initable_init (GInitable       *initable,
105                                               GCancellable    *cancellable,
106                                               GError         **error)
107 {
108   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
109   GTlsCertificate *cert;
110   gnutls_certificate_credentials_t creds;
111
112   if (!g_tls_server_connection_gnutls_parent_initable_iface->init (initable, cancellable, error))
113     return FALSE;
114
115   creds = g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
116   gnutls_certificate_set_retrieve_function2 (creds, g_tls_server_connection_gnutls_handshake_thread_retrieve_function);
117
118   /* Currently we don't know ahead of time if a PKCS #11 backed certificate has a private key. */
119   cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (initable));
120   if (cert && !g_tls_certificate_gnutls_has_key (G_TLS_CERTIFICATE_GNUTLS (cert)) &&
121       !g_tls_certificate_gnutls_is_pkcs11_backed (G_TLS_CERTIFICATE_GNUTLS (cert)))
122     {
123       g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
124                            _("Certificate has no private key"));
125       return FALSE;
126     }
127
128   return TRUE;
129 }
130
131 static void
132 g_tls_server_connection_gnutls_get_property (GObject    *object,
133                                              guint       prop_id,
134                                              GValue     *value,
135                                              GParamSpec *pspec)
136 {
137   GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
138
139   switch (prop_id)
140     {
141     case PROP_AUTHENTICATION_MODE:
142       g_value_set_enum (value, gnutls->authentication_mode);
143       break;
144       
145     default:
146       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147     }
148 }
149
150 static void
151 g_tls_server_connection_gnutls_set_property (GObject      *object,
152                                              guint         prop_id,
153                                              const GValue *value,
154                                              GParamSpec   *pspec)
155 {
156   GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
157
158   switch (prop_id)
159     {
160     case PROP_AUTHENTICATION_MODE:
161       gnutls->authentication_mode = g_value_get_enum (value);
162       break;
163
164     default:
165       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
166     }
167 }
168
169 static int
170 g_tls_server_connection_gnutls_handshake_thread_retrieve_function (gnutls_session_t              session,
171                                                                    const gnutls_datum_t         *req_ca_rdn,
172                                                                    int                           nreqs,
173                                                                    const gnutls_pk_algorithm_t  *pk_algos,
174                                                                    int                           pk_algos_length,
175                                                                    gnutls_pcert_st             **pcert,
176                                                                    unsigned int                 *pcert_length,
177                                                                    gnutls_privkey_t             *pkey)
178 {
179   GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (gnutls_transport_get_ptr (session));
180
181   clear_gnutls_certificate_copy (gnutls);
182
183   g_tls_connection_gnutls_handshake_thread_get_certificate (G_TLS_CONNECTION_GNUTLS (gnutls),
184                                                             pcert, pcert_length, pkey);
185
186   gnutls->pcert = *pcert;
187   gnutls->pcert_length = *pcert_length;
188   gnutls->pkey = *pkey;
189
190   return 0;
191 }
192
193 static void
194 g_tls_server_connection_gnutls_prepare_handshake (GTlsConnectionBase  *tls,
195                                                   gchar              **advertised_protocols)
196 {
197   GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (tls);
198   gnutls_session_t session;
199   gnutls_certificate_request_t req_mode;
200
201   switch (gnutls->authentication_mode)
202     {
203     case G_TLS_AUTHENTICATION_REQUESTED:
204       req_mode = GNUTLS_CERT_REQUEST;
205       break;
206     case G_TLS_AUTHENTICATION_REQUIRED:
207       req_mode = GNUTLS_CERT_REQUIRE;
208       break;
209     case G_TLS_AUTHENTICATION_NONE:
210     default:
211       req_mode = GNUTLS_CERT_IGNORE;
212       break;
213     }
214
215   session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls));
216   gnutls_certificate_server_set_request (session, req_mode);
217
218   G_TLS_CONNECTION_BASE_CLASS (g_tls_server_connection_gnutls_parent_class)->prepare_handshake (tls, advertised_protocols);
219 }
220
221 static void
222 g_tls_server_connection_gnutls_update_credentials (GTlsConnectionGnutls             *gnutls,
223                                                    gnutls_certificate_credentials_t  credentials)
224 {
225   gnutls_certificate_set_retrieve_function2 (credentials, g_tls_server_connection_gnutls_handshake_thread_retrieve_function);
226 }
227
228 static void
229 g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klass)
230 {
231   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
232   GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
233   GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
234
235   gobject_class->finalize     = g_tls_server_connection_gnutls_finalize;
236   gobject_class->get_property = g_tls_server_connection_gnutls_get_property;
237   gobject_class->set_property = g_tls_server_connection_gnutls_set_property;
238
239   base_class->prepare_handshake  = g_tls_server_connection_gnutls_prepare_handshake;
240
241   gnutls_class->update_credentials = g_tls_server_connection_gnutls_update_credentials;
242
243   g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
244 }
245
246 static void
247 g_tls_server_connection_gnutls_server_connection_interface_init (GTlsServerConnectionInterface *iface)
248 {
249 }
250
251 static void
252 g_tls_server_connection_gnutls_initable_interface_init (GInitableIface  *iface)
253 {
254   g_tls_server_connection_gnutls_parent_initable_iface = g_type_interface_peek_parent (iface);
255
256   iface->init = g_tls_server_connection_gnutls_initable_init;
257 }