ovpn-dco 2.8.4: non-elevated OpenVPN GUI fails with CreateFile access denied (errno=5) on Windows 11 [Provided Workaround script]
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 https://github.com/OpenVPN/openvpn/issues/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, amd64This 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 adapterThe same OpenVPN profile worked correctly when OpenVPN GUI was started with Run as administrator.
Adding:
disable-dcoalso 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
OpenVPNServiceInteractivewas running; - verifying that the user was a member of the
OpenVPN Administratorsgroup.
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:
Stops running
openvpn.exeandopenvpn-gui.exeprocesses.Checks whether a persistent DCO adapter exists by running:
tapctl.exe list --hwid ovpn-dcoIf the requested adapter does not exist, creates one with:
tapctl.exe create --hwid ovpn-dco --name "OpenVPN DCO User"Uses Windows SetupAPI to enumerate currently present devices in the Network device class:
{4d36e972-e325-11ce-bfc1-08002be10318}Selects only devices whose hardware ID is exactly:
ovpn-dcoReads the per-device security descriptor through:
SPDRP_SECURITY_SDSusing
SetupDiGetDeviceRegistryPropertyW().Saves the original device instance IDs and SDDL security descriptors to a JSON backup under:
C:\ProgramData\OpenVPN\DcoAclFixRetrieves the SID of the current Windows user.
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.
Writes the updated security descriptor through:
SetupDiSetDeviceRegistryPropertyW(..., SPDRP_SECURITY_SDS, ...)Restarts each affected DCO device with:
pnputil.exe /restart-device <INSTANCE_ID>Optionally updates a specified
.ovpnprofile by:removing
disable-dco;removing existing
dev-nodedirectives;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, LastWriteTimeFind 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 $ProfilePathThe result must be:
TrueReview 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.ps1For 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 $ScriptPathThe result must be:
TrueAllow scripts only for the current PowerShell process and unblock the downloaded file:
Set-ExecutionPolicy -Scope Process Bypass -Force
Unblock-File -LiteralPath $ScriptPathRun the script and provide the previously verified OpenVPN profile:
& $ScriptPath -ConfigPath $ProfilePathAdministrator 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
.ovpnfile;removes
disable-dco;removes existing
dev-nodedirectives;adds:
dev-node "OpenVPN DCO User"
An automatically created profile backup will have a name similar to:
Client.ovpn.bak-20260806-153000After the script completes:
- Close the elevated PowerShell window.
- Start OpenVPN GUI normally.
- Do not use Run as administrator.
- 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:
& $ScriptPathIn that mode, it changes the DCO device ACL but does not modify any .ovpn file.
The profile must then be edited manually:
Remove or comment out:
disable-dcoRemove any existing
dev-nodedirective.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 -RollbackThe script restores the original per-device SDDL values from the newest backup under:
C:\ProgramData\OpenVPN\DcoAclFixIt 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, LastWriteTimeTo restore a specific ACL backup:
$AclBackup = "C:\ProgramData\OpenVPN\DcoAclFix\DcoAclBackup-YYYYMMDD-HHMMSS.json"
& $ScriptPath `
-Rollback `
-BackupFile $AclBackupRestoring 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, LastWriteTimeSelect the backup that should be restored:
$ProfileBackup = "C:\Path\To\Client.ovpn.bak-YYYYMMDD-HHMMSS"Restore it:
Copy-Item `
-LiteralPath $ProfileBackup `
-Destination $ProfilePath `
-ForceThe 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.
Source: OpenVPN/openvpn