Release 1.25.0 -- Buddy Idle Time, RTF
[siplcs.git] / src / core / sipe-webticket.c
blobb211da2c7ee8a2a5fd70ebb182b9bc7824d8bc86
1 /**
2 * @file sipe-webticket.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2016 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)
259 gchar *lifetime = sipe_xml_extract_raw(raw, "Lifetime", FALSE);
260 gchar *timestamp = NULL;
261 if (lifetime)
262 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>",
263 lifetime);
264 g_free(lifetime);
265 return(timestamp);
268 static gchar *generate_keydata(const gchar *raw)
270 return(sipe_xml_extract_raw(raw, "Assertion", TRUE));
273 static gchar *generate_expires(const gchar *timestamp)
275 return(sipe_xml_extract_raw(timestamp, "Expires", FALSE));
278 static gchar *generate_fedbearer_wsse(const gchar *raw)
280 gchar *timestamp = generate_timestamp(raw);
281 gchar *keydata = sipe_xml_extract_raw(raw, "EncryptedData", TRUE);
282 gchar *wsse_security = NULL;
284 if (timestamp && keydata) {
285 SIPE_DEBUG_INFO_NOFORMAT("generate_fedbearer_wsse: found timestamp & keydata");
286 wsse_security = g_strconcat(timestamp, keydata, NULL);
289 g_free(keydata);
290 g_free(timestamp);
291 return(wsse_security);
294 static void generate_federation_wsse(struct sipe_webticket *webticket,
295 const gchar *raw)
297 gchar *timestamp = generate_timestamp(raw);
298 gchar *keydata = generate_keydata(raw);
300 /* clear old ADFS token */
301 g_free(webticket->adfs_token);
302 webticket->adfs_token = NULL;
304 if (timestamp && keydata) {
305 gchar *expires_string = generate_expires(timestamp);
307 if (expires_string) {
309 SIPE_DEBUG_INFO("generate_federation_wsse: found timestamp & keydata, expires %s",
310 expires_string);
312 /* cache ADFS token */
313 webticket->adfs_token = g_strconcat(timestamp,
314 keydata,
315 NULL);
316 webticket->adfs_token_expires = sipe_utils_str_to_time(expires_string);
317 g_free(expires_string);
321 g_free(keydata);
322 g_free(timestamp);
325 static gchar *generate_sha1_proof_wsse(const gchar *raw,
326 struct sipe_tls_random *entropy,
327 time_t *expires)
329 gchar *timestamp = generate_timestamp(raw);
330 gchar *keydata = generate_keydata(raw);
331 gchar *wsse_security = NULL;
333 if (timestamp && keydata) {
334 gchar *expires_string = generate_expires(timestamp);
336 if (entropy) {
337 gchar *assertionID = extract_raw_xml_attribute(keydata,
338 "AssertionID");
341 * WS-Trust 1.3
343 * http://docs.oasis-open.org/ws-sx/ws-trust/200512/CK/PSHA1:
345 * "The key is computed using P_SHA1() from the TLS sepcification to generate
346 * a bit stream using entropy from both sides. The exact form is:
348 * key = P_SHA1(Entropy_REQ, Entropy_RES)"
350 gchar *entropy_res_base64 = sipe_xml_extract_raw(raw, "BinarySecret", FALSE);
351 gsize entropy_res_length;
352 guchar *entropy_response = g_base64_decode(entropy_res_base64,
353 &entropy_res_length);
354 guchar *key = sipe_tls_p_sha1(entropy->buffer,
355 entropy->length,
356 entropy_response,
357 entropy_res_length,
358 entropy->length);
359 g_free(entropy_response);
360 g_free(entropy_res_base64);
362 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata");
364 if (assertionID && key) {
365 /* same as SIPE_DIGEST_HMAC_SHA1_LENGTH */
366 guchar digest[SIPE_DIGEST_SHA1_LENGTH];
367 gchar *base64;
368 gchar *signed_info;
369 gchar *canon;
371 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found assertionID and successfully computed the key");
373 /* Digest over reference element (#timestamp -> wsu:Timestamp) */
374 sipe_digest_sha1((guchar *) timestamp,
375 strlen(timestamp),
376 digest);
377 base64 = g_base64_encode(digest,
378 SIPE_DIGEST_SHA1_LENGTH);
380 /* XML-Sig: SignedInfo for reference element */
381 signed_info = g_strdup_printf("<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
382 "<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
383 "<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\"/>"
384 "<Reference URI=\"#timestamp\">"
385 "<Transforms>"
386 "<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
387 "</Transforms>"
388 "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>"
389 "<DigestValue>%s</DigestValue>"
390 "</Reference>"
391 "</SignedInfo>",
392 base64);
393 g_free(base64);
395 /* XML-Sig: SignedInfo in canonical form */
396 canon = sipe_xml_exc_c14n(signed_info);
397 g_free(signed_info);
399 if (canon) {
400 gchar *signature;
402 /* calculate signature */
403 sipe_digest_hmac_sha1(key, entropy->length,
404 (guchar *)canon,
405 strlen(canon),
406 digest);
407 base64 = g_base64_encode(digest,
408 SIPE_DIGEST_HMAC_SHA1_LENGTH);
410 /* XML-Sig: Signature from SignedInfo + Key */
411 signature = g_strdup_printf("<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
412 " %s"
413 " <SignatureValue>%s</SignatureValue>"
414 " <KeyInfo>"
415 " <wsse:SecurityTokenReference wsse:TokenType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1\">"
416 " <wsse:KeyIdentifier ValueType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID\">%s</wsse:KeyIdentifier>"
417 " </wsse:SecurityTokenReference>"
418 " </KeyInfo>"
419 "</Signature>",
420 canon,
421 base64,
422 assertionID);
423 g_free(base64);
424 g_free(canon);
426 wsse_security = g_strconcat(timestamp,
427 keydata,
428 signature,
429 NULL);
430 g_free(signature);
435 g_free(key);
436 g_free(assertionID);
437 } else {
438 /* token doesn't require signature */
439 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata, no signing required");
440 wsse_security = g_strconcat(timestamp,
441 keydata,
442 NULL);
445 *expires = 0;
446 if (expires_string) {
447 *expires = sipe_utils_str_to_time(expires_string);
448 g_free(expires_string);
452 g_free(keydata);
453 g_free(timestamp);
454 return(wsse_security);
457 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
458 struct webticket_callback_data *wcd);
459 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
460 struct webticket_callback_data *wcd);
461 static void webticket_token(struct sipe_core_private *sipe_private,
462 const gchar *uri,
463 const gchar *raw,
464 sipe_xml *soap_body,
465 gpointer callback_data)
467 struct webticket_callback_data *wcd = callback_data;
468 gboolean failed = TRUE;
470 if (soap_body) {
471 switch (wcd->token_state) {
472 case TOKEN_STATE_NONE:
473 SIPE_DEBUG_INFO_NOFORMAT("webticket_token: ILLEGAL STATE - should not happen...");
474 break;
476 case TOKEN_STATE_SERVICE: {
477 /* WebTicket for Web Service */
478 time_t expires;
479 gchar *wsse_security = generate_sha1_proof_wsse(raw,
480 wcd->requires_signing ? &wcd->entropy : NULL,
481 &expires);
483 if (wsse_security) {
484 /* cache takes ownership of wsse_security */
485 cache_token(sipe_private,
486 wcd->service_uri,
487 wcd->service_auth_uri,
488 wsse_security,
489 expires);
490 callback_execute(sipe_private,
491 wcd,
492 wcd->service_auth_uri,
493 wsse_security,
494 NULL);
495 failed = FALSE;
497 break;
500 case TOKEN_STATE_FEDERATION:
501 /* WebTicket from ADFS for federated authentication */
502 generate_federation_wsse(sipe_private->webticket,
503 raw);
505 if (sipe_private->webticket->adfs_token) {
507 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from ADFS %s",
508 uri);
510 if (federated_authentication(sipe_private,
511 wcd)) {
512 /* callback data passed down the line */
513 wcd = NULL;
516 break;
518 case TOKEN_STATE_FED_BEARER: {
519 /* WebTicket for federated authentication */
520 gchar *wsse_security = generate_fedbearer_wsse(raw);
522 if (wsse_security) {
524 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from service %s",
525 uri);
527 if (sipe_svc_webticket(sipe_private,
528 wcd->session,
529 wcd->webticket_fedbearer_uri,
530 wsse_security,
531 wcd->service_auth_uri,
532 &wcd->entropy,
533 webticket_token,
534 wcd)) {
535 wcd->token_state = TOKEN_STATE_SERVICE;
537 /* callback data passed down the line */
538 wcd = NULL;
540 g_free(wsse_security);
542 break;
545 /* end of: switch (wcd->token_state) { */
548 } else if (uri) {
549 /* Retry with federated authentication? */
550 if (wcd->webticket_fedbearer_uri) {
552 /* Authentication against ADFS failed? */
553 if (wcd->token_state == TOKEN_STATE_FEDERATION) {
554 struct sipe_webticket *webticket = sipe_private->webticket;
556 SIPE_LOG_WARNING_NOFORMAT("webticket_token: ADFS authentication failed - assuming Multi-Factor Authentication (MFA)");
558 /* forget ADFS URI */
559 g_free(webticket->webticket_adfs_uri);
560 webticket->webticket_adfs_uri = NULL;
563 if (!wcd->tried_fedbearer) {
564 SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication",
565 uri);
567 if (initiate_fedbearer(sipe_private, wcd)) {
568 /* callback data passed down the line */
569 wcd = NULL;
575 if (wcd) {
576 if (failed) {
577 gchar *failure_msg = NULL;
579 if (soap_body) {
580 failure_msg = sipe_xml_data(sipe_xml_child(soap_body,
581 "Body/Fault/Detail/error/internalerror/text"));
582 /* XML data can end in &#x000D;&#x000A; */
583 g_strstrip(failure_msg);
586 callback_execute(sipe_private,
587 wcd,
588 uri,
589 NULL,
590 failure_msg);
591 g_free(failure_msg);
593 callback_data_free(wcd);
597 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
598 struct webticket_callback_data *wcd)
600 gboolean success;
602 if ((success = sipe_svc_webticket_lmc_federated(sipe_private,
603 wcd->session,
604 sipe_private->webticket->adfs_token,
605 wcd->webticket_fedbearer_uri,
606 webticket_token,
607 wcd)))
608 wcd->token_state = TOKEN_STATE_FED_BEARER;
610 /* If TRUE then callback data has been passed down the line */
611 return(success);
614 static gboolean fedbearer_authentication(struct sipe_core_private *sipe_private,
615 struct webticket_callback_data *wcd)
617 struct sipe_webticket *webticket = sipe_private->webticket;
618 gboolean success;
620 /* make sure a cached ADFS token is still valid for 60 seconds */
621 if (webticket->adfs_token &&
622 (webticket->adfs_token_expires >= time(NULL) + 60)) {
624 SIPE_DEBUG_INFO_NOFORMAT("fedbearer_authentication: reusing cached ADFS token");
625 success = federated_authentication(sipe_private, wcd);
627 } else if (webticket->webticket_adfs_uri) {
628 if ((success = sipe_svc_webticket_adfs(sipe_private,
629 wcd->session,
630 webticket->webticket_adfs_uri,
631 webticket_token,
632 wcd)))
633 wcd->token_state = TOKEN_STATE_FEDERATION;
634 } else {
635 if ((success = sipe_svc_webticket_lmc(sipe_private,
636 wcd->session,
637 wcd->webticket_fedbearer_uri,
638 webticket_token,
639 wcd)))
640 wcd->token_state = TOKEN_STATE_FED_BEARER;
643 /* If TRUE then callback data has been passed down the line */
644 return(success);
647 static void realminfo(struct sipe_core_private *sipe_private,
648 const gchar *uri,
649 SIPE_UNUSED_PARAMETER const gchar *raw,
650 sipe_xml *realminfo,
651 gpointer callback_data)
653 struct sipe_webticket *webticket = sipe_private->webticket;
654 struct webticket_callback_data *wcd = callback_data;
656 /* Only try retrieving of RealmInfo once */
657 webticket->retrieved_realminfo = TRUE;
660 * We must specifically check for abort, because
661 * realminfo == NULL is a valid response
663 if (uri) {
664 if (realminfo) {
665 /* detect ADFS setup. See also:
667 * http://en.wikipedia.org/wiki/Active_Directory_Federation_Services
669 * NOTE: this is based on observed behaviour.
670 * It is unkown if this is documented somewhere...
672 SIPE_DEBUG_INFO("realminfo: data for user %s retrieved successfully",
673 sipe_private->username);
675 webticket->webticket_adfs_uri = sipe_xml_data(sipe_xml_child(realminfo,
676 "STSAuthURL"));
679 if (webticket->webticket_adfs_uri) {
680 SIPE_LOG_INFO_NOFORMAT("realminfo: ADFS setup detected");
681 SIPE_DEBUG_INFO("realminfo: ADFS URI: %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("service_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("service_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("service_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("service_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("service_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 static gboolean webticket_request(struct sipe_core_private *sipe_private,
890 struct sipe_svc_session *session,
891 const gchar *base_uri,
892 const gchar *auth_uri,
893 const gchar *port_name,
894 sipe_webticket_callback *callback,
895 gpointer callback_data)
897 struct sipe_webticket *webticket;
898 gboolean ret = FALSE;
900 sipe_webticket_init(sipe_private);
901 webticket = sipe_private->webticket;
903 if (webticket->shutting_down) {
904 SIPE_DEBUG_ERROR("webticket_request: new Web Ticket request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
905 "Base URI: %s\n"
906 "Port Name: %s\n",
907 base_uri,
908 port_name);
910 } else {
911 const struct webticket_token *wt = cache_hit(sipe_private, base_uri);
913 /* cache hit for this URI? */
914 if (wt) {
915 SIPE_DEBUG_INFO("webticket_request: using cached token for URI %s (Auth URI %s)",
916 base_uri, wt->auth_uri);
917 callback(sipe_private,
918 base_uri,
919 wt->auth_uri,
920 wt->token,
921 NULL,
922 callback_data);
923 ret = TRUE;
924 } else {
925 GHashTable *pending = webticket->pending;
926 struct webticket_callback_data *wcd = g_hash_table_lookup(pending,
927 base_uri);
929 /* is there already a pending request for this URI? */
930 if (wcd) {
931 SIPE_DEBUG_INFO("webticket_request: pending request found for URI %s - queueing",
932 base_uri);
933 queue_request(wcd, callback, callback_data);
934 ret = TRUE;
935 } else {
936 wcd = g_new0(struct webticket_callback_data, 1);
938 ret = sipe_svc_metadata(sipe_private,
939 session,
940 base_uri,
941 port_name ? service_metadata : webticket_metadata,
942 wcd);
944 if (ret) {
945 wcd->service_uri = g_strdup(base_uri);
946 wcd->service_port = port_name;
947 wcd->service_auth_uri = g_strdup(auth_uri);
948 wcd->callback = callback;
949 wcd->callback_data = callback_data;
950 wcd->session = session;
951 wcd->token_state = TOKEN_STATE_NONE;
952 g_hash_table_insert(pending,
953 wcd->service_uri, /* borrowed */
954 wcd); /* borrowed */
955 } else {
956 g_free(wcd);
962 return(ret);
965 gboolean sipe_webticket_request_with_port(struct sipe_core_private *sipe_private,
966 struct sipe_svc_session *session,
967 const gchar *base_uri,
968 const gchar *port_name,
969 sipe_webticket_callback *callback,
970 gpointer callback_data)
972 return(webticket_request(sipe_private,
973 session,
974 base_uri,
975 NULL, /* Auth URI is determined via port_name */
976 port_name,
977 callback,
978 callback_data));
981 gboolean sipe_webticket_request_with_auth(struct sipe_core_private *sipe_private,
982 struct sipe_svc_session *session,
983 const gchar *base_uri,
984 const gchar *auth_uri,
985 sipe_webticket_callback *callback,
986 gpointer callback_data)
988 return(webticket_request(sipe_private,
989 session,
990 base_uri,
991 auth_uri,
992 NULL,
993 callback,
994 callback_data));
998 Local Variables:
999 mode: c
1000 c-file-style: "bsd"
1001 indent-tabs-mode: t
1002 tab-width: 8
1003 End: