ms-dlx: send SearchAbRequest to service
[siplcs.git] / src / core / sipe-svc.c
blob936b3baa033a046c106f3d14597d8da1bde76e55
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-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 static void sipe_svc_request_free(struct svc_request *data)
71 if (data->conn)
72 http_conn_free(data->conn);
73 if (data->cb)
74 /* Callback: aborted */
75 (*data->cb)(data->sipe_private, NULL, NULL, NULL, data->cb_data);
76 g_free(data->soap_action);
77 g_free(data->uri);
78 g_free(data);
81 void sipe_svc_free(struct sipe_core_private *sipe_private)
83 struct sipe_svc *svc = sipe_private->svc;
84 if (!svc)
85 return;
87 if (svc->pending_requests) {
88 GSList *entry = svc->pending_requests;
89 while (entry) {
90 sipe_svc_request_free(entry->data);
91 entry = entry->next;
93 g_slist_free(svc->pending_requests);
96 g_free(svc);
97 sipe_private->svc = NULL;
100 static void sipe_svc_init(struct sipe_core_private *sipe_private)
102 if (sipe_private->svc)
103 return;
105 sipe_private->svc = g_new0(struct sipe_svc, 1);
108 static void sipe_svc_https_response(int return_code,
109 const gchar *body,
110 SIPE_UNUSED_PARAMETER const gchar *content_type,
111 HttpConn *conn,
112 void *callback_data)
114 struct svc_request *data = callback_data;
115 struct sipe_svc *svc = data->sipe_private->svc;
117 SIPE_DEBUG_INFO("sipe_svc_https_response: code %d", return_code);
118 http_conn_set_close(conn);
119 data->conn = NULL;
121 if ((return_code == 200) && body) {
122 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
123 /* Internal callback: success */
124 (*data->internal_cb)(data, body, xml);
125 sipe_xml_free(xml);
126 } else {
127 /* Internal callback: failed */
128 (*data->internal_cb)(data, NULL, NULL);
131 /* Internal callback has already called this */
132 data->cb = NULL;
134 svc->pending_requests = g_slist_remove(svc->pending_requests,
135 data);
136 sipe_svc_request_free(data);
139 static gboolean sipe_svc_https_request(struct sipe_core_private *sipe_private,
140 const gchar *method,
141 const gchar *uri,
142 const gchar *content_type,
143 const gchar *soap_action,
144 const gchar *body,
145 svc_callback *internal_callback,
146 sipe_svc_callback *callback,
147 gpointer callback_data)
149 struct svc_request *data = g_new0(struct svc_request, 1);
150 gboolean ret = FALSE;
152 data->sipe_private = sipe_private;
153 data->uri = g_strdup(uri);
155 if (soap_action)
156 data->soap_action = g_strdup_printf("SOAPAction: \"%s\"\r\n",
157 soap_action);
159 /* re-use SIP credentials */
160 data->auth.domain = sipe_private->authdomain;
161 data->auth.user = sipe_private->authuser ? sipe_private->authuser : sipe_private->username;
162 data->auth.password = sipe_private->password;
164 data->conn = http_conn_create(SIPE_CORE_PUBLIC,
165 NULL, /* HttpSession */
166 method,
167 HTTP_CONN_SSL,
168 HTTP_CONN_NO_REDIRECT,
169 uri,
170 body,
171 content_type,
172 data->soap_action,
173 &data->auth,
174 sipe_svc_https_response,
175 data);
177 if (data->conn) {
178 data->internal_cb = internal_callback;
179 data->cb = callback;
180 data->cb_data = callback_data;
181 sipe_svc_init(sipe_private);
182 sipe_private->svc->pending_requests = g_slist_prepend(sipe_private->svc->pending_requests,
183 data);
184 ret = TRUE;
185 } else {
186 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri);
187 sipe_svc_request_free(data);
190 return(ret);
193 static gboolean sipe_svc_wsdl_request(struct sipe_core_private *sipe_private,
194 const gchar *uri,
195 const gchar *additional_ns,
196 const gchar *soap_action,
197 const gchar *wsse_security,
198 const gchar *soap_body,
199 svc_callback *internal_callback,
200 sipe_svc_callback *callback,
201 gpointer callback_data)
203 /* Only generate SOAP header if we have a security token */
204 gchar *soap_header = wsse_security ?
205 g_strdup_printf("<soap:Header>"
206 " <wsa:To>%s</wsa:To>"
207 " <wsa:ReplyTo>"
208 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
209 " </wsa:ReplyTo>"
210 " <wsa:Action>%s</wsa:Action>"
211 " <wsse:Security>%s</wsse:Security>"
212 "</soap:Header>",
213 uri,
214 soap_action,
215 wsse_security) :
216 g_strdup("");
217 gchar *body = g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
218 "<soap:Envelope %s"
219 " xmlns:auth=\"http://schemas.xmlsoap.org/ws/2006/12/authorization\""
220 " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\""
221 " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\""
222 " xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\""
223 " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""
224 " >"
225 "%s"
226 " <soap:Body>%s</soap:Body>"
227 "</soap:Envelope>",
228 additional_ns,
229 soap_header,
230 soap_body);
232 gboolean ret = sipe_svc_https_request(sipe_private,
233 HTTP_CONN_POST,
234 uri,
235 "text/xml",
236 soap_action,
237 body,
238 internal_callback,
239 callback,
240 callback_data);
241 g_free(soap_header);
242 g_free(body);
244 return(ret);
247 static gboolean new_soap_req(struct sipe_core_private *sipe_private,
248 const gchar *uri,
249 const gchar *soap_action,
250 const gchar *wsse_security,
251 const gchar *soap_body,
252 svc_callback *internal_callback,
253 sipe_svc_callback *callback,
254 gpointer callback_data)
256 return(sipe_svc_wsdl_request(sipe_private,
257 uri,
258 "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" "
259 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
260 "xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"",
261 soap_action,
262 wsse_security,
263 soap_body,
264 internal_callback,
265 callback,
266 callback_data));
269 static void sipe_svc_wsdl_response(struct svc_request *data,
270 const gchar *raw,
271 sipe_xml *xml)
273 if (xml) {
274 /* Callback: success */
275 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
276 } else {
277 /* Callback: failed */
278 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
282 gboolean sipe_svc_get_and_publish_cert(struct sipe_core_private *sipe_private,
283 const gchar *uri,
284 const gchar *wsse_security,
285 const gchar *certreq,
286 sipe_svc_callback *callback,
287 gpointer callback_data)
289 struct sipe_tls_random id;
290 gchar *id_base64;
291 gchar *id_uuid;
292 gchar *uuid = get_uuid(sipe_private);
293 gchar *soap_body;
294 gboolean ret;
296 /* random request ID */
297 sipe_tls_fill_random(&id, 256);
298 id_base64 = g_base64_encode(id.buffer, id.length);
299 sipe_tls_free_random(&id);
300 id_uuid = generateUUIDfromEPID(id_base64);
301 g_free(id_base64);
303 soap_body = g_strdup_printf("<GetAndPublishCert"
304 " xmlns=\"http://schemas.microsoft.com/OCS/AuthWebServices/\""
305 " xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512/\""
306 " xmlns:wstep=\"http://schemas.microsoft.com/windows/pki/2009/01/enrollment\""
307 " DeviceId=\"{%s}\""
308 " Entity=\"%s\""
310 " <wst:RequestSecurityToken>"
311 " <wst:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wst:TokenType>"
312 " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>"
313 " <wsse:BinarySecurityToken"
314 " ValueType=\"http://schemas.microsoft.com/OCS/AuthWebServices.xsd#PKCS10\""
315 " EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#Base64Binary\""
316 " >\r\n%s</wsse:BinarySecurityToken>"
317 " <wstep:RequestID>%s</wstep:RequestID>"
318 " </wst:RequestSecurityToken>"
319 "</GetAndPublishCert>",
320 uuid,
321 sipe_private->username,
322 certreq,
323 id_uuid);
324 g_free(id_uuid);
325 g_free(uuid);
327 ret = new_soap_req(sipe_private,
328 uri,
329 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
330 wsse_security,
331 soap_body,
332 sipe_svc_wsdl_response,
333 callback,
334 callback_data);
335 g_free(soap_body);
337 return(ret);
340 gboolean sipe_svc_ab_entry_request(struct sipe_core_private *sipe_private,
341 const gchar *uri,
342 const gchar *wsse_security,
343 const gchar *search,
344 sipe_svc_callback *callback,
345 gpointer callback_data)
347 gboolean ret;
348 gchar *soap_body = g_strdup_printf("<SearchAbEntry"
349 " xmlns=\"DistributionListExpander\""
350 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
351 " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\""
353 " <AbEntryRequest>"
354 " <ChangeSearch xmlns:q1=\"DistributionListExpander\" soapenc:arrayType=\"q1:AbEntryRequest.ChangeSearchQuery[1]\">"
355 " <AbEntryRequest.ChangeSearchQuery>"
356 " %s"
357 " </AbEntryRequest.ChangeSearchQuery>"
358 " </ChangeSearch>"
359 " <Metadata>"
360 " <FromDialPad>false</FromDialPad>"
361 " <MaxResultNum>1</MaxResultNum>"
362 " <ReturnList>displayName,msRTCSIP-PrimaryUserAddress,telephoneNumber,homePhone,mobile,otherTelephone,custom1Phone,mail,company,country</ReturnList>"
363 " </Metadata>"
364 " </AbEntryRequest>"
365 "</SearchAbEntry>",
366 search);
368 ret = new_soap_req(sipe_private,
369 uri,
370 "DistributionListExpander/IAddressBook/SearchAbEntry",
371 wsse_security,
372 soap_body,
373 sipe_svc_wsdl_response,
374 callback,
375 callback_data);
376 g_free(soap_body);
378 return(ret);
382 * This functions encodes what the Microsoft Lync client does for
383 * Office365 accounts. It will most definitely fail for internal Lync
384 * installation that use TLS-DSK instead of NTLM.
386 * But for those anonymous authentication should already have succeeded.
387 * I guess we'll have to see what happens in real life...
389 gboolean sipe_svc_webticket_lmc(struct sipe_core_private *sipe_private,
390 const gchar *service_uri,
391 sipe_svc_callback *callback,
392 gpointer callback_data)
394 /* login.microsoftonline.com seems only to accept cleartext passwords :/ */
395 gchar *security = g_strdup_printf("<wsse:UsernameToken>"
396 " <wsse:Username>%s</wsse:Username>"
397 " <wsse:Password>%s</wsse:Password>"
398 "</wsse:UsernameToken>",
399 sipe_private->username,
400 sipe_private->password);
402 gchar *soap_body = g_strdup_printf("<ps:RequestMultipleSecurityTokens>"
403 " <wst:RequestSecurityToken>"
404 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
405 " <wsp:AppliesTo>"
406 " <wsa:EndpointReference>"
407 " <wsa:Address>%s</wsa:Address>"
408 " </wsa:EndpointReference>"
409 " </wsp:AppliesTo>"
410 " </wst:RequestSecurityToken>"
411 "</ps:RequestMultipleSecurityTokens>",
412 service_uri);
414 gboolean ret = sipe_svc_wsdl_request(sipe_private,
415 "https://login.microsoftonline.com:443/RST2.srf",
416 "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" "
417 "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
418 "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"",
419 "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
420 security,
421 soap_body,
422 sipe_svc_wsdl_response,
423 callback,
424 callback_data);
425 g_free(soap_body);
426 g_free(security);
428 return(ret);
431 gboolean sipe_svc_webticket(struct sipe_core_private *sipe_private,
432 const gchar *uri,
433 const gchar *wsse_security,
434 const gchar *service_uri,
435 const struct sipe_tls_random *entropy,
436 sipe_svc_callback *callback,
437 gpointer callback_data)
439 gchar *uuid = get_uuid(sipe_private);
440 gchar *security = NULL;
441 gchar *secret = g_base64_encode(entropy->buffer, entropy->length);
442 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken Context=\"%s\">"
443 " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>"
444 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
445 " <wsp:AppliesTo>"
446 " <wsa:EndpointReference>"
447 " <wsa:Address>%s</wsa:Address>"
448 " </wsa:EndpointReference>"
449 " </wsp:AppliesTo>"
450 " <wst:Claims Dialect=\"urn:component:Microsoft.Rtc.WebAuthentication.2010:authclaims\">"
451 " <auth:ClaimType Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri\" Optional=\"false\">"
452 " <auth:Value>sip:%s</auth:Value>"
453 " </auth:ClaimType>"
454 " </wst:Claims>"
455 " <wst:Entropy>"
456 " <wst:BinarySecret>%s</wst:BinarySecret>"
457 " </wst:Entropy>"
458 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
459 "</wst:RequestSecurityToken>",
460 uuid,
461 service_uri,
462 sipe_private->username,
463 secret);
465 gboolean ret = new_soap_req(sipe_private,
466 uri,
467 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
468 wsse_security,
469 soap_body,
470 sipe_svc_wsdl_response,
471 callback,
472 callback_data);
473 g_free(soap_body);
474 g_free(secret);
475 g_free(security);
476 g_free(uuid);
478 return(ret);
481 static void sipe_svc_metadata_response(struct svc_request *data,
482 const gchar *raw,
483 sipe_xml *xml)
485 if (xml) {
486 /* Callback: success */
487 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
488 } else {
489 /* Callback: failed */
490 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
494 gboolean sipe_svc_metadata(struct sipe_core_private *sipe_private,
495 const gchar *uri,
496 sipe_svc_callback *callback,
497 gpointer callback_data)
499 gchar *mex_uri = g_strdup_printf("%s/mex", uri);
500 gboolean ret = sipe_svc_https_request(sipe_private,
501 HTTP_CONN_GET,
502 mex_uri,
503 "text",
504 NULL,
505 NULL,
506 sipe_svc_metadata_response,
507 callback,
508 callback_data);
509 g_free(mex_uri);
510 return(ret);
514 Local Variables:
515 mode: c
516 c-file-style: "bsd"
517 indent-tabs-mode: t
518 tab-width: 8
519 End: