filetransfer: use 'deallocate' callback in sipe_file_transfer
[siplcs.git] / src / core / sipe-webticket.c
blob26a54c2f66c21418bff26f2ce1d85c59b3952282
1 /**
2 * @file sipe-webticket.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-OCAUTHWS]: http://msdn.microsoft.com/en-us/library/ff595592.aspx
27 * - MS Tech-Ed Europe 2010 "UNC310: Microsoft Lync 2010 Technology Explained"
28 * http://ecn.channel9.msdn.com/o9/te/Europe/2010/pptx/unc310.pptx
31 #include <string.h>
32 #include <time.h>
34 #include <glib.h>
36 #include "sipe-common.h"
37 #include "sipe-backend.h"
38 #include "sipe-core.h"
39 #include "sipe-core-private.h"
40 #include "sipe-digest.h"
41 #include "sipe-svc.h"
42 #include "sipe-tls.h"
43 #include "sipe-webticket.h"
44 #include "sipe-utils.h"
45 #include "sipe-xml.h"
47 struct webticket_queued_data {
48 sipe_webticket_callback *callback;
49 gpointer callback_data;
52 struct webticket_callback_data {
53 gchar *service_uri;
54 const gchar *service_port;
55 gchar *service_auth_uri;
57 gchar *webticket_negotiate_uri;
58 gchar *webticket_fedbearer_uri;
60 gboolean tried_fedbearer;
61 gboolean requires_signing;
62 enum {
63 TOKEN_STATE_NONE = 0,
64 TOKEN_STATE_SERVICE,
65 TOKEN_STATE_FEDERATION,
66 TOKEN_STATE_FED_BEARER,
67 } token_state;
69 struct sipe_tls_random entropy;
71 sipe_webticket_callback *callback;
72 gpointer callback_data;
74 struct sipe_svc_session *session;
76 GSList *queued;
79 struct webticket_token {
80 gchar *auth_uri;
81 gchar *token;
82 time_t expires;
85 struct sipe_webticket {
86 GHashTable *cache;
87 GHashTable *pending;
89 gchar *webticket_adfs_uri;
90 gchar *adfs_token;
91 time_t adfs_token_expires;
93 gboolean retrieved_realminfo;
94 gboolean shutting_down;
97 void sipe_webticket_free(struct sipe_core_private *sipe_private)
99 struct sipe_webticket *webticket = sipe_private->webticket;
100 if (!webticket)
101 return;
103 /* Web Ticket stack is shutting down: reject all new requests */
104 webticket->shutting_down = TRUE;
106 g_free(webticket->webticket_adfs_uri);
107 g_free(webticket->adfs_token);
108 if (webticket->pending)
109 g_hash_table_destroy(webticket->pending);
110 if (webticket->cache)
111 g_hash_table_destroy(webticket->cache);
112 g_free(webticket);
113 sipe_private->webticket = NULL;
116 static void free_token(gpointer data)
118 struct webticket_token *wt = data;
119 g_free(wt->auth_uri);
120 g_free(wt->token);
121 g_free(wt);
124 static void sipe_webticket_init(struct sipe_core_private *sipe_private)
126 struct sipe_webticket *webticket;
128 if (sipe_private->webticket)
129 return;
131 sipe_private->webticket = webticket = g_new0(struct sipe_webticket, 1);
133 webticket->cache = g_hash_table_new_full(g_str_hash,
134 g_str_equal,
135 g_free,
136 free_token);
137 webticket->pending = g_hash_table_new(g_str_hash,
138 g_str_equal);
141 /* takes ownership of "token" */
142 static void cache_token(struct sipe_core_private *sipe_private,
143 const gchar *service_uri,
144 const gchar *auth_uri,
145 gchar *token,
146 time_t expires)
148 struct webticket_token *wt = g_new0(struct webticket_token, 1);
149 wt->auth_uri = g_strdup(auth_uri);
150 wt->token = token;
151 wt->expires = expires;
152 g_hash_table_insert(sipe_private->webticket->cache,
153 g_strdup(service_uri),
154 wt);
157 static const struct webticket_token *cache_hit(struct sipe_core_private *sipe_private,
158 const gchar *service_uri)
160 const struct webticket_token *wt;
162 /* make sure a cached Web Ticket is still valid for 60 seconds */
163 wt = g_hash_table_lookup(sipe_private->webticket->cache,
164 service_uri);
165 if (wt && (wt->expires < time(NULL) + 60)) {
166 SIPE_DEBUG_INFO("cache_hit: cached token for URI %s has expired",
167 service_uri);
168 wt = NULL;
171 return(wt);
174 /* frees just the main request data, when this is called "queued" is cleared */
175 static void callback_data_free(struct webticket_callback_data *wcd)
177 if (wcd) {
178 sipe_tls_free_random(&wcd->entropy);
179 g_free(wcd->webticket_negotiate_uri);
180 g_free(wcd->webticket_fedbearer_uri);
181 g_free(wcd->service_auth_uri);
182 g_free(wcd->service_uri);
183 g_free(wcd);
187 static void queue_request(struct webticket_callback_data *wcd,
188 sipe_webticket_callback *callback,
189 gpointer callback_data)
191 struct webticket_queued_data *wqd = g_new0(struct webticket_queued_data, 1);
193 wqd->callback = callback;
194 wqd->callback_data = callback_data;
196 wcd->queued = g_slist_prepend(wcd->queued, wqd);
199 static void callback_execute(struct sipe_core_private *sipe_private,
200 struct webticket_callback_data *wcd,
201 const gchar *auth_uri,
202 const gchar *wsse_security,
203 const gchar *failure_msg)
205 GSList *entry = wcd->queued;
207 /* complete main request */
208 wcd->callback(sipe_private,
209 wcd->service_uri,
210 auth_uri,
211 wsse_security,
212 failure_msg,
213 wcd->callback_data);
215 /* complete queued requests */
216 while (entry) {
217 struct webticket_queued_data *wqd = entry->data;
219 SIPE_DEBUG_INFO("callback_execute: completing queue request URI %s (Auth URI %s)",
220 wcd->service_uri, auth_uri);
221 wqd->callback(sipe_private,
222 wcd->service_uri,
223 auth_uri,
224 wsse_security,
225 failure_msg,
226 wqd->callback_data);
228 g_free(wqd);
229 entry = entry->next;
231 g_slist_free(wcd->queued);
233 /* drop request from pending hash */
234 g_hash_table_remove(sipe_private->webticket->pending,
235 wcd->service_uri);
238 static gchar *extract_raw_xml_attribute(const gchar *xml,
239 const gchar *name)
241 gchar *attr_start = g_strdup_printf("%s=\"", name);
242 gchar *data = NULL;
243 const gchar *start = strstr(xml, attr_start);
245 if (start) {
246 const gchar *value = start + strlen(attr_start);
247 const gchar *end = strchr(value, '"');
248 if (end) {
249 data = g_strndup(value, end - value);
253 g_free(attr_start);
254 return(data);
257 static gchar *generate_timestamp(const gchar *raw,
258 const gchar *lifetime_tag)
260 gchar *lifetime = sipe_xml_extract_raw(raw, lifetime_tag, FALSE);
261 gchar *timestamp = NULL;
262 if (lifetime)
263 timestamp = g_strdup_printf("<wsu:Timestamp xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" wsu:Id=\"timestamp\">%s</wsu:Timestamp>",
264 lifetime);
265 g_free(lifetime);
266 return(timestamp);
269 static gchar *generate_fedbearer_wsse(const gchar *raw)
271 gchar *timestamp = generate_timestamp(raw, "wst:Lifetime");
272 gchar *keydata = sipe_xml_extract_raw(raw, "EncryptedData", TRUE);
273 gchar *wsse_security = NULL;
275 if (timestamp && keydata) {
276 SIPE_DEBUG_INFO_NOFORMAT("generate_fedbearer_wsse: found timestamp & keydata");
277 wsse_security = g_strconcat(timestamp, keydata, NULL);
280 g_free(keydata);
281 g_free(timestamp);
282 return(wsse_security);
285 static void generate_federation_wsse(struct sipe_webticket *webticket,
286 const gchar *raw)
288 gchar *timestamp = generate_timestamp(raw, "t:Lifetime");
289 gchar *keydata = sipe_xml_extract_raw(raw, "saml:Assertion", TRUE);
291 /* try alternative names */
292 if (!timestamp)
293 timestamp = generate_timestamp(raw, "wst:Lifetime");
294 if (!keydata)
295 keydata = sipe_xml_extract_raw(raw, "saml1:Assertion", TRUE);
297 /* clear old ADFS token */
298 g_free(webticket->adfs_token);
299 webticket->adfs_token = NULL;
301 if (timestamp && keydata) {
302 gchar *expires_string = sipe_xml_extract_raw(timestamp,
303 "wsu:Expires",
304 FALSE);
306 if (expires_string) {
308 SIPE_DEBUG_INFO("generate_federation_wsse: found timestamp & keydata, expires %s",
309 expires_string);
311 /* cache ADFS token */
312 webticket->adfs_token = g_strconcat(timestamp,
313 keydata,
314 NULL);
315 webticket->adfs_token_expires = sipe_utils_str_to_time(expires_string);
316 g_free(expires_string);
320 g_free(keydata);
321 g_free(timestamp);
324 static gchar *generate_sha1_proof_wsse(const gchar *raw,
325 struct sipe_tls_random *entropy,
326 time_t *expires)
328 gchar *timestamp = generate_timestamp(raw, "Lifetime");
329 gchar *keydata = sipe_xml_extract_raw(raw, "saml:Assertion", TRUE);
330 gchar *wsse_security = NULL;
332 if (timestamp && keydata) {
333 gchar *expires_string = sipe_xml_extract_raw(timestamp,
334 "Expires",
335 FALSE);
337 if (entropy) {
338 gchar *assertionID = extract_raw_xml_attribute(keydata,
339 "AssertionID");
342 * WS-Trust 1.3
344 * http://docs.oasis-open.org/ws-sx/ws-trust/200512/CK/PSHA1:
346 * "The key is computed using P_SHA1() from the TLS sepcification to generate
347 * a bit stream using entropy from both sides. The exact form is:
349 * key = P_SHA1(Entropy_REQ, Entropy_RES)"
351 gchar *entropy_res_base64 = sipe_xml_extract_raw(raw, "BinarySecret", FALSE);
352 gsize entropy_res_length;
353 guchar *entropy_response = g_base64_decode(entropy_res_base64,
354 &entropy_res_length);
355 guchar *key = sipe_tls_p_sha1(entropy->buffer,
356 entropy->length,
357 entropy_response,
358 entropy_res_length,
359 entropy->length);
360 g_free(entropy_response);
361 g_free(entropy_res_base64);
363 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata");
365 if (assertionID && key) {
366 /* same as SIPE_DIGEST_HMAC_SHA1_LENGTH */
367 guchar digest[SIPE_DIGEST_SHA1_LENGTH];
368 gchar *base64;
369 gchar *signed_info;
370 gchar *canon;
372 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found assertionID and successfully computed the key");
374 /* Digest over reference element (#timestamp -> wsu:Timestamp) */
375 sipe_digest_sha1((guchar *) timestamp,
376 strlen(timestamp),
377 digest);
378 base64 = g_base64_encode(digest,
379 SIPE_DIGEST_SHA1_LENGTH);
381 /* XML-Sig: SignedInfo for reference element */
382 signed_info = g_strdup_printf("<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
383 "<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
384 "<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\"/>"
385 "<Reference URI=\"#timestamp\">"
386 "<Transforms>"
387 "<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
388 "</Transforms>"
389 "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>"
390 "<DigestValue>%s</DigestValue>"
391 "</Reference>"
392 "</SignedInfo>",
393 base64);
394 g_free(base64);
396 /* XML-Sig: SignedInfo in canonical form */
397 canon = sipe_xml_exc_c14n(signed_info);
398 g_free(signed_info);
400 if (canon) {
401 gchar *signature;
403 /* calculate signature */
404 sipe_digest_hmac_sha1(key, entropy->length,
405 (guchar *)canon,
406 strlen(canon),
407 digest);
408 base64 = g_base64_encode(digest,
409 SIPE_DIGEST_HMAC_SHA1_LENGTH);
411 /* XML-Sig: Signature from SignedInfo + Key */
412 signature = g_strdup_printf("<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
413 " %s"
414 " <SignatureValue>%s</SignatureValue>"
415 " <KeyInfo>"
416 " <wsse:SecurityTokenReference wsse:TokenType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1\">"
417 " <wsse:KeyIdentifier ValueType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID\">%s</wsse:KeyIdentifier>"
418 " </wsse:SecurityTokenReference>"
419 " </KeyInfo>"
420 "</Signature>",
421 canon,
422 base64,
423 assertionID);
424 g_free(base64);
425 g_free(canon);
427 wsse_security = g_strconcat(timestamp,
428 keydata,
429 signature,
430 NULL);
431 g_free(signature);
436 g_free(key);
437 g_free(assertionID);
438 } else {
439 /* token doesn't require signature */
440 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata, no signing required");
441 wsse_security = g_strconcat(timestamp,
442 keydata,
443 NULL);
446 *expires = 0;
447 if (expires_string) {
448 *expires = sipe_utils_str_to_time(expires_string);
449 g_free(expires_string);
453 g_free(keydata);
454 g_free(timestamp);
455 return(wsse_security);
458 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
459 struct webticket_callback_data *wcd);
460 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
461 struct webticket_callback_data *wcd);
462 static void webticket_token(struct sipe_core_private *sipe_private,
463 const gchar *uri,
464 const gchar *raw,
465 sipe_xml *soap_body,
466 gpointer callback_data)
468 struct webticket_callback_data *wcd = callback_data;
469 gboolean failed = TRUE;
471 if (soap_body) {
472 switch (wcd->token_state) {
473 case TOKEN_STATE_NONE:
474 SIPE_DEBUG_INFO_NOFORMAT("webticket_token: ILLEGAL STATE - should not happen...");
475 break;
477 case TOKEN_STATE_SERVICE: {
478 /* WebTicket for Web Service */
479 time_t expires;
480 gchar *wsse_security = generate_sha1_proof_wsse(raw,
481 wcd->requires_signing ? &wcd->entropy : NULL,
482 &expires);
484 if (wsse_security) {
485 /* cache takes ownership of wsse_security */
486 cache_token(sipe_private,
487 wcd->service_uri,
488 wcd->service_auth_uri,
489 wsse_security,
490 expires);
491 callback_execute(sipe_private,
492 wcd,
493 wcd->service_auth_uri,
494 wsse_security,
495 NULL);
496 failed = FALSE;
498 break;
501 case TOKEN_STATE_FEDERATION:
502 /* WebTicket from ADFS for federated authentication */
503 generate_federation_wsse(sipe_private->webticket,
504 raw);
506 if (sipe_private->webticket->adfs_token) {
508 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from ADFS %s",
509 uri);
511 if (federated_authentication(sipe_private,
512 wcd)) {
513 /* callback data passed down the line */
514 wcd = NULL;
517 break;
519 case TOKEN_STATE_FED_BEARER: {
520 /* WebTicket for federated authentication */
521 gchar *wsse_security = generate_fedbearer_wsse(raw);
523 if (wsse_security) {
525 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from service %s",
526 uri);
528 if (sipe_svc_webticket(sipe_private,
529 wcd->session,
530 wcd->webticket_fedbearer_uri,
531 wsse_security,
532 wcd->service_auth_uri,
533 &wcd->entropy,
534 webticket_token,
535 wcd)) {
536 wcd->token_state = TOKEN_STATE_SERVICE;
538 /* callback data passed down the line */
539 wcd = NULL;
541 g_free(wsse_security);
543 break;
546 /* end of: switch (wcd->token_state) { */
549 } else if (uri) {
550 /* Retry with federated authentication? */
551 if (wcd->webticket_fedbearer_uri) {
553 /* Authentication against ADFS failed? */
554 if (wcd->token_state == TOKEN_STATE_FEDERATION) {
555 struct sipe_webticket *webticket = sipe_private->webticket;
557 SIPE_DEBUG_INFO_NOFORMAT("webticket_token: ADFS authentication failed - assuming Multi-Factor Authentication (MFA)");
559 /* forget ADFS URI */
560 g_free(webticket->webticket_adfs_uri);
561 webticket->webticket_adfs_uri = NULL;
564 if (!wcd->tried_fedbearer) {
565 SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication",
566 uri);
568 if (initiate_fedbearer(sipe_private, wcd)) {
569 /* callback data passed down the line */
570 wcd = NULL;
576 if (wcd) {
577 if (failed) {
578 gchar *failure_msg = NULL;
580 if (soap_body) {
581 failure_msg = sipe_xml_data(sipe_xml_child(soap_body,
582 "Body/Fault/Detail/error/internalerror/text"));
583 /* XML data can end in &#x000D;&#x000A; */
584 g_strstrip(failure_msg);
587 callback_execute(sipe_private,
588 wcd,
589 uri,
590 NULL,
591 failure_msg);
592 g_free(failure_msg);
594 callback_data_free(wcd);
598 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
599 struct webticket_callback_data *wcd)
601 gboolean success;
603 if ((success = sipe_svc_webticket_lmc_federated(sipe_private,
604 wcd->session,
605 sipe_private->webticket->adfs_token,
606 wcd->webticket_fedbearer_uri,
607 webticket_token,
608 wcd)))
609 wcd->token_state = TOKEN_STATE_FED_BEARER;
611 /* If TRUE then callback data has been passed down the line */
612 return(success);
615 static gboolean fedbearer_authentication(struct sipe_core_private *sipe_private,
616 struct webticket_callback_data *wcd)
618 struct sipe_webticket *webticket = sipe_private->webticket;
619 gboolean success;
621 /* make sure a cached ADFS token is still valid for 60 seconds */
622 if (webticket->adfs_token &&
623 (webticket->adfs_token_expires >= time(NULL) + 60)) {
625 SIPE_DEBUG_INFO_NOFORMAT("fedbearer_authentication: reusing cached ADFS token");
626 success = federated_authentication(sipe_private, wcd);
628 } else if (webticket->webticket_adfs_uri) {
629 if ((success = sipe_svc_webticket_adfs(sipe_private,
630 wcd->session,
631 webticket->webticket_adfs_uri,
632 webticket_token,
633 wcd)))
634 wcd->token_state = TOKEN_STATE_FEDERATION;
635 } else {
636 if ((success = sipe_svc_webticket_lmc(sipe_private,
637 wcd->session,
638 wcd->webticket_fedbearer_uri,
639 webticket_token,
640 wcd)))
641 wcd->token_state = TOKEN_STATE_FED_BEARER;
644 /* If TRUE then callback data has been passed down the line */
645 return(success);
648 static void realminfo(struct sipe_core_private *sipe_private,
649 const gchar *uri,
650 SIPE_UNUSED_PARAMETER const gchar *raw,
651 sipe_xml *realminfo,
652 gpointer callback_data)
654 struct sipe_webticket *webticket = sipe_private->webticket;
655 struct webticket_callback_data *wcd = callback_data;
657 /* Only try retrieving of RealmInfo once */
658 webticket->retrieved_realminfo = TRUE;
661 * We must specifically check for abort, because
662 * realminfo == NULL is a valid response
664 if (uri) {
665 if (realminfo) {
666 /* detect ADFS setup. See also:
668 * http://en.wikipedia.org/wiki/Active_Directory_Federation_Services
670 * NOTE: this is based on observed behaviour.
671 * It is unkown if this is documented somewhere...
673 SIPE_DEBUG_INFO("realminfo: data for user %s retrieved successfully",
674 sipe_private->username);
676 webticket->webticket_adfs_uri = sipe_xml_data(sipe_xml_child(realminfo,
677 "STSAuthURL"));
680 if (webticket->webticket_adfs_uri)
681 SIPE_DEBUG_INFO("realminfo: ADFS setup detected: %s",
682 webticket->webticket_adfs_uri);
683 else
684 SIPE_DEBUG_INFO_NOFORMAT("realminfo: no RealmInfo found or no ADFS setup detected - try direct login");
686 if (fedbearer_authentication(sipe_private, wcd)) {
687 /* callback data passed down the line */
688 wcd = NULL;
692 if (wcd) {
693 callback_execute(sipe_private,
694 wcd,
695 uri,
696 NULL,
697 NULL);
698 callback_data_free(wcd);
702 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
703 struct webticket_callback_data *wcd)
705 gboolean success;
707 if (sipe_private->webticket->retrieved_realminfo) {
708 /* skip retrieval and go to authentication */
709 wcd->tried_fedbearer = TRUE;
710 success = fedbearer_authentication(sipe_private, wcd);
711 } else {
712 success = sipe_svc_realminfo(sipe_private,
713 wcd->session,
714 realminfo,
715 wcd);
718 return(success);
721 static void webticket_metadata(struct sipe_core_private *sipe_private,
722 const gchar *uri,
723 SIPE_UNUSED_PARAMETER const gchar *raw,
724 sipe_xml *metadata,
725 gpointer callback_data)
727 struct webticket_callback_data *wcd = callback_data;
729 if (metadata) {
730 const sipe_xml *node;
732 SIPE_DEBUG_INFO("webticket_metadata: metadata for service %s retrieved successfully",
733 uri);
735 /* Authentication ports accepted by WebTicket Service */
736 for (node = sipe_xml_child(metadata, "service/port");
737 node;
738 node = sipe_xml_twin(node)) {
739 const gchar *auth_uri = sipe_xml_attribute(sipe_xml_child(node,
740 "address"),
741 "location");
743 if (auth_uri) {
744 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
745 "WebTicketServiceWinNegotiate")) {
746 SIPE_DEBUG_INFO("webticket_metadata: WebTicket Windows Negotiate Auth URI %s", auth_uri);
747 g_free(wcd->webticket_negotiate_uri);
748 wcd->webticket_negotiate_uri = g_strdup(auth_uri);
749 } else if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
750 "WsFedBearer")) {
751 SIPE_DEBUG_INFO("webticket_metadata: WebTicket FedBearer Auth URI %s", auth_uri);
752 g_free(wcd->webticket_fedbearer_uri);
753 wcd->webticket_fedbearer_uri = g_strdup(auth_uri);
758 if (wcd->webticket_negotiate_uri || wcd->webticket_fedbearer_uri) {
759 gboolean success;
761 /* Entropy: 256 random bits */
762 if (!wcd->entropy.buffer)
763 sipe_tls_fill_random(&wcd->entropy, 256);
765 if (wcd->webticket_negotiate_uri) {
766 /* Try Negotiate authentication first */
768 success = sipe_svc_webticket(sipe_private,
769 wcd->session,
770 wcd->webticket_negotiate_uri,
771 NULL,
772 wcd->service_auth_uri,
773 &wcd->entropy,
774 webticket_token,
775 wcd);
776 wcd->token_state = TOKEN_STATE_SERVICE;
777 } else {
778 success = initiate_fedbearer(sipe_private,
779 wcd);
782 if (success) {
783 /* callback data passed down the line */
784 wcd = NULL;
789 if (wcd) {
790 callback_execute(sipe_private,
791 wcd,
792 uri,
793 NULL,
794 NULL);
795 callback_data_free(wcd);
799 static void service_metadata(struct sipe_core_private *sipe_private,
800 const gchar *uri,
801 SIPE_UNUSED_PARAMETER const gchar *raw,
802 sipe_xml *metadata,
803 gpointer callback_data)
805 struct webticket_callback_data *wcd = callback_data;
807 if (metadata) {
808 const sipe_xml *node;
809 gchar *policy = g_strdup_printf("%s_policy", wcd->service_port);
810 gchar *ticket_uri = NULL;
812 SIPE_DEBUG_INFO("webservice_metadata: metadata for service %s retrieved successfully",
813 uri);
815 /* WebTicket policies accepted by Web Service */
816 for (node = sipe_xml_child(metadata, "Policy");
817 node;
818 node = sipe_xml_twin(node)) {
819 if (sipe_strcase_equal(sipe_xml_attribute(node, "Id"),
820 policy)) {
822 SIPE_DEBUG_INFO_NOFORMAT("webservice_metadata: WebTicket policy found");
824 ticket_uri = sipe_xml_data(sipe_xml_child(node,
825 "ExactlyOne/All/EndorsingSupportingTokens/Policy/IssuedToken/Issuer/Address"));
826 if (ticket_uri) {
827 /* this token type requires signing */
828 wcd->requires_signing = TRUE;
829 } else {
830 /* try alternative token type */
831 ticket_uri = sipe_xml_data(sipe_xml_child(node,
832 "ExactlyOne/All/SignedSupportingTokens/Policy/IssuedToken/Issuer/Address"));
834 if (ticket_uri) {
835 SIPE_DEBUG_INFO("webservice_metadata: WebTicket URI %s", ticket_uri);
837 break;
840 g_free(policy);
842 if (ticket_uri) {
844 /* Authentication ports accepted by Web Service */
845 for (node = sipe_xml_child(metadata, "service/port");
846 node;
847 node = sipe_xml_twin(node)) {
848 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
849 wcd->service_port)) {
850 const gchar *auth_uri;
852 SIPE_DEBUG_INFO_NOFORMAT("webservice_metadata: authentication port found");
854 auth_uri = sipe_xml_attribute(sipe_xml_child(node,
855 "address"),
856 "location");
857 if (auth_uri) {
858 SIPE_DEBUG_INFO("webservice_metadata: Auth URI %s", auth_uri);
860 if (sipe_svc_metadata(sipe_private,
861 wcd->session,
862 ticket_uri,
863 webticket_metadata,
864 wcd)) {
865 /* Remember for later */
866 wcd->service_auth_uri = g_strdup(auth_uri);
868 /* callback data passed down the line */
869 wcd = NULL;
872 break;
875 g_free(ticket_uri);
879 if (wcd) {
880 callback_execute(sipe_private,
881 wcd,
882 uri,
883 NULL,
884 NULL);
885 callback_data_free(wcd);
889 gboolean sipe_webticket_request(struct sipe_core_private *sipe_private,
890 struct sipe_svc_session *session,
891 const gchar *base_uri,
892 const gchar *port_name,
893 sipe_webticket_callback *callback,
894 gpointer callback_data)
896 struct sipe_webticket *webticket;
897 gboolean ret = FALSE;
899 sipe_webticket_init(sipe_private);
900 webticket = sipe_private->webticket;
902 if (webticket->shutting_down) {
903 SIPE_DEBUG_ERROR("sipe_webticket_request: new Web Ticket request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
904 "Base URI: %s\n"
905 "Port Name: %s\n",
906 base_uri,
907 port_name);
909 } else {
910 const struct webticket_token *wt = cache_hit(sipe_private, base_uri);
912 /* cache hit for this URI? */
913 if (wt) {
914 SIPE_DEBUG_INFO("sipe_webticket_request: using cached token for URI %s (Auth URI %s)",
915 base_uri, wt->auth_uri);
916 callback(sipe_private,
917 base_uri,
918 wt->auth_uri,
919 wt->token,
920 NULL,
921 callback_data);
922 ret = TRUE;
923 } else {
924 GHashTable *pending = webticket->pending;
925 struct webticket_callback_data *wcd = g_hash_table_lookup(pending,
926 base_uri);
928 /* is there already a pending request for this URI? */
929 if (wcd) {
930 SIPE_DEBUG_INFO("sipe_webticket_request: pending request found for URI %s - queueing",
931 base_uri);
932 queue_request(wcd, callback, callback_data);
933 ret = TRUE;
934 } else {
935 wcd = g_new0(struct webticket_callback_data, 1);
937 ret = sipe_svc_metadata(sipe_private,
938 session,
939 base_uri,
940 service_metadata,
941 wcd);
943 if (ret) {
944 wcd->service_uri = g_strdup(base_uri);
945 wcd->service_port = port_name;
946 wcd->callback = callback;
947 wcd->callback_data = callback_data;
948 wcd->session = session;
949 wcd->token_state = TOKEN_STATE_NONE;
950 g_hash_table_insert(pending,
951 wcd->service_uri, /* borrowed */
952 wcd); /* borrowed */
953 } else {
954 g_free(wcd);
960 return(ret);
964 Local Variables:
965 mode: c
966 c-file-style: "bsd"
967 indent-tabs-mode: t
968 tab-width: 8
969 End: