core cleanup: separate code for sipe_backend_account_status_and_note()
[siplcs.git] / src / core / sipe-svc.c
blob9d35446c6b69cb28989e03973067217b51b682a2
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-tls.h"
45 #include "sipe-utils.h"
46 #include "sipe-xml.h"
47 #include "sipe.h"
48 #include "uuid.h"
50 /* forward declaration */
51 struct svc_request;
52 typedef void (svc_callback)(struct svc_request *data,
53 const gchar *raw,
54 sipe_xml *xml);
56 struct svc_request {
57 struct sipe_core_private *sipe_private;
58 svc_callback *internal_cb;
59 sipe_svc_callback *cb;
60 gpointer *cb_data;
61 HttpConn *conn;
62 gchar *uri;
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->uri);
77 g_free(data);
80 void sipe_svc_free(struct sipe_core_private *sipe_private)
82 struct sipe_svc *svc = sipe_private->svc;
83 if (!svc)
84 return;
86 if (svc->pending_requests) {
87 GSList *entry = svc->pending_requests;
88 while (entry) {
89 sipe_svc_request_free(entry->data);
90 entry = entry->next;
92 g_slist_free(svc->pending_requests);
95 g_free(svc);
96 sipe_private->svc = NULL;
99 static void sipe_svc_init(struct sipe_core_private *sipe_private)
101 if (sipe_private->svc)
102 return;
104 sipe_private->svc = g_new0(struct sipe_svc, 1);
107 static void sipe_svc_https_response(int return_code,
108 const gchar *body,
109 SIPE_UNUSED_PARAMETER const gchar *content_type,
110 HttpConn *conn,
111 void *callback_data)
113 struct svc_request *data = callback_data;
114 struct sipe_svc *svc = data->sipe_private->svc;
116 SIPE_DEBUG_INFO("sipe_svc_https_response: code %d", return_code);
117 http_conn_set_close(conn);
118 data->conn = NULL;
120 if ((return_code == 200) && body) {
121 sipe_xml *xml = sipe_xml_parse(body, strlen(body));
122 /* Internal callback: success */
123 (*data->internal_cb)(data, body, xml);
124 sipe_xml_free(xml);
125 } else {
126 /* Internal callback: failed */
127 (*data->internal_cb)(data, NULL, NULL);
130 /* Internal callback has already called this */
131 data->cb = NULL;
133 svc->pending_requests = g_slist_remove(svc->pending_requests,
134 data);
135 sipe_svc_request_free(data);
138 static gboolean sipe_svc_https_request(struct sipe_core_private *sipe_private,
139 const gchar *method,
140 const gchar *uri,
141 const gchar *content_type,
142 const gchar *body,
143 svc_callback *internal_callback,
144 sipe_svc_callback *callback,
145 gpointer callback_data)
147 struct svc_request *data = g_new0(struct svc_request, 1);
148 gboolean ret = FALSE;
150 data->sipe_private = sipe_private;
151 data->uri = g_strdup(uri);
153 data->conn = http_conn_create(SIPE_CORE_PUBLIC,
154 NULL, /* HttpSession */
155 method,
156 HTTP_CONN_SSL,
157 HTTP_CONN_NO_REDIRECT,
158 uri,
159 body,
160 content_type,
161 NULL, /* HttpConnAuth */
162 sipe_svc_https_response,
163 data);
165 if (data->conn) {
166 data->internal_cb = internal_callback;
167 data->cb = callback;
168 data->cb_data = callback_data;
169 sipe_svc_init(sipe_private);
170 sipe_private->svc->pending_requests = g_slist_prepend(sipe_private->svc->pending_requests,
171 data);
172 ret = TRUE;
173 } else {
174 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri);
175 sipe_svc_request_free(data);
178 return(ret);
181 static gboolean sipe_svc_wsdl_request(struct sipe_core_private *sipe_private,
182 const gchar *uri,
183 const gchar *additional_ns,
184 const gchar *soap_action,
185 const gchar *wsse_security,
186 const gchar *soap_body,
187 svc_callback *internal_callback,
188 sipe_svc_callback *callback,
189 gpointer callback_data)
191 gchar *body = g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
192 "<soap:Envelope %s"
193 " xmlns:auth=\"http://schemas.xmlsoap.org/ws/2006/12/authorization\""
194 " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\""
195 " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\""
196 " xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\""
197 " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""
198 " >"
199 " <soap:Header>"
200 " <wsa:To>%s</wsa:To>"
201 " <wsa:ReplyTo>"
202 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
203 " </wsa:ReplyTo>"
204 " <wsa:Action>%s</wsa:Action>"
205 " <wsse:Security>%s</wsse:Security>"
206 " </soap:Header>"
207 " <soap:Body>%s</soap:Body>"
208 "</soap:Envelope>",
209 additional_ns,
210 uri,
211 soap_action,
212 wsse_security,
213 soap_body);
215 gboolean ret = sipe_svc_https_request(sipe_private,
216 HTTP_CONN_POST,
217 uri,
218 "text/xml",
219 body,
220 internal_callback,
221 callback,
222 callback_data);
223 g_free(body);
225 return(ret);
228 static gboolean new_soap_req(struct sipe_core_private *sipe_private,
229 const gchar *uri,
230 const gchar *soap_action,
231 const gchar *wsse_security,
232 const gchar *soap_body,
233 svc_callback *internal_callback,
234 sipe_svc_callback *callback,
235 gpointer callback_data)
237 return(sipe_svc_wsdl_request(sipe_private,
238 uri,
239 "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" "
240 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
241 "xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"",
242 soap_action,
243 wsse_security,
244 soap_body,
245 internal_callback,
246 callback,
247 callback_data));
250 static void sipe_svc_wsdl_response(struct svc_request *data,
251 const gchar *raw,
252 sipe_xml *xml)
254 if (xml) {
255 /* Callback: success */
256 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
257 } else {
258 /* Callback: failed */
259 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
263 gboolean sipe_svc_get_and_publish_cert(struct sipe_core_private *sipe_private,
264 const gchar *uri,
265 const gchar *authuser,
266 const gchar *wsse_security,
267 const gchar *certreq,
268 sipe_svc_callback *callback,
269 gpointer callback_data)
271 struct sipe_tls_random id;
272 gchar *id_base64;
273 gchar *id_uuid;
274 gchar *uuid = get_uuid(sipe_private);
275 gchar *soap_body;
276 gboolean ret;
278 /* random request ID */
279 sipe_tls_fill_random(&id, 256);
280 id_base64 = g_base64_encode(id.buffer, id.length);
281 sipe_tls_free_random(&id);
282 id_uuid = generateUUIDfromEPID(id_base64);
283 g_free(id_base64);
285 soap_body = g_strdup_printf("<GetAndPublishCert"
286 " xmlns=\"http://schemas.microsoft.com/OCS/AuthWebServices/\""
287 " xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512/\""
288 " xmlns:wstep=\"http://schemas.microsoft.com/windows/pki/2009/01/enrollment\""
289 " DeviceId=\"{%s}\""
290 " Entity=\"%s\""
292 " <wst:RequestSecurityToken>"
293 " <wst:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wst:TokenType>"
294 " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>"
295 " <wsse:BinarySecurityToken"
296 " ValueType=\"http://schemas.microsoft.com/OCS/AuthWebServices.xsd#PKCS10\""
297 " EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#Base64Binary\""
298 " >\r\n%s</wsse:BinarySecurityToken>"
299 " <wstep:RequestID>%s</wstep:RequestID>"
300 " </wst:RequestSecurityToken>"
301 "</GetAndPublishCert>",
302 uuid,
303 authuser,
304 certreq,
305 id_uuid);
306 g_free(id_uuid);
307 g_free(uuid);
309 ret = new_soap_req(sipe_private,
310 uri,
311 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
312 wsse_security,
313 soap_body,
314 sipe_svc_wsdl_response,
315 callback,
316 callback_data);
317 g_free(soap_body);
319 return(ret);
323 * This functions encodes what the Microsoft Lync client does for
324 * Office365 accounts. It will most definitely fail for internal Lync
325 * installation that use TLS-DSK instead of NTLM.
327 * But for those anonymous authentication should already have succeeded.
328 * I guess we'll have to see what happens in real life...
330 gboolean sipe_svc_webticket_lmc(struct sipe_core_private *sipe_private,
331 const gchar *authuser,
332 const gchar *service_uri,
333 sipe_svc_callback *callback,
334 gpointer callback_data)
336 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
337 /* login.microsoftonline.com seems only to accept cleartext passwords :/ */
338 gchar *security = g_strdup_printf("<wsse:UsernameToken>"
339 " <wsse:Username>%s</wsse:Username>"
340 " <wsse:Password>%s</wsse:Password>"
341 "</wsse:UsernameToken>",
342 authuser, sip->password);
344 gchar *soap_body = g_strdup_printf("<ps:RequestMultipleSecurityTokens>"
345 " <wst:RequestSecurityToken>"
346 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
347 " <wsp:AppliesTo>"
348 " <wsa:EndpointReference>"
349 " <wsa:Address>%s</wsa:Address>"
350 " </wsa:EndpointReference>"
351 " </wsp:AppliesTo>"
352 " </wst:RequestSecurityToken>"
353 "</ps:RequestMultipleSecurityTokens>",
354 service_uri);
356 gboolean ret = sipe_svc_wsdl_request(sipe_private,
357 "https://login.microsoftonline.com:443/RST2.srf",
358 "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" "
359 "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
360 "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"",
361 "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
362 security,
363 soap_body,
364 sipe_svc_wsdl_response,
365 callback,
366 callback_data);
367 g_free(soap_body);
368 g_free(security);
370 return(ret);
373 static gchar *sipe_svc_security_username(struct sipe_core_private *sipe_private,
374 const gchar *authuser)
376 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
377 struct sipe_tls_random nonce;
378 GTimeVal now;
379 guchar digest[SIPE_DIGEST_SHA1_LENGTH];
380 guchar *buf, *p;
381 gchar *base64, *nonce_base64, *created;
382 guint len, created_len, password_len;
383 gchar *ret;
385 /* nonce */
386 sipe_tls_fill_random(&nonce, 256);
387 nonce_base64 = g_base64_encode(nonce.buffer, nonce.length);
389 /* created */
390 g_get_current_time(&now);
391 created = g_time_val_to_iso8601(&now);
392 created_len = strlen(created);
394 /* password */
395 password_len = strlen(sip->password);
397 /* nonce + created + password */
398 len = nonce.length + created_len + password_len;
399 p = buf = g_malloc(len);
400 memcpy(p, nonce.buffer, nonce.length);
401 p += nonce.length;
402 memcpy(p, created, created_len);
403 p += created_len;
404 memcpy(p, sip->password, password_len);
406 /* Base64( SHA-1( nonce + created + password ) ) */
407 sipe_digest_sha1(buf, len, digest);
408 base64 = g_base64_encode(digest, SIPE_DIGEST_SHA1_LENGTH);
410 ret = g_strdup_printf("<wsse:UsernameToken>"
411 " <wsse:Username>%s</wsse:Username>"
412 " <wsse:Password Type=\"...#PasswordDigest\">%s</wsse:Password>"
413 " <wsse:Nonce>%s</wsse:Nonce>"
414 " <wsu:Created>%s</wsu:Created>"
415 "</wsse:UsernameToken>",
416 authuser, base64, nonce_base64, created);
418 g_free(base64);
419 g_free(buf);
420 g_free(created);
421 g_free(nonce_base64);
422 sipe_tls_free_random(&nonce);
424 return(ret);
427 gboolean sipe_svc_webticket(struct sipe_core_private *sipe_private,
428 const gchar *uri,
429 const gchar *authuser,
430 const gchar *wsse_security,
431 const gchar *service_uri,
432 const struct sipe_tls_random *entropy,
433 sipe_svc_callback *callback,
434 gpointer callback_data)
436 gchar *uuid = get_uuid(sipe_private);
437 gchar *security = NULL;
438 gchar *secret = g_base64_encode(entropy->buffer, entropy->length);
439 gchar *soap_body = g_strdup_printf("<wst:RequestSecurityToken Context=\"%s\">"
440 " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>"
441 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
442 " <wsp:AppliesTo>"
443 " <wsa:EndpointReference>"
444 " <wsa:Address>%s</wsa:Address>"
445 " </wsa:EndpointReference>"
446 " </wsp:AppliesTo>"
447 " <wst:Claims Dialect=\"urn:component:Microsoft.Rtc.WebAuthentication.2010:authclaims\">"
448 " <auth:ClaimType Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri\" Optional=\"false\">"
449 " <auth:Value>sip:%s</auth:Value>"
450 " </auth:ClaimType>"
451 " </wst:Claims>"
452 " <wst:Entropy>"
453 " <wst:BinarySecret>%s</wst:BinarySecret>"
454 " </wst:Entropy>"
455 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
456 "</wst:RequestSecurityToken>",
457 uuid,
458 service_uri,
459 authuser,
460 secret);
462 gboolean ret = new_soap_req(sipe_private,
463 uri,
464 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
465 wsse_security ?
466 wsse_security :
467 (security = sipe_svc_security_username(sipe_private, authuser)),
468 soap_body,
469 sipe_svc_wsdl_response,
470 callback,
471 callback_data);
472 g_free(soap_body);
473 g_free(secret);
474 g_free(security);
475 g_free(uuid);
477 return(ret);
480 static void sipe_svc_metadata_response(struct svc_request *data,
481 const gchar *raw,
482 sipe_xml *xml)
484 if (xml) {
485 /* Callback: success */
486 (*data->cb)(data->sipe_private, data->uri, raw, xml, data->cb_data);
487 } else {
488 /* Callback: failed */
489 (*data->cb)(data->sipe_private, data->uri, NULL, NULL, data->cb_data);
493 gboolean sipe_svc_metadata(struct sipe_core_private *sipe_private,
494 const gchar *uri,
495 sipe_svc_callback *callback,
496 gpointer callback_data)
498 gchar *mex_uri = g_strdup_printf("%s/mex", uri);
499 gboolean ret = sipe_svc_https_request(sipe_private,
500 HTTP_CONN_GET,
501 mex_uri,
502 "text",
504 sipe_svc_metadata_response,
505 callback,
506 callback_data);
507 g_free(mex_uri);
508 return(ret);
512 Local Variables:
513 mode: c
514 c-file-style: "bsd"
515 indent-tabs-mode: t
516 tab-width: 8
517 End: