certificate: switch from Anon to WinNegotiate port
[siplcs.git] / src / core / sipe-svc.c
blobfcc8f25401a49e1aa9cfa8b09e2f2ea78be99fd6
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 "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 HttpConnAuth auth;
62 gchar *uri;
63 gchar *soap_action;
66 struct sipe_svc {
67 GSList *pending_requests;
70 static void sipe_svc_request_free(struct svc_request *data)
72 if (data->conn)
73 http_conn_free(data->conn);
74 if (data->cb)
75 /* Callback: aborted */
76 (*data->cb)(data->sipe_private, NULL, NULL, NULL, data->cb_data);
77 g_free(data->soap_action);
78 g_free(data->uri);
79 g_free(data);
82 void sipe_svc_free(struct sipe_core_private *sipe_private)
84 struct sipe_svc *svc = sipe_private->svc;
85 if (!svc)
86 return;
88 if (svc->pending_requests) {
89 GSList *entry = svc->pending_requests;
90 while (entry) {
91 sipe_svc_request_free(entry->data);
92 entry = entry->next;
94 g_slist_free(svc->pending_requests);
97 g_free(svc);
98 sipe_private->svc = NULL;
101 static void sipe_svc_init(struct sipe_core_private *sipe_private)
103 if (sipe_private->svc)
104 return;
106 sipe_private->svc = g_new0(struct sipe_svc, 1);
109 static void sipe_svc_https_response(int return_code,
110 const gchar *body,
111 SIPE_UNUSED_PARAMETER const gchar *content_type,
112 HttpConn *conn,
113 void *callback_data)
115 struct svc_request *data = callback_data;
116 struct sipe_svc *svc = data->sipe_private->svc;
118 SIPE_DEBUG_INFO("sipe_svc_https_response: code %d", return_code);
119 http_conn_set_close(conn);
120 data->conn = NULL;
122 if ((return_code == 200) && body) {
123 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
124 /* Internal callback: success */
125 (*data->internal_cb)(data, body, xml);
126 sipe_xml_free(xml);
127 } else {
128 /* Internal callback: failed */
129 (*data->internal_cb)(data, NULL, NULL);
132 /* Internal callback has already called this */
133 data->cb = NULL;
135 svc->pending_requests = g_slist_remove(svc->pending_requests,
136 data);
137 sipe_svc_request_free(data);
140 static gboolean sipe_svc_https_request(struct sipe_core_private *sipe_private,
141 const gchar *method,
142 const gchar *uri,
143 const gchar *content_type,
144 const gchar *soap_action,
145 const gchar *body,
146 svc_callback *internal_callback,
147 sipe_svc_callback *callback,
148 gpointer callback_data)
150 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
151 struct svc_request *data = g_new0(struct svc_request, 1);
152 gboolean ret = FALSE;
154 data->sipe_private = sipe_private;
155 data->uri = g_strdup(uri);
157 if (soap_action)
158 data->soap_action = g_strdup_printf("SOAPAction: \"%s\"\r\n",
159 soap_action);
161 /* re-use SIP credentials */
162 data->auth.domain = sip->authdomain;
163 data->auth.user = sip->authuser ? sip->authuser : sipe_private->username;
164 data->auth.password = sip->password;
166 data->conn = http_conn_create(SIPE_CORE_PUBLIC,
167 NULL, /* HttpSession */
168 method,
169 HTTP_CONN_SSL,
170 HTTP_CONN_NO_REDIRECT,
171 uri,
172 body,
173 content_type,
174 data->soap_action,
175 &data->auth,
176 sipe_svc_https_response,
177 data);
179 if (data->conn) {
180 data->internal_cb = internal_callback;
181 data->cb = callback;
182 data->cb_data = callback_data;
183 sipe_svc_init(sipe_private);
184 sipe_private->svc->pending_requests = g_slist_prepend(sipe_private->svc->pending_requests,
185 data);
186 ret = TRUE;
187 } else {
188 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri);
189 sipe_svc_request_free(data);
192 return(ret);
195 static gboolean sipe_svc_wsdl_request(struct sipe_core_private *sipe_private,
196 const gchar *uri,
197 const gchar *additional_ns,
198 const gchar *soap_action,
199 const gchar *wsse_security,
200 const gchar *soap_body,
201 svc_callback *internal_callback,
202 sipe_svc_callback *callback,
203 gpointer callback_data)
205 /* Only generate SOAP header if we have a security token */
206 gchar *soap_header = wsse_security ?
207 g_strdup_printf("<soap:Header>"
208 " <wsa:To>%s</wsa:To>"
209 " <wsa:ReplyTo>"
210 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
211 " </wsa:ReplyTo>"
212 " <wsa:Action>%s</wsa:Action>"
213 " <wsse:Security>%s</wsse:Security>"
214 "</soap:Header>",
215 uri,
216 soap_action,
217 wsse_security) :
218 g_strdup("");
219 gchar *body = g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
220 "<soap:Envelope %s"
221 " xmlns:auth=\"http://schemas.xmlsoap.org/ws/2006/12/authorization\""
222 " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\""
223 " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\""
224 " xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\""
225 " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""
226 " >"
227 "%s"
228 " <soap:Body>%s</soap:Body>"
229 "</soap:Envelope>",
230 additional_ns,
231 soap_header,
232 soap_body);
234 gboolean ret = sipe_svc_https_request(sipe_private,
235 HTTP_CONN_POST,
236 uri,
237 "text/xml",
238 soap_action,
239 body,
240 internal_callback,
241 callback,
242 callback_data);
243 g_free(soap_header);
244 g_free(body);
246 return(ret);
249 static gboolean new_soap_req(struct sipe_core_private *sipe_private,
250 const gchar *uri,
251 const gchar *soap_action,
252 const gchar *wsse_security,
253 const gchar *soap_body,
254 svc_callback *internal_callback,
255 sipe_svc_callback *callback,
256 gpointer callback_data)
258 return(sipe_svc_wsdl_request(sipe_private,
259 uri,
260 "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" "
261 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
262 "xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"",
263 soap_action,
264 wsse_security,
265 soap_body,
266 internal_callback,
267 callback,
268 callback_data));
271 static void sipe_svc_wsdl_response(struct svc_request *data,
272 const gchar *raw,
273 sipe_xml *xml)
275 if (xml) {
276 /* Callback: success */
277 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
278 } else {
279 /* Callback: failed */
280 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
284 gboolean sipe_svc_get_and_publish_cert(struct sipe_core_private *sipe_private,
285 const gchar *uri,
286 const gchar *authuser,
287 const gchar *wsse_security,
288 const gchar *certreq,
289 sipe_svc_callback *callback,
290 gpointer callback_data)
292 struct sipe_tls_random id;
293 gchar *id_base64;
294 gchar *id_uuid;
295 gchar *uuid = get_uuid(sipe_private);
296 gchar *soap_body;
297 gboolean ret;
299 /* random request ID */
300 sipe_tls_fill_random(&id, 256);
301 id_base64 = g_base64_encode(id.buffer, id.length);
302 sipe_tls_free_random(&id);
303 id_uuid = generateUUIDfromEPID(id_base64);
304 g_free(id_base64);
306 soap_body = g_strdup_printf("<GetAndPublishCert"
307 " xmlns=\"http://schemas.microsoft.com/OCS/AuthWebServices/\""
308 " xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512/\""
309 " xmlns:wstep=\"http://schemas.microsoft.com/windows/pki/2009/01/enrollment\""
310 " DeviceId=\"{%s}\""
311 " Entity=\"%s\""
313 " <wst:RequestSecurityToken>"
314 " <wst:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wst:TokenType>"
315 " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>"
316 " <wsse:BinarySecurityToken"
317 " ValueType=\"http://schemas.microsoft.com/OCS/AuthWebServices.xsd#PKCS10\""
318 " EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#Base64Binary\""
319 " >\r\n%s</wsse:BinarySecurityToken>"
320 " <wstep:RequestID>%s</wstep:RequestID>"
321 " </wst:RequestSecurityToken>"
322 "</GetAndPublishCert>",
323 uuid,
324 authuser,
325 certreq,
326 id_uuid);
327 g_free(id_uuid);
328 g_free(uuid);
330 ret = new_soap_req(sipe_private,
331 uri,
332 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
333 wsse_security,
334 soap_body,
335 sipe_svc_wsdl_response,
336 callback,
337 callback_data);
338 g_free(soap_body);
340 return(ret);
344 * This functions encodes what the Microsoft Lync client does for
345 * Office365 accounts. It will most definitely fail for internal Lync
346 * installation that use TLS-DSK instead of NTLM.
348 * But for those anonymous authentication should already have succeeded.
349 * I guess we'll have to see what happens in real life...
351 gboolean sipe_svc_webticket_lmc(struct sipe_core_private *sipe_private,
352 const gchar *authuser,
353 const gchar *service_uri,
354 sipe_svc_callback *callback,
355 gpointer callback_data)
357 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
358 /* login.microsoftonline.com seems only to accept cleartext passwords :/ */
359 gchar *security = g_strdup_printf("<wsse:UsernameToken>"
360 " <wsse:Username>%s</wsse:Username>"
361 " <wsse:Password>%s</wsse:Password>"
362 "</wsse:UsernameToken>",
363 authuser, sip->password);
365 gchar *soap_body = g_strdup_printf("<ps:RequestMultipleSecurityTokens>"
366 " <wst:RequestSecurityToken>"
367 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
368 " <wsp:AppliesTo>"
369 " <wsa:EndpointReference>"
370 " <wsa:Address>%s</wsa:Address>"
371 " </wsa:EndpointReference>"
372 " </wsp:AppliesTo>"
373 " </wst:RequestSecurityToken>"
374 "</ps:RequestMultipleSecurityTokens>",
375 service_uri);
377 gboolean ret = sipe_svc_wsdl_request(sipe_private,
378 "https://login.microsoftonline.com:443/RST2.srf",
379 "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" "
380 "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
381 "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"",
382 "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
383 security,
384 soap_body,
385 sipe_svc_wsdl_response,
386 callback,
387 callback_data);
388 g_free(soap_body);
389 g_free(security);
391 return(ret);
394 gboolean sipe_svc_webticket(struct sipe_core_private *sipe_private,
395 const gchar *uri,
396 const gchar *authuser,
397 const gchar *wsse_security,
398 const gchar *service_uri,
399 const struct sipe_tls_random *entropy,
400 sipe_svc_callback *callback,
401 gpointer callback_data)
403 gchar *uuid = get_uuid(sipe_private);
404 gchar *security = NULL;
405 gchar *secret = g_base64_encode(entropy->buffer, entropy->length);
406 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken Context=\"%s\">"
407 " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>"
408 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
409 " <wsp:AppliesTo>"
410 " <wsa:EndpointReference>"
411 " <wsa:Address>%s</wsa:Address>"
412 " </wsa:EndpointReference>"
413 " </wsp:AppliesTo>"
414 " <wst:Claims Dialect=\"urn:component:Microsoft.Rtc.WebAuthentication.2010:authclaims\">"
415 " <auth:ClaimType Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri\" Optional=\"false\">"
416 " <auth:Value>sip:%s</auth:Value>"
417 " </auth:ClaimType>"
418 " </wst:Claims>"
419 " <wst:Entropy>"
420 " <wst:BinarySecret>%s</wst:BinarySecret>"
421 " </wst:Entropy>"
422 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
423 "</wst:RequestSecurityToken>",
424 uuid,
425 service_uri,
426 authuser,
427 secret);
429 gboolean ret = new_soap_req(sipe_private,
430 uri,
431 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
432 wsse_security,
433 soap_body,
434 sipe_svc_wsdl_response,
435 callback,
436 callback_data);
437 g_free(soap_body);
438 g_free(secret);
439 g_free(security);
440 g_free(uuid);
442 return(ret);
445 static void sipe_svc_metadata_response(struct svc_request *data,
446 const gchar *raw,
447 sipe_xml *xml)
449 if (xml) {
450 /* Callback: success */
451 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
452 } else {
453 /* Callback: failed */
454 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
458 gboolean sipe_svc_metadata(struct sipe_core_private *sipe_private,
459 const gchar *uri,
460 sipe_svc_callback *callback,
461 gpointer callback_data)
463 gchar *mex_uri = g_strdup_printf("%s/mex", uri);
464 gboolean ret = sipe_svc_https_request(sipe_private,
465 HTTP_CONN_GET,
466 mex_uri,
467 "text",
469 NULL,
470 sipe_svc_metadata_response,
471 callback,
472 callback_data);
473 g_free(mex_uri);
474 return(ret);
478 Local Variables:
479 mode: c
480 c-file-style: "bsd"
481 indent-tabs-mode: t
482 tab-width: 8
483 End: