Skip to content

ovpn-dco 2.8.4: non-elevated OpenVPN GUI fails with CreateFile access denied (errno=5) on Windows 11 [Provided Workaround script] #1092

Description

@s31t0n

This appears to be the same underlying problem reported in the previously closed issue, but it is still reproducible with OpenVPN 2.7.6 and ovpn-dco-win 2.8.4 on Windows 11.
Refered to the previus ticket #629

I was able to reproduce this issue and implement a working workaround.

Environment

  • Windows 11 Pro 25H2 x64, OS Build 26200.8875
  • OpenVPN Community 2.7.6
  • OpenVPN GUI 11.65.0.0
  • ovpn-dco-win 2.8.4
  • OpenVPN GUI running as a standard, non-elevated user

OpenVPN reports the operating system internally as:

Windows version: 10.0.26200, amd64

This is still Windows 11; Windows 11 retains the NT version number 10.0 for compatibility.

Symptoms

The relevant error was:

Using device interface: ...\ovpn-dco
CreateFile failed on ovpn-dco device: ... Access is denied. (errno=5)
Failed to open ovpn-dco adapter

The same OpenVPN profile worked correctly when OpenVPN GUI was started with Run as administrator.

Adding:

disable-dco

also allowed OpenVPN GUI to connect as a standard user, but throughput was significantly lower.

This confirmed that the VPN profile, authentication, routing, server, and DCO driver itself were working. The failure occurred specifically when a non-elevated openvpn.exe process tried to open the ovpn-dco device interface.

Troubleshooting already attempted

The following actions did not solve the problem:

  • uninstalling OpenVPN;
  • deleting all ovpn-dco adapter instances;
  • removing every ovpn-dco driver package from the Windows Driver Store;
  • rebooting;
  • reinstalling OpenVPN;
  • reinstalling ovpn-dco-win 2.8.4;
  • recreating the DCO adapter;
  • resetting the Windows network stack;
  • verifying that OpenVPNServiceInteractive was running;
  • verifying that the user was a member of the OpenVPN Administrators group.

After a complete uninstall, Driver Store cleanup, reboot, and clean reinstall, the newly created DCO device still returned:

Access is denied. (errno=5)

The failing operation remained the CreateFile() call against the ovpn-dco device path.

Root cause identified

The DCO driver was installed, loaded, and discoverable, but the effective per-device security descriptor did not allow the standard user's OpenVPN process to open the DCO device interface.

Reinstalling the driver did not correct that per-device ACL.

The workaround was to modify the security descriptor of the ovpn-dco Plug and Play device instance and grant access only to the current Windows user's SID.

What the workaround script does

The PowerShell script must be run once as administrator.

It performs the following operations:

  1. Stops running openvpn.exe and openvpn-gui.exe processes.

  2. Checks whether a persistent DCO adapter exists by running:

    tapctl.exe list --hwid ovpn-dco
    
  3. If the requested adapter does not exist, creates one with:

    tapctl.exe create --hwid ovpn-dco --name "OpenVPN DCO User"
    
  4. Uses Windows SetupAPI to enumerate currently present devices in the Network device class:

    {4d36e972-e325-11ce-bfc1-08002be10318}
    
  5. Selects only devices whose hardware ID is exactly:

    ovpn-dco
    
  6. Reads the per-device security descriptor through:

    SPDRP_SECURITY_SDS
    

    using SetupDiGetDeviceRegistryPropertyW().

  7. Saves the original device instance IDs and SDDL security descriptors to a JSON backup under:

    C:\ProgramData\OpenVPN\DcoAclFix
    
  8. Retrieves the SID of the current Windows user.

  9. Adds the following access-control entry to the device DACL:

    (A;;GRGWGX;;;<CURRENT_USER_SID>)
    

    This grants Generic Read, Generic Write, and Generic Execute access only to the current user.

  10. Writes the updated security descriptor through:

    SetupDiSetDeviceRegistryPropertyW(..., SPDRP_SECURITY_SDS, ...)
    
  11. Restarts each affected DCO device with:

    pnputil.exe /restart-device <INSTANCE_ID>
    
  12. Optionally updates a specified .ovpn profile by:

    • removing disable-dco;

    • removing existing dev-node directives;

    • adding:

      dev-node "OpenVPN DCO User"
      

The script does not grant device access to Everyone.

It does not modify the ACL of the ovpn-dco kernel service.

It modifies only the security descriptors of present Plug and Play devices whose hardware ID is exactly ovpn-dco.

How to locate and verify the OpenVPN profile

Before running the script with -ConfigPath, first identify the exact .ovpn profile used by the failing connection.

The profile filename is normally visible in the OpenVPN log, for example:

config = 'Client.ovpn'

It may also correspond to the connection name displayed by OpenVPN GUI.

Open PowerShell as Administrator and list the .ovpn profiles in the standard OpenVPN directories:

$OpenVpnRoots = @(
    "$env:USERPROFILE\OpenVPN",
    "$env:ProgramFiles\OpenVPN"
) | Where-Object {
    Test-Path -LiteralPath $_
}

Get-ChildItem `
    -Path $OpenVpnRoots `
    -Filter "*.ovpn" `
    -File `
    -Recurse `
    -ErrorAction SilentlyContinue |
    Sort-Object FullName |
    Select-Object FullName, LastWriteTime

Find the profile whose filename matches the profile shown in the OpenVPN log or OpenVPN GUI.

Set its exact path in a variable:

$ProfilePath = "C:\Path\To\Client.ovpn"

Verify that the file exists:

Test-Path -LiteralPath $ProfilePath

The result must be:

True

Review the relevant profile directives before making changes:

Get-Content -LiteralPath $ProfilePath |
    Select-String -Pattern '^\s*(remote|dev|dev-node|disable-dco)\b'

Confirm that this is the correct VPN profile before continuing.

Create an additional manual backup:

$ManualBackup = "$ProfilePath.manual-backup-$(Get-Date -Format 'yyyyMMdd-HHmmss')"

Copy-Item `
    -LiteralPath $ProfilePath `
    -Destination $ManualBackup

Write-Host "Manual profile backup: $ManualBackup"

The script also creates its own timestamped profile backup when -ConfigPath is used, but this manual backup provides an additional recovery copy.

How to run the workaround

Save or download the script as:

Fix-OpenVpnDcoAcl-English-Generic.ps1

For example, place it in the current user's Downloads directory.

Close OpenVPN GUI completely.

Open PowerShell as Administrator and define the script path:

$ScriptPath = "$env:USERPROFILE\Downloads\Fix-OpenVpnDcoAcl-English-Generic.ps1"

Verify that the script exists:

Test-Path -LiteralPath $ScriptPath

The result must be:

True

Allow scripts only for the current PowerShell process and unblock the downloaded file:

Set-ExecutionPolicy -Scope Process Bypass -Force
Unblock-File -LiteralPath $ScriptPath

Run the script and provide the previously verified OpenVPN profile:

& $ScriptPath -ConfigPath $ProfilePath

Administrator privileges are required only while applying the device ACL change.

When -ConfigPath is used, the script:

  • backs up the original device ACLs;

  • creates the persistent DCO adapter if necessary;

  • grants the current user's SID access to the DCO device;

  • restarts the affected device;

  • creates a timestamped backup of the .ovpn file;

  • removes disable-dco;

  • removes existing dev-node directives;

  • adds:

    dev-node "OpenVPN DCO User"
    

An automatically created profile backup will have a name similar to:

Client.ovpn.bak-20260806-153000

After the script completes:

  1. Close the elevated PowerShell window.
  2. Start OpenVPN GUI normally.
  3. Do not use Run as administrator.
  4. Connect using the modified profile.

After applying this per-device ACL change, OpenVPN GUI was able to open the DCO device successfully as a standard user. DCO remained enabled and normal throughput was restored.

Running the ACL fix without modifying a profile

The script can also be run without -ConfigPath:

& $ScriptPath

In that mode, it changes the DCO device ACL but does not modify any .ovpn file.

The profile must then be edited manually:

  1. Remove or comment out:

    disable-dco
    
  2. Remove any existing dev-node directive.

  3. Add:

    dev-node "OpenVPN DCO User"
    

Using -ConfigPath is safer when the exact profile has already been identified and verified because the script creates a timestamped backup before modifying it.

ACL rollback

To restore the most recent DCO device ACL backup, close OpenVPN GUI, open PowerShell as Administrator, and run:

$ScriptPath = "$env:USERPROFILE\Downloads\Fix-OpenVpnDcoAcl-English-Generic.ps1"

Set-ExecutionPolicy -Scope Process Bypass -Force
Unblock-File -LiteralPath $ScriptPath

& $ScriptPath -Rollback

The script restores the original per-device SDDL values from the newest backup under:

C:\ProgramData\OpenVPN\DcoAclFix

It then restarts the affected DCO devices.

To see the available ACL backups:

Get-ChildItem `
    -LiteralPath "C:\ProgramData\OpenVPN\DcoAclFix" `
    -Filter "DcoAclBackup-*.json" |
    Sort-Object LastWriteTime -Descending |
    Select-Object FullName, LastWriteTime

To restore a specific ACL backup:

$AclBackup = "C:\ProgramData\OpenVPN\DcoAclFix\DcoAclBackup-YYYYMMDD-HHMMSS.json"

& $ScriptPath `
    -Rollback `
    -BackupFile $AclBackup

Restoring the original OpenVPN profile

The ACL rollback does not automatically restore a modified .ovpn profile.

List the profile backups created by the script:

$ProfileDirectory = Split-Path -Parent $ProfilePath
$ProfileFileName  = Split-Path -Leaf $ProfilePath

Get-ChildItem `
    -LiteralPath $ProfileDirectory `
    -Filter "$ProfileFileName.bak-*" |
    Sort-Object LastWriteTime -Descending |
    Select-Object FullName, LastWriteTime

Select the backup that should be restored:

$ProfileBackup = "C:\Path\To\Client.ovpn.bak-YYYYMMDD-HHMMSS"

Restore it:

Copy-Item `
    -LiteralPath $ProfileBackup `
    -Destination $ProfilePath `
    -Force

The separate manual backup created before running the script can also be restored in the same way.

After restoring the original profile, restart OpenVPN GUI normally.

Conclusion

This appears to show that, on affected systems, reinstalling OpenVPN or ovpn-dco-win does not address the actual failure.

The driver is installed, loaded, and discoverable, but the effective per-device security descriptor prevents the intended non-elevated OpenVPN process from opening the DCO device interface.

This script is a workaround, not an upstream fix.

The driver installer or device creation path should ensure that newly created ovpn-dco device instances receive a security descriptor compatible with OpenVPN GUI's intended non-elevated execution model. Access could potentially be granted to the appropriate OpenVPN local group or through another suitably restricted device ACL.

Fix-OpenVpnDcoAcl-English-Generic.zip

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions