transport: do certificate initialization earlier
[siplcs.git] / src / core / sipe-certificate.c
blob711828084644539aab7036d2e28fe17d7d2de011
1 /**
2 * @file sipe-certificate.c
4 * pidgin-sipe
6 * Copyright (C) 2011 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Specification references:
26 * - [MS-SIPAE]: http://msdn.microsoft.com/en-us/library/cc431510.aspx
27 * - [MS-OCAUTHWS]: http://msdn.microsoft.com/en-us/library/ff595592.aspx
28 * - MS Tech-Ed Europe 2010 "UNC310: Microsoft Lync 2010 Technology Explained"
29 * http://ecn.channel9.msdn.com/o9/te/Europe/2010/pptx/unc310.pptx
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
36 #include <string.h>
38 #include <glib.h>
40 #include "sipe-common.h"
41 #include "sip-transport.h"
42 #include "sipe-backend.h"
43 #include "sipe-core.h"
44 #include "sipe-core-private.h"
45 #include "sipe-certificate.h"
46 #include "sipe-cert-crypto.h"
47 #include "sipe-nls.h"
48 #include "sipe-svc.h"
49 #include "sipe-webticket.h"
50 #include "sipe-xml.h"
52 struct sipe_certificate {
53 GHashTable *certificates;
54 struct sipe_cert_crypto *backend;
57 void sipe_certificate_free(struct sipe_core_private *sipe_private)
59 struct sipe_certificate *sc = sipe_private->certificate;
61 if (sc) {
62 g_hash_table_destroy(sc->certificates);
63 sipe_cert_crypto_free(sc->backend);
64 g_free(sc);
68 gboolean sipe_certificate_init(struct sipe_core_private *sipe_private)
70 struct sipe_certificate *sc;
71 struct sipe_cert_crypto *ssc;
73 if (sipe_private->certificate)
74 return(TRUE);
76 ssc = sipe_cert_crypto_init();
77 if (!ssc) {
78 SIPE_DEBUG_ERROR_NOFORMAT("sipe_certificate_init: crypto backend init FAILED!");
79 return(FALSE);
82 sc = g_new0(struct sipe_certificate, 1);
83 sc->certificates = g_hash_table_new_full(g_str_hash, g_str_equal,
84 g_free,
85 sipe_cert_crypto_destroy);
86 sc->backend = ssc;
88 SIPE_DEBUG_INFO_NOFORMAT("sipe_certificate_init: DONE");
90 sipe_private->certificate = sc;
91 return(TRUE);
94 static gchar *create_certreq(struct sipe_core_private *sipe_private,
95 const gchar *subject)
97 gchar *base64;
99 if (!sipe_certificate_init(sipe_private))
100 return(NULL);
102 SIPE_DEBUG_INFO_NOFORMAT("create_req: generating new certificate request");
104 base64 = sipe_cert_crypto_request(sipe_private->certificate->backend,
105 subject);
106 if (base64) {
107 GString *format = g_string_new(NULL);
108 gsize count = strlen(base64);
109 const gchar *p = base64;
111 /* Base64 needs to be formated correctly */
112 #define CERTREQ_BASE64_LINE_LENGTH 76
113 while (count > 0) {
114 gsize chunk = count > CERTREQ_BASE64_LINE_LENGTH ?
115 CERTREQ_BASE64_LINE_LENGTH : count;
116 g_string_append_len(format, p, chunk);
117 if (chunk == CERTREQ_BASE64_LINE_LENGTH)
118 g_string_append(format, "\r\n");
119 count -= chunk;
120 p += chunk;
123 /* swap Base64 buffers */
124 g_free(base64);
125 base64 = format->str;
126 g_string_free(format, FALSE);
129 return(base64);
132 static void add_certificate(struct sipe_core_private *sipe_private,
133 const gchar *target,
134 gpointer certificate)
136 struct sipe_certificate *sc = sipe_private->certificate;
137 g_hash_table_insert(sc->certificates, g_strdup(target), certificate);
140 gpointer sipe_certificate_tls_dsk_find(struct sipe_core_private *sipe_private,
141 const gchar *target)
143 struct sipe_certificate *sc = sipe_private->certificate;
144 gpointer certificate;
146 if (!target || !sc)
147 return(NULL);
149 certificate = g_hash_table_lookup(sc->certificates, target);
151 /* Let's make sure the certificate is still valid for another hour */
152 if (!sipe_cert_crypto_valid(certificate, 60 * 60)) {
153 SIPE_DEBUG_ERROR("sipe_certificate_tls_dsk_find: certificate for '%s' is invalid",
154 target);
155 return(NULL);
158 return(certificate);
161 static void certificate_failure(struct sipe_core_private *sipe_private,
162 const gchar *format,
163 const gchar *parameter)
165 gchar *tmp = g_strdup_printf(format, parameter);
166 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
167 SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
168 tmp);
169 g_free(tmp);
172 static void get_and_publish_cert(struct sipe_core_private *sipe_private,
173 const gchar *uri,
174 SIPE_UNUSED_PARAMETER const gchar *raw,
175 sipe_xml *soap_body,
176 gpointer callback_data)
178 gchar *target = callback_data;
179 gboolean success = (uri == NULL); /* abort case */
181 if (soap_body) {
182 gchar *cert_base64 = sipe_xml_data(sipe_xml_child(soap_body,
183 "Body/GetAndPublishCertResponse/RequestSecurityTokenResponse/RequestedSecurityToken/BinarySecurityToken"));
185 SIPE_DEBUG_INFO("get_and_publish_cert: received valid SOAP message from service %s",
186 uri);
188 if (cert_base64) {
189 gpointer opaque = sipe_cert_crypto_decode(sipe_private->certificate->backend,
190 cert_base64);
192 SIPE_DEBUG_INFO_NOFORMAT("get_and_publish_cert: found certificate");
194 if (opaque) {
195 add_certificate(sipe_private,
196 target,
197 opaque);
198 SIPE_DEBUG_INFO("get_and_publish_cert: certificate for target '%s' added",
199 target);
201 /* Let's try this again... */
202 sip_transport_authentication_completed(sipe_private);
203 success = TRUE;
206 g_free(cert_base64);
211 if (!success) {
212 certificate_failure(sipe_private,
213 _("Certificate request to %s failed"),
214 uri);
217 g_free(target);
220 static void certprov_webticket(struct sipe_core_private *sipe_private,
221 const gchar *base_uri,
222 const gchar *auth_uri,
223 const gchar *wsse_security,
224 gpointer callback_data)
226 gchar *target = callback_data;
228 if (wsse_security) {
229 /* Got a Web Ticket for Certificate Provisioning Service */
230 gchar *certreq_base64 = create_certreq(sipe_private,
231 sipe_private->username);
233 SIPE_DEBUG_INFO("certprov_webticket: got ticket for %s",
234 base_uri);
236 if (certreq_base64) {
238 SIPE_DEBUG_INFO_NOFORMAT("certprov_webticket: created certificate request");
240 if (sipe_svc_get_and_publish_cert(sipe_private,
241 auth_uri,
242 wsse_security,
243 certreq_base64,
244 get_and_publish_cert,
245 target))
246 /* callback data passed down the line */
247 target = NULL;
249 g_free(certreq_base64);
252 if (target) {
253 certificate_failure(sipe_private,
254 _("Certificate request to %s failed"),
255 base_uri);
258 } else if (auth_uri) {
259 certificate_failure(sipe_private,
260 _("Web ticket request to %s failed"),
261 base_uri);
264 g_free(target);
267 gboolean sipe_certificate_tls_dsk_generate(struct sipe_core_private *sipe_private,
268 const gchar *target,
269 const gchar *uri)
271 gchar *tmp = g_strdup(target);
272 gboolean ret;
274 ret = sipe_webticket_request(sipe_private,
275 uri,
276 "CertProvisioningServiceWebTicketProof_SHA1",
277 certprov_webticket,
278 tmp);
279 if (!ret)
280 g_free(tmp);
282 return(ret);
286 Local Variables:
287 mode: c
288 c-file-style: "bsd"
289 indent-tabs-mode: t
290 tab-width: 8
291 End: