Commit graph

3 commits

Author SHA1 Message Date
Mike Conley
bf25bf8cbe
fix: keep saved profile passwords working on Windows after qtkeychain library update (#9395)
#### Brief overview of PR changes/additions
- Adds a Windows-only migration that recovers stored passwords after
qtkeychain 0.17.0 changes the Windows Credential Manager naming scheme
(bare key -> "key@service"), re-storing them under the new name on first
read
- Fixes the existing old-format migration reads/cleanup, which relied on
the same pre-0.17 service-ignoring behaviour and would silently miss
under 0.17+
- Surfaces the linked qtkeychain version as a QTKEYCHAIN_LINKED_VERSION
compile definition (the library's own header macro is stale); it gates
the old-entry cleanup and is logged for diagnostics
- Adds CredentialManagerKeychainTest: Windows-only ctests that plant the
historical credential layouts in the real credential store (via
empty-service QKeychain jobs, which resolve to the bare TargetName on
every qtkeychain version) and verify round trip, bare-entry migration,
no resurrection of deleted passwords, old-format migration, and
colliding-format recovery

#### Motivation for adding to Mudlet
Once Windows builds pick up qtkeychain 0.17+ (e.g. via MSYS2), every
stored profile password would silently become unreadable and users would
have to re-enter them; this makes the transition seamless.

#### Other info (issues closed, discussion etc)
qtkeychain 0.17.0 breaking change:
https://github.com/frankosterfeld/qtkeychain/releases/tag/0.17.0. A read
with an empty service resolves to the bare key on every qtkeychain
version, so the migration layer works regardless of which version is
linked. On pre-0.17 builds it is normally dormant (the primary read
already looks up the bare key), though a transient read failure can
route into it as a harmless retry; deletion of the old entry only
happens when the linked qtkeychain is known to be 0.17+, so the
recovered credential can never be deleted on older versions. The new
keychain tests branch their expectations on QTKEYCHAIN_LINKED_VERSION:
on today's MSYS2 they verify the migration stays dormant, and they flip
to verifying the actual migration automatically once MSYS2 ships 0.17+.
They run on Windows PR CI via ctest and skip on other platforms or when
the credential store is unavailable.

**Test case:** CredentialManagerKeychainTest covers the scenarios
automatically on Windows CI. Manually: store a profile password,
restart, confirm auto-login still works (pre-0.17: migration is a
no-op). With a qtkeychain 0.17+ build: store a password using a pre-0.17
build, then run this build - the password should still load, and the
Credential Manager entry moves to the "key@service" name.

---------

Signed-off-by: Michael Conley <sousesider@gmail.com>
2026-07-12 20:59:37 +02:00
Mike Conley
fa61c537a7
Fix: Restore password loading from portable file in connection dialog (#8993)
#### Brief overview of PR changes/additions

Restores the ability for the connection dialog to load saved passwords
from the portable password file on disk, which stopped working after PR
#7956 introduced secure credential management.

#### Motivation for adding to Mudlet

After #7956, users who had passwords saved in the old portable file
format (`~/.config/mudlet/profiles/<name>/password`) could no longer see
their passwords pre-filled when selecting a profile in the connection
dialog. This affected users who had not yet migrated to the system
keychain.

The fix ensures all three password sources are checked in order: system
keychain, QSettings, and finally the portable file on disk. It also
moves the password migration step to run before the connection dialog
opens, so passwords are migrated in time to be found.

Includes the profile password collision fix from #8948 and adds new unit
tests for the password migration and fallback paths.

#### Other info (issues closed, discussion etc)

Reported by @SlySven — PR #7956 (commit 79ab7be4) broke password loading
for profiles that only had passwords in the portable file.

---------

Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
2026-03-08 14:38:15 +01:00
Mike Conley
79ab7be4e2
Improve: Secure credential management with system keychain integration and legacy migration (#7956)
#### Brief overview of PR changes/additions

This pull request implements secure credential management for Mudlet
with system keychain integration and encrypted fallback storage:

**Core Components:**

- **CredentialManager**: High-level API for secure credential storage
with QtKeychain integration
- **SecureStringUtils**: Qt-based cryptographic utilities for encrypted
file storage
- **Legacy Migration**: Automatic detection and migration of existing
plaintext passwords

**Key Features:**

- **System Keychain Integration**: Primary storage in macOS Keychain,
Windows Credential Store, and Linux Secret Service via QtKeychain
- **Encrypted File Fallback**: Qt crypto-based AES encryption for
portable mode and keychain unavailability
- **Seamless Migration**: Automatic detection and upgrade of legacy
password storage formats
- **Profile Isolation**: Per-profile encryption keys prevent
cross-profile credential access
- **Portable Mode Support**: Automatic detection and secure file-based
storage for portable installations

#### Motivation for adding to Mudlet

**Security Enhancement:**

- Eliminates plaintext password storage in profile XML files
- Provides industry-standard system keychain integration for credential
security
- Implements authenticated encryption for fallback scenarios

**User Experience:**  

- Zero configuration required - works automatically across all platforms
- Seamless migration from existing plaintext passwords to secure storage
- Native system integration provides familiar credential management
experience

**Future-Proofing:**

- Extensible architecture supports additional credential types (API
keys, tokens, etc.)
- Robust fallback ensures functionality in all deployment scenarios
- Prepares foundation for OAuth and external service integrations

#### Other info (issues closed, discussion etc)

**Security Architecture:**

- **Keychain-First Strategy**: Prefers system keychain with automatic
encrypted file fallback
- **Legacy Format Detection**: Automatically migrates passwords from
development branch keychain format
- **Memory Security**: Secure string clearing and controlled credential
lifecycle management
- **Input Validation**: Path traversal protection and service name
sanitization

**Implementation Highlights:**

- **Async Operations**: Non-blocking keychain operations with timeout
protection
- **Thread Safety**: Event-driven architecture prevents UI blocking and
race conditions
- **Comprehensive Testing**: Full test coverage for encryption,
migration, and fallback scenarios
- **Cross-Platform**: Unified API across Windows, macOS, and Linux with
platform-specific optimizations

**Version Compatibility & Migration:**

- **Mudlet 4.19.x Compatibility**: Preserves existing plaintext password
storage to ensure compatibility when switching between 4.19.x stable and
development builds
- **Mudlet 4.20.x Migration**: Post-4.20.0 release, automatic migration
begins converting plaintext passwords to secure storage
- **Bidirectional Safety**: Users can safely run both 4.19.x and
development versions without losing access to their passwords during the
transition period
- **Legacy Format Support**: Automatically detects and migrates
passwords from the original development branch keychain format
(`service="Mudlet profile"`) to the new secure format

# Credential Management Workflows

## 1. Credential Storage Strategy

```mermaid
flowchart TD
    A[Store Password Request] --> B{Portable Mode?}
    B -->|Yes| C[Encrypt & Store in Profile File]
    B -->|No| D[Store in System Keychain]
    D --> E{Keychain Success?}
    E -->|Yes| F[Remove Encrypted Fallback File]
    E -->|No| G[Fallback to Encrypted File]
    F --> H[Success]
    G --> I{Encryption Success?}
    I -->|Yes| H
    I -->|No| J[Failure]
    C --> I
    
    classDef primary fill:#4CAF50,stroke:#2E7D32,stroke-width:2px,color:#000
    classDef fallback fill:#FF9800,stroke:#E65100,stroke-width:2px,color:#000
    classDef decision fill:#2196F3,stroke:#0D47A1,stroke-width:2px,color:#fff
    classDef result fill:#9C27B0,stroke:#4A148C,stroke-width:2px,color:#fff
    
    class D,F primary
    class C,G fallback
    class B,E,I decision
    class H,J result
```

## 2. Legacy Migration Workflow

```mermaid
flowchart TD
    A[Retrieve Password Request] --> B[Try New Keychain Format]
    B --> C{Password Found?}
    C -->|Yes| D[Return Password]
    C -->|No| E[Check Legacy Keychain Format]
    E --> F{Legacy Found?}
    F -->|Yes| G[Migrate to New Format]
    G --> H[Store in New Format]
    H --> I[Remove Legacy Entry]
    I --> J[Return Migrated Password]
    F -->|No| K[Try Encrypted File]
    K --> L{File Found?}
    L -->|Yes| M[Decrypt & Return]
    L -->|No| N[Return Empty - No Password Stored]
    
    classDef newformat fill:#4CAF50,stroke:#2E7D32,stroke-width:2px,color:#000
    classDef legacy fill:#FF9800,stroke:#E65100,stroke-width:2px,color:#000
    classDef migration fill:#9C27B0,stroke:#4A148C,stroke-width:2px,color:#fff
    classDef fallback fill:#607D8B,stroke:#263238,stroke-width:2px,color:#fff
    classDef result fill:#2196F3,stroke:#0D47A1,stroke-width:2px,color:#fff
    
    class B,H newformat
    class E,I legacy
    class G migration
    class K fallback
    class D,J,M,N result
```

## 3. Cross-Platform Keychain Integration

```mermaid
flowchart TD
    A[QtKeychain Request] --> B{Platform Detection}
    B -->|macOS| C[Access Keychain Services]
    B -->|Windows| D[Access Credential Store]
    B -->|Linux| E[Access Secret Service]
    C --> F[Store/Retrieve Credential]
    D --> F
    E --> F
    F --> G{Operation Success?}
    G -->|Yes| H[Return Result]
    G -->|No| I[Log Error & Fallback]
    I --> J[Use Encrypted File Storage]
    J --> K[AES Encryption with Profile Key]
    K --> L[Store in Profile Directory]
    
    classDef platform fill:#2196F3,stroke:#0D47A1,stroke-width:2px,color:#fff
    classDef keychain fill:#4CAF50,stroke:#2E7D32,stroke-width:2px,color:#000
    classDef fallback fill:#FF9800,stroke:#E65100,stroke-width:2px,color:#000
    classDef crypto fill:#9C27B0,stroke:#4A148C,stroke-width:2px,color:#fff
    
    class C,D,E platform
    class F,H keychain
    class I,J fallback
    class K,L crypto
```

This implementation provides a comprehensive, secure, and user-friendly
credential management system that seamlessly upgrades existing Mudlet
installations while providing robust security for future credential
storage needs.

---------

Co-authored-by: Vadim Peretokin <vperetokin@hey.com>
2025-08-17 12:44:38 +02:00