certificate: extract webticket data from response
[siplcs.git] / src / core / sipe-certificate.c
blobb2fd7f91f2f0a4498726bfd317614c114ceb9d15
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 "sipe-backend.h"
42 #include "sipe-core.h"
43 #include "sipe-core-private.h"
44 #include "sipe-certificate.h"
45 #include "sipe-nls.h"
46 #include "sipe-svc.h"
47 #include "sipe-utils.h"
48 #include "sipe-xml.h"
50 struct certificate_callback_data {
51 gchar *target;
52 gchar *authuser;
53 gchar *webticket_anon_uri;
54 gchar *webticket_fedbearer_uri;
55 gchar *certprov_uri;
57 gboolean tried_fedbearer;
59 struct sipe_svc_random entropy;
62 static void callback_data_free(struct certificate_callback_data *ccd)
64 if (ccd) {
65 g_free(ccd->target);
66 g_free(ccd->authuser);
67 g_free(ccd->webticket_anon_uri);
68 g_free(ccd->webticket_fedbearer_uri);
69 g_free(ccd->certprov_uri);
70 sipe_svc_free_random(&ccd->entropy);
71 g_free(ccd);
75 gpointer sipe_certificate_tls_dsk_find(struct sipe_core_private *sipe_private,
76 const gchar *target)
78 if (!target)
79 return(NULL);
81 /* temporary */
82 (void)sipe_private;
84 return(NULL);
87 static void certificate_failure(struct sipe_core_private *sipe_private,
88 const gchar *format,
89 const gchar *parameter)
91 gchar *tmp = g_strdup_printf(format, parameter);
92 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
93 SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
94 tmp);
95 g_free(tmp);
98 static gchar *extract_raw_xml(const gchar *xml,
99 const gchar *tag,
100 gboolean include_tag)
102 gchar *tag_start = g_strdup_printf("<%s", tag);
103 gchar *tag_end = g_strdup_printf("</%s>", tag);
104 gchar *data = NULL;
105 const gchar *start = strstr(xml, tag_start);
107 if (start) {
108 const gchar *end = strstr(start + strlen(tag_start), tag_end);
109 if (end) {
110 if (include_tag) {
111 data = g_strndup(start, end + strlen(tag_end) - start);
112 } else {
113 const gchar *tmp = strchr(start + strlen(tag_start), '>') + 1;
114 data = g_strndup(tmp, end - tmp);
119 g_free(tag_end);
120 g_free(tag_start);
121 return(data);
124 static void webticket_token(struct sipe_core_private *sipe_private,
125 const gchar *uri,
126 const gchar *raw,
127 sipe_xml *soap_body,
128 gpointer callback_data)
130 struct certificate_callback_data *ccd = callback_data;
131 gboolean success = (uri == NULL); /* abort case */
133 if (soap_body) {
134 gchar *lifetime = extract_raw_xml(raw, "wst:Lifetime", FALSE);
135 gchar *keydata = extract_raw_xml(raw, "EncryptedData", TRUE);
137 if (lifetime && keydata) {
138 gchar *webticket_xml;
140 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from service %s",
141 uri);
143 /* Create security data for request */
144 webticket_xml = g_strdup_printf("<wsu:TimeStamp>%s</wsu:TimeStamp>%s",
145 lifetime, keydata);
146 success = TRUE;
148 /* TBD.... */
149 SIPE_DEBUG_INFO("webticket_token: TOKEN\n%s", webticket_xml);
150 g_free(webticket_xml);
153 g_free(keydata);
154 g_free(lifetime);
156 } else if (uri) {
157 /* Retry with federated authentication? */
158 success = !ccd->webticket_fedbearer_uri || ccd->tried_fedbearer;
159 if (!success) {
160 SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication",
161 uri);
163 ccd->tried_fedbearer = TRUE;
164 success = sipe_svc_webticket_lmc(sipe_private,
165 ccd->authuser,
166 ccd->webticket_fedbearer_uri,
167 webticket_token,
168 ccd);
169 if (success) {
170 /* callback data passed down the line */
171 ccd = NULL;
176 if (!success) {
177 certificate_failure(sipe_private,
178 _("Web ticket request to %s failed"),
179 uri);
182 callback_data_free(ccd);
185 static void webticket_metadata(struct sipe_core_private *sipe_private,
186 const gchar *uri,
187 SIPE_UNUSED_PARAMETER const gchar *raw,
188 sipe_xml *metadata,
189 gpointer callback_data)
191 struct certificate_callback_data *ccd = callback_data;
193 if (metadata) {
194 const sipe_xml *node;
196 SIPE_DEBUG_INFO("webticket_metadata: metadata for service %s retrieved successfully",
197 uri);
199 /* Authentication ports accepted by WebTicket Service */
200 for (node = sipe_xml_child(metadata, "service/port");
201 node;
202 node = sipe_xml_twin(node)) {
203 const gchar *auth_uri = sipe_xml_attribute(sipe_xml_child(node,
204 "address"),
205 "location");
207 if (auth_uri) {
208 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
209 "WebTicketServiceAnon")) {
210 SIPE_DEBUG_INFO("webticket_metadata: WebTicket Anon Auth URI %s", auth_uri);
211 g_free(ccd->webticket_anon_uri);
212 ccd->webticket_anon_uri = g_strdup(auth_uri);
213 } else if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
214 "WsFedBearer")) {
215 SIPE_DEBUG_INFO("webticket_metadata: WebTicket Anon Auth URI %s", auth_uri);
216 g_free(ccd->webticket_fedbearer_uri);
217 ccd->webticket_fedbearer_uri = g_strdup(auth_uri);
222 if (ccd->webticket_anon_uri || ccd->webticket_fedbearer_uri) {
223 gboolean success;
225 if (ccd->webticket_anon_uri) {
226 /* Try anonymous authentication first */
227 /* Entropy: 256 random bits */
228 sipe_svc_fill_random(&ccd->entropy, 256);
230 success = sipe_svc_webticket(sipe_private,
231 ccd->webticket_anon_uri,
232 ccd->authuser,
233 ccd->certprov_uri,
234 &ccd->entropy,
235 webticket_token,
236 ccd);
237 } else {
238 ccd->tried_fedbearer = TRUE;
239 success = sipe_svc_webticket_lmc(sipe_private,
240 ccd->authuser,
241 ccd->webticket_fedbearer_uri,
242 webticket_token,
243 ccd);
246 if (success) {
247 /* callback data passed down the line */
248 ccd = NULL;
249 } else {
250 certificate_failure(sipe_private,
251 _("Can't request security token from %s"),
252 ccd->webticket_anon_uri ? ccd->webticket_anon_uri : ccd->webticket_fedbearer_uri);
255 } else {
256 certificate_failure(sipe_private,
257 _("Can't find the authentication port for TLS-DSK web ticket URI %s"),
258 uri);
261 } else if (uri) {
262 certificate_failure(sipe_private,
263 _("Can't retrieve metadata for TLS-DSK web ticket URI %s"),
264 uri);
267 callback_data_free(ccd);
270 static void certprov_metadata(struct sipe_core_private *sipe_private,
271 const gchar *uri,
272 SIPE_UNUSED_PARAMETER const gchar *raw,
273 sipe_xml *metadata,
274 gpointer callback_data)
276 struct certificate_callback_data *ccd = callback_data;
278 if (metadata) {
279 const sipe_xml *node;
280 gchar *ticket_uri = NULL;
282 SIPE_DEBUG_INFO("certprov_metadata: metadata for service %s retrieved successfully",
283 uri);
285 /* WebTicket policies accepted by Certificate Provisioning Service */
286 for (node = sipe_xml_child(metadata, "Policy");
287 node;
288 node = sipe_xml_twin(node)) {
289 if (sipe_strcase_equal(sipe_xml_attribute(node, "Id"),
290 "CertProvisioningServiceWebTicketProof_SHA1_policy")) {
292 SIPE_DEBUG_INFO_NOFORMAT("certprov_metadata: WebTicket policy found");
294 ticket_uri = sipe_xml_data(sipe_xml_child(node,
295 "ExactlyOne/All/EndorsingSupportingTokens/Policy/IssuedToken/Issuer/Address"));
296 if (ticket_uri) {
297 SIPE_DEBUG_INFO("certprov_metadata: WebTicket URI %s", ticket_uri);
298 } else {
299 certificate_failure(sipe_private,
300 _("Can't find the WebTicket URI for TLS-DSK certificate provisioning URI %s"),
301 uri);
303 break;
307 if (ticket_uri) {
309 /* Authentication ports accepted by Certificate Provisioning Service */
310 for (node = sipe_xml_child(metadata, "service/port");
311 node;
312 node = sipe_xml_twin(node)) {
313 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
314 "CertProvisioningServiceWebTicketProof_SHA1")) {
315 const gchar *auth_uri;
317 SIPE_DEBUG_INFO_NOFORMAT("certprov_metadata: authentication port found");
319 auth_uri = sipe_xml_attribute(sipe_xml_child(node,
320 "address"),
321 "location");
322 if (auth_uri) {
323 SIPE_DEBUG_INFO("certprov_metadata: CertProv Auth URI %s", auth_uri);
325 if (sipe_svc_metadata(sipe_private,
326 ticket_uri,
327 webticket_metadata,
328 ccd)) {
329 /* Remember for later */
330 ccd->certprov_uri = g_strdup(auth_uri);
332 /* callback data passed down the line */
333 ccd = NULL;
334 } else {
335 certificate_failure(sipe_private,
336 _("Can't request metadata from %s"),
337 ticket_uri);
340 break;
344 g_free(ticket_uri);
346 if (!node) {
347 certificate_failure(sipe_private,
348 _("Can't find the authentication port for TLS-DSK certificate provisioning URI %s"),
349 uri);
352 } else {
353 certificate_failure(sipe_private,
354 _("Can't find the WebTicket Policy for TLS-DSK certificate provisioning URI %s"),
355 uri);
358 } else if (uri) {
359 certificate_failure(sipe_private,
360 _("Can't retrieve metadata for TLS-DSK certificate provisioning URI %s"),
361 uri);
364 callback_data_free(ccd);
367 gboolean sipe_certificate_tls_dsk_generate(struct sipe_core_private *sipe_private,
368 const gchar *target,
369 const gchar *authuser,
370 const gchar *uri)
372 struct certificate_callback_data *ccd = g_new0(struct certificate_callback_data, 1);
373 gboolean ret;
375 ccd->target = g_strdup(target);
376 ccd->authuser = g_strdup(authuser);
378 ret = sipe_svc_metadata(sipe_private, uri, certprov_metadata, ccd);
379 if (!ret)
380 callback_data_free(ccd);
382 return(ret);
386 Local Variables:
387 mode: c
388 c-file-style: "bsd"
389 indent-tabs-mode: t
390 tab-width: 8
391 End: