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
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"
44 #include "sipe-utils.h"
49 /* forward declaration */
51 typedef void (svc_callback
)(struct svc_request
*data
,
56 struct sipe_core_private
*sipe_private
;
57 svc_callback
*internal_cb
;
58 sipe_svc_callback
*cb
;
65 GSList
*pending_requests
;
68 static void sipe_svc_request_free(struct svc_request
*data
)
71 http_conn_free(data
->conn
);
73 /* Callback: aborted */
74 (*data
->cb
)(data
->sipe_private
, NULL
, NULL
, NULL
, data
->cb_data
);
79 void sipe_svc_free(struct sipe_core_private
*sipe_private
)
81 struct sipe_svc
*svc
= sipe_private
->svc
;
85 if (svc
->pending_requests
) {
86 GSList
*entry
= svc
->pending_requests
;
88 sipe_svc_request_free(entry
->data
);
91 g_slist_free(svc
->pending_requests
);
95 sipe_private
->svc
= NULL
;
98 static void sipe_svc_init(struct sipe_core_private
*sipe_private
)
100 if (sipe_private
->svc
)
103 sipe_private
->svc
= g_new0(struct sipe_svc
, 1);
106 void sipe_svc_fill_random(struct sipe_svc_random
*random
,
109 guint bytes
= ((bits
+ 15) / 16) * 2;
110 guint16
*p
= g_malloc(bytes
);
112 SIPE_DEBUG_INFO("sipe_svc_fill_random: %d bits -> %d bytes",
115 random
->buffer
= (guint8
*) p
;
116 random
->length
= bytes
;
118 for (bytes
/= 2; bytes
; bytes
--)
119 *p
++ = rand() & 0xFFFF;
122 void sipe_svc_free_random(struct sipe_svc_random
*random
)
124 g_free(random
->buffer
);
127 static void sipe_svc_https_response(int return_code
,
129 SIPE_UNUSED_PARAMETER
const gchar
*content_type
,
133 struct svc_request
*data
= callback_data
;
134 struct sipe_svc
*svc
= data
->sipe_private
->svc
;
136 SIPE_DEBUG_INFO("sipe_svc_https_response: code %d", return_code
);
137 http_conn_set_close(conn
);
140 if ((return_code
== 200) && body
) {
141 sipe_xml
*xml
= sipe_xml_parse(body
, strlen(body
));
142 /* Internal callback: success */
143 (*data
->internal_cb
)(data
, body
, xml
);
146 /* Internal callback: failed */
147 (*data
->internal_cb
)(data
, NULL
, NULL
);
150 /* Internal callback has already called this */
153 svc
->pending_requests
= g_slist_remove(svc
->pending_requests
,
155 sipe_svc_request_free(data
);
158 static gboolean
sipe_svc_https_request(struct sipe_core_private
*sipe_private
,
161 const gchar
*content_type
,
163 svc_callback
*internal_callback
,
164 sipe_svc_callback
*callback
,
165 gpointer callback_data
)
167 struct svc_request
*data
= g_new0(struct svc_request
, 1);
168 gboolean ret
= FALSE
;
170 data
->sipe_private
= sipe_private
;
171 data
->uri
= g_strdup(uri
);
173 data
->conn
= http_conn_create(SIPE_CORE_PUBLIC
,
174 NULL
, /* HttpSession */
177 HTTP_CONN_NO_REDIRECT
,
181 NULL
, /* HttpConnAuth */
182 sipe_svc_https_response
,
186 data
->internal_cb
= internal_callback
;
188 data
->cb_data
= callback_data
;
189 sipe_svc_init(sipe_private
);
190 sipe_private
->svc
->pending_requests
= g_slist_prepend(sipe_private
->svc
->pending_requests
,
194 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri
);
195 sipe_svc_request_free(data
);
201 static gboolean
sipe_svc_wsdl_request(struct sipe_core_private
*sipe_private
,
203 const gchar
*additional_ns
,
204 const gchar
*soap_action
,
205 const gchar
*wsse_security
,
206 const gchar
*soap_body
,
207 svc_callback
*internal_callback
,
208 sipe_svc_callback
*callback
,
209 gpointer callback_data
)
211 gchar
*body
= g_strdup_printf("<?xml version=\"1.0\"?>\r\n"
213 " xmlns:auth=\"http://schemas.xmlsoap.org/ws/2006/12/authorization\""
214 " xmlns:wsa=\"http://www.w3.org/2005/08/addressing\""
215 " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\""
216 " xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\""
217 " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""
220 " <wsa:To>%s</wsa:To>"
222 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
224 " <wsa:Action>%s</wsa:Action>"
225 " <wsse:Security>%s</wsse:Security>"
227 " <soap:Body>%s</soap:Body>"
235 gboolean ret
= sipe_svc_https_request(sipe_private
,
248 static gboolean
new_soap_req(struct sipe_core_private
*sipe_private
,
250 const gchar
*soap_action
,
251 const gchar
*wsse_security
,
252 const gchar
*soap_body
,
253 svc_callback
*internal_callback
,
254 sipe_svc_callback
*callback
,
255 gpointer callback_data
)
257 return(sipe_svc_wsdl_request(sipe_private
,
259 "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" "
260 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
261 "xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"",
270 static void sipe_svc_wsdl_response(struct svc_request
*data
,
275 /* Callback: success */
276 (*data
->cb
)(data
->sipe_private
, data
->uri
, raw
, xml
, data
->cb_data
);
278 /* Callback: failed */
279 (*data
->cb
)(data
->sipe_private
, data
->uri
, NULL
, NULL
, data
->cb_data
);
283 gboolean
sipe_svc_get_and_publish_cert(struct sipe_core_private
*sipe_private
,
285 const gchar
*authuser
,
286 const gchar
*wsse_security
,
287 const gchar
*certreq
,
288 sipe_svc_callback
*callback
,
289 gpointer callback_data
)
291 struct sipe_svc_random id
;
294 gchar
*uuid
= get_uuid(sipe_private
);
298 /* random request ID */
299 sipe_svc_fill_random(&id
, 256);
300 id_base64
= g_base64_encode(id
.buffer
, id
.length
);
301 sipe_svc_free_random(&id
);
302 id_uuid
= generateUUIDfromEPID(id_base64
);
305 soap_body
= g_strdup_printf("<GetAndPublishCert"
306 " xmlns=\"http://schemas.microsoft.com/OCS/AuthWebServices/\""
307 " xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512/\""
308 " xmlns:wstep=\"http://schemas.microsoft.com/windows/pki/2009/01/enrollment\""
312 " <wst:RequestSecurityToken>"
313 " <wst:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wst:TokenType>"
314 " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>"
315 " <wsse:BinarySecurityToken"
316 " ValueType=\"http://schemas.microsoft.com/OCS/AuthWebServices.xsd#PKCS10\""
317 " EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#Base64Binary\""
318 " >\r\n%s</wsse:BinarySecurityToken>"
319 " <wstep:RequestID>%s</wstep:RequestID>"
320 " </wst:RequestSecurityToken>"
321 "</GetAndPublishCert>",
329 ret
= new_soap_req(sipe_private
,
331 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
334 sipe_svc_wsdl_response
,
343 * This functions encodes what the Microsoft Lync client does for
344 * Office365 accounts. It will most definitely fail for internal Lync
345 * installation that use TLS-DSK instead of NTLM.
347 * But for those anonymous authentication should already have succeeded.
348 * I guess we'll have to see what happens in real life...
350 gboolean
sipe_svc_webticket_lmc(struct sipe_core_private
*sipe_private
,
351 const gchar
*authuser
,
352 const gchar
*service_uri
,
353 sipe_svc_callback
*callback
,
354 gpointer callback_data
)
356 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
357 /* login.microsoftonline.com seems only to accept cleartext passwords :/ */
358 gchar
*security
= g_strdup_printf("<wsse:UsernameToken>"
359 " <wsse:Username>%s</wsse:Username>"
360 " <wsse:Password>%s</wsse:Password>"
361 "</wsse:UsernameToken>",
362 authuser
, sip
->password
);
364 gchar
*soap_body
= g_strdup_printf("<ps:RequestMultipleSecurityTokens>"
365 " <wst:RequestSecurityToken>"
366 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
368 " <wsa:EndpointReference>"
369 " <wsa:Address>%s</wsa:Address>"
370 " </wsa:EndpointReference>"
372 " </wst:RequestSecurityToken>"
373 "</ps:RequestMultipleSecurityTokens>",
376 gboolean ret
= sipe_svc_wsdl_request(sipe_private
,
377 "https://login.microsoftonline.com:443/RST2.srf",
378 "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" "
379 "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
380 "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"",
381 "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue",
384 sipe_svc_wsdl_response
,
393 static gchar
*sipe_svc_security_username(struct sipe_core_private
*sipe_private
,
394 const gchar
*authuser
)
396 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
397 struct sipe_svc_random nonce
;
399 guchar digest
[SIPE_DIGEST_SHA1_LENGTH
];
401 gchar
*base64
, *nonce_base64
, *created
;
402 guint len
, created_len
, password_len
;
406 sipe_svc_fill_random(&nonce
, 256);
407 nonce_base64
= g_base64_encode(nonce
.buffer
, nonce
.length
);
410 g_get_current_time(&now
);
411 created
= g_time_val_to_iso8601(&now
);
412 created_len
= strlen(created
);
415 password_len
= strlen(sip
->password
);
417 /* nonce + created + password */
418 len
= nonce
.length
+ created_len
+ password_len
;
419 p
= buf
= g_malloc(len
);
420 memcpy(p
, nonce
.buffer
, nonce
.length
);
422 memcpy(p
, created
, created_len
);
424 memcpy(p
, sip
->password
, password_len
);
426 /* Base64( SHA-1( nonce + created + password ) ) */
427 sipe_digest_sha1(buf
, len
, digest
);
428 base64
= g_base64_encode(digest
, SIPE_DIGEST_SHA1_LENGTH
);
430 ret
= g_strdup_printf("<wsse:UsernameToken>"
431 " <wsse:Username>%s</wsse:Username>"
432 " <wsse:Password Type=\"...#PasswordDigest\">%s</wsse:Password>"
433 " <wsse:Nonce>%s</wsse:Nonce>"
434 " <wsu:Created>%s</wsu:Created>"
435 "</wsse:UsernameToken>",
436 authuser
, base64
, nonce_base64
, created
);
441 g_free(nonce_base64
);
442 sipe_svc_free_random(&nonce
);
447 gboolean
sipe_svc_webticket(struct sipe_core_private
*sipe_private
,
449 const gchar
*authuser
,
450 const gchar
*wsse_security
,
451 const gchar
*service_uri
,
452 const struct sipe_svc_random
*entropy
,
453 sipe_svc_callback
*callback
,
454 gpointer callback_data
)
456 gchar
*uuid
= get_uuid(sipe_private
);
457 gchar
*security
= NULL
;
458 gchar
*secret
= g_base64_encode(entropy
->buffer
, entropy
->length
);
459 gchar
*soap_body
= g_strdup_printf("<wst:RequestSecurityToken Context=\"%s\">"
460 " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>"
461 " <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
463 " <wsa:EndpointReference>"
464 " <wsa:Address>%s</wsa:Address>"
465 " </wsa:EndpointReference>"
467 " <wst:Claims Dialect=\"urn:component:Microsoft.Rtc.WebAuthentication.2010:authclaims\">"
468 " <auth:ClaimType Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/uri\" Optional=\"false\">"
469 " <auth:Value>sip:%s</auth:Value>"
473 " <wst:BinarySecret>%s</wst:BinarySecret>"
475 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
476 "</wst:RequestSecurityToken>",
482 gboolean ret
= new_soap_req(sipe_private
,
484 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
487 (security
= sipe_svc_security_username(sipe_private
, authuser
)),
489 sipe_svc_wsdl_response
,
500 static void sipe_svc_metadata_response(struct svc_request
*data
,
505 /* Callback: success */
506 (*data
->cb
)(data
->sipe_private
, data
->uri
, raw
, xml
, data
->cb_data
);
508 /* Callback: failed */
509 (*data
->cb
)(data
->sipe_private
, data
->uri
, NULL
, NULL
, data
->cb_data
);
513 gboolean
sipe_svc_metadata(struct sipe_core_private
*sipe_private
,
515 sipe_svc_callback
*callback
,
516 gpointer callback_data
)
518 gchar
*mex_uri
= g_strdup_printf("%s/mex", uri
);
519 gboolean ret
= sipe_svc_https_request(sipe_private
,
524 sipe_svc_metadata_response
,