# secmngr

Security manager — a bbfdm dm-service plugin implementing the TR-181 2.21
`Device.Security.` subtree (certificate inventory and CA bundles).

This plugin produces `libsecmngr.so`, loaded by the generic `dm-service`
daemon shipped with bbfdm. It is built and packaged by the `secmngr` package
in the iopsys feed (this source tree lives under that package as `src/`).

## Data model

### `Device.Security.`

| Object / parameter            | Access | Notes                                                              |
| ----------------------------- | ------ | ------------------------------------------------------------------ |
| `CertificateNumberOfEntries`  | R      | Number of `Certificate.{i}.` entries.                              |
| `CABundleNumberOfEntries`     | R      | Number of `CABundle.{i}.` entries.                                 |
| `SupportedBundleFormats`      | R      | Formats `AddCertificateBundle()` accepts: `PEM`.                   |
| `AddCertificateBundle()`      | cmd    | In: `Alias` (opt), `Name` (mand), `BundleFormat` (must be `PEM`), `Bundle`. Out: `Certificate`. Installs a cert+key bundle. |
| `AddCABundle()`               | cmd    | In: `Name`, `Bundle`. Out: `CABundle`, `NumberOfCertificates`. Installs PEM CA certs as a new bundle. |

### `Device.Security.Certificate.{i}.`

Read-mostly certificate inventory discovered from two directories: the system
trust store `/etc/ssl/certs` (OpenSSL hash-named entries) and `/etc/secmngr/certs`
(the `*.pem` files added via `AddCertificateBundle()`).

`Alias` (W), `Name` (R), `LastModif`, `SerialNumber`, `Issuer`,
`NotBefore`, `NotAfter`, `Subject`, `SubjectAlt`, `SignatureAlgorithm`,
`GetCertificateURI()` (cmd → `CertificateURI`, `PrivateKeyURI`), `Delete()` (cmd).

Unique keys: `Alias`, `Name`, and (`SerialNumber` + `Issuer`).

### `Device.Security.CABundle.{i}.`

Admin-managed groups of trusted CA certificates, persisted in UCI
(`/etc/config/secmngr`).

`Name` (R, unique), `CACertificates` (W), and the command `Delete()` (removes
the bundle).

> Note: `GetCertificateURI()` returns an RFC 8089 `file://` URI. A consumer
> wiring this into an OpenSSL/curl API must strip the `file://` scheme first
> (those APIs take a bare path).

> Note: the system-generated `Certificate.{i}.`/`CABundle.{i}.` rows (the
> `/etc/ssl/certs` trust store inventory and the aggregate system CA bundle)
> are read-only. Writing `CACertificates` on the system CABundle, or calling
> `Delete()` on a system-owned `Certificate`/`CABundle`, is silently
> discarded — the request reports success but has no effect, and no fault
> is raised.

## TR-181 2.21 alignment

The object tree matches the TR-181 2.21 `Device.Security.` definition: all three
`Security.` scalars plus `AddCertificateBundle()`/`AddCABundle()`, the full
`Certificate.{i}.` parameter set with `GetCertificateURI()` and `Delete()`, and the
full `CABundle.{i}.` set with `Delete()`. The
`AddCertificateBundle()`/`AddCABundle()` USP fault codes (7260–7267, 7270) follow the
spec.

Two points worth noting on how the implementation meets 2.21:

- **`Certificate.{i}.Alias`** uses the standard framework Alias handling
  (`bbf_get_alias`/`bbf_set_alias`): an unset Alias is auto-assigned `cpe-<instance>`,
  and a set value is validated and stored. Alias is **mutable** (not enforced
  write-once), matching every other TR-181 object in this stack — the framework
  helper does not implement write-once.
- **`Security.SupportedBundleFormats`** is a `string[]`. This stack has no distinct
  list type; a `string[]` is carried as a comma-separated `DMT_STRING` (exactly how
  `CABundle.{i}.CACertificates`, also a spec `string[]`, is done). The value
  `"PEM"` is therefore the correct representation — PKCS#12 is not supported as of now.

`GetCertificateURI()` returns RFC 8089 `file://` FileURIs.

## Build

Standalone (development):

```sh
cd src
make            # produces libsecmngr.so
```

