6 * Copyright (C) 2011-12 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
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"
44 #include "sipe-utils.h"
48 /* forward declaration */
50 typedef void (svc_callback
)(struct svc_request
*data
,
55 struct sipe_core_private
*sipe_private
;
56 svc_callback
*internal_cb
;
57 sipe_svc_callback
*cb
;
66 GSList
*pending_requests
;
69 struct sipe_svc_session
{
73 static void sipe_svc_request_free(struct svc_request
*data
)
76 http_conn_free(data
->conn
);
78 /* Callback: aborted */
79 (*data
->cb
)(data
->sipe_private
, NULL
, NULL
, NULL
, data
->cb_data
);
80 g_free(data
->soap_action
);
85 void sipe_svc_free(struct sipe_core_private
*sipe_private
)
87 struct sipe_svc
*svc
= sipe_private
->svc
;
91 if (svc
->pending_requests
) {
92 GSList
*entry
= svc
->pending_requests
;
94 sipe_svc_request_free(entry
->data
);
97 g_slist_free(svc
->pending_requests
);
101 sipe_private
->svc
= NULL
;
104 static void sipe_svc_init(struct sipe_core_private
*sipe_private
)
106 if (sipe_private
->svc
)
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();
119 void sipe_svc_session_close(struct sipe_svc_session
*session
)
122 http_conn_session_free(session
->session
);
127 static void sipe_svc_https_response(int return_code
,
129 SIPE_UNUSED_PARAMETER GSList
*headers
,
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
);
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
);
146 /* Internal callback: failed */
147 (*data
->internal_cb
)(data
, NULL
, NULL
);
150 /* Internal callback has already called this */
153 svc
->pending_requests
= g_slist_remove(svc
->pending_requests
,
155 sipe_svc_request_free(data
);
158 static gboolean
sipe_svc_https_request(struct sipe_core_private
*sipe_private
,
160 struct sipe_svc_session
*session
,
162 const gchar
*content_type
,
163 const gchar
*soap_action
,
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
);
176 data
->soap_action
= g_strdup_printf("SOAPAction: \"%s\"\r\n",
179 /* re-use SIP credentials */
180 data
->auth
.domain
= sipe_private
->authdomain
;
181 data
->auth
.user
= sipe_private
->authuser
? sipe_private
->authuser
: sipe_private
->username
;
182 data
->auth
.password
= sipe_private
->password
;
184 data
->conn
= http_conn_create(SIPE_CORE_PUBLIC
,
188 HTTP_CONN_NO_REDIRECT
,
194 sipe_svc_https_response
,
198 data
->internal_cb
= internal_callback
;
200 data
->cb_data
= callback_data
;
201 sipe_svc_init(sipe_private
);
202 sipe_private
->svc
->pending_requests
= g_slist_prepend(sipe_private
->svc
->pending_requests
,
206 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri
);
207 sipe_svc_request_free(data
);
213 static gboolean
sipe_svc_wsdl_request(struct sipe_core_private
*sipe_private
,
214 struct sipe_svc_session
*session
,
216 const gchar
*additional_ns
,
217 const gchar
*soap_action
,
218 const gchar
*wsse_security
,
219 const gchar
*soap_body
,
220 svc_callback
*internal_callback
,
221 sipe_svc_callback
*callback
,
222 gpointer callback_data
)
224 /* Only generate SOAP header if we have a security token */
225 gchar
*soap_header
= wsse_security
?
226 g_strdup_printf("<soap:Header>"
227 " <wsa:To>%s</wsa:To>"
229 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
231 " <wsa:Action>%s</wsa:Action>"
232 " <wsse:Security>%s</wsse:Security>"
238 gchar
*body
= g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
240 " xmlns:auth=\"http://schemas.xmlsoap.org/ws/2006/12/authorization\""
241 " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\""
242 " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\""
243 " xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\""
244 " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""
247 " <soap:Body>%s</soap:Body>"
253 gboolean ret
= sipe_svc_https_request(sipe_private
,
269 static gboolean
new_soap_req(struct sipe_core_private
*sipe_private
,
270 struct sipe_svc_session
*session
,
272 const gchar
*soap_action
,
273 const gchar
*wsse_security
,
274 const gchar
*soap_body
,
275 svc_callback
*internal_callback
,
276 sipe_svc_callback
*callback
,
277 gpointer callback_data
)
279 return(sipe_svc_wsdl_request(sipe_private
,
282 "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" "
283 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
284 "xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"",
293 static void sipe_svc_wsdl_response(struct svc_request
*data
,
298 /* Callback: success */
299 (*data
->cb
)(data
->sipe_private
, data
->uri
, raw
, xml
, data
->cb_data
);
301 /* Callback: failed */
302 (*data
->cb
)(data
->sipe_private
, data
->uri
, NULL
, NULL
, data
->cb_data
);
306 gboolean
sipe_svc_get_and_publish_cert(struct sipe_core_private
*sipe_private
,
307 struct sipe_svc_session
*session
,
309 const gchar
*wsse_security
,
310 const gchar
*certreq
,
311 sipe_svc_callback
*callback
,
312 gpointer callback_data
)
314 struct sipe_tls_random id
;
317 gchar
*uuid
= get_uuid(sipe_private
);
321 /* random request ID */
322 sipe_tls_fill_random(&id
, 256);
323 id_base64
= g_base64_encode(id
.buffer
, id
.length
);
324 sipe_tls_free_random(&id
);
325 id_uuid
= generateUUIDfromEPID(id_base64
);
328 soap_body
= g_strdup_printf("<GetAndPublishCert"
329 " xmlns=\"http://schemas.microsoft.com/OCS/AuthWebServices/\""
330 " xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512/\""
331 " xmlns:wstep=\"http://schemas.microsoft.com/windows/pki/2009/01/enrollment\""
335 " <wst:RequestSecurityToken>"
336 " <wst:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wst:TokenType>"
337 " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>"
338 " <wsse:BinarySecurityToken"
339 " ValueType=\"http://schemas.microsoft.com/OCS/AuthWebServices.xsd#PKCS10\""
340 " EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#Base64Binary\""
341 " >\r\n%s</wsse:BinarySecurityToken>"
342 " <wstep:RequestID>%s</wstep:RequestID>"
343 " </wst:RequestSecurityToken>"
344 "</GetAndPublishCert>",
346 sipe_private
->username
,
352 ret
= new_soap_req(sipe_private
,
355 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
358 sipe_svc_wsdl_response
,
366 gboolean
sipe_svc_ab_entry_request(struct sipe_core_private
*sipe_private
,
367 struct sipe_svc_session
*session
,
369 const gchar
*wsse_security
,
373 sipe_svc_callback
*callback
,
374 gpointer callback_data
)
377 gchar
*soap_body
= g_strdup_printf("<SearchAbEntry"
378 " xmlns=\"DistributionListExpander\""
379 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
380 " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\""
383 " <ChangeSearch xmlns:q1=\"DistributionListExpander\" soapenc:arrayType=\"q1:AbEntryRequest.ChangeSearchQuery[%d]\">"
387 " <FromDialPad>false</FromDialPad>"
388 " <MaxResultNum>%d</MaxResultNum>"
389 " <ReturnList>displayName,msRTCSIP-PrimaryUserAddress,title,telephoneNumber,homePhone,mobile,otherTelephone,mail,company,country,photoRelPath,photoSize,photoHash</ReturnList>"
397 ret
= new_soap_req(sipe_private
,
400 "DistributionListExpander/IAddressBook/SearchAbEntry",
403 sipe_svc_wsdl_response
,
411 /* Requests to login.microsoftonline.com & ADFS */
412 static gboolean
request_passport(struct sipe_core_private
*sipe_private
,
413 struct sipe_svc_session
*session
,
414 const gchar
*service_uri
,
415 const gchar
*auth_uri
,
416 const gchar
*wsse_security
,
417 sipe_svc_callback
*callback
,
418 gpointer callback_data
)
420 gchar
*soap_body
= g_strdup_printf("<ps:RequestMultipleSecurityTokens>"
421 " <wst:RequestSecurityToken>"
422 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
424 " <wsa:EndpointReference>"
425 " <wsa:Address>%s</wsa:Address>"
426 " </wsa:EndpointReference>"
428 " </wst:RequestSecurityToken>"
429 "</ps:RequestMultipleSecurityTokens>",
432 gboolean ret
= sipe_svc_wsdl_request(sipe_private
,
435 "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" "
436 "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
437 "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"",
438 "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
441 sipe_svc_wsdl_response
,
449 static gboolean
request_user_password(struct sipe_core_private
*sipe_private
,
450 struct sipe_svc_session
*session
,
451 const gchar
*service_uri
,
452 const gchar
*auth_uri
,
453 sipe_svc_callback
*callback
,
454 gpointer callback_data
)
456 /* Only cleartext passwords seem to be accepted... */
457 gchar
*wsse_security
= g_strdup_printf("<wsse:UsernameToken>"
458 " <wsse:Username>%s</wsse:Username>"
459 " <wsse:Password>%s</wsse:Password>"
460 "</wsse:UsernameToken>",
461 sipe_private
->username
,
462 sipe_private
->password
);
464 gboolean ret
= request_passport(sipe_private
,
471 g_free(wsse_security
);
476 gboolean
sipe_svc_webticket_adfs(struct sipe_core_private
*sipe_private
,
477 struct sipe_svc_session
*session
,
478 const gchar
*adfs_uri
,
479 sipe_svc_callback
*callback
,
480 gpointer callback_data
)
482 return(request_user_password(sipe_private
,
484 "urn:federation:MicrosoftOnline",
490 #define LMC_URI "https://login.microsoftonline.com:443/RST2.srf"
492 gboolean
sipe_svc_webticket_lmc(struct sipe_core_private
*sipe_private
,
493 struct sipe_svc_session
*session
,
494 const gchar
*service_uri
,
495 sipe_svc_callback
*callback
,
496 gpointer callback_data
)
498 return(request_user_password(sipe_private
,
506 gboolean
sipe_svc_webticket_lmc_federated(struct sipe_core_private
*sipe_private
,
507 struct sipe_svc_session
*session
,
508 const gchar
*wsse_security
,
509 const gchar
*service_uri
,
510 sipe_svc_callback
*callback
,
511 gpointer callback_data
)
513 return(request_passport(sipe_private
,
522 gboolean
sipe_svc_webticket(struct sipe_core_private
*sipe_private
,
523 struct sipe_svc_session
*session
,
525 const gchar
*wsse_security
,
526 const gchar
*service_uri
,
527 const struct sipe_tls_random
*entropy
,
528 sipe_svc_callback
*callback
,
529 gpointer callback_data
)
531 gchar
*uuid
= get_uuid(sipe_private
);
532 gchar
*secret
= g_base64_encode(entropy
->buffer
, entropy
->length
);
533 gchar
*soap_body
= g_strdup_printf("<wst:RequestSecurityToken Context=\"%s\">"
534 " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>"
535 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
537 " <wsa:EndpointReference>"
538 " <wsa:Address>%s</wsa:Address>"
539 " </wsa:EndpointReference>"
541 " <wst:Claims Dialect=\"urn:component:Microsoft.Rtc.WebAuthentication.2010:authclaims\">"
542 " <auth:ClaimType Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri\" Optional=\"false\">"
543 " <auth:Value>sip:%s</auth:Value>"
547 " <wst:BinarySecret>%s</wst:BinarySecret>"
549 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
550 "</wst:RequestSecurityToken>",
553 sipe_private
->username
,
556 gboolean ret
= new_soap_req(sipe_private
,
559 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
562 sipe_svc_wsdl_response
,
572 static void sipe_svc_metadata_response(struct svc_request
*data
,
577 /* Callback: success */
578 (*data
->cb
)(data
->sipe_private
, data
->uri
, raw
, xml
, data
->cb_data
);
580 /* Callback: failed */
581 (*data
->cb
)(data
->sipe_private
, data
->uri
, NULL
, NULL
, data
->cb_data
);
585 gboolean
sipe_svc_realminfo(struct sipe_core_private
*sipe_private
,
586 struct sipe_svc_session
*session
,
587 sipe_svc_callback
*callback
,
588 gpointer callback_data
)
590 gchar
*realminfo_uri
= g_strdup_printf("https://login.microsoftonline.com/getuserrealm.srf?login=%s&xml=1",
591 sipe_private
->username
);
592 gboolean ret
= sipe_svc_https_request(sipe_private
,
599 sipe_svc_metadata_response
,
602 g_free(realminfo_uri
);
606 gboolean
sipe_svc_metadata(struct sipe_core_private
*sipe_private
,
607 struct sipe_svc_session
*session
,
609 sipe_svc_callback
*callback
,
610 gpointer callback_data
)
612 gchar
*mex_uri
= g_strdup_printf("%s/mex", uri
);
613 gboolean ret
= sipe_svc_https_request(sipe_private
,
620 sipe_svc_metadata_response
,