Skip to content

fix: scope ISP credential lookup to its connection target - #3

Open
rimuln wants to merge 1 commit into
lucatze:mainfrom
rimuln:fix/block-scoped-isp-credentials
Open

fix: scope ISP credential lookup to its connection target#3
rimuln wants to merge 1 commit into
lucatze:mainfrom
rimuln:fix/block-scoped-isp-credentials

Conversation

@rimuln

@rimuln rimuln commented Jul 31, 2026

Copy link
Copy Markdown

Fixes #2.

The problem

get_internet_config() looked up the ISP credentials with an unscoped re.search() over the whole of ar7.cfg, so it returned whichever username/passwd pair came first in the file. On a FRITZ!Box 7590 running FRITZ!OS 154.08.25 that is ar7cfg > serialcfg — a disabled mobile dial-up block whose stock APN entry decrypts to the placeholder ppp for both fields.

The result was serial_username: "ppp" / serial_password: "ppp" in the output, with the real provider login never reported. Because the output still contains something that looks like a credential pair, there is no signal that the actual one was missed — that is the part which makes this worse than a plain missing field.

Where the credentials actually are

ar7cfg > targets > local. AVM writes repeated entries as one block name followed by several brace-delimited bodies:

targets {
        type = pppcfg_target_internet;
        name = "internet";
        local {
                username = "$$$$...";   <- provider login
                passwd   = "$$$$...";
        }
        ...
} {
        name = "voip";
        local {
                username = "";
                passwd = "";
        }
        ...
}

Only the target named internet carries the provider login, so the lookup has to be scoped to a single entry.

What this changes

  • _iter_block_entries() — brace-depth traversal yielding each body of a repeated name { … } { … } construct. Regular expressions cannot track nesting, which is why the existing _extract_blocks() cannot express this. That helper is currently unused, so I left it untouched.
  • _get_nested_block() — the first body of a named block, brace-aware.
  • _get_isp_credentials() — walks the targets entries, prefers the one named internet, and falls back to the first entry carrying a non-empty username. It also returns the originating target name, exposed as a new pppoe_target key so it is clear which target a credential came from.
  • get_internet_config() — uses the scoped lookup; keeps pppoeuser/pppoepwd/ppppasswd as a fallback for firmware generations that use those names; and reports serial_username/serial_password only when serialcfg is actually enabled (mode != serialmode_off), read from that block rather than from the whole file.

Existing key names are unchanged, so anything parsing the JSON keeps working. pppoe_target is purely additive.

Verification

Tested against a real password-protected 7590 export (FRITZ!OS 154.08.25, VDSL, active_provider = "other"), comparing f540572 against this branch. The expected lengths below come from an independent manual decrypt of the same file.

field before after
pppoe_username missing 9 chars ✓
pppoe_password missing 8 chars ✓
pppoe_target missing internet
serial_username ppp (3 chars) suppressed ✓
serial_password ppp (3 chars) suppressed ✓

No regressions: VoIP accounts 2, WLAN networks 3, phonebook 52, users 4, devices 2 — identical on both builds. All 149 encrypted values in that export decrypt with zero MD5 integrity failures.

I cannot contribute the export itself as a fixture, for the obvious reason. If it would help, I am happy to add a synthetic ar7.cfg fragment with the same block shape and dummy $$$$ values as a regression test.

Note on scope

The same unscoped-first-match pattern applies to _get_cfg_value() / _get_cfg_encrypted() in general. The export used here holds 31 username and 12 passwd keys spread across serialcfg, targets > local, ddns > accounts, webui, TR_064, emailnotify and 20 entries under apps > apps, so which one wins depends on file ordering. I deliberately kept this PR to the internet-config path; the helpers added here would make the remaining call sites straightforward to scope in a follow-up if you want that.

get_internet_config() searched the whole of ar7.cfg for username/passwd with
re.search(), so it returned whichever pair appeared first in the file. On a
FRITZ!Box 7590 running FRITZ!OS 154.08.25 that is ar7cfg > serialcfg, a
disabled mobile dial-up block holding vendor placeholders, which surfaced as
serial_username/serial_password "ppp"/"ppp" while the real provider login was
never reported at all. Output that looks like a credential pair but is not one
leaves the user no signal that the actual one was missed.

The credentials live in ar7cfg > targets > local. AVM writes repeated entries
as one block name followed by several brace-delimited bodies, as in
"targets { ...internet... } { ...voip... }", and only the target named
"internet" carries the provider login, so the lookup has to be scoped to a
single entry rather than to the file.

Add brace-depth-aware block traversal, read the credentials from the matching
target, and report the originating target name as pppoe_target. Keep the
pppoeuser/pppoepwd/ppppasswd key names as a fallback for firmware generations
that use them. Report serial_username/serial_password only when serialcfg is
enabled, so inactive vendor defaults stay out of the output.

Verified against a real password-protected 7590 export: the provider login and
password now come from targets > local, the placeholders are gone, and VoIP,
WLAN, phonebook, user and device extraction are unchanged.

Refs lucatze#2

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@rimuln
rimuln force-pushed the fix/block-scoped-isp-credentials branch from b7263c4 to 7782935 Compare July 31, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

get_internet_config() reports placeholder ppp/ppp instead of the real PPPoE credentials (FRITZ!Box 7590, FRITZ!OS 8.25)

1 participant