Files
profiled/src/PawletProfileBinderService.cpp
T
oxmc a86ad3298e Rename project from vesperprofiled to pawletprofiled (PawletOS fork)
This checkout is PawletOS's fork (git.oxmc.me/PawletOS/profiled), so its
own identity should read PawletOS, not VesperOS: binary/package name,
AIDL package+interface (me.oxmc.vesperos.profile -> os.pawlet.profiled),
D-Bus service/object/error names, C++ namespace (vesperos::profile ->
pawletos::profile), sepolicy types, data paths (/data/system/vesperos ->
/data/system/pawletos, /etc/vesperprofiled -> /etc/pawletprofiled), the
vendored OpenSSL static-lib module names, and the ZTE protocol string.

Also drops generated build output (obj-x86_64-linux-gnu/, debian/.debhelper,
debian staging dir, debhelper log/substvars files) that had been committed
by mistake, and adds a .gitignore so they don't come back.

The stock/upstream vesperprofiled at git.oxmc.me/VesperOS/vesperprofiled
is untouched -- this commit only goes to the pawletos remote.
2026-07-25 00:50:46 -07:00

323 lines
12 KiB
C++

#include "PawletProfileBinderService.h"
#include "payloads/PayloadHandler.h"
#include <cstring>
#include <sstream>
#include <syslog.h>
namespace pawletos::profile {
using ::ndk::ScopedAStatus;
// Service-specific error codes returned via ScopedAStatus — mirrors
// PawletProfileService's D-Bus error domain (kErrNotFound etc.) one-for-one
// so a future shared client-side error mapping stays simple.
namespace {
constexpr int kErrNotFound = 1;
constexpr int kErrSignature = 2;
constexpr int kErrParse = 3;
constexpr int kErrLocked = 4;
constexpr int kErrWrongPassword = 5;
constexpr int kErrSigningRequired = 6;
} // namespace
ScopedAStatus PawletProfileBinderService::serviceError(const char* name, const std::string& message) {
syslog(LOG_WARNING, "pawletprofiled: %s: %s", name, message.c_str());
int code = 0;
if (strcmp(name, "NotFound") == 0) code = kErrNotFound;
else if (strcmp(name, "Signature") == 0) code = kErrSignature;
else if (strcmp(name, "Parse") == 0) code = kErrParse;
else if (strcmp(name, "Locked") == 0) code = kErrLocked;
else if (strcmp(name, "WrongPassword") == 0) code = kErrWrongPassword;
else if (strcmp(name, "SigningRequired") == 0) code = kErrSigningRequired;
return ScopedAStatus::fromServiceSpecificErrorWithMessage(code, message.c_str());
}
PawletProfileBinderService::PawletProfileBinderService() {
syslog(LOG_INFO, "pawletprofiled: Binder service starting");
mStore.load();
}
// ── installProfileDirect (preinstalled profiles, same as D-Bus version) ────
bool PawletProfileBinderService::installProfileDirect(
const std::vector<uint8_t>& profileData, std::string& outUuid) {
std::lock_guard<std::mutex> lock(mLock);
std::vector<uint8_t> yamlBytes;
bool isSigned = false;
SignatureVerifier::TrustLevel trust = SignatureVerifier::TrustLevel::UNSIGNED;
if (mVerifier.isCmsWrapped(profileData)) {
auto result = mVerifier.verify(profileData, yamlBytes);
if (result == SignatureVerifier::VerifyResult::INVALID) {
syslog(LOG_ERR, "[installProfileDirect] signature invalid");
return false;
}
isSigned = true;
trust = (result == SignatureVerifier::VerifyResult::TRUSTED)
? SignatureVerifier::TrustLevel::TRUSTED
: SignatureVerifier::TrustLevel::UNVERIFIED;
} else {
yamlBytes = profileData;
}
ParsedProfile profile;
if (!mParser.parse(yamlBytes, profile)) {
syslog(LOG_ERR, "[installProfileDirect] parse failed");
return false;
}
ParsedProfile existing;
if (mStore.load(profile.uuid, existing)) {
syslog(LOG_INFO, "[installProfileDirect] already installed uuid=%s, skipping",
profile.uuid.c_str());
outUuid = profile.uuid;
return true;
}
for (const auto& payload : profile.payloads) {
bool sigRequired =
payload.type == "mdm" ||
payload.type == "removal-password" ||
payload.type == "kiosk" ||
payload.type == "asam";
if (sigRequired && !isSigned) {
syslog(LOG_ERR, "[installProfileDirect] type=%s requires signed profile",
payload.type.c_str());
return false;
}
}
profile.trustLevel = trust;
if (!mStore.save(profile)) {
syslog(LOG_ERR, "[installProfileDirect] save failed uuid=%s", profile.uuid.c_str());
return false;
}
applyProfile(profile);
outUuid = profile.uuid;
syslog(LOG_INFO, "[installProfileDirect] installed uuid=%s", profile.uuid.c_str());
return true;
}
// ── installProfile ──────────────────────────────────────────────────────
ScopedAStatus PawletProfileBinderService::installProfile(
const std::vector<uint8_t>& in_profileData, std::string* _aidl_return) {
std::lock_guard<std::mutex> lock(mLock);
std::vector<uint8_t> yamlBytes;
bool isSigned = false;
SignatureVerifier::TrustLevel trust = SignatureVerifier::TrustLevel::UNSIGNED;
if (mVerifier.isCmsWrapped(in_profileData)) {
auto result = mVerifier.verify(in_profileData, yamlBytes);
if (result == SignatureVerifier::VerifyResult::INVALID) {
return serviceError("Signature", "Profile signature is invalid or tampered.");
}
isSigned = true;
trust = (result == SignatureVerifier::VerifyResult::TRUSTED)
? SignatureVerifier::TrustLevel::TRUSTED
: SignatureVerifier::TrustLevel::UNVERIFIED;
} else {
yamlBytes = in_profileData;
}
ParsedProfile profile;
if (!mParser.parse(yamlBytes, profile)) {
return serviceError("Parse", "Profile YAML is malformed or schema-invalid.");
}
for (const auto& payload : profile.payloads) {
bool sigRequired =
payload.type == "mdm" ||
payload.type == "removal-password" ||
payload.type == "kiosk" ||
payload.type == "asam";
if (sigRequired && !isSigned) {
return serviceError("SigningRequired",
"Payload type '" + payload.type + "' requires a signed profile.");
}
}
profile.trustLevel = trust;
if (!mStore.save(profile)) {
return serviceError("Parse", "Failed to persist profile.");
}
applyProfile(profile);
syslog(LOG_INFO, "pawletprofiled: installed profile uuid=%s", profile.uuid.c_str());
*_aidl_return = profile.uuid;
return ScopedAStatus::ok();
}
// ── removeProfile ────────────────────────────────────────────────────────
ScopedAStatus PawletProfileBinderService::removeProfile(
const std::string& in_uuid, const std::string& in_removalPassword) {
std::lock_guard<std::mutex> lock(mLock);
ParsedProfile profile;
if (!mStore.load(in_uuid, profile)) {
return serviceError("NotFound", "Profile not found: " + in_uuid);
}
if (profile.lifecycle.removal == "locked") {
return serviceError("Locked", "This profile can only be removed by the MDM server.");
}
if (profile.lifecycle.removal == "password") {
if (in_removalPassword.empty() || !mStore.checkRemovalPassword(in_uuid, in_removalPassword)) {
return serviceError("WrongPassword", "Incorrect removal password.");
}
}
revertProfile(profile);
mStore.remove(in_uuid);
syslog(LOG_INFO, "pawletprofiled: removed profile uuid=%s", in_uuid.c_str());
return ScopedAStatus::ok();
}
// ── listProfiles ─────────────────────────────────────────────────────────
ScopedAStatus PawletProfileBinderService::listProfiles(std::vector<std::string>* _aidl_return) {
std::lock_guard<std::mutex> lock(mLock);
*_aidl_return = mStore.listUuids();
return ScopedAStatus::ok();
}
// ── getProfileInfo ───────────────────────────────────────────────────────
ScopedAStatus PawletProfileBinderService::getProfileInfo(
const std::string& in_uuid, std::string* _aidl_return) {
std::lock_guard<std::mutex> lock(mLock);
ParsedProfile profile;
if (!mStore.load(in_uuid, profile)) {
return serviceError("NotFound", "Not found: " + in_uuid);
}
std::ostringstream j;
j << "{"
<< "\"uuid\":\"" << profile.uuid << "\","
<< "\"id\":\"" << profile.id << "\","
<< "\"name\":\"" << profile.meta.name << "\","
<< "\"organization\":\"" << profile.meta.organization << "\","
<< "\"scope\":\"" << profile.scope << "\","
<< "\"removal\":\"" << profile.lifecycle.removal << "\","
<< "\"trusted\":" << (profile.trustLevel != SignatureVerifier::TrustLevel::UNSIGNED ? "true" : "false") << ","
<< "\"payloadCount\":" << profile.payloads.size()
<< "}";
*_aidl_return = j.str();
return ScopedAStatus::ok();
}
// ── isDeviceManaged ──────────────────────────────────────────────────────
ScopedAStatus PawletProfileBinderService::isDeviceManaged(bool* _aidl_return) {
std::lock_guard<std::mutex> lock(mLock);
bool managed = false;
for (const auto& uuid : mStore.listUuids()) {
ParsedProfile p;
if (mStore.load(uuid, p))
for (const auto& pl : p.payloads)
if (pl.type == "mdm") { managed = true; break; }
if (managed) break;
}
*_aidl_return = managed;
return ScopedAStatus::ok();
}
// ── getMdmServerUrl ──────────────────────────────────────────────────────
ScopedAStatus PawletProfileBinderService::getMdmServerUrl(std::string* _aidl_return) {
std::lock_guard<std::mutex> lock(mLock);
std::string url;
for (const auto& uuid : mStore.listUuids()) {
ParsedProfile p;
if (!mStore.load(uuid, p)) continue;
for (const auto& pl : p.payloads) {
if (pl.type == "mdm") {
auto it = pl.fields.find("server-url");
if (it != pl.fields.end()) url = it->second;
if (url.size() >= 2 && url.front() == '"') url = url.substr(1, url.size() - 2);
break;
}
}
if (!url.empty()) break;
}
*_aidl_return = url;
return ScopedAStatus::ok();
}
// ── isSupervised ─────────────────────────────────────────────────────────
ScopedAStatus PawletProfileBinderService::isSupervised(bool* _aidl_return) {
std::lock_guard<std::mutex> lock(mLock);
bool supervised = false;
for (const auto& uuid : mStore.listUuids()) {
ParsedProfile p;
if (!mStore.load(uuid, p)) continue;
for (const auto& pl : p.payloads)
if (pl.type == "kiosk" || pl.type == "asam") { supervised = true; break; }
if (supervised) break;
}
*_aidl_return = supervised;
return ScopedAStatus::ok();
}
// ── getPayloadsOfType ────────────────────────────────────────────────────
ScopedAStatus PawletProfileBinderService::getPayloadsOfType(
const std::string& in_payloadType, std::string* _aidl_return) {
std::lock_guard<std::mutex> lock(mLock);
std::ostringstream j;
j << "[";
bool first = true;
for (const auto& uuid : mStore.listUuids()) {
ParsedProfile p;
if (!mStore.load(uuid, p)) continue;
for (const auto& pl : p.payloads) {
if (pl.type != in_payloadType) continue;
if (!first) j << ",";
first = false;
j << "{\"profileUuid\":\"" << p.uuid << "\","
<< "\"payloadUuid\":\"" << pl.uuid << "\"";
for (const auto& [k, v] : pl.fields)
j << ",\"" << k << "\":" << v;
j << "}";
}
}
j << "]";
*_aidl_return = j.str();
return ScopedAStatus::ok();
}
// ── applyProfile / revertProfile ─────────────────────────────────────────
void PawletProfileBinderService::applyProfile(const ParsedProfile& profile) {
for (const auto& payload : profile.payloads) {
auto* handler = PayloadHandlerRegistry::get(payload.type);
if (handler) {
if (!handler->apply(payload))
syslog(LOG_WARNING, "pawletprofiled: handler failed type=%s uuid=%s",
payload.type.c_str(), payload.uuid.c_str());
} else {
syslog(LOG_WARNING, "pawletprofiled: no handler for type=%s", payload.type.c_str());
}
}
}
void PawletProfileBinderService::revertProfile(const ParsedProfile& profile) {
for (auto it = profile.payloads.rbegin(); it != profile.payloads.rend(); ++it) {
auto* handler = PayloadHandlerRegistry::get(it->type);
if (handler) handler->revert(*it);
}
}
} // namespace pawletos::profile