svc: add alternative xmlns:wst to GetAndPublishCert request
[siplcs.git] / src / core / sipe-svc.c
blob006cc86273b6277b4d941fed0e1a5cd490c95970
1 /**
2 * @file sipe-svc.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 #include <stdlib.h>
33 #include <string.h>
35 #include <glib.h>
37 #include "sipe-common.h"
38 #include "http-conn.h"
39 #include "sipe-backend.h"
40 #include "sipe-core.h"
41 #include "sipe-core-private.h"
42 #include "sipe-digest.h"
43 #include "sipe-svc.h"
44 #include "sipe-utils.h"
45 #include "sipe-xml.h"
46 #include "sipe.h"
47 #include "uuid.h"
49 /* forward declaration */
50 struct svc_request;
51 typedef void (svc_callback)(struct svc_request *data,
52 const gchar *raw,
53 sipe_xml *xml);
55 struct svc_request {
56 struct sipe_core_private *sipe_private;
57 svc_callback *internal_cb;
58 sipe_svc_callback *cb;
59 gpointer *cb_data;
60 HttpConn *conn;
61 gchar *uri;
64 struct sipe_svc {
65 GSList *pending_requests;
68 static void sipe_svc_request_free(struct svc_request *data)
70 if (data->conn)
71 http_conn_free(data->conn);
72 if (data->cb)
73 /* Callback: aborted */
74 (*data->cb)(data->sipe_private, NULL, NULL, NULL, data->cb_data);
75 g_free(data->uri);
76 g_free(data);
79 void sipe_svc_free(struct sipe_core_private *sipe_private)
81 struct sipe_svc *svc = sipe_private->svc;
82 if (!svc)
83 return;
85 if (svc->pending_requests) {
86 GSList *entry = svc->pending_requests;
87 while (entry) {
88 sipe_svc_request_free(entry->data);
89 entry = entry->next;
91 g_slist_free(svc->pending_requests);
94 g_free(svc);
95 sipe_private->svc = NULL;
98 static void sipe_svc_init(struct sipe_core_private *sipe_private)
100 if (sipe_private->svc)
101 return;
103 sipe_private->svc = g_new0(struct sipe_svc, 1);
106 void sipe_svc_fill_random(struct sipe_svc_random *random,
107 guint bits)
109 guint bytes = ((bits + 15) / 16) * 2;
110 guint16 *p = g_malloc(bytes);
112 SIPE_DEBUG_INFO("sipe_svc_fill_random: %d bits -> %d bytes",
113 bits, bytes);
115 random->buffer = (guint8*) p;
116 random->length = bytes;
118 for (bytes /= 2; bytes; bytes--)
119 *p++ = rand() & 0xFFFF;
122 void sipe_svc_free_random(struct sipe_svc_random *random)
124 g_free(random->buffer);
127 static void sipe_svc_https_response(int return_code,
128 const gchar *body,
129 SIPE_UNUSED_PARAMETER const gchar *content_type,
130 HttpConn *conn,
131 void *callback_data)
133 struct svc_request *data = callback_data;
134 struct sipe_svc *svc = data->sipe_private->svc;
136 SIPE_DEBUG_INFO("sipe_svc_https_response: code %d", return_code);
137 http_conn_set_close(conn);
138 data->conn = NULL;
140 if ((return_code == 200) && body) {
141 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
142 /* Internal callback: success */
143 (*data->internal_cb)(data, body, xml);
144 sipe_xml_free(xml);
145 } else {
146 /* Internal callback: failed */
147 (*data->internal_cb)(data, NULL, NULL);
150 /* Internal callback has already called this */
151 data->cb = NULL;
153 svc->pending_requests = g_slist_remove(svc->pending_requests,
154 data);
155 sipe_svc_request_free(data);
158 static gboolean sipe_svc_https_request(struct sipe_core_private *sipe_private,
159 const gchar *method,
160 const gchar *uri,
161 const gchar *content_type,
162 const gchar *body,
163 svc_callback *internal_callback,
164 sipe_svc_callback *callback,
165 gpointer callback_data)
167 struct svc_request *data = g_new0(struct svc_request, 1);
168 gboolean ret = FALSE;
170 data->sipe_private = sipe_private;
171 data->uri = g_strdup(uri);
173 data->conn = http_conn_create(SIPE_CORE_PUBLIC,
174 NULL, /* HttpSession */
175 method,
176 HTTP_CONN_SSL,
177 HTTP_CONN_NO_REDIRECT,
178 uri,
179 body,
180 content_type,
181 NULL, /* HttpConnAuth */
182 sipe_svc_https_response,
183 data);
185 if (data->conn) {
186 data->internal_cb = internal_callback;
187 data->cb = callback;
188 data->cb_data = callback_data;
189 sipe_svc_init(sipe_private);
190 sipe_private->svc->pending_requests = g_slist_prepend(sipe_private->svc->pending_requests,
191 data);
192 ret = TRUE;
193 } else {
194 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri);
195 sipe_svc_request_free(data);
198 return(ret);
201 static gboolean sipe_svc_wsdl_request(struct sipe_core_private *sipe_private,
202 const gchar *uri,
203 const gchar *additional_ns,
204 const gchar *soap_action,
205 const gchar *wsse_security,
206 const gchar *soap_body,
207 svc_callback *internal_callback,
208 sipe_svc_callback *callback,
209 gpointer callback_data)
211 gchar *body = g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
212 "<soap:Envelope %s"
213 " xmlns:auth=\"http://schemas.xmlsoap.org/ws/2006/12/authorization\""
214 " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\""
215 " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\""
216 " xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\""
217 " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""
218 " >"
219 " <soap:Header>"
220 " <wsa:To>%s</wsa:To>"
221 " <wsa:ReplyTo>"
222 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
223 " </wsa:ReplyTo>"
224 " <wsa:Action>%s</wsa:Action>"
225 " <wsse:Security>%s</wsse:Security>"
226 " </soap:Header>"
227 " <soap:Body>%s</soap:Body>"
228 "</soap:Envelope>",
229 additional_ns,
230 uri,
231 soap_action,
232 wsse_security,
233 soap_body);
235 gboolean ret = sipe_svc_https_request(sipe_private,
236 HTTP_CONN_POST,
237 uri,
238 "text/xml",
239 body,
240 internal_callback,
241 callback,
242 callback_data);
243 g_free(body);
245 return(ret);
248 static gboolean new_soap_req(struct sipe_core_private *sipe_private,
249 const gchar *uri,
250 const gchar *soap_action,
251 const gchar *wsse_security,
252 const gchar *soap_body,
253 svc_callback *internal_callback,
254 sipe_svc_callback *callback,
255 gpointer callback_data)
257 return(sipe_svc_wsdl_request(sipe_private,
258 uri,
259 "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" "
260 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
261 "xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"",
262 soap_action,
263 wsse_security,
264 soap_body,
265 internal_callback,
266 callback,
267 callback_data));
270 static void sipe_svc_wsdl_response(struct svc_request *data,
271 const gchar *raw,
272 sipe_xml *xml)
274 if (xml) {
275 /* Callback: success */
276 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
277 } else {
278 /* Callback: failed */
279 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
283 gboolean sipe_svc_get_and_publish_cert(struct sipe_core_private *sipe_private,
284 const gchar *uri,
285 const gchar *authuser,
286 const gchar *wsse_security,
287 const gchar *certreq,
288 sipe_svc_callback *callback,
289 gpointer callback_data)
291 struct sipe_svc_random id;
292 gchar *id_base64;
293 gchar *id_uuid;
294 gchar *uuid = get_uuid(sipe_private);
295 gchar *soap_body;
296 gboolean ret;
298 /* random request ID */
299 sipe_svc_fill_random(&id, 256);
300 id_base64 = g_base64_encode(id.buffer, id.length);
301 sipe_svc_free_random(&id);
302 id_uuid = generateUUIDfromEPID(id_base64);
303 g_free(id_base64);
305 soap_body = g_strdup_printf("<GetAndPublishCert"
306 " xmlns=\"http://schemas.microsoft.com/OCS/AuthWebServices/\""
307 " xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512/\""
308 " xmlns:wstep=\"http://schemas.microsoft.com/windows/pki/2009/01/enrollment\""
309 " DeviceId=\"{%s}\""
310 " Entity=\"%s\""
312 " <wst:RequestSecurityToken>"
313 " <wst:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wst:TokenType>"
314 " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>"
315 " <wsse:BinarySecurityToken"
316 " ValueType=\"http://schemas.microsoft.com/OCS/AuthWebServices.xsd#PKCS10\""
317 " EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#Base64Binary\""
318 " >\r\n%s</wsse:BinarySecurityToken>"
319 " <wstep:RequestID>%s</wstep:RequestID>"
320 " </wst:RequestSecurityToken>"
321 "</GetAndPublishCert>",
322 uuid,
323 authuser,
324 certreq,
325 id_uuid);
326 g_free(id_uuid);
327 g_free(uuid);
329 ret = new_soap_req(sipe_private,
330 uri,
331 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
332 wsse_security,
333 soap_body,
334 sipe_svc_wsdl_response,
335 callback,
336 callback_data);
337 g_free(soap_body);
339 return(ret);
343 * This functions encodes what the Microsoft Lync client does for
344 * Office365 accounts. It will most definitely fail for internal Lync
345 * installation that use TLS-DSK instead of NTLM.
347 * But for those anonymous authentication should already have succeeded.
348 * I guess we'll have to see what happens in real life...
350 gboolean sipe_svc_webticket_lmc(struct sipe_core_private *sipe_private,
351 const gchar *authuser,
352 const gchar *service_uri,
353 sipe_svc_callback *callback,
354 gpointer callback_data)
356 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
357 /* login.microsoftonline.com seems only to accept cleartext passwords :/ */
358 gchar *security = g_strdup_printf("<wsse:UsernameToken>"
359 " <wsse:Username>%s</wsse:Username>"
360 " <wsse:Password>%s</wsse:Password>"
361 "</wsse:UsernameToken>",
362 authuser, sip->password);
364 gchar *soap_body = g_strdup_printf("<ps:RequestMultipleSecurityTokens>"
365 " <wst:RequestSecurityToken>"
366 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
367 " <wsp:AppliesTo>"
368 " <wsa:EndpointReference>"
369 " <wsa:Address>%s</wsa:Address>"
370 " </wsa:EndpointReference>"
371 " </wsp:AppliesTo>"
372 " </wst:RequestSecurityToken>"
373 "</ps:RequestMultipleSecurityTokens>",
374 service_uri);
376 gboolean ret = sipe_svc_wsdl_request(sipe_private,
377 "https://login.microsoftonline.com:443/RST2.srf",
378 "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" "
379 "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
380 "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"",
381 "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
382 security,
383 soap_body,
384 sipe_svc_wsdl_response,
385 callback,
386 callback_data);
387 g_free(soap_body);
388 g_free(security);
390 return(ret);
393 static gchar *sipe_svc_security_username(struct sipe_core_private *sipe_private,
394 const gchar *authuser)
396 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
397 struct sipe_svc_random nonce;
398 GTimeVal now;
399 guchar digest[SIPE_DIGEST_SHA1_LENGTH];
400 guchar *buf, *p;
401 gchar *base64, *nonce_base64, *created;
402 guint len, created_len, password_len;
403 gchar *ret;
405 /* nonce */
406 sipe_svc_fill_random(&nonce, 256);
407 nonce_base64 = g_base64_encode(nonce.buffer, nonce.length);
409 /* created */
410 g_get_current_time(&now);
411 created = g_time_val_to_iso8601(&now);
412 created_len = strlen(created);
414 /* password */
415 password_len = strlen(sip->password);
417 /* nonce + created + password */
418 len = nonce.length + created_len + password_len;
419 p = buf = g_malloc(len);
420 memcpy(p, nonce.buffer, nonce.length);
421 p += nonce.length;
422 memcpy(p, created, created_len);
423 p += created_len;
424 memcpy(p, sip->password, password_len);
426 /* Base64( SHA-1( nonce + created + password ) ) */
427 sipe_digest_sha1(buf, len, digest);
428 base64 = g_base64_encode(digest, SIPE_DIGEST_SHA1_LENGTH);
430 ret = g_strdup_printf("<wsse:UsernameToken>"
431 " <wsse:Username>%s</wsse:Username>"
432 " <wsse:Password Type=\"...#PasswordDigest\">%s</wsse:Password>"
433 " <wsse:Nonce>%s</wsse:Nonce>"
434 " <wsu:Created>%s</wsu:Created>"
435 "</wsse:UsernameToken>",
436 authuser, base64, nonce_base64, created);
438 g_free(base64);
439 g_free(buf);
440 g_free(created);
441 g_free(nonce_base64);
442 sipe_svc_free_random(&nonce);
444 return(ret);
447 gboolean sipe_svc_webticket(struct sipe_core_private *sipe_private,
448 const gchar *uri,
449 const gchar *authuser,
450 const gchar *wsse_security,
451 const gchar *service_uri,
452 const struct sipe_svc_random *entropy,
453 sipe_svc_callback *callback,
454 gpointer callback_data)
456 gchar *uuid = get_uuid(sipe_private);
457 gchar *security = NULL;
458 gchar *secret = g_base64_encode(entropy->buffer, entropy->length);
459 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken Context=\"%s\">"
460 " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>"
461 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
462 " <wsp:AppliesTo>"
463 " <wsa:EndpointReference>"
464 " <wsa:Address>%s</wsa:Address>"
465 " </wsa:EndpointReference>"
466 " </wsp:AppliesTo>"
467 " <wst:Claims Dialect=\"urn:component:Microsoft.Rtc.WebAuthentication.2010:authclaims\">"
468 " <auth:ClaimType Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri\" Optional=\"false\">"
469 " <auth:Value>sip:%s</auth:Value>"
470 " </auth:ClaimType>"
471 " </wst:Claims>"
472 " <wst:Entropy>"
473 " <wst:BinarySecret>%s</wst:BinarySecret>"
474 " </wst:Entropy>"
475 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
476 "</wst:RequestSecurityToken>",
477 uuid,
478 service_uri,
479 authuser,
480 secret);
482 gboolean ret = new_soap_req(sipe_private,
483 uri,
484 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
485 wsse_security ?
486 wsse_security :
487 (security = sipe_svc_security_username(sipe_private, authuser)),
488 soap_body,
489 sipe_svc_wsdl_response,
490 callback,
491 callback_data);
492 g_free(soap_body);
493 g_free(secret);
494 g_free(security);
495 g_free(uuid);
497 return(ret);
500 static void sipe_svc_metadata_response(struct svc_request *data,
501 const gchar *raw,
502 sipe_xml *xml)
504 if (xml) {
505 /* Callback: success */
506 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
507 } else {
508 /* Callback: failed */
509 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
513 gboolean sipe_svc_metadata(struct sipe_core_private *sipe_private,
514 const gchar *uri,
515 sipe_svc_callback *callback,
516 gpointer callback_data)
518 gchar *mex_uri = g_strdup_printf("%s/mex", uri);
519 gboolean ret = sipe_svc_https_request(sipe_private,
520 HTTP_CONN_GET,
521 mex_uri,
522 "text",
524 sipe_svc_metadata_response,
525 callback,
526 callback_data);
527 g_free(mex_uri);
528 return(ret);
532 Local Variables:
533 mode: c
534 c-file-style: "bsd"
535 indent-tabs-mode: t
536 tab-width: 8
537 End: