l10n: sync translations with transifex.net (hu)
[siplcs.git] / src / core / sipe-webticket.c
blob50a6142ff38dcf78baacbe9f21b87fe3617ebc91
1 /**
2 * @file sipe-webticket.c
4 * pidgin-sipe
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-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;
96 void sipe_webticket_free(struct sipe_core_private *sipe_private)
98 struct sipe_webticket *webticket = sipe_private->webticket;
99 if (!webticket)
100 return;
102 g_free(webticket->webticket_adfs_uri);
103 g_free(webticket->adfs_token);
104 if (webticket->pending)
105 g_hash_table_destroy(webticket->pending);
106 if (webticket->cache)
107 g_hash_table_destroy(webticket->cache);
108 g_free(webticket);
109 sipe_private->webticket = NULL;
112 static void free_token(gpointer data)
114 struct webticket_token *wt = data;
115 g_free(wt->auth_uri);
116 g_free(wt->token);
117 g_free(wt);
120 static void sipe_webticket_init(struct sipe_core_private *sipe_private)
122 struct sipe_webticket *webticket;
124 if (sipe_private->webticket)
125 return;
127 sipe_private->webticket = webticket = g_new0(struct sipe_webticket, 1);
129 webticket->cache = g_hash_table_new_full(g_str_hash,
130 g_str_equal,
131 g_free,
132 free_token);
133 webticket->pending = g_hash_table_new(g_str_hash,
134 g_str_equal);
137 /* takes ownership of "token" */
138 static void cache_token(struct sipe_core_private *sipe_private,
139 const gchar *service_uri,
140 const gchar *auth_uri,
141 gchar *token,
142 time_t expires)
144 struct webticket_token *wt = g_new0(struct webticket_token, 1);
145 wt->auth_uri = g_strdup(auth_uri);
146 wt->token = token;
147 wt->expires = expires;
148 g_hash_table_insert(sipe_private->webticket->cache,
149 g_strdup(service_uri),
150 wt);
153 static const struct webticket_token *cache_hit(struct sipe_core_private *sipe_private,
154 const gchar *service_uri)
156 const struct webticket_token *wt;
158 sipe_webticket_init(sipe_private);
160 /* make sure a cached Web Ticket is still valid for 60 seconds */
161 wt = g_hash_table_lookup(sipe_private->webticket->cache,
162 service_uri);
163 if (wt && (wt->expires < time(NULL) + 60)) {
164 SIPE_DEBUG_INFO("cache_hit: cached token for URI %s has expired",
165 service_uri);
166 wt = NULL;
169 return(wt);
172 /* frees just the main request data, when this is called "queued" is cleared */
173 static void callback_data_free(struct webticket_callback_data *wcd)
175 if (wcd) {
176 sipe_tls_free_random(&wcd->entropy);
177 g_free(wcd->webticket_negotiate_uri);
178 g_free(wcd->webticket_fedbearer_uri);
179 g_free(wcd->service_auth_uri);
180 g_free(wcd->service_uri);
181 g_free(wcd);
185 static void queue_request(struct webticket_callback_data *wcd,
186 sipe_webticket_callback *callback,
187 gpointer callback_data)
189 struct webticket_queued_data *wqd = g_new0(struct webticket_queued_data, 1);
191 wqd->callback = callback;
192 wqd->callback_data = callback_data;
194 wcd->queued = g_slist_prepend(wcd->queued, wqd);
197 static void callback_execute(struct sipe_core_private *sipe_private,
198 struct webticket_callback_data *wcd,
199 const gchar *auth_uri,
200 const gchar *wsse_security,
201 const gchar *failure_msg)
203 GSList *entry = wcd->queued;
205 /* complete main request */
206 wcd->callback(sipe_private,
207 wcd->service_uri,
208 auth_uri,
209 wsse_security,
210 failure_msg,
211 wcd->callback_data);
213 /* complete queued requests */
214 while (entry) {
215 struct webticket_queued_data *wqd = entry->data;
217 SIPE_DEBUG_INFO("callback_execute: completing queue request URI %s (Auth URI %s)",
218 wcd->service_uri, auth_uri);
219 wqd->callback(sipe_private,
220 wcd->service_uri,
221 auth_uri,
222 wsse_security,
223 failure_msg,
224 wqd->callback_data);
226 g_free(wqd);
227 entry = entry->next;
229 g_slist_free(wcd->queued);
231 /* drop request from pending hash */
232 g_hash_table_remove(sipe_private->webticket->pending,
233 wcd->service_uri);
236 static gchar *extract_raw_xml_attribute(const gchar *xml,
237 const gchar *name)
239 gchar *attr_start = g_strdup_printf("%s=\"", name);
240 gchar *data = NULL;
241 const gchar *start = strstr(xml, attr_start);
243 if (start) {
244 const gchar *value = start + strlen(attr_start);
245 const gchar *end = strchr(value, '"');
246 if (end) {
247 data = g_strndup(value, end - value);
251 g_free(attr_start);
252 return(data);
255 static gchar *generate_timestamp(const gchar *raw,
256 const gchar *lifetime_tag)
258 gchar *lifetime = sipe_xml_extract_raw(raw, lifetime_tag, FALSE);
259 gchar *timestamp = NULL;
260 if (lifetime)
261 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>",
262 lifetime);
263 g_free(lifetime);
264 return(timestamp);
267 static gchar *generate_fedbearer_wsse(const gchar *raw)
269 gchar *timestamp = generate_timestamp(raw, "wst:Lifetime");
270 gchar *keydata = sipe_xml_extract_raw(raw, "EncryptedData", TRUE);
271 gchar *wsse_security = NULL;
273 if (timestamp && keydata) {
274 SIPE_DEBUG_INFO_NOFORMAT("generate_fedbearer_wsse: found timestamp & keydata");
275 wsse_security = g_strconcat(timestamp, keydata, NULL);
278 g_free(keydata);
279 g_free(timestamp);
280 return(wsse_security);
283 static void generate_federation_wsse(struct sipe_webticket *webticket,
284 const gchar *raw)
286 gchar *timestamp = generate_timestamp(raw, "t:Lifetime");
287 gchar *keydata = sipe_xml_extract_raw(raw, "saml:Assertion", TRUE);
290 /* clear old ADFS token */
291 g_free(webticket->adfs_token);
292 webticket->adfs_token = NULL;
294 if (timestamp && keydata) {
295 gchar *expires_string = sipe_xml_extract_raw(timestamp,
296 "wsu:Expires",
297 FALSE);
299 if (expires_string) {
301 SIPE_DEBUG_INFO("generate_federation_wsse: found timestamp & keydata, expires %s",
302 expires_string);
304 /* cache ADFS token */
305 webticket->adfs_token = g_strconcat(timestamp,
306 keydata,
307 NULL);
308 webticket->adfs_token_expires = sipe_utils_str_to_time(expires_string);
309 g_free(expires_string);
313 g_free(keydata);
314 g_free(timestamp);
317 static gchar *generate_sha1_proof_wsse(const gchar *raw,
318 struct sipe_tls_random *entropy,
319 time_t *expires)
321 gchar *timestamp = generate_timestamp(raw, "Lifetime");
322 gchar *keydata = sipe_xml_extract_raw(raw, "saml:Assertion", TRUE);
323 gchar *wsse_security = NULL;
325 if (timestamp && keydata) {
326 gchar *expires_string = sipe_xml_extract_raw(timestamp,
327 "Expires",
328 FALSE);
330 if (entropy) {
331 gchar *assertionID = extract_raw_xml_attribute(keydata,
332 "AssertionID");
335 * WS-Trust 1.3
337 * http://docs.oasis-open.org/ws-sx/ws-trust/200512/CK/PSHA1:
339 * "The key is computed using P_SHA1() from the TLS sepcification to generate
340 * a bit stream using entropy from both sides. The exact form is:
342 * key = P_SHA1(Entropy_REQ, Entropy_RES)"
344 gchar *entropy_res_base64 = sipe_xml_extract_raw(raw, "BinarySecret", FALSE);
345 gsize entropy_res_length;
346 guchar *entropy_response = g_base64_decode(entropy_res_base64,
347 &entropy_res_length);
348 guchar *key = sipe_tls_p_sha1(entropy->buffer,
349 entropy->length,
350 entropy_response,
351 entropy_res_length,
352 entropy->length);
353 g_free(entropy_response);
354 g_free(entropy_res_base64);
356 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata");
358 if (assertionID && key) {
359 /* same as SIPE_DIGEST_HMAC_SHA1_LENGTH */
360 guchar digest[SIPE_DIGEST_SHA1_LENGTH];
361 gchar *base64;
362 gchar *signed_info;
363 gchar *canon;
365 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found assertionID and successfully computed the key");
367 /* Digest over reference element (#timestamp -> wsu:Timestamp) */
368 sipe_digest_sha1((guchar *) timestamp,
369 strlen(timestamp),
370 digest);
371 base64 = g_base64_encode(digest,
372 SIPE_DIGEST_SHA1_LENGTH);
374 /* XML-Sig: SignedInfo for reference element */
375 signed_info = g_strdup_printf("<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
376 "<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
377 "<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\"/>"
378 "<Reference URI=\"#timestamp\">"
379 "<Transforms>"
380 "<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
381 "</Transforms>"
382 "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>"
383 "<DigestValue>%s</DigestValue>"
384 "</Reference>"
385 "</SignedInfo>",
386 base64);
387 g_free(base64);
389 /* XML-Sig: SignedInfo in canonical form */
390 canon = sipe_xml_exc_c14n(signed_info);
391 g_free(signed_info);
393 if (canon) {
394 gchar *signature;
396 /* calculate signature */
397 sipe_digest_hmac_sha1(key, entropy->length,
398 (guchar *)canon,
399 strlen(canon),
400 digest);
401 base64 = g_base64_encode(digest,
402 SIPE_DIGEST_HMAC_SHA1_LENGTH);
404 /* XML-Sig: Signature from SignedInfo + Key */
405 signature = g_strdup_printf("<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
406 " %s"
407 " <SignatureValue>%s</SignatureValue>"
408 " <KeyInfo>"
409 " <wsse:SecurityTokenReference wsse:TokenType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1\">"
410 " <wsse:KeyIdentifier ValueType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID\">%s</wsse:KeyIdentifier>"
411 " </wsse:SecurityTokenReference>"
412 " </KeyInfo>"
413 "</Signature>",
414 canon,
415 base64,
416 assertionID);
417 g_free(base64);
418 g_free(canon);
420 wsse_security = g_strconcat(timestamp,
421 keydata,
422 signature,
423 NULL);
424 g_free(signature);
429 g_free(key);
430 g_free(assertionID);
431 } else {
432 /* token doesn't require signature */
433 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata, no signing required");
434 wsse_security = g_strconcat(timestamp,
435 keydata,
436 NULL);
439 *expires = 0;
440 if (expires_string) {
441 *expires = sipe_utils_str_to_time(expires_string);
442 g_free(expires_string);
446 g_free(keydata);
447 g_free(timestamp);
448 return(wsse_security);
451 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
452 struct webticket_callback_data *wcd);
453 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
454 struct webticket_callback_data *wcd);
455 static void webticket_token(struct sipe_core_private *sipe_private,
456 const gchar *uri,
457 const gchar *raw,
458 sipe_xml *soap_body,
459 gpointer callback_data)
461 struct webticket_callback_data *wcd = callback_data;
462 gboolean failed = TRUE;
464 if (soap_body) {
465 switch (wcd->token_state) {
466 case TOKEN_STATE_NONE:
467 SIPE_DEBUG_INFO_NOFORMAT("webticket_token: ILLEGAL STATE - should not happen...");
468 break;
470 case TOKEN_STATE_SERVICE: {
471 /* WebTicket for Web Service */
472 time_t expires;
473 gchar *wsse_security = generate_sha1_proof_wsse(raw,
474 wcd->requires_signing ? &wcd->entropy : NULL,
475 &expires);
477 if (wsse_security) {
478 /* cache takes ownership of wsse_security */
479 cache_token(sipe_private,
480 wcd->service_uri,
481 wcd->service_auth_uri,
482 wsse_security,
483 expires);
484 callback_execute(sipe_private,
485 wcd,
486 wcd->service_auth_uri,
487 wsse_security,
488 NULL);
489 failed = FALSE;
491 break;
494 case TOKEN_STATE_FEDERATION:
495 /* WebTicket from ADFS for federated authentication */
496 generate_federation_wsse(sipe_private->webticket,
497 raw);
499 if (sipe_private->webticket->adfs_token) {
501 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from ADFS %s",
502 uri);
504 if (federated_authentication(sipe_private,
505 wcd)) {
506 /* callback data passed down the line */
507 wcd = NULL;
510 break;
512 case TOKEN_STATE_FED_BEARER: {
513 /* WebTicket for federated authentication */
514 gchar *wsse_security = generate_fedbearer_wsse(raw);
516 if (wsse_security) {
518 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from service %s",
519 uri);
521 if (sipe_svc_webticket(sipe_private,
522 wcd->session,
523 wcd->webticket_fedbearer_uri,
524 wsse_security,
525 wcd->service_auth_uri,
526 &wcd->entropy,
527 webticket_token,
528 wcd)) {
529 wcd->token_state = TOKEN_STATE_SERVICE;
531 /* callback data passed down the line */
532 wcd = NULL;
534 g_free(wsse_security);
536 break;
539 /* end of: switch (wcd->token_state) { */
542 } else if (uri) {
543 /* Retry with federated authentication? */
544 if (wcd->webticket_fedbearer_uri && !wcd->tried_fedbearer) {
545 SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication",
546 uri);
548 if (initiate_fedbearer(sipe_private, wcd)) {
549 /* callback data passed down the line */
550 wcd = NULL;
555 if (wcd) {
556 if (failed) {
557 gchar *failure_msg = NULL;
559 if (soap_body) {
560 failure_msg = sipe_xml_data(sipe_xml_child(soap_body,
561 "Body/Fault/Detail/error/internalerror/text"));
562 /* XML data can end in &#x000D;&#x000A; */
563 g_strstrip(failure_msg);
566 callback_execute(sipe_private,
567 wcd,
568 uri,
569 NULL,
570 failure_msg);
571 g_free(failure_msg);
573 callback_data_free(wcd);
577 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
578 struct webticket_callback_data *wcd)
580 gboolean success;
582 if ((success = sipe_svc_webticket_lmc_federated(sipe_private,
583 wcd->session,
584 sipe_private->webticket->adfs_token,
585 wcd->webticket_fedbearer_uri,
586 webticket_token,
587 wcd)))
588 wcd->token_state = TOKEN_STATE_FED_BEARER;
590 /* If TRUE then callback data has been passed down the line */
591 return(success);
594 static gboolean fedbearer_authentication(struct sipe_core_private *sipe_private,
595 struct webticket_callback_data *wcd)
597 struct sipe_webticket *webticket = sipe_private->webticket;
598 gboolean success;
600 /* make sure a cached ADFS token is still valid for 60 seconds */
601 if (webticket->adfs_token &&
602 (webticket->adfs_token_expires >= time(NULL) + 60)) {
604 SIPE_DEBUG_INFO_NOFORMAT("fedbearer_authentication: reusing cached ADFS token");
605 success = federated_authentication(sipe_private, wcd);
607 } else if (webticket->webticket_adfs_uri) {
608 if ((success = sipe_svc_webticket_adfs(sipe_private,
609 wcd->session,
610 webticket->webticket_adfs_uri,
611 webticket_token,
612 wcd)))
613 wcd->token_state = TOKEN_STATE_FEDERATION;
614 } else {
615 if ((success = sipe_svc_webticket_lmc(sipe_private,
616 wcd->session,
617 wcd->webticket_fedbearer_uri,
618 webticket_token,
619 wcd)))
620 wcd->token_state = TOKEN_STATE_FED_BEARER;
623 /* If TRUE then callback data has been passed down the line */
624 return(success);
627 static void realminfo(struct sipe_core_private *sipe_private,
628 const gchar *uri,
629 SIPE_UNUSED_PARAMETER const gchar *raw,
630 sipe_xml *realminfo,
631 gpointer callback_data)
633 struct sipe_webticket *webticket = sipe_private->webticket;
634 struct webticket_callback_data *wcd = callback_data;
636 /* Only try retrieving of RealmInfo once */
637 webticket->retrieved_realminfo = TRUE;
639 if (realminfo) {
640 /* detect ADFS setup. See also:
642 * http://en.wikipedia.org/wiki/Active_Directory_Federation_Services
644 * NOTE: this is based on observed behaviour.
645 * It is unkown if this is documented somewhere...
647 SIPE_DEBUG_INFO("realminfo: data for user %s retrieved successfully",
648 sipe_private->username);
650 webticket->webticket_adfs_uri = sipe_xml_data(sipe_xml_child(realminfo,
651 "STSAuthURL"));
654 if (webticket->webticket_adfs_uri)
655 SIPE_DEBUG_INFO("realminfo: ADFS setup detected: %s",
656 webticket->webticket_adfs_uri);
657 else
658 SIPE_DEBUG_INFO_NOFORMAT("realminfo: no RealmInfo found or no ADFS setup detected - try direct login");
660 if (!fedbearer_authentication(sipe_private, wcd)) {
661 callback_execute(sipe_private,
662 wcd,
663 uri,
664 NULL,
665 NULL);
666 callback_data_free(wcd);
670 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
671 struct webticket_callback_data *wcd)
673 gboolean success;
675 if (sipe_private->webticket->retrieved_realminfo) {
676 /* skip retrieval and go to authentication */
677 success = fedbearer_authentication(sipe_private, wcd);
678 } else {
679 success = sipe_svc_realminfo(sipe_private,
680 wcd->session,
681 realminfo,
682 wcd);
685 wcd->tried_fedbearer = TRUE;
687 return(success);
690 static void webticket_metadata(struct sipe_core_private *sipe_private,
691 const gchar *uri,
692 SIPE_UNUSED_PARAMETER const gchar *raw,
693 sipe_xml *metadata,
694 gpointer callback_data)
696 struct webticket_callback_data *wcd = callback_data;
698 if (metadata) {
699 const sipe_xml *node;
701 SIPE_DEBUG_INFO("webticket_metadata: metadata for service %s retrieved successfully",
702 uri);
704 /* Authentication ports accepted by WebTicket Service */
705 for (node = sipe_xml_child(metadata, "service/port");
706 node;
707 node = sipe_xml_twin(node)) {
708 const gchar *auth_uri = sipe_xml_attribute(sipe_xml_child(node,
709 "address"),
710 "location");
712 if (auth_uri) {
713 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
714 "WebTicketServiceWinNegotiate")) {
715 SIPE_DEBUG_INFO("webticket_metadata: WebTicket Windows Negotiate Auth URI %s", auth_uri);
716 g_free(wcd->webticket_negotiate_uri);
717 wcd->webticket_negotiate_uri = g_strdup(auth_uri);
718 } else if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
719 "WsFedBearer")) {
720 SIPE_DEBUG_INFO("webticket_metadata: WebTicket FedBearer Auth URI %s", auth_uri);
721 g_free(wcd->webticket_fedbearer_uri);
722 wcd->webticket_fedbearer_uri = g_strdup(auth_uri);
727 if (wcd->webticket_negotiate_uri || wcd->webticket_fedbearer_uri) {
728 gboolean success;
730 /* Entropy: 256 random bits */
731 if (!wcd->entropy.buffer)
732 sipe_tls_fill_random(&wcd->entropy, 256);
734 if (wcd->webticket_negotiate_uri) {
735 /* Try Negotiate authentication first */
737 success = sipe_svc_webticket(sipe_private,
738 wcd->session,
739 wcd->webticket_negotiate_uri,
740 NULL,
741 wcd->service_auth_uri,
742 &wcd->entropy,
743 webticket_token,
744 wcd);
745 wcd->token_state = TOKEN_STATE_SERVICE;
746 } else {
747 success = initiate_fedbearer(sipe_private,
748 wcd);
751 if (success) {
752 /* callback data passed down the line */
753 wcd = NULL;
758 if (wcd) {
759 callback_execute(sipe_private,
760 wcd,
761 uri,
762 NULL,
763 NULL);
764 callback_data_free(wcd);
768 static void service_metadata(struct sipe_core_private *sipe_private,
769 const gchar *uri,
770 SIPE_UNUSED_PARAMETER const gchar *raw,
771 sipe_xml *metadata,
772 gpointer callback_data)
774 struct webticket_callback_data *wcd = callback_data;
776 if (metadata) {
777 const sipe_xml *node;
778 gchar *policy = g_strdup_printf("%s_policy", wcd->service_port);
779 gchar *ticket_uri = NULL;
781 SIPE_DEBUG_INFO("webservice_metadata: metadata for service %s retrieved successfully",
782 uri);
784 /* WebTicket policies accepted by Web Service */
785 for (node = sipe_xml_child(metadata, "Policy");
786 node;
787 node = sipe_xml_twin(node)) {
788 if (sipe_strcase_equal(sipe_xml_attribute(node, "Id"),
789 policy)) {
791 SIPE_DEBUG_INFO_NOFORMAT("webservice_metadata: WebTicket policy found");
793 ticket_uri = sipe_xml_data(sipe_xml_child(node,
794 "ExactlyOne/All/EndorsingSupportingTokens/Policy/IssuedToken/Issuer/Address"));
795 if (ticket_uri) {
796 /* this token type requires signing */
797 wcd->requires_signing = TRUE;
798 } else {
799 /* try alternative token type */
800 ticket_uri = sipe_xml_data(sipe_xml_child(node,
801 "ExactlyOne/All/SignedSupportingTokens/Policy/IssuedToken/Issuer/Address"));
803 if (ticket_uri) {
804 SIPE_DEBUG_INFO("webservice_metadata: WebTicket URI %s", ticket_uri);
806 break;
809 g_free(policy);
811 if (ticket_uri) {
813 /* Authentication ports accepted by Web Service */
814 for (node = sipe_xml_child(metadata, "service/port");
815 node;
816 node = sipe_xml_twin(node)) {
817 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
818 wcd->service_port)) {
819 const gchar *auth_uri;
821 SIPE_DEBUG_INFO_NOFORMAT("webservice_metadata: authentication port found");
823 auth_uri = sipe_xml_attribute(sipe_xml_child(node,
824 "address"),
825 "location");
826 if (auth_uri) {
827 SIPE_DEBUG_INFO("webservice_metadata: Auth URI %s", auth_uri);
829 if (sipe_svc_metadata(sipe_private,
830 wcd->session,
831 ticket_uri,
832 webticket_metadata,
833 wcd)) {
834 /* Remember for later */
835 wcd->service_auth_uri = g_strdup(auth_uri);
837 /* callback data passed down the line */
838 wcd = NULL;
841 break;
844 g_free(ticket_uri);
848 if (wcd) {
849 callback_execute(sipe_private,
850 wcd,
851 uri,
852 NULL,
853 NULL);
854 callback_data_free(wcd);
858 gboolean sipe_webticket_request(struct sipe_core_private *sipe_private,
859 struct sipe_svc_session *session,
860 const gchar *base_uri,
861 const gchar *port_name,
862 sipe_webticket_callback *callback,
863 gpointer callback_data)
865 const struct webticket_token *wt = cache_hit(sipe_private, base_uri);
866 gboolean ret;
868 /* cache hit for this URI? */
869 if (wt) {
870 SIPE_DEBUG_INFO("sipe_webticket_request: using cached token for URI %s (Auth URI %s)",
871 base_uri, wt->auth_uri);
872 callback(sipe_private,
873 base_uri,
874 wt->auth_uri,
875 wt->token,
876 NULL,
877 callback_data);
878 ret = TRUE;
879 } else {
880 GHashTable *pending = sipe_private->webticket->pending;
881 struct webticket_callback_data *wcd = g_hash_table_lookup(pending,
882 base_uri);
884 /* is there already a pending request for this URI? */
885 if (wcd) {
886 SIPE_DEBUG_INFO("sipe_webticket_request: pending request found for URI %s - queueing",
887 base_uri);
888 queue_request(wcd, callback, callback_data);
889 ret = TRUE;
890 } else {
891 wcd = g_new0(struct webticket_callback_data, 1);
893 ret = sipe_svc_metadata(sipe_private,
894 session,
895 base_uri,
896 service_metadata,
897 wcd);
899 if (ret) {
900 wcd->service_uri = g_strdup(base_uri);
901 wcd->service_port = port_name;
902 wcd->callback = callback;
903 wcd->callback_data = callback_data;
904 wcd->session = session;
905 wcd->token_state = TOKEN_STATE_NONE;
906 g_hash_table_insert(pending,
907 wcd->service_uri, /* borrowed */
908 wcd); /* borrowed */
909 } else {
910 g_free(wcd);
915 return(ret);
919 Local Variables:
920 mode: c
921 c-file-style: "bsd"
922 indent-tabs-mode: t
923 tab-width: 8
924 End: