Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix elevation on webOS 3.x #194

Merged
merged 1 commit into from
Sep 28, 2024
Merged
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
142 changes: 82 additions & 60 deletions services/elevate-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@ type Permission = {
outbound?: string[];
};

type Roles = {
type Role = {
appId: string;
type: string;
allowedNames: string[];
trustLevel: string;
permissions: Permission[];
};

type LegacyRoles = {
type LegacyRole = {
role: {
exeName: string;
type: string;
Expand All @@ -89,27 +89,27 @@ function isRecord(obj: unknown): obj is Record<string, any> {
return typeof obj === 'object' && !Array.isArray(obj);
}

function patchRolesFile(path: string, legacy: boolean, requiredNames: string[] = ['*', 'com.webos.service.capture.client*']) {
const rolesOriginal = readFileSync(path).toString();
const rolesNew = JSON.parse(rolesOriginal) as Roles | LegacyRoles;
function patchRoleFile(path: string, legacy: boolean, requiredNames: string[] = ['*', 'com.webos.service.capture.client*']) {
const roleOriginal = readFileSync(path).toString();
const roleNew = JSON.parse(roleOriginal) as Role | LegacyRole;

let allowedNames: string[] | null = null;

if (legacy) {
// webOS <4.x /var/palm/ls2-dev role file
const legacyRoles = rolesNew as LegacyRoles;
if (isRecord(legacyRoles.role) && Array.isArray(legacyRoles.role.allowedNames)) {
allowedNames = legacyRoles.role.allowedNames;
const legacyRole = roleNew as LegacyRole;
if (isRecord(legacyRole.role) && Array.isArray(legacyRole.role.allowedNames)) {
allowedNames = legacyRole.role.allowedNames;
} else {
console.warn('[!] Legacy roles is missing allowedNames');
console.warn('[!] Legacy role file is missing allowedNames');
}
} else {
// webOS 4.x+ /var/luna-service2 role file
const newRoles = rolesNew as Roles;
if (Array.isArray(newRoles.allowedNames)) {
allowedNames = newRoles.allowedNames;
const newRole = roleNew as Role;
if (Array.isArray(newRole.allowedNames)) {
allowedNames = newRole.allowedNames;
} else {
console.warn('[!] Roles file is missing allowedNames');
console.warn('[!] Role file is missing allowedNames');
}
}

Expand All @@ -130,9 +130,9 @@ function patchRolesFile(path: string, legacy: boolean, requiredNames: string[] =
// pieces of software verify explicit permission "service" key, thus we
// sometimes may need some extra allowedNames/permissions, even though we
// default to "*"
if (Array.isArray(rolesNew.permissions)) {
if (Array.isArray(roleNew.permissions)) {
const missingPermissionNames = requiredNames;
rolesNew.permissions.forEach((perm: { outbound?: string[]; service?: string }) => {
roleNew.permissions.forEach((perm: { outbound?: string[]; service?: string }) => {
if (perm.service && missingPermissionNames.includes(perm.service))
missingPermissionNames.splice(missingPermissionNames.indexOf(perm.service), 1);
if (perm.outbound && !perm.outbound.includes('*')) {
Expand All @@ -142,20 +142,20 @@ function patchRolesFile(path: string, legacy: boolean, requiredNames: string[] =

for (const name of missingPermissionNames) {
console.info(`[ ] Adding permission for name: ${name}`);
rolesNew.permissions.push({
roleNew.permissions.push({
service: name,
inbound: ['*'],
outbound: ['*'],
});
}
}

const rolesNewContents = JSON.stringify(rolesNew);
if (rolesNewContents !== JSON.stringify(JSON.parse(rolesOriginal))) {
console.info(`[ ] Updating roles definition: ${path}`);
console.info('-', rolesOriginal);
console.info('+', rolesNewContents);
writeFileSync(path, rolesNewContents);
const roleNewContents = JSON.stringify(roleNew);
if (roleNewContents !== JSON.stringify(JSON.parse(roleOriginal))) {
console.info(`[ ] Updating role definition: ${path}`);
console.info('-', roleOriginal);
console.info('+', roleNewContents);
writeFileSync(path, roleNewContents);
return true;
}

Expand All @@ -181,7 +181,62 @@ function main(argv: string[]) {

let configChanged = false;

for (const lunaRoot of ['/var/luna-service2-dev', '/var/luna-service2']) {
let foundLegacyDev = false;
let foundLegacyNonDev = false;

const legacyLunaRootDev = '/var/palm/ls2-dev';
const legacyLunaRootNonDev = '/var/palm/ls2';

for (const legacyLunaRoot of [legacyLunaRootDev, legacyLunaRootNonDev]) {
const legacyPubServiceFile = `${legacyLunaRoot}/services/pub/${serviceName}.service`;
const legacyPrvServiceFile = `${legacyLunaRoot}/services/prv/${serviceName}.service`;
const legacyPubRoleFile = `${legacyLunaRoot}/roles/pub/${serviceName}.json`;
const legacyPrvRoleFile = `${legacyLunaRoot}/roles/prv/${serviceName}.json`;

if (isFile(legacyPubServiceFile)) {
console.info(`[~] Found legacy webOS <3.x service file: ${legacyPubServiceFile}`);
if (patchServiceFile(legacyPubServiceFile)) {
console.info(`[ ] Patched legacy public service file: ${legacyPubServiceFile}`);
configChanged = true;
}

if (isFile(legacyPrvServiceFile)) {
if (patchServiceFile(legacyPrvServiceFile)) {
console.info(`[ ] Patched legacy private service file: ${legacyPrvServiceFile}`);
configChanged = true;
}
} else {
console.warn(`[!] Did not find legacy private service file: ${legacyPrvServiceFile}`);
}

if (legacyLunaRoot === legacyLunaRootDev) {
foundLegacyDev = true;
} else if (legacyLunaRoot === legacyLunaRootNonDev) {
foundLegacyNonDev = true;
} else {
console.error('[!] Something is wrong: unexpected path');
}
}

if (isFile(legacyPubRoleFile)) {
if (patchRoleFile(legacyPubRoleFile, true)) {
console.info(`[ ] Patched legacy public role file: ${legacyPubRoleFile}`);
configChanged = true;
}
}

if (isFile(legacyPrvRoleFile)) {
if (patchRoleFile(legacyPrvRoleFile, true)) {
console.info(`[ ] Patched legacy private role file: ${legacyPrvRoleFile}`);
configChanged = true;
}
}
}

const lunaRootDev = '/var/luna-service2-dev';
const lunaRootNonDev = '/var/luna-service2';

for (const lunaRoot of [lunaRootDev, lunaRootNonDev]) {
const serviceFile = `${lunaRoot}/services.d/${serviceName}.service`;
const clientPermFile = `${lunaRoot}/client-permissions.d/${serviceName}.root.json`;
const apiPermFile = `${lunaRoot}/api-permissions.d/${serviceName}.api.public.json`;
Expand All @@ -193,8 +248,8 @@ function main(argv: string[]) {
if (patchServiceFile(serviceFile)) {
configChanged = true;
}
} else {
// Skip everything else if service file is not found.
} else if ((lunaRoot === lunaRootDev && !foundLegacyDev) || (lunaRoot === lunaRootNonDev && !foundLegacyNonDev)) {
// Skip everything else if no service file was found (including in legacy dir)
continue;
}

Expand All @@ -221,7 +276,8 @@ function main(argv: string[]) {
}

if (isFile(roleFile)) {
if (patchRolesFile(roleFile, false)) {
if (patchRoleFile(roleFile, false)) {
console.info(`[ ] Patched role file: ${roleFile}`);
configChanged = true;
}
}
Expand Down Expand Up @@ -251,40 +307,6 @@ function main(argv: string[]) {
}
}

for (const legacyLunaRoot of ['/var/palm/ls2-dev', '/var/palm/ls2']) {
const legacyPubServiceFile = `${legacyLunaRoot}/services/pub/${serviceName}.service`;
const legacyPrvServiceFile = `${legacyLunaRoot}/services/prv/${serviceName}.service`;
const legacyPubRolesFile = `${legacyLunaRoot}/roles/pub/${serviceName}.json`;
const legacyPrvRolesFile = `${legacyLunaRoot}/roles/prv/${serviceName}.json`;

if (isFile(legacyPubServiceFile)) {
console.info(`[~] Found legacy webOS <3.x service file: ${legacyPubServiceFile}`);
if (patchServiceFile(legacyPubServiceFile)) {
configChanged = true;
}

if (isFile(legacyPrvServiceFile)) {
if (patchServiceFile(legacyPrvServiceFile)) {
configChanged = true;
}
} else {
console.warn(`[!] Did not find legacy private service file: ${legacyPrvServiceFile}`);
}
}

if (isFile(legacyPubRolesFile)) {
if (patchRolesFile(legacyPubRolesFile, true)) {
configChanged = true;
}
}

if (isFile(legacyPrvRolesFile)) {
if (patchRolesFile(legacyPrvRolesFile, true)) {
configChanged = true;
}
}
}

if (configChanged) {
console.info('[+] Refreshing services...');
execFile('ls-control', ['scan-services'], { timeout: 10000 }, (err, stderr, stdout) => {
Expand Down