selinux: Add support for parsing mac_permissions.xml and keys.conf
This commit is contained in:
parent
fe09189d55
commit
3961bde95f
1 changed files with 166 additions and 0 deletions
166
src/sepolicy/keys.ts
Normal file
166
src/sepolicy/keys.ts
Normal file
|
@ -0,0 +1,166 @@
|
|||
import { promises as fs } from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as xml2js from 'xml2js'
|
||||
|
||||
import { exists, listFilesRecursive } from '../util/fs'
|
||||
import { parseLines } from '../util/parse'
|
||||
import { EXT_SYS_PARTITIONS } from '../util/partitions'
|
||||
|
||||
export interface MacSigner {
|
||||
cert: string | Uint8Array
|
||||
seinfoId: string
|
||||
}
|
||||
|
||||
export interface KeyInfo {
|
||||
keyId: string
|
||||
certPaths: Map<string, string>
|
||||
}
|
||||
|
||||
function parseHex(hex: string) {
|
||||
let buf = new Uint8Array(hex.length / 2)
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
buf[i / 2] = parseInt(hex.slice(i, i + 2), 16)
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
async function parseMacPermissions(xml: string) {
|
||||
let doc = await xml2js.parseStringPromise(xml)
|
||||
let signers = []
|
||||
|
||||
if (doc.policy) {
|
||||
for (let { $: { signature: rawSig }, seinfo: [{ $: { value: seinfoId }}]} of doc.policy.signer) {
|
||||
// Parse base64 cert or leave it as a reference
|
||||
let cert = rawSig.startsWith('@') ? rawSig.slice(1) : parseHex(rawSig)
|
||||
signers.push({
|
||||
cert: cert,
|
||||
seinfoId: seinfoId,
|
||||
} as MacSigner)
|
||||
}
|
||||
}
|
||||
|
||||
return signers
|
||||
}
|
||||
|
||||
export async function readMacPermissionsRecursive(root: string) {
|
||||
let signers = []
|
||||
for await (let file of listFilesRecursive(root)) {
|
||||
if (path.basename(file) == 'mac_permissions.xml') {
|
||||
let xml = await fs.readFile(file, { encoding: 'utf8' })
|
||||
signers.push(...(await parseMacPermissions(xml)))
|
||||
}
|
||||
}
|
||||
|
||||
return signers
|
||||
}
|
||||
|
||||
export async function readPartMacPermissions(root: string) {
|
||||
let signers = []
|
||||
for (let partition of EXT_SYS_PARTITIONS) {
|
||||
let path = `${root}/${partition}/etc/selinux/${partition}_mac_permissions.xml`
|
||||
if (!(await exists(path))) {
|
||||
continue
|
||||
}
|
||||
|
||||
let xml = await fs.readFile(path, { encoding: 'utf8' })
|
||||
signers.push(...(await parseMacPermissions(xml)))
|
||||
}
|
||||
|
||||
return signers
|
||||
}
|
||||
|
||||
function parseKeysConf(conf: string) {
|
||||
let curKeyId: string | null = null
|
||||
let curPaths: Map<string, string> | null = null
|
||||
|
||||
let keys = []
|
||||
for (let line of parseLines(conf)) {
|
||||
let startBlock = line.match(/^\[@(.+)\]$/)
|
||||
if (startBlock != undefined) {
|
||||
// Finish last block
|
||||
if (curKeyId != null) {
|
||||
keys.push({
|
||||
keyId: curKeyId,
|
||||
certPaths: curPaths!,
|
||||
} as KeyInfo)
|
||||
}
|
||||
|
||||
curKeyId = startBlock[1]
|
||||
curPaths = new Map<string, string>()
|
||||
continue
|
||||
}
|
||||
|
||||
let pathLine = line.match(/^(.+)\s*:\s*(.+)$/)
|
||||
if (pathLine != undefined) {
|
||||
let [_, buildType, path] = pathLine
|
||||
if (curPaths != null) {
|
||||
curPaths.set(buildType, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finish last block
|
||||
if (curKeyId != null) {
|
||||
keys.push({
|
||||
keyId: curKeyId,
|
||||
certPaths: curPaths!,
|
||||
} as KeyInfo)
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
export async function readKeysConfRecursive(root: string) {
|
||||
let keys = []
|
||||
for await (let file of listFilesRecursive(root)) {
|
||||
if (path.basename(file) == 'keys.conf') {
|
||||
let xml = await fs.readFile(file, { encoding: 'utf8' })
|
||||
keys.push(...parseKeysConf(xml))
|
||||
}
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
export function resolveKeys(srcKeys: Array<KeyInfo>, srcMacPerms: Array<MacSigner>, compiledMacPerms: Array<MacSigner>) {
|
||||
console.log({
|
||||
srcKeys: srcKeys,
|
||||
srcMacPerms: srcMacPerms,
|
||||
compiledMacPerms: compiledMacPerms,
|
||||
})
|
||||
// Build key ID -> paths map
|
||||
let keyToPaths = new Map(srcKeys.map(k => [k.keyId, Array.from(k.certPaths.values())]))
|
||||
|
||||
// Build seinfo -> paths map
|
||||
let seinfoToPaths = new Map(srcMacPerms.filter(s => typeof s.cert == 'string')
|
||||
.map(s => [s.seinfoId, keyToPaths.get(s.cert as string)!]))
|
||||
|
||||
// Build cert -> paths map
|
||||
return new Map(compiledMacPerms.filter(s => seinfoToPaths.has(s.seinfoId) && s.cert instanceof Uint8Array)
|
||||
.map(s => [s.cert as Uint8Array, seinfoToPaths.get(s.seinfoId)!]))
|
||||
}
|
||||
|
||||
function serializeCert(cert: Uint8Array) {
|
||||
let base64 = Buffer.from(cert).toString('base64')
|
||||
// Wrap to 76 chars to match Google's PEMs
|
||||
let wrapped = base64.replace(/(.{76})/g, '$1\n')
|
||||
|
||||
return `-----BEGIN CERTIFICATE-----
|
||||
${wrapped}
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
}
|
||||
|
||||
export async function writeMappedKeys(keys: Map<Uint8Array, Iterable<string>>) {
|
||||
for (let [cert, paths] of keys.entries()) {
|
||||
let serialized = serializeCert(cert)
|
||||
for (let path of paths) {
|
||||
console.log({
|
||||
path: path,
|
||||
cert: serialized,
|
||||
})
|
||||
await fs.writeFile(path, serialized)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue