Release 1.25.0 -- Buddy Idle Time, RTF
[siplcs.git] / src / core / sipe-certificate.c
blob2764aa2607d897f01849f53be60af81a93027e33
1 /**
2 * @file sipe-certificate.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2016 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 struct certificate_callback_data {
58 gchar *target;
59 struct sipe_svc_session *session;
62 static void callback_data_free(struct certificate_callback_data *ccd)
64 if (ccd) {
65 sipe_svc_session_close(ccd->session);
66 g_free(ccd->target);
67 g_free(ccd);
71 void sipe_certificate_free(struct sipe_core_private *sipe_private)
73 struct sipe_certificate *sc = sipe_private->certificate;
75 if (sc) {
76 g_hash_table_destroy(sc->certificates);
77 sipe_cert_crypto_free(sc->backend);
78 g_free(sc);
82 gboolean sipe_certificate_init(struct sipe_core_private *sipe_private)
84 struct sipe_certificate *sc;
85 struct sipe_cert_crypto *ssc;
87 if (sipe_private->certificate)
88 return(TRUE);
90 ssc = sipe_cert_crypto_init();
91 if (!ssc) {
92 SIPE_DEBUG_ERROR_NOFORMAT("sipe_certificate_init: crypto backend init FAILED!");
93 return(FALSE);
96 sc = g_new0(struct sipe_certificate, 1);
97 sc->certificates = g_hash_table_new_full(g_str_hash, g_str_equal,
98 g_free,
99 sipe_cert_crypto_destroy);
100 sc->backend = ssc;
102 SIPE_DEBUG_INFO_NOFORMAT("sipe_certificate_init: DONE");
104 sipe_private->certificate = sc;
105 return(TRUE);
108 static gchar *create_certreq(struct sipe_core_private *sipe_private,
109 const gchar *subject)
111 gchar *base64;
113 if (!sipe_certificate_init(sipe_private))
114 return(NULL);
116 SIPE_DEBUG_INFO_NOFORMAT("create_req: generating new certificate request");
118 base64 = sipe_cert_crypto_request(sipe_private->certificate->backend,
119 subject);
120 if (base64) {
121 GString *format = g_string_new(NULL);
122 gsize count = strlen(base64);
123 const gchar *p = base64;
125 /* Base64 needs to be formated correctly */
126 #define CERTREQ_BASE64_LINE_LENGTH 76
127 while (count > 0) {
128 gsize chunk = count > CERTREQ_BASE64_LINE_LENGTH ?
129 CERTREQ_BASE64_LINE_LENGTH : count;
130 g_string_append_len(format, p, chunk);
131 if (chunk == CERTREQ_BASE64_LINE_LENGTH)
132 g_string_append(format, "\r\n");
133 count -= chunk;
134 p += chunk;
137 /* swap Base64 buffers */
138 g_free(base64);
139 base64 = format->str;
140 g_string_free(format, FALSE);
143 return(base64);
146 static void add_certificate(struct sipe_core_private *sipe_private,
147 const gchar *target,
148 gpointer certificate)
150 struct sipe_certificate *sc = sipe_private->certificate;
151 g_hash_table_insert(sc->certificates, g_strdup(target), certificate);
154 gpointer sipe_certificate_tls_dsk_find(struct sipe_core_private *sipe_private,
155 const gchar *target)
157 struct sipe_certificate *sc = sipe_private->certificate;
158 gpointer certificate;
160 if (!target || !sc)
161 return(NULL);
163 certificate = g_hash_table_lookup(sc->certificates, target);
165 /* Let's make sure the certificate is still valid for another hour */
166 if (!sipe_cert_crypto_valid(certificate, 60 * 60)) {
167 SIPE_DEBUG_ERROR("sipe_certificate_tls_dsk_find: certificate for '%s' is invalid",
168 target);
169 return(NULL);
172 return(certificate);
175 static void certificate_failure(struct sipe_core_private *sipe_private,
176 const gchar *format,
177 const gchar *parameter,
178 const gchar *failure_info)
180 gchar *tmp = g_strdup_printf(format, parameter);
181 if (failure_info) {
182 gchar *tmp2 = g_strdup_printf("%s\n(%s)", tmp, failure_info);
183 g_free(tmp);
184 tmp = tmp2;
186 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
187 SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
188 tmp);
189 g_free(tmp);
192 static void get_and_publish_cert(struct sipe_core_private *sipe_private,
193 const gchar *uri,
194 SIPE_UNUSED_PARAMETER const gchar *raw,
195 sipe_xml *soap_body,
196 gpointer callback_data)
198 struct certificate_callback_data *ccd = callback_data;
199 gboolean success = (uri == NULL); /* abort case */
201 if (soap_body) {
202 gchar *cert_base64 = sipe_xml_data(sipe_xml_child(soap_body,
203 "Body/GetAndPublishCertResponse/RequestSecurityTokenResponse/RequestedSecurityToken/BinarySecurityToken"));
205 SIPE_DEBUG_INFO("get_and_publish_cert: received valid SOAP message from service %s",
206 uri);
208 if (cert_base64) {
209 gpointer opaque = sipe_cert_crypto_decode(sipe_private->certificate->backend,
210 cert_base64);
212 SIPE_DEBUG_INFO_NOFORMAT("get_and_publish_cert: found certificate");
214 if (opaque) {
215 add_certificate(sipe_private,
216 ccd->target,
217 opaque);
218 SIPE_DEBUG_INFO("get_and_publish_cert: certificate for target '%s' added",
219 ccd->target);
221 /* Let's try this again... */
222 sip_transport_authentication_completed(sipe_private);
223 success = TRUE;
226 g_free(cert_base64);
231 if (!success) {
232 certificate_failure(sipe_private,
233 _("Certificate request to %s failed"),
234 uri,
235 NULL);
238 callback_data_free(ccd);
241 static void certprov_webticket(struct sipe_core_private *sipe_private,
242 const gchar *base_uri,
243 const gchar *auth_uri,
244 const gchar *wsse_security,
245 const gchar *failure_msg,
246 gpointer callback_data)
248 struct certificate_callback_data *ccd = callback_data;
250 if (wsse_security) {
251 /* Got a Web Ticket for Certificate Provisioning Service */
252 gchar *certreq_base64 = create_certreq(sipe_private,
253 sipe_private->username);
255 SIPE_DEBUG_INFO("certprov_webticket: got ticket for %s",
256 base_uri);
258 if (certreq_base64) {
260 SIPE_DEBUG_INFO_NOFORMAT("certprov_webticket: created certificate request");
262 if (sipe_svc_get_and_publish_cert(sipe_private,
263 ccd->session,
264 auth_uri,
265 wsse_security,
266 certreq_base64,
267 get_and_publish_cert,
268 ccd))
269 /* callback data passed down the line */
270 ccd = NULL;
272 g_free(certreq_base64);
275 if (ccd) {
276 certificate_failure(sipe_private,
277 _("Certificate request to %s failed"),
278 base_uri,
279 NULL);
282 } else if (auth_uri) {
283 certificate_failure(sipe_private,
284 _("Web ticket request to %s failed"),
285 base_uri,
286 failure_msg);
289 if (ccd)
290 callback_data_free(ccd);
293 gboolean sipe_certificate_tls_dsk_generate(struct sipe_core_private *sipe_private,
294 const gchar *target,
295 const gchar *uri)
297 struct certificate_callback_data *ccd = g_new0(struct certificate_callback_data, 1);
298 gboolean ret;
300 ccd->session = sipe_svc_session_start();
302 ret = sipe_webticket_request_with_port(sipe_private,
303 ccd->session,
304 uri,
305 "CertProvisioningServiceWebTicketProof_SHA1",
306 certprov_webticket,
307 ccd);
308 if (ret) {
309 ccd->target = g_strdup(target);
311 } else {
312 callback_data_free(ccd);
315 return(ret);
319 Local Variables:
320 mode: c
321 c-file-style: "bsd"
322 indent-tabs-mode: t
323 tab-width: 8
324 End: