2 * @file sipe-certificate.c
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
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"
49 #include "sipe-webticket.h"
52 struct sipe_certificate
{
53 GHashTable
*certificates
;
54 struct sipe_cert_crypto
*backend
;
57 struct certificate_callback_data
{
59 struct sipe_svc_session
*session
;
62 static void callback_data_free(struct certificate_callback_data
*ccd
)
65 sipe_svc_session_close(ccd
->session
);
71 void sipe_certificate_free(struct sipe_core_private
*sipe_private
)
73 struct sipe_certificate
*sc
= sipe_private
->certificate
;
76 g_hash_table_destroy(sc
->certificates
);
77 sipe_cert_crypto_free(sc
->backend
);
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
)
90 ssc
= sipe_cert_crypto_init();
92 SIPE_DEBUG_ERROR_NOFORMAT("sipe_certificate_init: crypto backend init FAILED!");
96 sc
= g_new0(struct sipe_certificate
, 1);
97 sc
->certificates
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
99 sipe_cert_crypto_destroy
);
102 SIPE_DEBUG_INFO_NOFORMAT("sipe_certificate_init: DONE");
104 sipe_private
->certificate
= sc
;
108 static gchar
*create_certreq(struct sipe_core_private
*sipe_private
,
109 const gchar
*subject
)
113 if (!sipe_certificate_init(sipe_private
))
116 SIPE_DEBUG_INFO_NOFORMAT("create_req: generating new certificate request");
118 base64
= sipe_cert_crypto_request(sipe_private
->certificate
->backend
,
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
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");
137 /* swap Base64 buffers */
139 base64
= format
->str
;
140 g_string_free(format
, FALSE
);
146 static void add_certificate(struct sipe_core_private
*sipe_private
,
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
,
157 struct sipe_certificate
*sc
= sipe_private
->certificate
;
158 gpointer certificate
;
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",
175 static void certificate_failure(struct sipe_core_private
*sipe_private
,
177 const gchar
*parameter
,
178 const gchar
*failure_info
)
180 gchar
*tmp
= g_strdup_printf(format
, parameter
);
182 gchar
*tmp2
= g_strdup_printf("%s\n(%s)", tmp
, failure_info
);
186 sipe_backend_connection_error(SIPE_CORE_PUBLIC
,
187 SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED
,
192 static void get_and_publish_cert(struct sipe_core_private
*sipe_private
,
194 SIPE_UNUSED_PARAMETER
const gchar
*raw
,
196 gpointer callback_data
)
198 struct certificate_callback_data
*ccd
= callback_data
;
199 gboolean success
= (uri
== NULL
); /* abort case */
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",
209 gpointer opaque
= sipe_cert_crypto_decode(sipe_private
->certificate
->backend
,
212 SIPE_DEBUG_INFO_NOFORMAT("get_and_publish_cert: found certificate");
215 add_certificate(sipe_private
,
218 SIPE_DEBUG_INFO("get_and_publish_cert: certificate for target '%s' added",
221 /* Let's try this again... */
222 sip_transport_authentication_completed(sipe_private
);
232 certificate_failure(sipe_private
,
233 _("Certificate request to %s failed"),
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
;
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",
258 if (certreq_base64
) {
260 SIPE_DEBUG_INFO_NOFORMAT("certprov_webticket: created certificate request");
262 if (sipe_svc_get_and_publish_cert(sipe_private
,
267 get_and_publish_cert
,
269 /* callback data passed down the line */
272 g_free(certreq_base64
);
276 certificate_failure(sipe_private
,
277 _("Certificate request to %s failed"),
282 } else if (auth_uri
) {
283 certificate_failure(sipe_private
,
284 _("Web ticket request to %s failed"),
290 callback_data_free(ccd
);
293 gboolean
sipe_certificate_tls_dsk_generate(struct sipe_core_private
*sipe_private
,
297 struct certificate_callback_data
*ccd
= g_new0(struct certificate_callback_data
, 1);
300 ccd
->session
= sipe_svc_session_start();
302 ret
= sipe_webticket_request_with_port(sipe_private
,
305 "CertProvisioningServiceWebTicketProof_SHA1",
309 ccd
->target
= g_strdup(target
);
312 callback_data_free(ccd
);