/* Plugin of Miranda IM for communicating with users of the MSN Messenger protocol. Copyright (c) 2012-2014 Miranda NG Team Copyright (c) 2007-2012 Boris Krasnovskiy. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "stdafx.h" #include "msn_proto.h" #include "des.h" /* SkyLogin Prototypes */ typedef void* SkyLogin; typedef SkyLogin (*pfnSkyLogin_Init)(); typedef void (*pfnSkyLogin_Exit)(SkyLogin pInst); typedef int (*pfnSkyLogin_LoadCredentials)(SkyLogin pInst, char *pszUser); typedef int (*pfnSkyLogin_PerformLogin)(SkyLogin pInst, char *pszUser, char *pszPass); typedef int (*pfnSkyLogin_CreateUICString)(SkyLogin pInst, const char *pszNonce, char *pszOutUIC); typedef int (*pfnSkyLogin_PerformLoginOAuth)(SkyLogin pInst, const char *OAuth); typedef int (*pfnSkyLogin_GetCredentialsUIC)(SkyLogin pInst, char *pszOutUIC); typedef char *(*pfnSkyLogin_GetUser)(SkyLogin pInst); #define LOAD_FN(name) (##name = (pfn##name)GetProcAddress(hLibSkylogin, #name)) static const char defaultPassportUrl[] = "https://login.live.com/RST2.srf"; static const char authPacket[] = "" "" "" "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue" "HTTPS://login.live.com:443//RST2.srf" "%u" "" "{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}" "5" "1" "" "AQAAAAIAAABsYwQAAAAxMDMz" "" "" "" "%s" "%s" "" "" "%S" "%S" "" "" "" "" "" "" "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" "" "" "http://Passport.NET/tb" "" "" "" "" "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" "" "" "chatservice.live.com" "" "" "" "" "" "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" "" "" "messenger.msn.com" "" "" "" "" "" "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" "" "" "messengersecure.live.com" "" "" "" "" "" "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" "" "" "contacts.msn.com" "" "" "" "" "" "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" "" "" "storage.msn.com" "" "" "" "" "" "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" "" "" "sup.live.com" "" "" "" "" "" "" ""; ///////////////////////////////////////////////////////////////////////////////////////// // Performs the MSN Passport login via TLS int CMsnProto::MSN_GetPassportAuth(void) { int retVal = -1; char szPassword[100]; db_get_static(NULL, m_szModuleName, "Password", szPassword, sizeof(szPassword)); szPassword[16] = 0; time_t ts = time(NULL); TCHAR szTs1[64], szTs2[64]; tmi.printTimeStamp(UTC_TIME_HANDLE, ts, _T("I"), szTs1, SIZEOF(szTs1), 0); tmi.printTimeStamp(UTC_TIME_HANDLE, ts + 20 * 60, _T("I"), szTs2, SIZEOF(szTs2), 0); CMStringA szAuthInfo(FORMAT, authPacket, int(ts), MyOptions.szEmail, ptrA(HtmlEncode(szPassword)), szTs1, szTs2); char* szPassportHost = (char*)mir_alloc(256); if (db_get_static(NULL, m_szModuleName, "MsnPassportHost", szPassportHost, 256)) mir_strcpy(szPassportHost, defaultPassportUrl); bool defaultUrlAllow = mir_strcmp(szPassportHost, defaultPassportUrl) != 0; char *tResult = NULL; while (retVal == -1) { unsigned status; tResult = getSslResult(&szPassportHost, szAuthInfo, NULL, status); if (tResult == NULL) { if (defaultUrlAllow) { mir_strcpy(szPassportHost, defaultPassportUrl); defaultUrlAllow = false; continue; } else { retVal = 4; break; } } switch (status) { case 200: const char *errurl; { errurl = NULL; ezxml_t xml = ezxml_parse_str(tResult, mir_strlen(tResult)); ezxml_t tokr = ezxml_get(xml, "S:Body", 0, "wst:RequestSecurityTokenResponseCollection", 0, "wst:RequestSecurityTokenResponse", -1); while (tokr != NULL) { ezxml_t toks = ezxml_get(tokr, "wst:RequestedSecurityToken", 0, "wsse:BinarySecurityToken", -1); const char* addr = ezxml_txt(ezxml_get(tokr, "wsp:AppliesTo", 0, "wsa:EndpointReference", 0, "wsa:Address", -1)); if (mir_strcmp(addr, "http://Passport.NET/tb") == 0) { ezxml_t node = ezxml_get(tokr, "wst:RequestedSecurityToken", 0, "EncryptedData", -1); free(hotAuthToken); hotAuthToken = ezxml_toxml(node, 0); setString("hotAuthToken", hotAuthToken); node = ezxml_get(tokr, "wst:RequestedProofToken", 0, "wst:BinarySecret", -1); replaceStr(hotSecretToken, ezxml_txt(node)); setString("hotSecretToken", hotSecretToken); } else if (mir_strcmp(addr, "chatservice.live.com") == 0) { ezxml_t node = ezxml_get(tokr, "wst:RequestedProofToken", 0, "wst:BinarySecret", -1); if (toks) { replaceStr(authStrToken, ezxml_txt(toks)); replaceStr(authSecretToken, ezxml_txt(node)); retVal = 0; } else { errurl = ezxml_txt(ezxml_get(tokr, "S:Fault", 0, "psf:pp", 0, "psf:flowurl", -1)); } } else if (mir_strcmp(addr, "messenger.msn.com") == 0 && toks) { const char* tok = ezxml_txt(toks); char* ch = (char*)strchr(tok, '&'); *ch = 0; replaceStr(tAuthToken, tok + 2); replaceStr(pAuthToken, ch + 3); *ch = '&'; } else if (mir_strcmp(addr, "contacts.msn.com") == 0 && toks) { replaceStr(authContactToken, ezxml_txt(toks)); setString("authContactToken", authContactToken); } else if (mir_strcmp(addr, "messengersecure.live.com") == 0 && toks) { replaceStr(oimSendToken, ezxml_txt(toks)); } else if (mir_strcmp(addr, "storage.msn.com") == 0 && toks) { replaceStr(authStorageToken, ezxml_txt(toks)); setString("authStorageToken", authStorageToken); } tokr = ezxml_next(tokr); } if (retVal != 0) { if (errurl) { debugLogA("Starting URL: '%s'", errurl); CallService(MS_UTILS_OPENURL, OUF_NEWWINDOW, (LPARAM)errurl); } ezxml_t tokf = ezxml_get(xml, "S:Body", 0, "S:Fault", 0, "S:Detail", -1); ezxml_t tokrdr = ezxml_child(tokf, "psf:redirectUrl"); if (tokrdr != NULL) { mir_strcpy(szPassportHost, ezxml_txt(tokrdr)); debugLogA("Redirected to '%s'", szPassportHost); } else { const char* szFault = ezxml_txt(ezxml_get(tokf, "psf:error", 0, "psf:value", -1)); retVal = mir_strcmp(szFault, "0x80048821") == 0 ? 3 : (tokf ? 5 : 7); if (retVal != 3 && defaultUrlAllow) { mir_strcpy(szPassportHost, defaultPassportUrl); defaultUrlAllow = false; retVal = -1; } else if (retVal != 3 && retVal != 7) { char err[512]; mir_snprintf(err, SIZEOF(err), "Unknown Authentication error: %s", szFault); MSN_ShowError(err); } } } ezxml_free(xml); } break; default: if (defaultUrlAllow) { mir_strcpy(szPassportHost, defaultPassportUrl); defaultUrlAllow = false; } else retVal = 6; } mir_free(tResult); } if (retVal != 0) { if (!g_bTerminated) { switch (retVal) { case 3: MSN_ShowError("Your username or password is incorrect"); ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD); break; case 5: break; default: MSN_ShowError("Unable to contact MS Passport servers check proxy/firewall settings"); ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NOSERVER); break; } } } else setString("MsnPassportHost", szPassportHost); mir_free(szPassportHost); debugLogA("MSN_CheckRedirector exited with errorCode = %d", retVal); return retVal; } static void derive_key(BYTE* der, unsigned char* key, size_t keylen, unsigned char* data, size_t datalen) { BYTE hash1[MIR_SHA1_HASH_SIZE]; BYTE hash2[MIR_SHA1_HASH_SIZE]; BYTE hash3[MIR_SHA1_HASH_SIZE]; BYTE hash4[MIR_SHA1_HASH_SIZE]; const size_t buflen = MIR_SHA1_HASH_SIZE + datalen; BYTE* buf = (BYTE*)alloca(buflen); mir_hmac_sha1(hash1, key, keylen, data, datalen); mir_hmac_sha1(hash3, key, keylen, hash1, MIR_SHA1_HASH_SIZE); memcpy(buf, hash1, MIR_SHA1_HASH_SIZE); memcpy(buf + MIR_SHA1_HASH_SIZE, data, datalen); mir_hmac_sha1(hash2, key, keylen, buf, buflen); memcpy(buf, hash3, MIR_SHA1_HASH_SIZE); memcpy(buf + MIR_SHA1_HASH_SIZE, data, datalen); mir_hmac_sha1(hash4, key, keylen, buf, buflen); memcpy(der, hash2, MIR_SHA1_HASH_SIZE); memcpy(der + MIR_SHA1_HASH_SIZE, hash4, 4); } typedef struct tag_MsgrUsrKeyHdr { unsigned size; unsigned cryptMode; unsigned cipherType; unsigned hashType; unsigned ivLen; unsigned hashLen; unsigned long cipherLen; } MsgrUsrKeyHdr; static const MsgrUsrKeyHdr userKeyHdr = { sizeof(MsgrUsrKeyHdr), 1, // CRYPT_MODE_CBC 0x6603, // CALG_3DES 0x8004, // CALG_SHA1 8, // sizeof(ivBytes) MIR_SHA1_HASH_SIZE, 72 // sizeof(cipherBytes); }; static unsigned char* PKCS5_Padding(char* in, size_t &len) { const size_t nlen = ((len >> 3) + 1) << 3; unsigned char* res = (unsigned char*)mir_alloc(nlen); memcpy(res, in, len); const unsigned char pad = 8 - (len & 7); memset(res + len, pad, pad); len = nlen; return res; } char* CMsnProto::GenerateLoginBlob(char* challenge) { unsigned key1len; BYTE *key1 = (BYTE*)mir_base64_decode(authSecretToken, &key1len); BYTE key2[MIR_SHA1_HASH_SIZE + 4]; BYTE key3[MIR_SHA1_HASH_SIZE + 4]; static const unsigned char encdata1[] = "WS-SecureConversationSESSION KEY HASH"; static const unsigned char encdata2[] = "WS-SecureConversationSESSION KEY ENCRYPTION"; derive_key(key2, key1, key1len, (unsigned char*)encdata1, sizeof(encdata1) - 1); derive_key(key3, key1, key1len, (unsigned char*)encdata2, sizeof(encdata2) - 1); size_t chllen = mir_strlen(challenge); BYTE hash[MIR_SHA1_HASH_SIZE]; mir_hmac_sha1(hash, key2, MIR_SHA1_HASH_SIZE + 4, (BYTE*)challenge, chllen); unsigned char* newchl = PKCS5_Padding(challenge, chllen); const size_t pktsz = sizeof(MsgrUsrKeyHdr) + MIR_SHA1_HASH_SIZE + 8 + chllen; unsigned char* userKey = (unsigned char*)alloca(pktsz); unsigned char* p = userKey; memcpy(p, &userKeyHdr, sizeof(MsgrUsrKeyHdr)); ((MsgrUsrKeyHdr*)p)->cipherLen = (int)chllen; p += sizeof(MsgrUsrKeyHdr); unsigned char iv[8]; CallService(MS_UTILS_GETRANDOM, sizeof(iv), (LPARAM)iv); memcpy(p, iv, sizeof(iv)); p += sizeof(iv); memcpy(p, hash, sizeof(hash)); p += MIR_SHA1_HASH_SIZE; des3_context ctxd; memset(&ctxd, 0, sizeof(ctxd)); des3_set_3keys(&ctxd, key3); des3_cbc_encrypt(&ctxd, iv, newchl, p, (int)chllen); mir_free(newchl); return mir_base64_encode(userKey, (unsigned)pktsz); } CMStringA CMsnProto::HotmailLogin(const char* url) { unsigned char nonce[24]; CallService(MS_UTILS_GETRANDOM, sizeof(nonce), (LPARAM)nonce); unsigned key1len; BYTE *key1 = (BYTE*)mir_base64_decode(hotSecretToken, &key1len); static const unsigned char encdata[] = "WS-SecureConversation"; const size_t data1len = sizeof(nonce) + sizeof(encdata) - 1; unsigned char* data1 = (unsigned char*)alloca(data1len); memcpy(data1, encdata, sizeof(encdata) - 1); memcpy(data1 + sizeof(encdata) - 1, nonce, sizeof(nonce)); unsigned char key2[MIR_SHA1_HASH_SIZE + 4]; derive_key(key2, key1, key1len, data1, data1len); CMStringA result; result.Format("%s&da=%s&nonce=", url, ptrA(mir_urlEncode(hotAuthToken))); ptrA noncenc(mir_base64_encode(nonce, sizeof(nonce))); result.Append(ptrA(mir_urlEncode(noncenc))); BYTE hash[MIR_SHA1_HASH_SIZE]; mir_hmac_sha1(hash, key2, sizeof(key2), (BYTE*)result.GetString(), result.GetLength()); ptrA szHash(mir_base64_encode(hash, sizeof(hash))); result.AppendFormat("&hash=%s", ptrA(mir_urlEncode(szHash))); return result; } /* 1 - Login successful 0 - Login failed -1 - Loading Skylogin library failed -2 - Functions cannot be loaded from Skylogin library -3 - Initializing Skylogin library failed */ int CMsnProto::MSN_SkypeAuth(const char *pszNonce, char *pszUIC) { int iRet = -1; pfnSkyLogin_Init SkyLogin_Init; pfnSkyLogin_Exit SkyLogin_Exit; pfnSkyLogin_LoadCredentials SkyLogin_LoadCredentials; pfnSkyLogin_PerformLogin SkyLogin_PerformLogin; pfnSkyLogin_CreateUICString SkyLogin_CreateUICString; HMODULE hLibSkylogin; if ((hLibSkylogin = LoadLibraryA("Plugins\\skylogin.dll"))) { SkyLogin hLogin; char szPassword[100]; // load function pointers if (!LOAD_FN(SkyLogin_Init) || !LOAD_FN(SkyLogin_Exit) || !LOAD_FN(SkyLogin_LoadCredentials) || !LOAD_FN(SkyLogin_PerformLogin) || !LOAD_FN(SkyLogin_CreateUICString)) { FreeLibrary(hLibSkylogin); return -2; } // Perform login if (hLogin = SkyLogin_Init()) { db_get_static(NULL, m_szModuleName, "Password", szPassword, sizeof(szPassword)); if (SkyLogin_LoadCredentials(hLogin, MyOptions.szEmail) || SkyLogin_PerformLogin(hLogin, MyOptions.szEmail, szPassword)) { if (SkyLogin_CreateUICString(hLogin, pszNonce, pszUIC)) iRet = 1; } else iRet = 0; SkyLogin_Exit(hLogin); } else iRet = -3; FreeLibrary(hLibSkylogin); } return iRet; } /* 1 - Login successful 0 - Login failed -1 - Loading Skylogin library failed -2 - Functions cannot be loaded from Skylogin library -3 - Initializing Skylogin library failed */ int CMsnProto::LoginSkypeOAuth(const char *pRefreshToken) { int iRet = -1; pfnSkyLogin_Init SkyLogin_Init; pfnSkyLogin_Exit SkyLogin_Exit; pfnSkyLogin_LoadCredentials SkyLogin_LoadCredentials; pfnSkyLogin_PerformLoginOAuth SkyLogin_PerformLoginOAuth; pfnSkyLogin_GetCredentialsUIC SkyLogin_GetCredentialsUIC; pfnSkyLogin_GetUser SkyLogin_GetUser; HMODULE hLibSkylogin; if ((hLibSkylogin = LoadLibraryA("Plugins\\skylogin.dll"))) { SkyLogin hLogin; // load function pointers if (!LOAD_FN(SkyLogin_Init) || !LOAD_FN(SkyLogin_Exit) || !LOAD_FN(SkyLogin_LoadCredentials) || !LOAD_FN(SkyLogin_PerformLoginOAuth) || !LOAD_FN(SkyLogin_GetCredentialsUIC) || !LOAD_FN(SkyLogin_GetUser)) { FreeLibrary(hLibSkylogin); return -2; } // Perform login if (hLogin = SkyLogin_Init()) { char szLoginToken[1024]; if (RefreshOAuth(pRefreshToken, "service::login.skype.com::MBI_SSL", szLoginToken) && SkyLogin_PerformLoginOAuth(hLogin, szLoginToken)) { char szUIC[1024]; if (SkyLogin_GetCredentialsUIC(hLogin, szUIC)) { char *pszPartner; replaceStr(authUIC, szUIC); iRet = 1; if (pszPartner = SkyLogin_GetUser(hLogin)) setString("SkypePartner", pszPartner); } } else iRet = 0; SkyLogin_Exit(hLogin); } else iRet = -3; FreeLibrary(hLibSkylogin); } return iRet; } static int CopyCookies(NETLIBHTTPREQUEST *nlhrReply, NETLIBHTTPHEADER *hdr) { int i, nSize = 1; char *p; if (hdr) { hdr->szName = "Cookie"; *hdr->szValue = 0; } for (i = 0; i < nlhrReply->headersCount; i++) { if (mir_strcmpi(nlhrReply->headers[i].szName, "Set-Cookie")) continue; if (p=strchr(nlhrReply->headers[i].szValue, ';')) *p=0; if (hdr) { if (*hdr->szValue) mir_strcat (hdr->szValue, "; "); mir_strcat (hdr->szValue, nlhrReply->headers[i].szValue); } else nSize += (int)mir_strlen(nlhrReply->headers[i].szValue) + 2; } return nSize; } /* pszService: service::login.skype.com::MBI_SSL - For LoginSkypeOAuth service::ssl.live.com::MBI_SSL - For ssl-compact-ticket service::contacts.msn.com::MBI_SSL - Contact SOAP service -> authContactToken service::m.hotmail.com::MBI_SSL - ActiveSync contactlist, not used by us service::storage.live.com::MBI_SSL - Storage service (authStorageToken) service::skype.com::MBI_SSL - Root of all OAuth tokens service::skype.net::MBI_SSL - ? */ bool CMsnProto::RefreshOAuth(const char *pszRefreshToken, const char *pszService, char *pszAccessToken, char *pszOutRefreshToken, time_t *ptExpires) { NETLIBHTTPREQUEST nlhr = { 0 }; NETLIBHTTPREQUEST *nlhrReply; NETLIBHTTPHEADER headers[3]; bool bRet = false; CMStringA post; if (!authCookies) return false; // initialize the netlib request nlhr.cbSize = sizeof(nlhr); nlhr.requestType = REQUEST_POST; nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_PERSISTENT | NLHRF_REDIRECT; nlhr.nlc = hHttpsConnection; nlhr.headersCount = 3; nlhr.headers = headers; nlhr.headers[0].szName = "User-Agent"; nlhr.headers[0].szValue = (char*)MSN_USER_AGENT; nlhr.headers[1].szName = "Content-Type"; nlhr.headers[1].szValue = "application/x-www-form-urlencoded"; nlhr.headers[2].szName = "Cookie"; nlhr.headers[2].szValue = authCookies; post.Format("client_id=00000000480BC46C&scope=%s&grant_type=refresh_token&refresh_token=%s", ptrA(mir_urlEncode(pszService)), pszRefreshToken); nlhr.pData = (char*)(const char*)post; nlhr.dataLength = (int)mir_strlen(nlhr.pData); nlhr.szUrl = "https://login.live.com/oauth20_token.srf"; // Query mHttpsTS = clock(); nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); mHttpsTS = clock(); if (nlhrReply) { hHttpsConnection = nlhrReply->nlc; if (nlhrReply->resultCode == 200 && nlhrReply->pData) { char *p; bRet = true; if (pszAccessToken && (p=strstr(nlhrReply->pData, "\"access_token\":"))) bRet &= sscanf(p+sizeof("\"access_token\""), "\"%[^\"]\"", pszAccessToken)==1; if (pszOutRefreshToken && (p=strstr(nlhrReply->pData, "\"refresh_token\":"))) bRet &= sscanf(p+sizeof("\"refresh_token\""), "\"%[^\"]\"", pszOutRefreshToken)==1; if (ptExpires && (p=strstr(nlhrReply->pData, "\"expires_in\""))) { int expires; if (sscanf(p+sizeof("\"expires_in\""), "%d,", &expires) == 1) { time(ptExpires); *ptExpires+=expires; bRet&=true; } } } CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); } else hHttpsConnection = NULL; return bRet; } void CMsnProto::LoadAuthTokensDB(void) { DBVARIANT dbv; authTokenExpiretime = getDword("authTokenExpiretime", 0); authMethod = getDword("authMethod", 0); if (getString("authUser", &dbv) == 0) { replaceStr(authUser, dbv.pszVal); db_free(&dbv); } if (getString("authSSLToken", &dbv) == 0) { replaceStr(authSSLToken, dbv.pszVal); db_free(&dbv); } if (getString("authContactToken", &dbv) == 0) { replaceStr(authContactToken, dbv.pszVal); db_free(&dbv); } if (getString("authUIC", &dbv) == 0) { replaceStr(authUIC, dbv.pszVal); db_free(&dbv); } if (getString("authCookies", &dbv) == 0) { replaceStr(authCookies, dbv.pszVal); db_free(&dbv); } if (getString("authStrToken", &dbv) == 0) { replaceStr(authStrToken, dbv.pszVal); db_free(&dbv); } if (getString("hotSecretToken", &dbv) == 0) { replaceStr(hotSecretToken, dbv.pszVal); db_free(&dbv); } if (getString("hotAuthToken", &dbv) == 0) { free(hotAuthToken); hotAuthToken = strdup(dbv.pszVal); db_free(&dbv); } if (getString("authStorageToken", &dbv) == 0) { replaceStr(authStorageToken, dbv.pszVal); db_free(&dbv); } if (getString("authRefreshToken", &dbv) == 0) { replaceStr(authRefreshToken, dbv.pszVal); db_free(&dbv); } if (getString("authSkypeComToken", &dbv) == 0) { replaceStr(authSkypeComToken, dbv.pszVal); db_free(&dbv); } if (getString("authSkypeToken", &dbv) == 0) { replaceStr(authSkypeToken, dbv.pszVal); db_free(&dbv); } } void CMsnProto::SaveAuthTokensDB(void) { setDword("authTokenExpiretime", authTokenExpiretime); setDword("authMethod", authMethod); setString("authUser", authUser); setString("authSSLToken", authSSLToken); setString("authUIC", authUIC); setString("authCookies", authCookies); setString("authStrToken", authStrToken); setString("authRefreshToken", authRefreshToken); } // -1 - Error on login sequence // 0 - Login failed (invalid username?) // 1 - Login via Skype login server succeeded // 2 - Login via Skypeweb succeeded int CMsnProto::MSN_AuthOAuth(void) { int retVal = -1; const char *pszPostParams = "client_id=00000000480BC46C&scope=service%3A%3Askype.com%3A%3AMBI_SSL&response_type=token&redirect_uri=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf"; NETLIBHTTPREQUEST nlhr = { 0 }; NETLIBHTTPREQUEST *nlhrReply; NETLIBHTTPHEADER headers[3]; time_t t; // Load credentials from DB so that we don't have to do all this stuff if token isn't expired if (!authTokenExpiretime) LoadAuthTokensDB(); // Is there already a valid token and we can skip this? if (time(&t)+10 < authTokenExpiretime && !mir_strcmp(authUser, MyOptions.szEmail)) return authMethod; // initialize the netlib request nlhr.cbSize = sizeof(nlhr); nlhr.requestType = REQUEST_GET; nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_PERSISTENT | NLHRF_REDIRECT; nlhr.nlc = hHttpsConnection; nlhr.headersCount = 1; nlhr.headers = headers; nlhr.headers[0].szName = "User-Agent"; nlhr.headers[0].szValue = (char*)MSN_USER_AGENT; // Get oauth20 login data CMStringA url; url.Format("https://login.live.com/oauth20_authorize.srf?%s", pszPostParams); nlhr.szUrl = (char*)(const char*)url; mHttpsTS = clock(); nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); mHttpsTS = clock(); if (nlhrReply) { hHttpsConnection = nlhrReply->nlc; if (nlhrReply->resultCode == 200 && nlhrReply->pData) { char *pPPFT, *pEnd; /* Get PPFT */ if ((pPPFT = strstr(nlhrReply->pData, "name=\"PPFT\"")) && (pPPFT = strstr(pPPFT, "value=\"")) && (pEnd=strchr(pPPFT+7, '"'))) { *pEnd=0; pPPFT+=7; /* Get POST URL if available */ if ((nlhr.szUrl = strstr(nlhrReply->pData, "urlPost:'")) && (pEnd=strchr(nlhr.szUrl+9, '\''))) { *pEnd=0; nlhr.szUrl += 9; } else { url.Format("https://login.live.com/ppsecure/post.srf?%s", pszPostParams); nlhr.szUrl = (char*)(const char*)url; } /* Get Cookies */ nlhr.headers[1].szValue = (char*)alloca(CopyCookies(nlhrReply, NULL)); CopyCookies(nlhrReply, &nlhr.headers[1]); if (*nlhr.headers[1].szValue) nlhr.headersCount++; /* Create POST data */ CMStringA post; char szPassword[100]; db_get_static(NULL, m_szModuleName, "Password", szPassword, sizeof(szPassword)); szPassword[16] = 0; post.Format("PPFT=%s&login=%s&passwd=%s", ptrA(mir_urlEncode(pPPFT)), ptrA(mir_urlEncode(MyOptions.szEmail)), ptrA(mir_urlEncode(szPassword))); /* Setup headers */ nlhr.headers[nlhr.headersCount].szName = "Content-Type"; nlhr.headers[nlhr.headersCount++].szValue = "application/x-www-form-urlencoded"; /* Do the login and get the required tokens */ nlhr.requestType = REQUEST_POST; nlhr.flags &= (~NLHRF_REDIRECT); mHttpsTS = clock(); nlhr.dataLength = (int)mir_strlen(post); nlhr.pData = (char*)(const char*)post; nlhr.nlc = hHttpsConnection; NETLIBHTTPREQUEST *nlhrReply2 = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); nlhrReply = nlhrReply2; mHttpsTS = clock(); if (nlhrReply) { hHttpsConnection = nlhrReply->nlc; if (nlhrReply->resultCode == 302) { char *pAccessToken; /* Extract access_token from Location can be found */ for (int i = 0; i < nlhrReply->headersCount; i++) if (!mir_strcmpi(nlhrReply->headers[i].szName, "Location") && (pAccessToken = strstr(nlhrReply->headers[i].szValue, "access_token=")) && (pEnd=strchr(pAccessToken+13, '&'))) { char *pRefreshToken, *pExpires, szToken[1024]; bool bLogin=false; *pEnd = 0; pAccessToken+=13; UrlDecode(pAccessToken); replaceStr(authAccessToken, pAccessToken); /* Extract refresh token */ if ((pRefreshToken = strstr(pEnd+1, "refresh_token=")) && (pEnd=strchr(pRefreshToken+14, '&'))) { *pEnd = 0; pRefreshToken+=14; } replaceStr(authRefreshToken, pRefreshToken); /* Extract expire time */ time(&authTokenExpiretime); if ((pExpires = strstr(pEnd+1, "expires_in=")) && (pEnd=strchr(pExpires+11, '&'))) { *pEnd = 0; pExpires+=11; authTokenExpiretime+=atoi(pExpires); } else authTokenExpiretime+=86400; /* Copy auth Cookies to class for other web requests like contact list fetching to avoid ActiveSync */ mir_free(authCookies); authCookies = nlhr.headers[1].szValue = (char*)mir_alloc(CopyCookies(nlhrReply, NULL)); CopyCookies(nlhrReply, &nlhr.headers[1]); int loginRet; /* Do Login via Skype login server, if not possible switch to SkypeWebExperience login */ if ((loginRet = LoginSkypeOAuth(pRefreshToken))<1) { if (loginRet<0) bLogin=true; else retVal = 0; } else { /* SkyLogin succeeded, request required tokens */ if (RefreshOAuth(pRefreshToken, "service::ssl.live.com::MBI_SSL", szToken)) { replaceStr(authSSLToken, szToken); replaceStr(authUser, MyOptions.szEmail); authMethod=retVal=1; } } mir_free(authSkypeComToken); authSkypeComToken = NULL; mir_free(authSkypeToken); authSkypeToken = NULL; /* If you need Skypewebexperience login, as i.e. skylogin.dll is not available, we do this here */ if (bLogin) { /* Prepare headers*/ nlhr.headers[2].szValue = "application/json"; nlhr.pData = "{\"trouterurl\":\"https://\",\"connectionid\":\"a\"}"; nlhr.dataLength = (int)mir_strlen(nlhr.pData); nlhr.szUrl = "https://skypewebexperience.live.com/v1/User/Initialization"; nlhr.nlc = hHttpsConnection; /* Request MappingContainer */ mHttpsTS = clock(); CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); mHttpsTS = clock(); if (nlhrReply) { hHttpsConnection = nlhrReply->nlc; if (nlhrReply->resultCode == 200 && nlhrReply->pData) { /* Parse JSON stuff for MappingContainer */ char *pMappingContainer; if ((pMappingContainer = strstr(nlhrReply->pData, "\"MappingContainer\":\"")) && (pEnd=strchr(pMappingContainer+20, '"'))) { *pEnd = 0; pMappingContainer+=20; UrlDecode(pMappingContainer); replaceStr(authUIC, pMappingContainer); replaceStr(authUser, MyOptions.szEmail); authMethod = retVal = 2; } else retVal = 0; } else retVal = 0; } else hHttpsConnection = NULL; } } } else { /* There may be a problem with login, i.e. M$ security measures. Open up browser * window with same URL in order to let user correct this */ if (nlhrReply->resultCode == 200 && nlhrReply->pData && strstr(nlhrReply->pData, "ar/cancel")) { url.Format("https://login.live.com/oauth20_authorize.srf?%s", pszPostParams); CallService(MS_UTILS_OPENURL, OUF_NEWWINDOW, (LPARAM)url.GetString()); } hHttpsConnection = NULL; } } } } if (nlhrReply) CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); } else hHttpsConnection = NULL; if (retVal<=0) authTokenExpiretime=0; else { MSN_GetPassportAuth(); SaveAuthTokensDB(); } return retVal; } int CMsnProto::GetMyNetID(void) { return strchr(MyOptions.szEmail, '@')?NETID_MSN:NETID_SKYPE; } const char *CMsnProto::GetMyUsername(int netId) { static char szPartner[128]; if (netId == NETID_SKYPE) { if (MyOptions.netId==NETID_MSN) { if (db_get_static(NULL, m_szModuleName, "SkypePartner", szPartner, sizeof(szPartner)) == 0) return szPartner; } } return MyOptions.szEmail; } const char *CMsnProto::GetSkypeToken(bool bAsAuthHeader) { char szToken[1024]; // Ensure that token isn't expired if (MyOptions.netId == NETID_MSN) MSN_AuthOAuth(); // No token available, fetch it if (!authSkypeToken) { NETLIBHTTPREQUEST nlhr = { 0 }; NETLIBHTTPREQUEST *nlhrReply; NETLIBHTTPHEADER headers[1]; char szPOST[2048]; nlhr.cbSize = sizeof(nlhr); nlhr.requestType = REQUEST_POST; nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_PERSISTENT | NLHRF_REDIRECT; nlhr.nlc = hHttpsConnection; nlhr.headersCount = SIZEOF(headers); nlhr.headers = headers; nlhr.headers[0].szName = "User-Agent"; nlhr.headers[0].szValue = (char*)MSN_USER_AGENT; nlhr.pData = szPOST; if (MyOptions.netId == NETID_SKYPE) { BYTE digest[16]; int cbPasswd; char szPassword[100]={0}; cbPasswd=mir_snprintf(szPassword, sizeof(szPassword), "%s\nskyper\n", MyOptions.szEmail); db_get_static(NULL, m_szModuleName, "Password", szPassword+cbPasswd, sizeof(szPassword)-cbPasswd-1); mir_md5_hash((BYTE*)szPassword, mir_strlen(szPassword), digest); mir_base64_encodebuf(digest, sizeof(digest), szPassword, sizeof(szPassword)); nlhr.szUrl = "https://api.skype.com/login/skypetoken"; nlhr.dataLength = mir_snprintf(szPOST, sizeof(szPOST), "scopes=client&clientVersion=%s&username=%s&passwordHash=%s", msnProductVer, MyOptions.szEmail, szPassword); } else { // Get skype.com OAuth token needed to acquire skype_token if (!authSkypeComToken) { if (RefreshOAuth(authRefreshToken, "service::skype.com::MBI_SSL", szToken)) replaceStr(authSkypeComToken, szToken); else return NULL; } // Get skype_token nlhr.szUrl = "https://api.skype.com/rps/skypetoken"; nlhr.dataLength = mir_snprintf(szPOST, sizeof(szPOST), "scopes=client&clientVersion=%s&access_token=%s&partner=999", msnProductVer, authSkypeComToken); } mHttpsTS = clock(); nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); mHttpsTS = clock(); if (nlhrReply) { hHttpsConnection = nlhrReply->nlc; if (nlhrReply->resultCode == 200 && nlhrReply->pData) { char *pSkypeToken, *pEnd; if ((pSkypeToken = strstr(nlhrReply->pData, "\"skypetoken\":\"")) && (pEnd=strchr(pSkypeToken+15, '"'))) { *pEnd = 0; pSkypeToken+=14; mir_snprintf (szToken, sizeof(szToken), "skype_token %s", pSkypeToken); replaceStr(authSkypeToken, szToken); setString("authSkypeToken", authSkypeToken); } } CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); } else hHttpsConnection = NULL; } if (!bAsAuthHeader && authSkypeToken) { char *pszRet = strchr(authSkypeToken, ' '); if (pszRet) return pszRet+1; } return authSkypeToken; } void CMsnProto::FreeAuthTokens(void) { mir_free(pAuthToken); mir_free(tAuthToken); mir_free(oimSendToken); mir_free(authStrToken); mir_free(authSecretToken); mir_free(authContactToken); mir_free(authStorageToken); mir_free(hotSecretToken); mir_free(authUIC); mir_free(authCookies); mir_free(authSSLToken); mir_free(authSkypeComToken); mir_free(authSkypeToken); mir_free(authUser); mir_free(authAccessToken); mir_free(authRefreshToken); free(hotAuthToken); }