How I Got Xfinity Stream to Work on Linux (a Tale of Widevine, Chrome OS, and a Patched glibc)

6 minute read

Working Xfinity Stream

(If you want to skip to the tutorial, click here.)

If your household uses Xfinity for cable, you have almost definitely heard of Xfinity Stream at some point. If you haven’t, Xfinity Stream is a service that allows you to access Xfinity’s on-demand TV over the internet. This can be useful if you want to watch something on Xfinity’s on-demand TV but do not want to go to your actual TV.

This is nice because some media still goes to on-demand before its respective streaming service (if it even has one). And of course, if you are already paying for Xfinity, it is nice not to have to pay for another streaming service if you can avoid it.

However, it has some limitations as well, mainly that you can only access it on your local Xfinity network… and its completely arbitrary Linux block. That’s right, if you have ever tried to load Xfinity Stream on Linux, you will have seen this lovely error message:

Broken Xfinity Stream

And if you look up that error message, you will find a common theme: Xfinity Stream does not support Linux.

Also of note is that Xfinity Stream will refuse to load at all on Google Chrome without changing your user-agent, but it will load on Firefox, just for it to fail when you try to watch anything.

How does it block Linux?

One of the first methods I tried to use to bypass this block was changing my user-agent… just for the block to persist anyways. Which raised the question “how does Xfinity know I am running Linux?” Because if I can trick it into thinking I am running Windows or MacOS, then it should not be able to block me anymore.

So I dug into Firefox’s network logs, which led me to the source of the error:

Widevine License Request

This POST request was generated using the MediaKeySession.generateRequest() API, which isn turn used the browser’s built-in Widevine CDM to generate a DRM license request. At this point, I had formed a hypothesis: the Linux build of the Widevine CDM was embedding metadata into this request which Xfinity was using to block Linux.

How do I bypass that?

Unfortunately, just patching the Widevine CDM’s binary is beyond my skill set, especially since it is ridiculously obfuscated.

However, I eventually came to a realization: Xfinity Stream probably does not block Chrome OS, and Chrome OS is basically GNU/Linux internally. So I formed a plan: extract Chrome OS’s Widevine CDM from a recovery image, load it into Firefox, and try to use Xfinity Stream.

It did not work, at all.

The Widevine CDM immediately crashed, and when I tried to load it into a standalone program using dlopen(), it caused a segmentation fault before it even finished loading. This incredibly vague error stumped me for a while until I found this incredibly helpful GitHub issue from a project that was trying to do something similar.

This GitHub issue revealed the problem: Chrome OS was not exactly GNU/Linux. Namely, it was complied with a feature that was not included in my version of glibc that Google had patched into their version: the DT_RELR relocation format.

With some more research, I learned that glibc had actually merged support for this feature in version 2.36! So I loaded Ubuntu 22.10 on a USB flash drive and tried loading Chrome OS’s Widevine CDM into my testing program again.

And it still did not work.

This time the error was slightly more helpful though: DT_RELR without GLIBC_ABI_DT_RELR dependency.

What in the world is a GLIBC_ABI_DT_RELR dependency?

It turns out, the glibc maintainers actually agreed with my earlier assessment: immediately causing a segmentation fault on older versions of glibc just because a program used the DT_RELR relocation format was not good behavior.

They decided that for every binary compiled with the DT_RELR relocation format, the linker would include a version dependency called GLIBC_ABI_DT_RELR on libc.so.6. This would cause any program using the DT_RELR relocation format to give a more sane error on older versions of glibc: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_ABI_DT_RELR' not found (required by ./program).

However, the glibc maintainers also decided to implement this functionality in reverse as well: glibc refuses to load any binary containing the DT_RELR relocation format that does not also contain the GLIBC_ABI_DT_RELR version dependency. I do not know why they did this, but this is what caused the previous error. This is because Chrome OS’s toolchain evidently predates GLIBC_ABI_DT_RELR so it was never included when linking the Widevine CDM.

