i18n: update bug tracker URL in script
[siplcs.git] / src / core / sipe-svc.c
blobe2035ee5670c71bc93b8360c463e9923c772e3ce
1 /**
2 * @file sipe-svc.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2013 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-svc.h"
43 #include "sipe-tls.h"
44 #include "sipe-utils.h"
45 #include "sipe-xml.h"
46 #include "uuid.h"
48 /* forward declaration */
49 struct svc_request;
50 typedef void (svc_callback)(struct svc_request *data,
51 const gchar *raw,
52 sipe_xml *xml);
54 struct svc_request {
55 struct sipe_core_private *sipe_private;
56 svc_callback *internal_cb;
57 sipe_svc_callback *cb;
58 gpointer *cb_data;
59 HttpConn *conn;
60 HttpConnAuth auth;
61 gchar *uri;
62 gchar *soap_action;
65 struct sipe_svc {
66 GSList *pending_requests;
69 struct sipe_svc_session {
70 HttpSession *session;
73 static void sipe_svc_request_free(struct svc_request *data)
75 if (data->conn)
76 http_conn_free(data->conn);
77 if (data->cb)
78 /* Callback: aborted */
79 (*data->cb)(data->sipe_private, NULL, NULL, NULL, data->cb_data);
80 g_free(data->soap_action);
81 g_free(data->uri);
82 g_free(data);
85 void sipe_svc_free(struct sipe_core_private *sipe_private)
87 struct sipe_svc *svc = sipe_private->svc;
88 if (!svc)
89 return;
91 if (svc->pending_requests) {
92 GSList *entry = svc->pending_requests;
93 while (entry) {
94 sipe_svc_request_free(entry->data);
95 entry = entry->next;
97 g_slist_free(svc->pending_requests);
100 g_free(svc);
101 sipe_private->svc = NULL;
104 static void sipe_svc_init(struct sipe_core_private *sipe_private)
106 if (sipe_private->svc)
107 return;
109 sipe_private->svc = g_new0(struct sipe_svc, 1);
112 struct sipe_svc_session *sipe_svc_session_start(void)
114 struct sipe_svc_session *session = g_new0(struct sipe_svc_session, 1);
115 session->session = http_conn_session_create();
116 return(session);
119 void sipe_svc_session_close(struct sipe_svc_session *session)
121 if (session) {
122 http_conn_session_free(session->session);
123 g_free(session);
127 static void sipe_svc_https_response(int return_code,
128 const gchar *body,
129 SIPE_UNUSED_PARAMETER GSList *headers,
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 struct sipe_svc_session *session,
161 const gchar *uri,
162 const gchar *content_type,
163 const gchar *soap_action,
164 const gchar *body,
165 svc_callback *internal_callback,
166 sipe_svc_callback *callback,
167 gpointer callback_data)
169 struct svc_request *data = g_new0(struct svc_request, 1);
170 gboolean ret = FALSE;
172 data->sipe_private = sipe_private;
173 data->uri = g_strdup(uri);
175 if (soap_action)
176 data->soap_action = g_strdup_printf("SOAPAction: \"%s\"\r\n",
177 soap_action);
179 /* re-use SIP credentials */
180 data->auth.domain = sipe_private->authdomain;
181 data->auth.user = sipe_private->authuser;
182 data->auth.password = sipe_private->password;
184 data->conn = http_conn_create(SIPE_CORE_PUBLIC,
185 session->session,
186 method,
187 HTTP_CONN_SSL,
188 HTTP_CONN_NO_REDIRECT,
189 uri,
190 body,
191 content_type,
192 data->soap_action,
193 /* use credentials only when SSO is not selected */
194 SIPE_CORE_PRIVATE_FLAG_IS(SSO) ? NULL : &data->auth,
195 sipe_svc_https_response,
196 data);
198 if (data->conn) {
199 data->internal_cb = internal_callback;
200 data->cb = callback;
201 data->cb_data = callback_data;
202 sipe_svc_init(sipe_private);
203 sipe_private->svc->pending_requests = g_slist_prepend(sipe_private->svc->pending_requests,
204 data);
205 ret = TRUE;
206 } else {
207 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri);
208 sipe_svc_request_free(data);
211 return(ret);
214 static gboolean sipe_svc_wsdl_request(struct sipe_core_private *sipe_private,
215 struct sipe_svc_session *session,
216 const gchar *uri,
217 const gchar *additional_ns,
218 const gchar *soap_action,
219 const gchar *wsse_security,
220 const gchar *soap_body,
221 const gchar *content_type,
222 svc_callback *internal_callback,
223 sipe_svc_callback *callback,
224 gpointer callback_data)
226 /* Only generate SOAP header if we have a security token */
227 gchar *soap_header = wsse_security ?
228 g_strdup_printf("<soap:Header>"
229 " <wsa:To>%s</wsa:To>"
230 " <wsa:ReplyTo>"
231 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
232 " </wsa:ReplyTo>"
233 " <wsa:Action>%s</wsa:Action>"
234 " <wsse:Security>%s</wsse:Security>"
235 "</soap:Header>",
236 uri,
237 soap_action,
238 wsse_security) :
239 g_strdup("");
240 gchar *body = g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
241 "<soap:Envelope %s"
242 " xmlns:auth=\"http://schemas.xmlsoap.org/ws/2006/12/authorization\""
243 " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\""
244 " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\""
245 " xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\""
246 " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""
247 " >"
248 "%s"
249 " <soap:Body>%s</soap:Body>"
250 "</soap:Envelope>",
251 additional_ns,
252 soap_header,
253 soap_body);
255 gboolean ret = sipe_svc_https_request(sipe_private,
256 HTTP_CONN_POST,
257 session,
258 uri,
259 content_type ? content_type : "text/xml",
260 soap_action,
261 body,
262 internal_callback,
263 callback,
264 callback_data);
265 g_free(soap_header);
266 g_free(body);
268 return(ret);
271 static gboolean new_soap_req(struct sipe_core_private *sipe_private,
272 struct sipe_svc_session *session,
273 const gchar *uri,
274 const gchar *soap_action,
275 const gchar *wsse_security,
276 const gchar *soap_body,
277 svc_callback *internal_callback,
278 sipe_svc_callback *callback,
279 gpointer callback_data)
281 return(sipe_svc_wsdl_request(sipe_private,
282 session,
283 uri,
284 "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" "
285 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
286 "xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"",
287 soap_action,
288 wsse_security,
289 soap_body,
290 NULL,
291 internal_callback,
292 callback,
293 callback_data));
296 static void sipe_svc_wsdl_response(struct svc_request *data,
297 const gchar *raw,
298 sipe_xml *xml)
300 if (xml) {
301 /* Callback: success */
302 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
303 } else {
304 /* Callback: failed */
305 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
309 gboolean sipe_svc_get_and_publish_cert(struct sipe_core_private *sipe_private,
310 struct sipe_svc_session *session,
311 const gchar *uri,
312 const gchar *wsse_security,
313 const gchar *certreq,
314 sipe_svc_callback *callback,
315 gpointer callback_data)
317 struct sipe_tls_random id;
318 gchar *id_base64;
319 gchar *id_uuid;
320 gchar *uuid = get_uuid(sipe_private);
321 gchar *soap_body;
322 gboolean ret;
324 /* random request ID */
325 sipe_tls_fill_random(&id, 256);
326 id_base64 = g_base64_encode(id.buffer, id.length);
327 sipe_tls_free_random(&id);
328 id_uuid = generateUUIDfromEPID(id_base64);
329 g_free(id_base64);
331 soap_body = g_strdup_printf("<GetAndPublishCert"
332 " xmlns=\"http://schemas.microsoft.com/OCS/AuthWebServices/\""
333 " xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512/\""
334 " xmlns:wstep=\"http://schemas.microsoft.com/windows/pki/2009/01/enrollment\""
335 " DeviceId=\"{%s}\""
336 " Entity=\"%s\""
338 " <wst:RequestSecurityToken>"
339 " <wst:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wst:TokenType>"
340 " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>"
341 " <wsse:BinarySecurityToken"
342 " ValueType=\"http://schemas.microsoft.com/OCS/AuthWebServices.xsd#PKCS10\""
343 " EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#Base64Binary\""
344 " >\r\n%s</wsse:BinarySecurityToken>"
345 " <wstep:RequestID>%s</wstep:RequestID>"
346 " </wst:RequestSecurityToken>"
347 "</GetAndPublishCert>",
348 uuid,
349 sipe_private->username,
350 certreq,
351 id_uuid);
352 g_free(id_uuid);
353 g_free(uuid);
355 ret = new_soap_req(sipe_private,
356 session,
357 uri,
358 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
359 wsse_security,
360 soap_body,
361 sipe_svc_wsdl_response,
362 callback,
363 callback_data);
364 g_free(soap_body);
366 return(ret);
369 gboolean sipe_svc_ab_entry_request(struct sipe_core_private *sipe_private,
370 struct sipe_svc_session *session,
371 const gchar *uri,
372 const gchar *wsse_security,
373 const gchar *search,
374 guint entries,
375 guint max_returns,
376 sipe_svc_callback *callback,
377 gpointer callback_data)
379 gboolean ret;
380 gchar *soap_body = g_strdup_printf("<SearchAbEntry"
381 " xmlns=\"DistributionListExpander\""
382 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
383 " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\""
385 " <AbEntryRequest>"
386 " <ChangeSearch xmlns:q1=\"DistributionListExpander\" soapenc:arrayType=\"q1:AbEntryRequest.ChangeSearchQuery[%d]\">"
387 " %s"
388 " </ChangeSearch>"
389 " <Metadata>"
390 " <FromDialPad>false</FromDialPad>"
391 " <MaxResultNum>%d</MaxResultNum>"
392 " <ReturnList>displayName,msRTCSIP-PrimaryUserAddress,title,telephoneNumber,homePhone,mobile,otherTelephone,mail,company,country,photoRelPath,photoSize,photoHash</ReturnList>"
393 " </Metadata>"
394 " </AbEntryRequest>"
395 "</SearchAbEntry>",
396 entries,
397 search,
398 max_returns);
400 ret = new_soap_req(sipe_private,
401 session,
402 uri,
403 "DistributionListExpander/IAddressBook/SearchAbEntry",
404 wsse_security,
405 soap_body,
406 sipe_svc_wsdl_response,
407 callback,
408 callback_data);
409 g_free(soap_body);
411 return(ret);
414 /* Requests to login.microsoftonline.com & ADFS */
415 static gboolean request_passport(struct sipe_core_private *sipe_private,
416 struct sipe_svc_session *session,
417 const gchar *service_uri,
418 const gchar *auth_uri,
419 const gchar *wsse_security,
420 const gchar *content_type,
421 const gchar *request_extension,
422 sipe_svc_callback *callback,
423 gpointer callback_data)
425 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken>"
426 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
427 " <wsp:AppliesTo>"
428 " <wsa:EndpointReference>"
429 " <wsa:Address>%s</wsa:Address>"
430 " </wsa:EndpointReference>"
431 " </wsp:AppliesTo>"
432 " %s"
433 "</wst:RequestSecurityToken>",
434 service_uri,
435 request_extension ? request_extension : "");
437 gboolean ret = sipe_svc_wsdl_request(sipe_private,
438 session,
439 auth_uri,
440 "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
441 "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"",
442 "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
443 wsse_security,
444 soap_body,
445 content_type,
446 sipe_svc_wsdl_response,
447 callback,
448 callback_data);
449 g_free(soap_body);
451 return(ret);
454 static gboolean request_user_password(struct sipe_core_private *sipe_private,
455 struct sipe_svc_session *session,
456 const gchar *service_uri,
457 const gchar *auth_uri,
458 const gchar *authuser,
459 const gchar *content_type,
460 const gchar *request_extension,
461 sipe_svc_callback *callback,
462 gpointer callback_data)
464 /* Only cleartext passwords seem to be accepted... */
465 gchar *wsse_security = g_strdup_printf("<wsse:UsernameToken>"
466 " <wsse:Username>%s</wsse:Username>"
467 " <wsse:Password>%s</wsse:Password>"
468 "</wsse:UsernameToken>",
469 authuser,
470 sipe_private->password ? sipe_private->password : "");
472 gboolean ret = request_passport(sipe_private,
473 session,
474 service_uri,
475 auth_uri,
476 wsse_security,
477 content_type,
478 request_extension,
479 callback,
480 callback_data);
481 g_free(wsse_security);
483 return(ret);
486 gboolean sipe_svc_webticket_adfs(struct sipe_core_private *sipe_private,
487 struct sipe_svc_session *session,
488 const gchar *adfs_uri,
489 sipe_svc_callback *callback,
490 gpointer callback_data)
492 return(request_user_password(sipe_private,
493 session,
494 "urn:federation:MicrosoftOnline",
495 adfs_uri,
496 sipe_private->username,
497 /* ADFS is special, *sigh* */
498 "application/soap+xml; charset=utf-8",
499 "<wst:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</wst:KeyType>",
500 callback,
501 callback_data));
504 #define LMC_URI "https://login.microsoftonline.com:443/RST2.srf"
506 gboolean sipe_svc_webticket_lmc(struct sipe_core_private *sipe_private,
507 struct sipe_svc_session *session,
508 const gchar *service_uri,
509 sipe_svc_callback *callback,
510 gpointer callback_data)
512 return(request_user_password(sipe_private,
513 session,
514 service_uri,
515 LMC_URI,
516 sipe_private->authuser ? sipe_private->authuser : sipe_private->username,
517 NULL,
518 NULL,
519 callback,
520 callback_data));
523 gboolean sipe_svc_webticket_lmc_federated(struct sipe_core_private *sipe_private,
524 struct sipe_svc_session *session,
525 const gchar *wsse_security,
526 const gchar *service_uri,
527 sipe_svc_callback *callback,
528 gpointer callback_data)
530 return(request_passport(sipe_private,
531 session,
532 service_uri,
533 LMC_URI,
534 wsse_security,
535 NULL,
536 NULL,
537 callback,
538 callback_data));
541 gboolean sipe_svc_webticket(struct sipe_core_private *sipe_private,
542 struct sipe_svc_session *session,
543 const gchar *uri,
544 const gchar *wsse_security,
545 const gchar *service_uri,
546 const struct sipe_tls_random *entropy,
547 sipe_svc_callback *callback,
548 gpointer callback_data)
550 gchar *uuid = get_uuid(sipe_private);
551 gchar *secret = g_base64_encode(entropy->buffer, entropy->length);
552 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken Context=\"%s\">"
553 " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>"
554 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
555 " <wsp:AppliesTo>"
556 " <wsa:EndpointReference>"
557 " <wsa:Address>%s</wsa:Address>"
558 " </wsa:EndpointReference>"
559 " </wsp:AppliesTo>"
560 " <wst:Claims Dialect=\"urn:component:Microsoft.Rtc.WebAuthentication.2010:authclaims\">"
561 " <auth:ClaimType Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri\" Optional=\"false\">"
562 " <auth:Value>sip:%s</auth:Value>"
563 " </auth:ClaimType>"
564 " </wst:Claims>"
565 " <wst:Entropy>"
566 " <wst:BinarySecret>%s</wst:BinarySecret>"
567 " </wst:Entropy>"
568 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
569 "</wst:RequestSecurityToken>",
570 uuid,
571 service_uri,
572 sipe_private->username,
573 secret);
575 gboolean ret = new_soap_req(sipe_private,
576 session,
577 uri,
578 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
579 wsse_security,
580 soap_body,
581 sipe_svc_wsdl_response,
582 callback,
583 callback_data);
584 g_free(soap_body);
585 g_free(secret);
586 g_free(uuid);
588 return(ret);
591 static void sipe_svc_metadata_response(struct svc_request *data,
592 const gchar *raw,
593 sipe_xml *xml)
595 if (xml) {
596 /* Callback: success */
597 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
598 } else {
599 /* Callback: failed */
600 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
604 gboolean sipe_svc_realminfo(struct sipe_core_private *sipe_private,
605 struct sipe_svc_session *session,
606 sipe_svc_callback *callback,
607 gpointer callback_data)
609 gchar *realminfo_uri = g_strdup_printf("https://login.microsoftonline.com/getuserrealm.srf?login=%s&xml=1",
610 sipe_private->username);
611 gboolean ret = sipe_svc_https_request(sipe_private,
612 HTTP_CONN_GET,
613 session,
614 realminfo_uri,
615 "text",
616 NULL,
617 NULL,
618 sipe_svc_metadata_response,
619 callback,
620 callback_data);
621 g_free(realminfo_uri);
622 return(ret);
625 gboolean sipe_svc_metadata(struct sipe_core_private *sipe_private,
626 struct sipe_svc_session *session,
627 const gchar *uri,
628 sipe_svc_callback *callback,
629 gpointer callback_data)
631 gchar *mex_uri = g_strdup_printf("%s/mex", uri);
632 gboolean ret = sipe_svc_https_request(sipe_private,
633 HTTP_CONN_GET,
634 session,
635 mex_uri,
636 "text",
637 NULL,
638 NULL,
639 sipe_svc_metadata_response,
640 callback,
641 callback_data);
642 g_free(mex_uri);
643 return(ret);
647 Local Variables:
648 mode: c
649 c-file-style: "bsd"
650 indent-tabs-mode: t
651 tab-width: 8
652 End: