GitHub Security Advisory: GHSA-246w-jgmq-88fg
Package: github.com/jkroepke/openvpn-auth-oauth2 (GO)
Severity: CRITICAL (CVSS 10.0)
CWE: CWE-287: Improper Authentication
Vulnerable versions: >= 1.26.3, < 1.27.3
Patched in: 1.27.3

Summary: openvpn-auth-oauth2 returns FUNC_SUCCESS on client-deny, allowing unauthenticated VPN access

Details:
# Summary

When `openvpn-auth-oauth2` is deployed in the **experimental plugin mode** (shared library loaded by OpenVPN via the `plugin` directive), clients that do not support WebAuth/SSO (e.g., the `openvpn` CLI on Linux) are incorrectly admitted to the VPN despite being denied by the authentication logic. **The default management-interface mode is not affected** because it does not use the OpenVPN plugin return-code mechanism.

# Impact

**Authentication bypass — any VPN client that does not advertise WebAuth/SSO support (`IV_SSO=webauth`) is granted full network access without completing OIDC authentication.**

This affects only deployments running the **experimental plugin mode** in versions 1.26.3 through 1.27.2. The default and recommended deployment via the management interface is **not affected**.

An unauthenticated attacker can connect to the OpenVPN server using any standard OpenVPN client that does not support webauth (e.g., the Linux `openvpn` CLI). The plugin correctly issues a `client-deny` command via the management interface, but returns `OPENVPN_PLUGIN_FUNC_SUCCESS` (status=0) to OpenVPN. Because the `auth_control_file` content is only consulted when the plugin returns `FUNC_DEFERRED`, OpenVPN interprets status=0 as "authentication passed" and admits the client — granting full access to the internal network behind the VPN.


## Root Cause

In `lib/openvpn-auth-oauth2/openvpn/handle.go`, the `ClientAuthDeny` branch of `handleAuthUserPassVerify` wrote `"0"` (deny) to the `auth_control_file` but returned `OPENVPN_PLUGIN_FUNC_SUCCESS`. OpenVPN only reads the `auth_control_file` when the plugin returns `FUNC_DEFERRED`; a synchronous `FUNC_SUCCESS` return is treated as immediate approval regardless of file contents.

**Before fix:**
```go
case management.ClientAuthDeny:
    // ... writes "0" to auth_control_file ...
    if err := openVPNClient.WriteToAuthFile("0"); err != nil {
        // only returned ERROR on write failure
        return c.OpenVPNPluginFuncError
    }
    return c.OpenVPNPluginFuncSuccess  // ← BUG: OpenVPN sees this as "auth passed"
```

**After fix (commit [`36f69a6`](https://github.com/jkroepke/openvpn-auth-oauth2/commit/36f69a6c67c1054da7cbfa04ced3f0555127c8f2)):**
```go
case management.ClientAuthDeny:
    // ... writes "0" to auth_control_file ...
    if err := openVPNClient.WriteToAuthFile("0"); err != nil {
        logger.ErrorContext(p.ctx, "write to auth file", slog.Any("err", err))
    }
    return c.OpenVPNPluginFuncError  // ← FIX: OpenVPN now correctly rejects the client
```

# Patches

This vulnerability is fixed in **v1.27.3**. Users of the experimental plugin mode should upgrade immediately.

- **Fix commit:** [`36f69a6`](https://github.com/jkroepke/openvpn-auth-oauth2/commit/36f69a6c67c1054da7cbfa04ced3f0555127c8f2)
- **Fix PR:** [#829](https://github.com/jkroepke/openvpn-auth-oauth2/pull/829)

# Workarounds

- **Switch to standalone management client mode** (the default, non-plugin deployment). Th