The vendor prefix defaults to `X_IOWRT_EU_` unless `CFLAGS` already defines
`-DBBF_VENDOR_PREFIX`. In the iopsys feed the package Makefile passes the
configured `CONFIG_BBF_VENDOR_PREFIX` through, so feed builds use the
configured namespace (e.g. `X_IOPSYS_EU_`).

## Certificate storage

When a certificate or CA bundle is added through the data model, the files
land on disk as follows:

| Source                              | On-disk location                                  | UCI record                                              |
| ----------------------------------- | ------------------------------------------------- | ------------------------------------------------------- |
| `AddCertificateBundle()` (PEM) | cert → `/etc/secmngr/certs/<serial>.pem`, key → `/etc/secmngr/private/<serial>.key` (mode 0600) | `config certificate` with `path`, `key_path`, `enable`, `name`, `alias` |
| `AddCABundle()` (PEM)  | combined blob → `/etc/secmngr/cabundles/<name>.pem` | `config cabundle` named `<name>` with a `cert` list of member Names |

- `<serial>` is the certificate's X.509 serial number rendered as lowercase
  hex, so re-adding a cert with the same serial overwrites in place.
- The base directory is fixed at `/etc/secmngr` (`SECMNGR_DEFAULT_BASE_PATH`
  in `src/common.h`) — not configurable via UCI. Certs, keys, and CA bundles
  are all stored under it (`certs/`, `private/`, `cabundles/` respectively).
- Added certs appear in `Device.Security.Certificate.{i}.` directly (the data
  model scans `/etc/secmngr/certs` as well as `/etc/ssl/certs`).

## Configuration options

The `globals` section in `/etc/config/secmngr` holds plugin-wide options:

| Option             | Default        | Purpose                                                        |
| ------------------ | -------------- | -------------------------------------------------------------- |
| `pkcs12_password`  | (empty)        | Password used to decrypt encrypted PEM private keys in `AddCertificateBundle()`. (Option name retained for backward compatibility; PKCS#12 bundles are not supported as of now.) |

## CA bundle reconciliation (`/etc/init.d/secmngr`)

`AddCABundle()` snapshots the submitted blob to `cabundles/<name>.pem` once,
at add time. If a member certificate is later removed independently (e.g.
via its own `Certificate.{i}.Delete()`), or a bundle's `cert` list is edited
directly in UCI, that snapshot can drift from the bundle's actual current
membership. The init script (`START=95`) rebuilds each bundle's snapshot
from its live `cert` list — leaving it untouched if nothing changed — so the
on-disk file never stays stale for longer than the next boot or reload.

It runs at boot and can be re-run on demand:

```sh
/etc/init.d/secmngr reload
```

## Install (via the iopsys feed package)

The package Makefile installs:

| Artifact                  | Destination                                  | How                        |
| ------------------------- | -------------------------------------------- | -------------------------- |
| `libsecmngr.so`           | bbfdm micro-services dir                     | `BBFDM_INSTALL_MS_DM`      |
| `bbfdm_service.json`      | `/etc/bbfdm/services/secmngr.json`           | `BBFDM_REGISTER_SERVICES`  |
| `files/etc/config/secmngr`| `/etc/config/secmngr`                        | `INSTALL_CONF`             |
| `files/etc/init.d/secmngr`| `/etc/init.d/secmngr`                        | `INSTALL_BIN`              |
| `files/lib/upgrade/keep.d/secmngr` | `/lib/upgrade/keep.d/secmngr`       | `INSTALL_DATA`             |

The `keep.d` entry preserves the certificate store
(`/etc/secmngr/{certs,private,cabundles}`) across firmware upgrades, so added
certs and CA bundles survive a sysupgrade.

## Runtime

`dm-service` reads `/etc/bbfdm/services/secmngr.json` and loads the plugin.
Verify with:

```sh
dmcli get Device.Security.
```

## UCI persistence

All state lives in `/etc/config/secmngr`: the `globals` section (see
[Configuration options](#configuration-options)), one `config cabundle` per CA
bundle, and one `config certificate` per added cert — see
[Certificate storage](#certificate-storage) for the per-section fields. The
shipped default is in `files/etc/config/secmngr`.
