Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 111 additions & 58 deletions AltSign/AppleAPI+Authentication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ extern "C" {
}

#include <ostream>
#include <thread>
#include <chrono>
#include <algorithm>

using namespace std;
using namespace utility; // Common utilities like string conversions
Expand All @@ -39,6 +42,33 @@ extern bool decompress(const uint8_t* input, size_t input_size, std::vector<uint

static const char ALTHexCharacters[] = "0123456789abcdef";

static const int ALTMaximumGSARetries = 5;

// Apple's GSA edge answers rejected requests with an HTML error page rather than a plist.
// Failing plist parsing on that response hides both the HTTP status and the fact that this
// is a server-side failure rather than incorrect credentials, so surface them instead.
static LocalizedAPIError BadGSAResponseError(web::http::status_code statusCode, utility::string_t contentType, std::string body)
{
std::stringstream ss;
ss << "Apple's servers returned an unexpected response (HTTP " << statusCode << ").";

if (!contentType.empty())
{
ss << " Content-Type: " << StringFromWideString(contentType) << ".";
}

std::string snippet = body.substr(0, std::min<size_t>(body.size(), 256));
std::replace(snippet.begin(), snippet.end(), '\n', ' ');
std::replace(snippet.begin(), snippet.end(), '\r', ' ');

if (!snippet.empty())
{
ss << " Body: " << snippet;
}

return LocalizedAPIError((int)statusCode, ss.str());
}

struct ccrng_state* RNG = NULL;

std::vector<unsigned char> DataFromBytes(const char* bytes, size_t count)
Expand Down Expand Up @@ -800,33 +830,37 @@ pplx::task<bool> AppleAPI::RequestTrustedDeviceTwoFactorCode(
.then([=](http_response response)
{
odslog("Received 2FA response status code: " << response.status_code());
return response.extract_vector();
})
.then([=](std::vector<unsigned char> compressedData)
{
std::vector<uint8_t> decompressedData;

if (compressedData.size() > 2 && compressedData[0] == '<' && compressedData[1] == '?')
{
// Already decompressed
decompressedData = compressedData;
}
else
auto statusCode = response.status_code();
auto contentType = response.headers().content_type();

return response.extract_vector()
.then([=](std::vector<unsigned char> compressedData)
{
decompress((const uint8_t*)compressedData.data(), (size_t)compressedData.size(), decompressedData);
}
std::vector<uint8_t> decompressedData;

std::string decompressedXML = std::string(decompressedData.begin(), decompressedData.end());
if (compressedData.size() > 2 && compressedData[0] == '<' && compressedData[1] == '?')
{
// Already decompressed
decompressedData = compressedData;
}
else
{
decompress((const uint8_t*)compressedData.data(), (size_t)compressedData.size(), decompressedData);
}

plist_t plist = nullptr;
plist_from_xml(decompressedXML.c_str(), (int)decompressedXML.size(), &plist);
std::string decompressedXML = std::string(decompressedData.begin(), decompressedData.end());

if (plist == nullptr)
{
throw APIError(APIErrorCode::InvalidResponse);
}
plist_t plist = nullptr;
plist_from_xml(decompressedXML.c_str(), (int)decompressedXML.size(), &plist);

return plist;
if (plist == nullptr)
{
throw BadGSAResponseError(statusCode, contentType, decompressedXML);
}

return plist;
});
})
.then([this](plist_t plist)
{
Expand Down Expand Up @@ -1012,55 +1046,77 @@ pplx::task<plist_t> AppleAPI::SendAuthenticationRequest(std::map<std::string, pl
uint32_t length = 0;
plist_to_xml(plist, &plistXML, &length);

std::string bodyXML(plistXML, length);

free(plistXML);
plist_free(plist);

std::map<utility::string_t, utility::string_t> headers = {
{L"Content-Type", L"text/x-xml-plist"},
{L"X-Mme-Client-Info", WideStringFromString(anisetteData->deviceDescription())},
{L"Accept", L"*/*"},
{L"User-Agent", L"akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0"}
{L"User-Agent", L"AuthKit/1 (Macintosh; OS X 26.5.2) (com.apple.dt.Xcode/26.0)"}
};

uri_builder builder(U("/grandslam/GsService2"));
auto task = pplx::create_task([=]() -> plist_t
{
http_response response;

http_request request(methods::POST);
request.set_request_uri(builder.to_string());
request.set_body(plistXML);
for (int attempt = 0;; attempt++)
{
uri_builder builder(U("/grandslam/GsService2"));

for (auto& pair : headers)
{
if (request.headers().has(pair.first))
{
request.headers().remove(pair.first);
}
http_request request(methods::POST);
request.set_request_uri(builder.to_string());
request.set_body(bodyXML);

request.headers().add(pair.first, pair.second);
}
for (auto& pair : headers)
{
if (request.headers().has(pair.first))
{
request.headers().remove(pair.first);
}

request.headers().add(pair.first, pair.second);
}

// Apple's GSA edge keeps a connection pinned to a backend node, and once that node
// starts failing every subsequent request on the same keep-alive connection returns
// 5xx and never recovers. A fresh http_client per attempt forces a new connection.
http_client_config config;
config.set_validate_certificates(false);

http_client client(U("https://gsa.apple.com"), config);

response = client.request(request).get();
response.content_ready().get();

auto task = this->gsaClient().request(request)
.then([=](http_response response)
{
return response.content_ready();
})
.then([=](http_response response)
{
odslog("Received auth response status code: " << response.status_code());
return response.extract_vector();
})
.then([=](std::vector<unsigned char> compressedData)
{
std::vector<uint8_t> decompressedData = compressedData;

std::string decompressedXML = std::string(decompressedData.begin(), decompressedData.end());
// A 5xx means the request was never processed, so retrying is safe.
if (response.status_code() >= 500 && response.status_code() <= 599 && attempt < ALTMaximumGSARetries - 1)
{
int delay = std::min<int>(1 << attempt, 8);
std::this_thread::sleep_for(std::chrono::seconds(delay));
continue;
}

break;
}

plist_t plist = nullptr;
plist_from_xml(decompressedXML.c_str(), (int)decompressedXML.size(), &plist);
auto data = response.extract_vector().get();
std::string responseXML = std::string(data.begin(), data.end());

if (plist == nullptr)
{
throw APIError(APIErrorCode::InvalidResponse);
}
plist_t responsePlist = nullptr;
plist_from_xml(responseXML.c_str(), (int)responseXML.size(), &responsePlist);

return plist;
})
if (responsePlist == nullptr)
{
throw BadGSAResponseError(response.status_code(), response.headers().content_type(), responseXML);
}

return responsePlist;
})
.then([=](plist_t plist)
{
auto dictionary = plist_dict_get_item(plist, "Response");
Expand Down Expand Up @@ -1141,9 +1197,6 @@ pplx::task<plist_t> AppleAPI::SendAuthenticationRequest(std::map<std::string, pl
}
});

free(plistXML);
plist_free(plist);

return task;
}

Expand Down