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"
45 #include "sipe-utils.h"
50 /* forward declaration */
52 typedef void (svc_callback
)(struct svc_request
*data
,
57 struct sipe_core_private
*sipe_private
;
58 svc_callback
*internal_cb
;
59 sipe_svc_callback
*cb
;
66 GSList
*pending_requests
;
69 static void sipe_svc_request_free(struct svc_request
*data
)
72 http_conn_free(data
->conn
);
74 /* Callback: aborted */
75 (*data
->cb
)(data
->sipe_private
, NULL
, NULL
, NULL
, data
->cb_data
);
80 void sipe_svc_free(struct sipe_core_private
*sipe_private
)
82 struct sipe_svc
*svc
= sipe_private
->svc
;
86 if (svc
->pending_requests
) {
87 GSList
*entry
= svc
->pending_requests
;
89 sipe_svc_request_free(entry
->data
);
92 g_slist_free(svc
->pending_requests
);
96 sipe_private
->svc
= NULL
;
99 static void sipe_svc_init(struct sipe_core_private
*sipe_private
)
101 if (sipe_private
->svc
)
104 sipe_private
->svc
= g_new0(struct sipe_svc
, 1);
107 static void sipe_svc_https_response(int return_code
,
109 SIPE_UNUSED_PARAMETER
const gchar
*content_type
,
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
);
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
);
126 /* Internal callback: failed */
127 (*data
->internal_cb
)(data
, NULL
, NULL
);
130 /* Internal callback has already called this */
133 svc
->pending_requests
= g_slist_remove(svc
->pending_requests
,
135 sipe_svc_request_free(data
);
138 static gboolean
sipe_svc_https_request(struct sipe_core_private
*sipe_private
,
141 const gchar
*content_type
,
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 */
157 HTTP_CONN_NO_REDIRECT
,
161 NULL
, /* HttpConnAuth */
162 sipe_svc_https_response
,
166 data
->internal_cb
= internal_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
,
174 SIPE_DEBUG_ERROR("failed to create HTTP connection to %s", uri
);
175 sipe_svc_request_free(data
);
181 static gboolean
sipe_svc_wsdl_request(struct sipe_core_private
*sipe_private
,
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"
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\""
200 " <wsa:To>%s</wsa:To>"
202 " <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>"
204 " <wsa:Action>%s</wsa:Action>"
205 " <wsse:Security>%s</wsse:Security>"
207 " <soap:Body>%s</soap:Body>"
215 gboolean ret
= sipe_svc_https_request(sipe_private
,
228 static gboolean
new_soap_req(struct sipe_core_private
*sipe_private
,
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
,
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\"",
250 static void sipe_svc_wsdl_response(struct svc_request
*data
,
255 /* Callback: success */
256 (*data
->cb
)(data
->sipe_private
, data
->uri
, raw
, xml
, data
->cb_data
);
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
,
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
;
274 gchar
*uuid
= get_uuid(sipe_private
);
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
);
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\""
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>",
309 ret
= new_soap_req(sipe_private
,
311 "http://schemas.microsoft.com/OCS/AuthWebServices/GetAndPublishCert",
314 sipe_svc_wsdl_response
,
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>"
348 " <wsa:EndpointReference>"
349 " <wsa:Address>%s</wsa:Address>"
350 " </wsa:EndpointReference>"
352 " </wst:RequestSecurityToken>"
353 "</ps:RequestMultipleSecurityTokens>",
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",
364 sipe_svc_wsdl_response
,
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
;
379 guchar digest
[SIPE_DIGEST_SHA1_LENGTH
];
381 gchar
*base64
, *nonce_base64
, *created
;
382 guint len
, created_len
, password_len
;
386 sipe_tls_fill_random(&nonce
, 256);
387 nonce_base64
= g_base64_encode(nonce
.buffer
, nonce
.length
);
390 g_get_current_time(&now
);
391 created
= g_time_val_to_iso8601(&now
);
392 created_len
= strlen(created
);
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
);
402 memcpy(p
, created
, 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
);
421 g_free(nonce_base64
);
422 sipe_tls_free_random(&nonce
);
427 gboolean
sipe_svc_webticket(struct sipe_core_private
*sipe_private
,
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>"
443 " <wsa:EndpointReference>"
444 " <wsa:Address>%s</wsa:Address>"
445 " </wsa:EndpointReference>"
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>"
453 " <wst:BinarySecret>%s</wst:BinarySecret>"
455 " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>"
456 "</wst:RequestSecurityToken>",
462 gboolean ret
= new_soap_req(sipe_private
,
464 "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue",
467 (security
= sipe_svc_security_username(sipe_private
, authuser
)),
469 sipe_svc_wsdl_response
,
480 static void sipe_svc_metadata_response(struct svc_request
*data
,
485 /* Callback: success */
486 (*data
->cb
)(data
->sipe_private
, data
->uri
, raw
, xml
, data
->cb_data
);
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
,
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
,
504 sipe_svc_metadata_response
,