comparison libpurple/protocols/msn/nexus.c @ 23513:1b98e2090a71

Update MSN nexus functions so that tokens are properly updateable. It also uses a callback to signify that the token has been updated. Note: The updating does not actually work yet, but this commit is so that the next two updates will compile.
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Sat, 07 Jun 2008 06:08:01 +0000
parents d756a0477c06
children 9fdf0accd4aa
comparison
equal deleted inserted replaced
23512:b3890180aa2e 23513:1b98e2090a71
24 #include "msn.h" 24 #include "msn.h"
25 #include "soap2.h" 25 #include "soap2.h"
26 #include "nexus.h" 26 #include "nexus.h"
27 #include "notification.h" 27 #include "notification.h"
28 28
29
30 /************************************************************************** 29 /**************************************************************************
31 * Valid Ticket Tokens 30 * Valid Ticket Tokens
32 **************************************************************************/ 31 **************************************************************************/
33 32
34 #define SSO_VALID_TICKET_DOMAIN 0 33 #define SSO_VALID_TICKET_DOMAIN 0
78 } 77 }
79 78
80 g_free(nexus->tokens); 79 g_free(nexus->tokens);
81 g_free(nexus->policy); 80 g_free(nexus->policy);
82 g_free(nexus->nonce); 81 g_free(nexus->nonce);
82 g_free(nexus->cipher);
83 g_free(nexus->secret);
83 g_free(nexus); 84 g_free(nexus);
84 } 85 }
85 86
86 /************************************************************************** 87 /**************************************************************************
87 * RPS/SSO Authentication 88 * RPS/SSO Authentication
88 **************************************************************************/ 89 **************************************************************************/
89 90
90 static char * 91 static char *
91 sha1_hmac(const char *key, int key_len, const char *message, int msg_len) 92 rps_create_key(const char *key, int key_len, const char *data, size_t data_len)
92 { 93 {
93 PurpleCipherContext *context; 94 const guchar magic[] = "WS-SecureConversation";
95 const int magic_len = sizeof(magic) - 1;
96
97 PurpleCipherContext *hmac;
98 guchar hash1[20], hash2[20], hash3[20], hash4[20];
94 char *result; 99 char *result;
95 gboolean ret; 100
96 101 hmac = purple_cipher_context_new_by_name("hmac", NULL);
97 context = purple_cipher_context_new_by_name("hmac", NULL); 102
98 purple_cipher_context_set_option(context, "hash", "sha1"); 103 purple_cipher_context_set_option(hmac, "hash", "sha1");
99 purple_cipher_context_set_key_with_len(context, (guchar *)key, key_len); 104 purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
100 105 purple_cipher_context_append(hmac, magic, magic_len);
101 purple_cipher_context_append(context, (guchar *)message, msg_len); 106 purple_cipher_context_append(hmac, (guchar *)data, data_len);
102 result = g_malloc(20); 107 purple_cipher_context_digest(hmac, sizeof(hash1), hash1, NULL);
103 ret = purple_cipher_context_digest(context, 20, (guchar *)result, NULL); 108
104 109 purple_cipher_context_reset(hmac, NULL);
105 purple_cipher_context_destroy(context); 110 purple_cipher_context_set_option(hmac, "hash", "sha1");
111 purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
112 purple_cipher_context_append(hmac, hash1, 20);
113 purple_cipher_context_append(hmac, magic, magic_len);
114 purple_cipher_context_append(hmac, (guchar *)data, data_len);
115 purple_cipher_context_digest(hmac, sizeof(hash2), hash2, NULL);
116
117 purple_cipher_context_reset(hmac, NULL);
118 purple_cipher_context_set_option(hmac, "hash", "sha1");
119 purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
120 purple_cipher_context_append(hmac, hash1, 20);
121 purple_cipher_context_digest(hmac, sizeof(hash3), hash3, NULL);
122
123 purple_cipher_context_reset(hmac, NULL);
124 purple_cipher_context_set_option(hmac, "hash", "sha1");
125 purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
126 purple_cipher_context_append(hmac, hash3, sizeof(hash3));
127 purple_cipher_context_append(hmac, magic, magic_len);
128 purple_cipher_context_append(hmac, (guchar *)data, data_len);
129 purple_cipher_context_digest(hmac, sizeof(hash4), hash4, NULL);
130
131 purple_cipher_context_destroy(hmac);
132
133 result = g_malloc(24);
134 memcpy(result, hash2, sizeof(hash2));
135 memcpy(result + sizeof(hash2), hash4, 4);
106 136
107 return result; 137 return result;
108 } 138 }
109 139
110 static char * 140 static char *
111 rps_create_key(const char *key, int key_len, const char *data, size_t data_len) 141 des3_cbc(const char *key, const char *iv, const char *data, int len, gboolean decrypt)
112 {
113 char *hash1, *hash2, *hash3, *hash4;
114 char *result;
115
116 hash1 = sha1_hmac(key, key_len, data, data_len);
117 hash1 = g_realloc(hash1, 20 + data_len);
118 memcpy(hash1 + 20, data, data_len);
119 hash2 = sha1_hmac(key, key_len, hash1, 20 + data_len);
120
121 hash3 = sha1_hmac(key, key_len, hash1, 20);
122
123 hash3 = g_realloc(hash3, 20 + data_len);
124 memcpy(hash3 + 20, data, data_len);
125 hash4 = sha1_hmac(key, key_len, hash3, 20 + data_len);
126
127 result = g_malloc(24);
128 memcpy(result, hash2, 20);
129 memcpy(result + 20, hash4, 4);
130
131 g_free(hash1);
132 g_free(hash2);
133 g_free(hash3);
134 g_free(hash4);
135
136 return result;
137 }
138
139 static char *
140 des3_cbc(const char *key, const char *iv, const char *data, int len)
141 { 142 {
142 PurpleCipherContext *des3; 143 PurpleCipherContext *des3;
143 char *out; 144 char *out;
144 size_t outlen; 145 size_t outlen;
145 146
147 purple_cipher_context_set_key(des3, (guchar *)key); 148 purple_cipher_context_set_key(des3, (guchar *)key);
148 purple_cipher_context_set_batch_mode(des3, PURPLE_CIPHER_BATCH_MODE_CBC); 149 purple_cipher_context_set_batch_mode(des3, PURPLE_CIPHER_BATCH_MODE_CBC);
149 purple_cipher_context_set_iv(des3, (guchar *)iv, 8); 150 purple_cipher_context_set_iv(des3, (guchar *)iv, 8);
150 151
151 out = g_malloc(len); 152 out = g_malloc(len);
152 purple_cipher_context_encrypt(des3, (guchar *)data, len, (guchar *)out, &outlen); 153 if (decrypt)
154 purple_cipher_context_decrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
155 else
156 purple_cipher_context_encrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
153 157
154 purple_cipher_context_destroy(des3); 158 purple_cipher_context_destroy(des3);
155 159
156 return out; 160 return out;
157 } 161 }
161 #define HASH_SHA1 0x8004 165 #define HASH_SHA1 0x8004
162 static char * 166 static char *
163 msn_rps_encrypt(MsnNexus *nexus) 167 msn_rps_encrypt(MsnNexus *nexus)
164 { 168 {
165 MsnUsrKey *usr_key; 169 MsnUsrKey *usr_key;
166 const char *magic1 = "WS-SecureConversationSESSION KEY HASH"; 170 const char magic1[] = "SESSION KEY HASH";
167 const char *magic2 = "WS-SecureConversationSESSION KEY ENCRYPTION"; 171 const char magic2[] = "SESSION KEY ENCRYPTION";
172 PurpleCipherContext *hmac;
168 size_t len; 173 size_t len;
169 char *hash; 174 guchar hash[20];
170 char *key1, *key2, *key3; 175 char *key1, *key2, *key3;
171 gsize key1_len; 176 gsize key1_len;
177 int *iv;
172 char *nonce_fixed; 178 char *nonce_fixed;
173 char *cipher; 179 char *cipher;
174 char *response; 180 char *response;
175 181
176 usr_key = g_malloc(sizeof(MsnUsrKey)); 182 usr_key = g_malloc(sizeof(MsnUsrKey));
181 usr_key->iv_len = GUINT32_TO_LE(8); 187 usr_key->iv_len = GUINT32_TO_LE(8);
182 usr_key->hash_len = GUINT32_TO_LE(20); 188 usr_key->hash_len = GUINT32_TO_LE(20);
183 usr_key->cipher_len = GUINT32_TO_LE(72); 189 usr_key->cipher_len = GUINT32_TO_LE(72);
184 190
185 key1 = (char *)purple_base64_decode((const char *)nexus->tokens[MSN_AUTH_MESSENGER].secret, &key1_len); 191 key1 = (char *)purple_base64_decode((const char *)nexus->tokens[MSN_AUTH_MESSENGER].secret, &key1_len);
186 len = strlen(magic1); 192 key2 = rps_create_key(key1, key1_len, magic1, sizeof(magic1) - 1);
187 key2 = rps_create_key(key1, key1_len, magic1, len); 193 key3 = rps_create_key(key1, key1_len, magic2, sizeof(magic2) - 1);
188 len = strlen(magic2); 194
189 key3 = rps_create_key(key1, key1_len, magic2, len); 195 iv = (int *)usr_key->iv;
190 196 iv[0] = rand();
191 usr_key->iv[0] = 0x46; //rand() % 256; 197 iv[1] = rand();
192 usr_key->iv[1] = 0xC4;
193 usr_key->iv[2] = 0x14;
194 usr_key->iv[3] = 0x9F;
195 usr_key->iv[4] = 0xFF;
196 usr_key->iv[5] = 0xFC;
197 usr_key->iv[6] = 0x91;
198 usr_key->iv[7] = 0x61;
199 198
200 len = strlen(nexus->nonce); 199 len = strlen(nexus->nonce);
201 hash = sha1_hmac(key2, 24, nexus->nonce, len); 200 hmac = purple_cipher_context_new_by_name("hmac", NULL);
201 purple_cipher_context_set_option(hmac, "hash", "sha1");
202 purple_cipher_context_set_key_with_len(hmac, (guchar *)key2, 24);
203 purple_cipher_context_append(hmac, (guchar *)nexus->nonce, len);
204 purple_cipher_context_digest(hmac, 20, hash, NULL);
205 purple_cipher_context_destroy(hmac);
202 206
203 /* We need to pad this to 72 bytes, apparently */ 207 /* We need to pad this to 72 bytes, apparently */
204 nonce_fixed = g_malloc(len + 8); 208 nonce_fixed = g_malloc(len + 8);
205 memcpy(nonce_fixed, nexus->nonce, len); 209 memcpy(nonce_fixed, nexus->nonce, len);
206 memset(nonce_fixed + len, 0x08, 8); 210 memset(nonce_fixed + len, 0x08, 8);
207 cipher = des3_cbc(key3, usr_key->iv, nonce_fixed, len + 8); 211 cipher = des3_cbc(key3, usr_key->iv, nonce_fixed, len + 8, FALSE);
208 g_free(nonce_fixed); 212 g_free(nonce_fixed);
209 213
210 memcpy(usr_key->hash, hash, 20); 214 memcpy(usr_key->hash, hash, 20);
211 memcpy(usr_key->cipher, cipher, 72); 215 memcpy(usr_key->cipher, cipher, 72);
212 216
213 g_free(key1); 217 g_free(key1);
214 g_free(key2); 218 g_free(key2);
215 g_free(key3); 219 g_free(key3);
216 g_free(hash);
217 g_free(cipher); 220 g_free(cipher);
218 221
219 response = purple_base64_encode((guchar *)usr_key, sizeof(MsnUsrKey)); 222 response = purple_base64_encode((guchar *)usr_key, sizeof(MsnUsrKey));
220 223
221 g_free(usr_key); 224 g_free(usr_key);
225 228
226 /************************************************************************** 229 /**************************************************************************
227 * Login 230 * Login
228 **************************************************************************/ 231 **************************************************************************/
229 232
233 /* Used to specify which token to update when only doing single updates */
234 typedef struct _MsnNexusUpdateData MsnNexusUpdateData;
235 struct _MsnNexusUpdateData {
236 MsnNexus *nexus;
237 int id;
238 GSourceFunc cb;
239 gpointer data;
240 };
241
242 static void
243 save_tokens(GHashTable *table, const char *str)
244 {
245 char **elems, **cur, **tokens;
246
247 elems = g_strsplit(str, "&", 0);
248
249 for (cur = elems; *cur != NULL; cur++) {
250 tokens = g_strsplit(*cur, "=", 2);
251 g_hash_table_insert(table, tokens[0], tokens[1]);
252 /* Don't free each of the tokens, only the array. */
253 g_free(tokens);
254 }
255 g_strfreev(elems);
256 }
257
230 static gboolean 258 static gboolean
231 nexus_parse_response(MsnNexus *nexus, xmlnode *xml) 259 nexus_parse_response(MsnNexus *nexus, int id, xmlnode *xml)
232 { 260 {
233 xmlnode *node; 261 xmlnode *node;
262 xmlnode *cipher;
263 xmlnode *secret;
264 char *data;
234 gboolean result = FALSE; 265 gboolean result = FALSE;
266 gboolean parse_all = (id == -1);
235 267
236 node = xmlnode_get_child(xml, "Body/RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse"); 268 node = xmlnode_get_child(xml, "Body/RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse");
237 269
238 if (node) 270 if (!node)
239 node = node->next; /* The first one is not useful */
240 else
241 return FALSE; 271 return FALSE;
242 272
243 for (; node; node = node->next) { 273 /* The first node contains the stuff for updating tokens. */
274 cipher = xmlnode_get_child(node, "RequestedSecurityToken/EncryptedData/CipherData/CipherValue");
275 nexus->cipher = xmlnode_get_data(cipher);
276 secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
277 data = xmlnode_get_data(secret);
278 nexus->secret = (char *)purple_base64_decode(data, NULL);
279 g_free(data);
280
281 for (node = node->next; node; node = node->next) {
244 xmlnode *token = xmlnode_get_child(node, "RequestedSecurityToken/BinarySecurityToken"); 282 xmlnode *token = xmlnode_get_child(node, "RequestedSecurityToken/BinarySecurityToken");
245 xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret"); 283 xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
246 xmlnode *expires = xmlnode_get_child(node, "LifeTime/Expires"); 284 xmlnode *expires = xmlnode_get_child(node, "LifeTime/Expires");
247 285
248 if (token) { 286 if (token) {
249 char *token_str, *expiry_str; 287 char *token_str, *expiry_str;
250 const char *id_str = xmlnode_get_attrib(token, "Id"); 288 const char *id_str = xmlnode_get_attrib(token, "Id");
251 char **elems, **cur, **tokens;
252 int id;
253 289
254 if (id_str == NULL) continue; 290 if (id_str == NULL) continue;
255 291
256 id = atol(id_str + 7) - 1; /* 'Compact#' or 'PPToken#' */ 292 if (parse_all)
293 id = atol(id_str + 7) - 1; /* 'Compact#' or 'PPToken#' */
257 if (id >= nexus->token_len) 294 if (id >= nexus->token_len)
258 continue; /* Where did this come from? */ 295 continue; /* Where did this come from? */
259 296
260 token_str = xmlnode_get_data(token); 297 token_str = xmlnode_get_data(token);
261 if (token_str == NULL) continue; 298 if (token_str == NULL) continue;
262 elems = g_strsplit(token_str, "&", 0); 299
263 300 save_tokens(nexus->tokens[id].token, token_str);
264 for (cur = elems; *cur != NULL; cur++){
265 tokens = g_strsplit(*cur, "=", 2);
266 g_hash_table_insert(nexus->tokens[id].token, tokens[0], tokens[1]);
267 /* Don't free each of the tokens, only the array. */
268 g_free(tokens);
269 }
270
271 g_free(token_str); 301 g_free(token_str);
272 g_strfreev(elems);
273 302
274 if (secret) 303 if (secret)
275 nexus->tokens[id].secret = xmlnode_get_data(secret); 304 nexus->tokens[id].secret = xmlnode_get_data(secret);
276 else 305 else
277 nexus->tokens[id].secret = NULL; 306 nexus->tokens[id].secret = NULL;
296 static void 325 static void
297 nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) 326 nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
298 { 327 {
299 MsnNexus *nexus = data; 328 MsnNexus *nexus = data;
300 MsnSession *session = nexus->session; 329 MsnSession *session = nexus->session;
301 char *msn_twn_t, *msn_twn_p, *ticket; 330 const char *ticket;
302 char *response; 331 char *response;
303 332
304 if (resp == NULL) { 333 if (resp == NULL) {
305 msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect")); 334 msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect"));
306 return; 335 return;
307 } 336 }
308 337
309 if (!nexus_parse_response(nexus, resp->xml)) { 338 if (!nexus_parse_response(nexus, -1, resp->xml)) {
310 msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Invalid response")); 339 msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Invalid response"));
311 return; 340 return;
312 } 341 }
313 342
314 /*setup the t and p parameter for session*/ 343 ticket = msn_nexus_get_token_str(nexus, MSN_AUTH_MESSENGER);
315 msn_twn_t = g_hash_table_lookup(nexus->tokens[MSN_AUTH_MESSENGER].token, "t");
316 msn_twn_p = g_hash_table_lookup(nexus->tokens[MSN_AUTH_MESSENGER].token, "p");
317 g_free(session->passport_info.t);
318 session->passport_info.t = g_strdup(msn_twn_t);
319 g_free(session->passport_info.p);
320 session->passport_info.p = g_strdup(msn_twn_p);
321
322 ticket = g_strdup_printf("t=%s&p=%s", msn_twn_t, msn_twn_p);
323 response = msn_rps_encrypt(nexus); 344 response = msn_rps_encrypt(nexus);
324 msn_got_login_params(session, ticket, response); 345 msn_got_login_params(session, ticket, response);
325 g_free(ticket);
326 g_free(response); 346 g_free(response);
327 } 347 }
328 348
329 /*when connect, do the SOAP Style windows Live ID authentication */ 349 /*when connect, do the SOAP Style windows Live ID authentication */
330 void 350 void
331 msn_nexus_connect(MsnNexus *nexus) 351 msn_nexus_connect(MsnNexus *nexus)
332 { 352 {
333 MsnSession *session = nexus->session; 353 MsnSession *session = nexus->session;
334 char *username, *password; 354 const char *username;
355 char *password;
335 GString *domains; 356 GString *domains;
336 char *request; 357 char *request;
337 int i; 358 int i;
338 359
339 MsnSoapMessage *soap; 360 MsnSoapMessage *soap;
340 361
362 purple_debug_info("MSN Nexus","Starting Windows Live ID authentication\n");
341 msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE); 363 msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
342 364
343 username = g_strdup(purple_account_get_username(session->account)); 365 username = purple_account_get_username(session->account);
344 password = g_strndup(purple_connection_get_password(session->account->gc), 16); 366 password = g_strndup(purple_connection_get_password(session->account->gc), 16);
345 367
346 purple_debug_info("msnp15", "Logging on %s, with policy '%s', nonce '%s'\n", 368 purple_debug_info("msnp15", "Logging on %s, with policy '%s', nonce '%s'\n",
347 username, nexus->policy, nexus->nonce); 369 username, nexus->policy, nexus->nonce);
348 370
355 ticket_domains[i][SSO_VALID_TICKET_POLICY] : 377 ticket_domains[i][SSO_VALID_TICKET_POLICY] :
356 nexus->policy); 378 nexus->policy);
357 } 379 }
358 380
359 request = g_strdup_printf(MSN_SSO_TEMPLATE, username, password, domains->str); 381 request = g_strdup_printf(MSN_SSO_TEMPLATE, username, password, domains->str);
360 g_free(username);
361 g_free(password); 382 g_free(password);
362 g_string_free(domains, TRUE); 383 g_string_free(domains, TRUE);
363 384
364 soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1)); 385 soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1));
365 g_free(request); 386 g_free(request);
368 } 389 }
369 390
370 static void 391 static void
371 nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) 392 nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
372 { 393 {
373 MsnNexus *nexus = data; 394 MsnNexusUpdateData *ud = data;
374 395 MsnNexus *nexus = ud->nexus;
375 nexus_parse_response(nexus, resp->xml); 396 char iv[8] = {0,0,0,0,0,0,0,0};
376 } 397 xmlnode *enckey;
377 398 char *tmp;
378 static void 399 char *nonce;
379 msn_nexus_update_token(MsnNexus *nexus, int id) 400 gsize len;
401 char *key;
402
403 char *decrypted_pp;
404 char *decrypted_data;
405
406 purple_debug_info("msnp15", "Got Update Response for %s.\n", ticket_domains[ud->id][SSO_VALID_TICKET_DOMAIN]);
407
408 enckey = xmlnode_get_child(resp->xml, "Header/Security/DerivedKeyToken");
409 while (enckey) {
410 if (g_str_equal(xmlnode_get_attrib(enckey, "Id"), "EncKey"))
411 break;
412 enckey = xmlnode_get_next_twin(enckey);
413 }
414 if (!enckey) {
415 purple_debug_info("msnp15", "Invalid Response.\n");
416 return;
417 }
418
419 tmp = xmlnode_get_data(xmlnode_get_child(enckey, "Nonce"));
420 nonce = (char *)purple_base64_decode(tmp, &len);
421 key = rps_create_key(nexus->secret, 24, nonce, len);
422 g_free(tmp);
423 g_free(nonce);
424
425 tmp = xmlnode_get_data(xmlnode_get_child(resp->xml,
426 "Header/EncryptedPP/EncryptedData/CipherData/CipherValue"));
427 if (tmp) {
428 /* Don't know what this is for yet */
429 decrypted_pp = des3_cbc(key, iv, tmp, len, TRUE);
430 g_free(tmp);
431 purple_debug_info("msnp15", "Got Response Header EncryptedPP: %s\n", decrypted_pp);
432 g_free(decrypted_pp);
433 }
434
435 tmp = xmlnode_get_data(xmlnode_get_child(resp->xml,
436 "Body/EncryptedData/CipherData/CipherValue"));
437 if (tmp) {
438 char *unescaped;
439 xmlnode *rstresponse;
440 unescaped = (char *)purple_base64_decode(tmp, &len);
441 g_free(tmp);
442 decrypted_data = des3_cbc(key, iv, unescaped, len, TRUE);
443 g_free(unescaped);
444 rstresponse = xmlnode_from_str(decrypted_data, -1);
445 g_hash_table_remove_all(nexus->tokens[ud->id].token);
446 save_tokens(nexus->tokens[ud->id].token,
447 xmlnode_get_data(xmlnode_get_child(rstresponse,
448 "RequestSecurityTokenResponse/RequestedSecurityToken/BinarySecurityToken")));
449 purple_debug_info("msnp15", "Got Response Body EncryptedData: %s\n", decrypted_data);
450 g_free(decrypted_data);
451 }
452
453 if (ud->cb)
454 purple_timeout_add(0, ud->cb, ud->data);
455
456 g_free(ud);
457 }
458
459 void
460 msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
380 { 461 {
381 MsnSession *session = nexus->session; 462 MsnSession *session = nexus->session;
382 char *username, *password; 463 MsnNexusUpdateData *ud;
464 PurpleCipherContext *sha1;
465 PurpleCipherContext *hmac;
466
467 char *key;
468
469 guchar digest[20];
470
471 struct tm *tm;
472 time_t now;
473 char *now_str;
474 char *timestamp;
475 char *timestamp_b64;
476
383 char *domain; 477 char *domain;
478 char *domain_b64;
479
480 char *signedinfo;
481 gint32 nonce[6];
482 int i;
483 char *nonce_b64;
484 char *signature_b64;
485 guchar signature[20];
486
384 char *request; 487 char *request;
385
386 MsnSoapMessage *soap; 488 MsnSoapMessage *soap;
387 489
388 username = g_strdup(purple_account_get_username(session->account)); 490 purple_debug_info("msnp15",
389 password = g_strndup(purple_connection_get_password(session->account->gc), 16); 491 "Updating ticket for user '%s' on domain '%s'\n",
390 492 purple_account_get_username(session->account),
391 purple_debug_info("msnp15", "Updating ticket for user '%s' on domain '%s'\n", 493 ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
392 username, ticket_domains[id][SSO_VALID_TICKET_DOMAIN]); 494
393 495 ud = g_new0(MsnNexusUpdateData, 1);
394 /* TODO: This really assumes if we send RSTn, the server responds with 496 ud->nexus = nexus;
395 Compactn, even if there is no RST(n-1). This needs checking. 497 ud->id = id;
396 */ 498 ud->cb = cb;
499 ud->data = data;
500
501 sha1 = purple_cipher_context_new_by_name("sha1", NULL);
502
397 domain = g_strdup_printf(MSN_SSO_RST_TEMPLATE, 503 domain = g_strdup_printf(MSN_SSO_RST_TEMPLATE,
398 id, 504 0,
399 ticket_domains[id][SSO_VALID_TICKET_DOMAIN], 505 ticket_domains[id][SSO_VALID_TICKET_DOMAIN],
400 ticket_domains[id][SSO_VALID_TICKET_POLICY] != NULL ? 506 ticket_domains[id][SSO_VALID_TICKET_POLICY] != NULL ?
401 ticket_domains[id][SSO_VALID_TICKET_POLICY] : 507 ticket_domains[id][SSO_VALID_TICKET_POLICY] :
402 nexus->policy); 508 nexus->policy);
403 509 purple_cipher_context_append(sha1, (guchar *)domain, strlen(domain));
404 request = g_strdup_printf(MSN_SSO_TEMPLATE, username, password, domain); 510 purple_cipher_context_digest(sha1, 20, digest, NULL);
405 g_free(username); 511 domain_b64 = purple_base64_encode(digest, 20);
406 g_free(password); 512
513 now = time(NULL);
514 tm = gmtime(&now);
515 now_str = g_strdup(purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm));
516 now += 5*60;
517 tm = gmtime(&now);
518 timestamp = g_strdup_printf(MSN_SSO_TIMESTAMP_TEMPLATE,
519 now_str,
520 purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm));
521 purple_cipher_context_reset(sha1, NULL);
522 purple_cipher_context_append(sha1, (guchar *)timestamp, strlen(timestamp));
523 purple_cipher_context_digest(sha1, 20, digest, NULL);
524 timestamp_b64 = purple_base64_encode(digest, 20);
525 g_free(now_str);
526
527 purple_cipher_context_destroy(sha1);
528
529 signedinfo = g_strdup_printf(MSN_SSO_SIGNEDINFO_TEMPLATE,
530 domain_b64,
531 timestamp_b64);
532
533 for (i = 0; i < 6; i++)
534 nonce[i] = rand();
535 nonce_b64 = purple_base64_encode((guchar *)&nonce, sizeof(nonce));
536
537 key = rps_create_key(nexus->secret, 24, (char *)nonce, sizeof(nonce));
538 hmac = purple_cipher_context_new_by_name("hmac", NULL);
539 purple_cipher_context_set_option(hmac, "hash", "sha1");
540 purple_cipher_context_set_key_with_len(hmac, (guchar *)key, 24);
541 purple_cipher_context_append(hmac, (guchar *)signedinfo, strlen(signedinfo));
542 purple_cipher_context_digest(hmac, 20, signature, NULL);
543 purple_cipher_context_destroy(hmac);
544 signature_b64 = purple_base64_encode(signature, 20);
545
546 request = g_strdup_printf(MSN_SSO_TOKEN_UPDATE_TEMPLATE,
547 nexus->cipher,
548 nonce_b64,
549 timestamp,
550 signedinfo,
551 signature_b64,
552 domain);
553
554 g_free(nonce_b64);
555 g_free(domain_b64);
556 g_free(timestamp_b64);
557 g_free(timestamp);
558 g_free(key);
559 g_free(signature_b64);
560 g_free(signedinfo);
407 g_free(domain); 561 g_free(domain);
408 562
409 soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1)); 563 soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1));
410 g_free(request); 564 g_free(request);
411 msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL, 565 msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL,
412 nexus_got_update_cb, nexus); 566 nexus_got_update_cb, ud);
413 } 567 }
414 568
415 GHashTable * 569 GHashTable *
416 msn_nexus_get_token(MsnNexus *nexus, MsnAuthDomains id) 570 msn_nexus_get_token(MsnNexus *nexus, MsnAuthDomains id)
417 { 571 {
418 g_return_val_if_fail(nexus != NULL, NULL); 572 g_return_val_if_fail(nexus != NULL, NULL);
419 g_return_val_if_fail(id < nexus->token_len, NULL); 573 g_return_val_if_fail(id < nexus->token_len, NULL);
420
421 if (time(NULL) > nexus->tokens[id].expiry)
422 msn_nexus_update_token(nexus, id);
423 574
424 return nexus->tokens[id].token; 575 return nexus->tokens[id].token;
425 } 576 }
426 577
427 const char * 578 const char *
439 msn_p = g_hash_table_lookup(token, "p"); 590 msn_p = g_hash_table_lookup(token, "p");
440 591
441 g_return_val_if_fail(msn_t != NULL, NULL); 592 g_return_val_if_fail(msn_t != NULL, NULL);
442 g_return_val_if_fail(msn_p != NULL, NULL); 593 g_return_val_if_fail(msn_p != NULL, NULL);
443 594
444 ret = g_snprintf(buf, sizeof(buf) - 1, "t=%s&amp;p=%s", msn_t, msn_p); 595 ret = g_snprintf(buf, sizeof(buf) - 1, "t=%s&p=%s", msn_t, msn_p);
445 g_return_val_if_fail(ret != -1, NULL); 596 g_return_val_if_fail(ret != -1, NULL);
446 597
447 return buf; 598 return buf;
448 } 599 }
449 600