The Android side is being rewritten as a platform-signed system app + device owner (android_packages_apps_PawletProfiled) instead of a native NDK Binder daemon -- most of what these payloads need on Android (DevicePolicyManager, VpnManager, WifiManager, WallpaperManager, KeyChain) is Java-SDK-first and awkward or impossible to reach cleanly from a native process. Drops: Android.bp, main_android.cpp, PawletProfileBinderService.*, platform/android/*.cpp, aidl/, sepolicy/, third_party/openssl-android/, pawletprofiled.rc/.xml. Also strips the now-dead __ANDROID__ branches from SignatureVerifier/ProfileStore. This repo is Linux-only from here on.
250 lines
8.0 KiB
Markdown
250 lines
8.0 KiB
Markdown
# pawletprofiled
|
|
|
|
PawletOS's Linux device management daemon — a native root-run systemd
|
|
service. One binary, two subsystems.
|
|
|
|
This repo is Linux-only. The Android implementation is a separate,
|
|
independently-written app (platform-signed priv-app + device owner, not a
|
|
native daemon — most of what these payloads need on Android is Java-SDK
|
|
first): [`android_packages_apps_PawletProfiled`](https://git.oxmc.me/PawletOS/android_packages_apps_PawletProfiled).
|
|
Same `.vconfig` profile format and payload types on both platforms; no
|
|
shared code between the two implementations.
|
|
|
|
---
|
|
|
|
## Subsystems
|
|
|
|
### Profile Service
|
|
D-Bus system service at `os.pawlet.ProfiledService`.
|
|
Parses, verifies, persists, and applies `.vconfig` profiles.
|
|
The allowed payload types are controlled by the separately-installed
|
|
`pawletprofiled-config-schema` package — OEMs can restrict the
|
|
configuration surface without touching the daemon binary.
|
|
|
|
### ZTE Client
|
|
Zero-Touch Enrollment. Runs as a background thread.
|
|
Watches NetworkManager for internet connectivity.
|
|
On first connection, queries a ZTE lookup server with the device's
|
|
hardware identity. Downloads and installs the enrollment profile
|
|
automatically via the local D-Bus service.
|
|
Handles the deferred case: if no network at first boot,
|
|
enrolls on the first successful connection later.
|
|
|
|
---
|
|
|
|
## Package layout
|
|
|
|
```
|
|
pawletprofiled/
|
|
├── CMakeLists.txt Linux CMake build
|
|
├── zte.conf.example ZTE client config
|
|
│
|
|
├── src/
|
|
│ ├── main.cpp Entry point
|
|
│ ├── PawletProfileService.h/.cpp D-Bus service
|
|
│ ├── ProfileParser.h/.cpp YAML parser (libyaml)
|
|
│ ├── ProfileStore.h/.cpp Disk persistence + password hashing
|
|
│ ├── SignatureVerifier.h/.cpp CMS/PKCS#7 verification (OpenSSL)
|
|
│ ├── payloads/
|
|
│ │ ├── PayloadHandler.h Handler contract + PayloadUtil.h helpers
|
|
│ │ └── PayloadHandlers.cpp Dispatch — forwards to platform/linux/*.cpp
|
|
│ └── platform/
|
|
│ ├── <Name>.h Per-payload-type contract (apply/revert)
|
|
│ └── linux/<Name>.cpp One real implementation per payload type
|
|
│
|
|
├── src/zte/
|
|
│ ├── DeviceIdentity.h/.cpp Hardware identity collection
|
|
│ ├── ZTELookupClient.h/.cpp ZTE server HTTPS client (libcurl)
|
|
│ └── ConnectivityWatcher.h/.cpp NM D-Bus connectivity watcher
|
|
│
|
|
├── systemd/pawletprofiled.service Systemd unit
|
|
├── dbus/os.pawlet.ProfiledService.conf D-Bus policy
|
|
├── apparmor/pawletprofiled AppArmor MAC profile
|
|
└── debian/ Debian Trixie packaging
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencies
|
|
|
|
### Runtime
|
|
| Package | Used for |
|
|
|---|---|
|
|
| `pawletprofiled-config-schema` | Schema defining allowed payload types |
|
|
| `libssl3` | CMS signature verification, PBKDF2 |
|
|
| `libyaml-0-2` | YAML parsing |
|
|
| `libdbus-1-3` | D-Bus system service |
|
|
| `libcurl4` | ZTE HTTPS lookups |
|
|
| `network-manager` | WiFi/Ethernet/VPN keyfile reload |
|
|
|
|
### Recommended (enable specific payload handlers)
|
|
| Package | Payload types |
|
|
|---|---|
|
|
| `cloud-init` | `mdm`, `first-boot` |
|
|
| `nftables` | `firewall` |
|
|
| `network-manager-strongswan` | `vpn` (IKEv2) |
|
|
| `network-manager-l2tp` | `vpn` (L2TP) |
|
|
| `libpam-pwquality` | `passcode` (complexity) |
|
|
| `libpam-faillock` | `passcode` (lockout) |
|
|
| `libnss3-tools` | `pkcs12` (NSSDB import) |
|
|
| `unattended-upgrades` | `software-update` |
|
|
| `dconf-cli` | `desktop`, `screensaver`, `first-boot` |
|
|
| `tpm2-tools` | ZTE TPM EK detection |
|
|
|
|
---
|
|
|
|
## Building — Linux (Debian Trixie)
|
|
|
|
```bash
|
|
# Build dependencies
|
|
sudo apt install -y \
|
|
cmake libssl-dev libyaml-dev libdbus-1-dev \
|
|
libsystemd-dev libcurl4-openssl-dev pkg-config
|
|
|
|
# Build .deb (includes pawletprofiled-config-schema as a dep)
|
|
dpkg-buildpackage -us -uc -b
|
|
sudo apt install \
|
|
../pawletprofiled-config-schema_1.0.0-1_all.deb \
|
|
../pawletprofiled_1.0.0-1_amd64.deb
|
|
|
|
# Or build without packaging
|
|
cmake -B build -DCMAKE_BUILD_TYPE=Release
|
|
cmake --build build -j$(nproc)
|
|
sudo cmake --install build
|
|
```
|
|
|
|
---
|
|
|
|
## Android
|
|
|
|
Not built from this repo — see
|
|
[`android_packages_apps_PawletProfiled`](https://git.oxmc.me/PawletOS/android_packages_apps_PawletProfiled),
|
|
a platform-signed priv-app + device owner with its own independent
|
|
implementation of every payload type in this daemon's schema.
|
|
|
|
---
|
|
|
|
## Verifying the installation
|
|
|
|
```bash
|
|
# Service status
|
|
systemctl status pawletprofiled
|
|
|
|
# D-Bus connectivity
|
|
dbus-send --system --print-reply \
|
|
--dest=os.pawlet.ProfiledService \
|
|
/os/pawlet/ProfiledService \
|
|
os.pawlet.IProfiledService.IsDeviceManaged
|
|
|
|
# List installed profiles
|
|
dbus-send --system --print-reply \
|
|
--dest=os.pawlet.ProfiledService \
|
|
/os/pawlet/ProfiledService \
|
|
os.pawlet.IProfiledService.ListProfiles
|
|
|
|
# ZTE enrollment state
|
|
cat /var/lib/pawletprofiled/zte_state.json
|
|
|
|
# Live logs
|
|
journalctl -u pawletprofiled -f
|
|
```
|
|
|
|
---
|
|
|
|
## Installing a profile from the command line
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
import dbus, sys
|
|
|
|
with open(sys.argv[1], 'rb') as f:
|
|
data = f.read()
|
|
|
|
bus = dbus.SystemBus()
|
|
obj = bus.get_object('os.pawlet.ProfiledService',
|
|
'/os/pawlet/ProfiledService')
|
|
svc = dbus.Interface(obj, 'os.pawlet.IProfiledService')
|
|
uuid = svc.InstallProfile(dbus.ByteArray(data))
|
|
print('Installed profile UUID:', uuid)
|
|
```
|
|
|
|
---
|
|
|
|
## Signing profiles
|
|
|
|
Profiles containing `mdm`, `kiosk`, `asam`, or `removal-password` payloads
|
|
must be CMS/PKCS#7 signed. Unsigned profiles install with a trust warning.
|
|
|
|
```bash
|
|
# Generate a development signing key and CA
|
|
openssl req -x509 -newkey rsa:4096 -days 3650 -nodes \
|
|
-keyout /etc/pawletprofiled/signing_key.pem \
|
|
-out /etc/pawletprofiled/profile_ca.pem \
|
|
-subj "/CN=PawletOS Profile Signing/O=oxmc"
|
|
|
|
# Sign a profile
|
|
openssl cms -sign \
|
|
-in my.vconfig \
|
|
-out my.signed.vconfig \
|
|
-signer /etc/pawletprofiled/profile_ca.pem \
|
|
-inkey /etc/pawletprofiled/signing_key.pem \
|
|
-outform DER -nodetach
|
|
```
|
|
|
|
The CA at `/etc/pawletprofiled/profile_ca.pem` is trusted automatically.
|
|
For a system-wide trusted badge, also install it as a system CA:
|
|
|
|
```bash
|
|
cp /etc/pawletprofiled/profile_ca.pem \
|
|
/usr/local/share/ca-certificates/pawletos-profile-ca.crt
|
|
update-ca-certificates
|
|
```
|
|
|
|
---
|
|
|
|
## Preinstalled profiles
|
|
|
|
Drop signed `.vconfig` files into `/etc/pawletprofiled/preinstalled/`
|
|
before first boot. The daemon applies them at startup,
|
|
**before** `cloud-init-local.service` starts (enforced via
|
|
`Before=cloud-init-local.service` in the systemd unit).
|
|
|
|
This lets image builders bake in WiFi credentials, first-boot GNOME
|
|
setup suppression, and MDM enrollment — all applied before cloud-init
|
|
tries to reach its datasource.
|
|
|
|
---
|
|
|
|
## Zero-Touch Enrollment
|
|
|
|
ZTE configuration lives at `/etc/pawletprofiled/zte.conf` (copy from
|
|
`zte.conf.example`). The key setting is `server_url`, which points to
|
|
your `pawlet-zte-server` instance.
|
|
|
|
```ini
|
|
server_url=https://zte.yourdomain.com
|
|
```
|
|
|
|
Hardware identity priority used for the ZTE lookup:
|
|
|
|
| Priority | Source | Path |
|
|
|---|---|---|
|
|
| 1 | DMI system UUID | `/sys/class/dmi/id/product_uuid` |
|
|
| 2 | Board serial | `/sys/class/dmi/id/board_serial` |
|
|
| 3 | System serial | `/sys/class/dmi/id/product_serial` |
|
|
| 4 | machine-id | `/etc/machine-id` |
|
|
| 5 | Permanent MAC | `/sys/class/net/<if>/perm_address` |
|
|
|
|
---
|
|
|
|
## Bugs fixed vs. previous iterations
|
|
|
|
| Bug | Fix |
|
|
|---|---|
|
|
| `ProfileParser.cpp` used `android-base/logging.h` and `LOG()` | Replaced with `syslog()` throughout |
|
|
| `debian/rules` had postinst `install -d` lines leaked after `override_dh_installsystemd` | Removed stray lines |
|
|
| `sepolicy/pawletprofiled.te` had typo `vesperprofrofiled` | Fixed |
|
|
| `apparmor/pawletprofiled` used `abstractions/openssl` (doesn't exist on Debian) | Replaced with explicit `/etc/ssl/certs/` and network socket rules |
|
|
| `ZTELookupClient` called `curl_global_init` but never `curl_global_cleanup` | Added to destructor |
|
|
| `debian/control` missing `Depends: pawletprofiled-config-schema` | Added |
|