Mercurial > pidgin.yaz
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&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 |