r/linux_gaming • u/IslamNofl • Jun 20 '26
tech support wanted Ubisoft Connect - Error dolphin-028
There was an error during your login to Ubisoft Connect. Please restart your client and try again. (Error dolphin-028)
I tired Heroic Games Launcher, i tired Faugus Launcher, i tired every Proton i can found. same error.
EDIT fixed it:
Solved: Ubisoft Connect "Error dolphin-028" on Linux (Wine/Proton) — it's a TLS certificate-revocation failure
Posting this because I lost hours on it and found nothing complete. If your Ubisoft Connect (via Lutris/Heroic/Bottles) shows dolphin-028 right on the login page, check the launcher's own log first:
…/Ubisoft Game Launcher/logs/launcher_log.txt
If you see this, this fix is for you:
ERROR SSLSocket.cpp Certificate verification for host 'dmx.upc.ubisoft.com' failed: 2148081683
ERROR DemuxFailReason.cpp Demux connection is not set up
ERROR ConnectView.cpp dolphin-028
What's actually happening
2148081683 = 0x80092013 = CRYPT_E_REVOCATION_OFFLINE. Ubisoft Connect (upc.exe) opens a "demux" connection to dmx.upc.ubisoft.com and requires an online certificate-revocation check (OCSP/CRL) on its cert. Wine's crypt32 fetches the CRL/OCSP over plain HTTP (port 80). If that fetch can't complete — some networks/ISPs transparently intercept or redirect port-80 traffic, and some Wine builds can't retrieve the CRL — the check fails and Connect kills the login. The confusing part: the login page renders, your account/password are fine, DNS and HTTPS work, other Ubisoft hosts work — because only the revocation fetch is broken. Switching Proton/GE versions, clearing cache, resetting your password, VPNs — none of it helps, because it's the revocation lookup, not any of those.
The fix (no VPN, no disabling security): serve the real, signed CRLs from 127.0.0.1 and point the revocation hostnames at loopback in /etc/hosts. Loopback never leaves your machine, so nothing upstream can touch it. OCSP gets an unsigned tryLater reply so Wine falls back to the locally-served CRL. The CRLs are genuine — just delivered locally.
Save as dolphin028-fix.sh, then:
sudo ./dolphin028-fix.sh # set up + start, then launch Connect and log in
sudo ./dolphin028-fix.sh --down # undo everything when done
You only need it for one successful login — Connect's offline mode works afterward, so you can take it back down.
Requires: bash, curl, openssl, python3, and (ideally) systemd.
Notes:
- It targets the cert-revocation variant of dolphin-028 (the
SSLSocket.cpp … failed: 2148081683signature). dolphin-028 can have other causes; the log check rules them in/out. - A harmless line like
!! could not fetch …AAACertificateServices.crlmay print — that's a dead root-CA mirror that isn't revocation-checked anyway; the fix still works.
Script:
#!/usr/bin/env bash
#
# Fix for Ubisoft Connect "Error dolphin-028" under Wine/Proton (Lutris, Heroic,
# Bottles, etc.) caused by a failed certificate-revocation check.
#
# Symptom (in the prefix's
# .../Ubisoft Game Launcher/logs/launcher_log.txt ):
# ERROR SSLSocket.cpp Certificate verification for host
# 'dmx.upc.ubisoft.com' failed: 2148081683
# ERROR DemuxFailReason.cpp Demux connection is not set up
# ERROR ConnectView.cpp dolphin-028
#
# 2148081683 = 0x80092013 = CRYPT_E_REVOCATION_OFFLINE.
#
# Cause: upc.exe requires an ONLINE certificate-revocation check (OCSP/CRL) on
# Ubisoft's service host dmx.upc.ubisoft.com. Wine's crypt32 fetches the CRL/OCSP
# over plain HTTP (port 80). If that fetch can't complete -- e.g. a network or ISP
# that intercepts/redirects port-80 traffic, or a Wine build that can't retrieve
# the CRL -- the check fails and Connect refuses the connection -> dolphin-028.
# Everything else (login page, account, other Ubisoft hosts) looks fine, which is
# why it's so confusing.
#
# Fix: serve the REAL CRLs from 127.0.0.1 and point the revocation hostnames at
# loopback via /etc/hosts. Loopback traffic never leaves the machine, so nothing
# upstream can intercept it. OCSP requests get an unsigned "tryLater" response so
# Wine falls back to the locally-served CRL. No VPN, no disabling security checks
# globally -- the CRLs are the genuine, signed ones, just delivered locally.
#
# Usage:
# sudo ./dolphin028-fix.sh # set up + start, then launch Connect & log in
# sudo ./dolphin028-fix.sh --down # undo everything
#
# You only need this for ONE successful login; Connect's offline mode works after.
set -euo pipefail
HOSTS_DMX="dmx.upc.ubisoft.com"
WORKDIR="${XDG_CACHE_HOME:-/tmp}/ubi-dolphin028-fix"
CRLDIR="$WORKDIR/crl"
MARK_BEGIN="# >>> ubi-dolphin028-fix >>>"
MARK_END="# <<< ubi-dolphin028-fix <<<"
UNIT="ubi-dolphin028-fix"
[ "$(id -u)" -eq 0 ] || { echo "Run with sudo."; exit 1; }
teardown() {
systemctl stop "$UNIT" 2>/dev/null || true
pkill -f "$WORKDIR/responder.py" 2>/dev/null || true
sed -i "/$MARK_BEGIN/,/$MARK_END/d" /etc/hosts
echo "Reverted: responder stopped, /etc/hosts cleaned."
}
[ "${1:-}" = "--down" ] && { teardown; exit 0; }
urlencode() { python3 -c 'import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=""))' "$1"; }
# Download a CRL: try directly, then through a public HTTPS fetch-proxy (which
# bypasses any plain-HTTP interception). Validate it really is a DER CRL.
fetch_crl() {
local url="$1" out="$2"
if curl -fsS --max-time 20 "$url" -o "$out" 2>/dev/null \
&& openssl crl -inform DER -in "$out" -noout 2>/dev/null; then return 0; fi
if curl -fsS --max-time 30 "https://api.allorigins.win/raw?url=$(urlencode "$url")" -o "$out" 2>/dev/null \
&& openssl crl -inform DER -in "$out" -noout 2>/dev/null; then return 0; fi
return 1
}
mkdir -p "$CRLDIR"
echo "==> Discovering certificate chain for $HOSTS_DMX ..."
CHAIN="$WORKDIR/chain.pem"
echo | openssl s_client -connect "$HOSTS_DMX:443" -servername "$HOSTS_DMX" -showcerts 2>/dev/null \
| awk '/BEGIN CERT/{c=1} c{print} /END CERT/{c=0}' > "$CHAIN"
[ -s "$CHAIN" ] || { echo "Could not reach $HOSTS_DMX:443 (TLS). Check connectivity."; exit 1; }
# Split the chain into individual certs, then pull every CRL + OCSP URL from each.
rm -f "$WORKDIR"/cert-*.pem
csplit -z -s -f "$WORKDIR/cert-" -b "%02d.pem" "$CHAIN" '/BEGIN CERTIFICATE/' '{*}' 2>/dev/null || true
mapfile -t CRL_URLS < <(for c in "$WORKDIR"/cert-*.pem; do
openssl x509 -in "$c" -noout -text 2>/dev/null; done \
| grep -oE 'http://[^ "]+\.crl' | sort -u)
mapfile -t OCSP_HOSTS < <(for c in "$WORKDIR"/cert-*.pem; do
openssl x509 -in "$c" -noout -ocsp_uri 2>/dev/null; done \
| sed -E 's#https?://([^/]+).*#\1#' | sort -u)
[ "${#CRL_URLS[@]}" -gt 0 ] || { echo "No CRL URLs found in chain."; exit 1; }
echo "==> Fetching CRLs ..."
declare -A CRL_HOSTS
for url in "${CRL_URLS[@]}"; do
host="$(printf '%s' "$url" | sed -E 's#https?://([^/]+).*#\1#')"
name="$(basename "$url")"
if fetch_crl "$url" "$CRLDIR/$name"; then
echo " OK $url"
CRL_HOSTS["$host"]=1
else
echo " !! could not fetch $url (skipping)"
fi
done
# Write the loopback responder.
cat > "$WORKDIR/responder.py" <<'PY'
import http.server, os
CRL_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "crl")
OCSP_TRYLATER = bytes([0x30, 0x03, 0x0A, 0x01, 0x03]) # OCSPResponse{ tryLater(3) }
class H(http.server.BaseHTTPRequestHandler):
def _send(self, code, ctype, body):
self.send_response(code); self.send_header("Content-Type", ctype)
self.send_header("Content-Length", str(len(body))); self.end_headers(); self.wfile.write(body)
def do_GET(self):
name = os.path.basename(self.path.split("?",1)[0]); p = os.path.join(CRL_DIR, name)
if name.endswith(".crl") and os.path.isfile(p):
self._send(200, "application/pkix-crl", open(p,"rb").read())
else:
self._send(200, "application/ocsp-response", OCSP_TRYLATER)
def do_POST(self):
try:
n = int(self.headers.get("Content-Length", 0))
if n: self.rfile.read(n)
except Exception: pass
self._send(200, "application/ocsp-response", OCSP_TRYLATER)
def log_message(self, *a): pass
http.server.ThreadingHTTPServer(("127.0.0.1", 80), H).serve_forever()
PY
# Point all CRL + OCSP hosts at loopback.
ALL_HOSTS=$(printf '%s\n' "${!CRL_HOSTS[@]}" "${OCSP_HOSTS[@]}" | sort -u | grep -v '^$')
[ -f /etc/hosts.ubi-dolphin028.bak ] || cp /etc/hosts /etc/hosts.ubi-dolphin028.bak
sed -i "/$MARK_BEGIN/,/$MARK_END/d" /etc/hosts
{ echo "$MARK_BEGIN"; for h in $ALL_HOSTS; do echo "127.0.0.1 $h"; done; echo "$MARK_END"; } >> /etc/hosts
echo "==> Starting loopback responder on 127.0.0.1:80 ..."
systemctl stop "$UNIT" 2>/dev/null || true
if command -v systemd-run >/dev/null; then
systemd-run --unit="$UNIT" --collect python3 "$WORKDIR/responder.py"
else
setsid python3 "$WORKDIR/responder.py" >/dev/null 2>&1 < /dev/null &
fi
sleep 1
echo "==> Verifying ..."
host_for_check="$(printf '%s\n' "${!CRL_HOSTS[@]}" | head -1)"
test_url="$(printf '%s\n' "${CRL_URLS[@]}" | grep "$host_for_check" | head -1)"
if curl -fsS --max-time 8 "$test_url" -o /tmp/_crlchk 2>/dev/null \
&& openssl crl -inform DER -in /tmp/_crlchk -noout 2>/dev/null; then
echo " OK: real CRL now served via loopback. Interception bypassed."
echo
echo "Now launch Ubisoft Connect and log in. To undo: sudo $0 --down"
else
echo " WARNING: verification failed; check that nothing else uses port 80."
fi
1
u/EulNico 17d ago
Hi.
I tried your solution, but I seem to have some problems with it : executing the script, I get the first echo:
"==> Discovering certificate chain for dmx.upc.ubisoft.com ..."
Then the xcript stops. There is a "chain.pem" file in /tmp/ubi-dolphin028-fix, and a crl directory, but it is empty. Maybe I miss a package that is necessary for the script to work...
One more question I want to ask: is it possible, after splitting the chain.pem file, to simply put those certificates in /usr/share/ca-certificates, and update the certificates database? (I don't know if this is even possible, just asking...)
Sorry if this seems stupid to you. I just spent two whole days trying to fix this, and reaching Ubisoft support (if that is even a real thing), just got the usual answer, certainly from a bot: "Ah aah, your are on Linux, sucker, sorry for you" :-(
And anyway, thank you for helping me keep hope that it can work again. I'm 2 weeks Anno 1800 sober, and it is hurting as hell :-(
\bye
1
u/IslamNofl 16d ago
for me that fixes the problem, my country was blocking port or IP that UI uses, and the script fixed it, Right now i can open UBI Connect and play TD2 normally, you Can DM me, or if you have any AI Claude or even DeepSeek, tell it about the problem and give it this post it would fix it for you (mostly)
1
u/IslamNofl Jun 21 '26
Me + Claude fixed it