Originally, I tried to patch the Widevine CDM binary to include the GLIBC_ABI_DT_RELR version dependency using LIEF. However, the resulting binary was invalid and caused a segmentation fault when started. Ultimately, I decided to instead remove the check itself from 'glibc manually.

The GLIBC_ABI_DT_RELR check was quite trivial to patch out of glibc (although waiting for glibc to compile was certainly tedious). And once done, I was able to load up Firefox with Chrome OS’s Widevine CDM… and Xfinity Stream finally worked on Linux!

How do I actually do this?

  1. Install a Linux distribution that contains glibc v2.36 or higher. I would also recommend taking a backup or using a more disposable installation medium such as a USB flash drive or a VM.
    • You could use an older version of glibc if you really want to, but you would have to patch in support for the DT_RELR relocation format yourself.
  2. Use this script to patch glibc (this script is Debian-specific, but it should not be too hard to adapt it to other distributions):
    #!/bin/sh
    
    set -e
    
    # Create Working Directory
    rm -rf glibc-work
    mkdir glibc-work
    cd glibc-work
    
    # Download Sources
    apt-get source glibc
    cd glibc-*
    
    # Patch
    patch -p1 << EOF
    --- glibc-master-old/elf/dl-version.c
    +++ glibc-master/elf/dl-version.c
    @@ -362,7 +362,7 @@
       /* When there is a DT_VERNEED entry with libc.so on DT_NEEDED, issue
          an error if there is a DT_RELR entry without GLIBC_ABI_DT_RELR
          dependency.  */
    -  if (dyn != NULL
    +  if (0 && dyn != NULL
           && map->l_info[DT_NEEDED] != NULL
           && map->l_info[DT_RELR] != NULL
           && __glibc_unlikely (!map->l_dt_relr_ref))
    EOF
    
    # Commit Patch
    EDITOR='/bin/true' dpkg-source -q --commit . disable-GLIBC_ABI_DT_RELR-check.patch
    
    # Update Package Version
    EMAIL='example@example.com' dch -n 'Disable GLIBC_ABI_DT_RELR Check'
    
    # Disable Testing
    export DEB_BUILD_OPTIONS=nocheck
    
    # Build
    dpkg-buildpackage -us -uc
    
    # Install
    sudo apt-get install -y ../*.deb
    
  3. Download an x86_64 Chrome OS recovery image. I used the stable version of atlas, which was Chrome OS v107 at the time of writing.
  4. Extract /opt/google/chrome/WidevineCdm/_platform_specific/cros_x64/libwidevinecdm.so from the image.
  5. Browser-specific steps:
    • Google Chrome
      1. Copy libwidevinecdm.so to /opt/google/chrome/WidevineCdm/_platform_specific/linux_x64/libwidevinecdm.so.
      2. You will need to use the Chrome Developer Tools user-agent switcher when using Xfinity Stream. Other user-agent switcher extensions do not appear to work.
    • Firefox
      1. Install a non-Snap/Flatpak version of Firefox. This is needed because we need Firefox (and therefore the Widevine CDM) to use the host’s glibc.
      2. If you are using the Firefox build from ppa:mozillateam/ppa, you will also need to modify Firefox’s AppArmor configuration so that the Widevine CDM does not crash.
  6. Use Xfinity Stream!
  7. You will need to re-patch glibc every time your distribution releases an updated version.

Conclusion

All in all, this whole situation is ridiculous. It should not be this much harder to access media legally than it is to pirate. It is insane how much effort we as a species waste on this DRM that does not stop anybody. If I wanted to pirate anything, I could probably pull up a high-resolution copy in under an hour with minimal effort. But instead, because I tried to access this media the legal way, I have to either go through this whole complicated process or set up an entirely different operating system solely for that purpose. That is so unbelievably silly. The only reason I can remotely justify the time spent on this is because it was more enjoyable than watching whatever it was I was originally planning to watch!

In all honesty, if you want to use Xfinity Stream, I would recommend just setting up a Windows VM. It will probably be much more resistant to whatever nonsense Xfinity or Google try to pull in the future. But if you would rather not watch it at all than set up a Windows VM like me, well here is your method.

Updated: