La licence est déjà dans le firmware
Beaucoup de gens possèdent une licence du Fisher-Price OS (Windows) sans en avoir jamais vu la clé de produit. Elle est venue avec la machine, et la clé vit dans le firmware de cette machine, dans une petite table ACPI appelée MSDM.
Puis la machine cesse d’être l’endroit où se fait le travail. Soit son propriétaire veut cette licence dans une VM qu’il vous loue désormais, soit la machine elle-même est effacée, transformée en hôte Proxmox, et la copie de l’OS livrée avec elle est voulue de retour sous forme de VM par-dessus.
Mécaniquement, c’est une seule option QEMU. C’est bien le problème. QEMU transmet n’importe quelle table à un invité sans la vérifier, l’invité s’active avec la clé qu’il trouve, et rien, nulle part dans la pile, ne demande si la licence permet quoi que ce soit de tout cela. Ce billet couvre ce qu’est la table, comment la faire entrer dans une VM sans en transmettre une corrompue, ce que disent les conditions de Microsoft sur son déplacement, et pourquoi une licence en volume ne s’en approche jamais.
Ce qu’est la table
Depuis OEM Activation 3.0, un fabricant de PC n’imprime plus la clé sur une étiquette sous le boîtier, où n’importe qui avec un téléphone peut la photographier, et la clé va dans le firmware de la machine à la place. L’outillage d’usine de Microsoft « injects the product keys into the firmware », et son étape de validation vérifie « that the MSDM table exists » et que son en-tête et ses entrées « comply with the correct formats »1. Une machine préparée ainsi est « activated by using the OA3 DPK in the firmware »2. MSDM est une table ACPI ordinaire, et sous Linux elle se lit directement depuis le firmware :
sudo cat /sys/firmware/acpi/tables/MSDM > msdm.bin
Le portable sur lequel ce billet a été écrit en porte une. 85 octets.
La spécification publiée par Microsoft définit l’en-tête ACPI standard de 36 octets avec la signature MSDM, puis s’arrête.
Tout ce qui suit l’en-tête est une « Proprietary data structure that contains all the licensing data necessary to enable Windows activation »3.
| Octets | Contient | Connu par |
|---|---|---|
| 0 à 35 | l’en-tête ACPI standard : signature MSDM, longueur, somme de contrôle, OEM ID | la spécification de Microsoft |
| 36 à 55 | 20 octets de champs : version, type de données, longueur des données | de vraies tables, pas une spécification publiée |
| 56 à 84 | la clé de produit de 29 caractères, cinq groupes de cinq | de vraies tables, pas une spécification publiée |
QEMU transmettra tout ce que vous lui donnez
Le -acpitable file= de QEMU prend la « whole ACPI table from the specified files, including all ACPI headers (possible overridden by other options) »4, et l’invité voit alors une table MSDM exactement comme le firmware d’un portable la présenterait.
Cela a été vérifié avec une clé délibérément fausse.
Les octets sont ressortis de l’autre côté identiques, de la signature au dernier caractère.
QEMU ne refuse rien.
Voici ce que fait hw/acpi/core.c d’un fichier cassé5 :
| Ce qui ne va pas dans le fichier | Ce que fait QEMU | Ce que voit l’invité |
|---|---|---|
| La longueur de l’en-tête ne correspond pas au fichier | avertit, puis écrase la longueur avec la taille réelle | un en-tête valide |
| La somme de contrôle ne donne pas zéro | la recalcule, sur chaque table, à chaque fois | une somme de contrôle valide |
| Clé tronquée ou abîmée | rien, les données après l’en-tête ne le regardent pas | une table d’apparence valide autour d’une clé cassée |
L’invité n’a aucun moyen de faire la différence, et vous non plus tant que quelqu’un n’a pas ouvert un ticket de support. La première fois que quiconque en entend parler, c’est un client dont l’activation a échoué.
Si des clients les téléversent, vérifiez-les donc avant que QEMU les voie :
#!/usr/bin/env python3
"""Refuse anything that is not a well-formed MSDM table before QEMU sees it."""
import re, struct, sys
t = open(sys.argv[1], 'rb').read()
fail = lambda why: sys.exit(f"{sys.argv[1]}: {why}")
if len(t) < 56: fail(f"{len(t)} bytes, too short for an MSDM table")
sig, length = t[:4], struct.unpack_from('<I', t, 4)[0]
if sig != b'MSDM': fail(f"signature is {sig!r}, not MSDM")
if length != len(t): fail(f"header says {length} bytes, file is {len(t)}")
if sum(t) & 0xff: fail("checksum does not sum to zero")
ver, _, dtype, _, dlen = struct.unpack_from('<5I', t, 36)
if (ver, dtype) != (1, 1): fail(f"version {ver}, data type {dtype}: expected 1 and 1")
if dlen != 29 or 56 + dlen != length: fail(f"data length {dlen}: expected 29")
key = t[56:].decode('ascii', 'replace')
if not re.fullmatch(r'([0-9A-Z]{5}-){4}[0-9A-Z]{5}', key): fail("data is not a 5x5 product key")
print(f"OK OEM {t[10:16].decode(errors='replace').strip()!r} key *****-*****-*****-*****-{key[-5:]}")
| Vérifie | Tient le fichier à | Un échec veut dire |
|---|---|---|
| Signature, longueur, somme de contrôle | l’en-tête documenté par Microsoft | le fichier est cassé |
| Version, type de données, longueur des données, forme de la clé | la forme que portent les vraies tables | regardez celui-ci à la main, pas « c’est un faux » |
Il n’affiche jamais la clé, seulement le dernier groupe. Une clé de produit dans un fichier journal est une clé de produit que quelqu’un d’autre peut utiliser.
Lancé contre une bonne table et trois copies cassées de celle-ci :
OK OEM 'EXMPLE' key *****-*****-*****-*****-EEEEE
bad-sum.bin: checksum does not sum to zero
bad-trunc.bin: header says 85 bytes, file is 70
bad-sig.bin: signature is b'SLIC', not MSDM
Où elle va, et qui peut l’y mettre
Une clé de produit est un secret.
Elle n’a rien à faire là où www-data peut la lire, et pmxcfs vous donne exactement un endroit dans /etc/pve où il ne le peut pas6 :
| Où la table pourrait vivre | Qui peut la lire | Sur chaque nœud où la VM peut migrer |
|---|---|---|
/etc/pve/priv | root seulement | oui |
n’importe où ailleurs dans /etc/pve | lisible par le groupe, www-data de l’interface web compris | oui |
| un répertoire sur un seul nœud | ce que vous avez défini | non |
À 85 octets, elle est très loin de la limite de 1 MiB par fichier de pmxcfs7 :
mkdir -p /etc/pve/priv/msdm
check-msdm.py upload.bin && cp upload.bin /etc/pve/priv/msdm/9000.bin
qm set 9000 --args "-acpitable file=/etc/pve/priv/msdm/9000.bin"
qm set --args remplace la ligne entière.
Si la VM porte déjà des options SMBIOS ou de firmware dans args, écrivez-les toutes ensemble.
Et comme args est réservé à root, c’est un travail pour votre provisionnement, pas quelque chose qu’un client peut faire depuis l’interface web.
C’est le bon sens.
Le client fournit le fichier, vos outils le vérifient et l’attachent.
La table déplace une clé, pas un droit
C’est ici que la fonctionnalité demande la tête froide. Déplacer la table MSDM dans une VM déplace la clé. Pas la licence.
Les conditions de Microsoft elles-mêmes le disent, et les lignes qui comptent tiennent dans une table :
| Cas | Ce que dit Microsoft | Source |
|---|---|---|
| Licence OEM, la déplacer | transférable à un autre utilisateur « only with the licensed device » | conditions de licence OEM8 |
| Licence OEM, combien d’installations | « only one instance of the software for use on one device, whether that device is physical or virtual » | conditions de licence OEM8 |
| Licence OEM, en clair | « locked » au PC d’origine « and cannot be transferred to any other PC » | blog Microsoft pour les petites entreprises9 |
| L’exception | les dispositions de transfert « do not apply » lorsque le logiciel a été acquis en Allemagne ou dans une liste d’autres pays | conditions de licence OEM8 |
| L’héberger pour des clients | le Services Provider License Agreement est destiné aux « hosted applications to end customers » | SPLA10 |
| Éditions de bureau en VM hébergées | « VMs must be hosted by a Qualified Multitenant Hoster (QMTH) » | Microsoft Learn11 |
La table vient toujours de la machine sous licence du client lui-même, jamais de vos hôtes. Le cas qui tombe carrément dans le texte est le plus simple : la machine avec laquelle la licence a été vendue, qui fait maintenant tourner Proxmox, avec cette même copie du Fisher-Price OS (Windows) déplacée dans une seule VM dessus. C’est toujours une instance sur un appareil, et les conditions OEM l’autorisent « whether that device is physical or virtual »8. Une deuxième VM, ou une autre machine, et ce n’est plus le cas.
Dans ce cas précis, la machine du client et l’hyperviseur sont la même boîte, il n’y a donc rien à téléverser et rien à copier.
Le noyau de Proxmox expose déjà la table MSDM du firmware sous /sys/firmware/acpi/tables/, lisible par root seulement, et QEMU sur Proxmox tourne en root, il peut donc prendre la table directement là :
qm set 100 --args "-acpitable file=/sys/firmware/acpi/tables/MSDM"
Pointer vers la table vivante plutôt que vers une copie a deux conséquences.
Le chemin est lu sur le nœud où la VM démarre, quel qu’il soit, ceci a donc sa place sur un hôte isolé et pas dans un cluster : migrez la VM et soit elle reçoit la table de ce nœud, une autre licence sur une autre machine, soit elle ne démarre pas du tout, parce que QEMU refuse un fichier manquant avec can't open file … No such file or directory.
Et c’est une ligne, ce qui en fait une ligne à coller sur une deuxième VM.
Cette deuxième VM est le cas que les conditions ne couvrent pas, alors une seule VM la reçoit et aucune autre.
Construisez donc la fonctionnalité. Le mécanisme est solide, et il y a des clients qui ont le droit de s’en servir ; celui qui a acheté sa licence en Allemagne pourrait bien en faire partie. Mais mettez le droit à sa place : dans vos conditions de service, le client garantit qu’il détient une licence qui couvre son exécution dans votre VM, et il le fait avant que le bouton de téléversement fasse quoi que ce soit. Le validateur prouve que la table est bien formée. Rien ne prouve qu’elle est à lui.
Les licences en volume ne s’approchent jamais de la table
Un client avec une licence en volume peut-il prendre le même chemin ? Non. La table n’a rien à porter.
MSDM appartient au canal OEM et à rien d’autre. Le guide de planification de Microsoft lui-même dit que l’activation OEM « is available only for computers that are purchased through OEM channels and have the Windows operating system preinstalled »12. Les licences en volume s’activent par l’un de trois autres modèles, « Multiple Activation Keys (MAK) », « KMS » et « Active Directory-based activation »12, et dans chacun d’eux la clé est installée dans le système d’exploitation. Rien ne la lit depuis le firmware.
| Méthode | Où vit la clé | À quoi parle l’invité | Dans une VM Proxmox |
|---|---|---|---|
| OEM, OA 3.0 | la table MSDM dans le firmware | les serveurs d’activation de Microsoft | la voie -acpitable ci-dessus |
| MAK | installée dans l’invité | Microsoft, une fois, décomptée des activations de la clé | marche avec un accès internet ou par téléphone |
| KMS | une clé générique (GVLK) dans l’invité | l’hôte KMS du client sur TCP 1688 | il faut une route vers lui, et 25 clients ou 5 serveurs avant qu’il n’active quoi que ce soit |
| Active Directory | une GVLK dans l’invité | les contrôleurs de domaine du client, au moins tous les 180 jours | il faut que la VM soit jointe à leur domaine |
| AVMA | une clé générique dans l’invité | la licence Datacenter propre de l’hôte Hyper-V | pas disponible du tout |
Pour un client en volume, il n’y a donc rien à construire sur l’hyperviseur. Sa clé va dans son image, et votre part est le chemin réseau : le port 1688 vers son hôte KMS, ou un VPN vers ses contrôleurs de domaine. L’emplacement MSDM reste vide, et c’est normal.
Deux choses piègent les gens. La clé générique des médias en volume n’active rien toute seule, parce que « the GVLK doesn’t work unless a valid KMS host key can be found »12, si bien qu’une VM construite depuis l’ISO Enterprise du client sans route vers chez lui reste non activée. Et une licence en volume pour poste de travail n’est pas une licence à elle seule. Les programmes en volume « cover upgrades to Windows client operating systems only », et « an existing retail or OEM operating system license is needed for each computer »12. La licence en volume se pose sur une licence de base, très souvent la même licence OEM dont parlait la section précédente, et savoir si elle peut tourner dans votre VM reste la question d’hébergement de cette table, pas une question d’activation.
AVMA mérite une ligne à part, parce que c’est celle qui ressemble à la réponse. C’est le moyen que Microsoft donne à un hôte pour activer ses propres invités serveur, en liant « the VM activation to the licensed virtualization host », et il exige « a Windows Server Datacenter edition with the Hyper-V server host role installed »13. Microsoft est clair sur le reste : « AVMA doesn’t work with other server virtualization technologies »13. Sur Proxmox, les invités serveur s’activent par vos clés SPLA ou par le KMS du client.
Rien dans la pile ne vérifie les papiers
Chaque couche ici fait son travail et rien de plus. Le firmware stocke une clé. QEMU copie les octets et range l’en-tête. L’invité lit la clé et s’active avec. Aucune ne peut dire si la machine en dessous est celle avec laquelle la licence a été vendue, et aucune n’a jamais été faite pour.
La vérification retombe donc sur celui qui fait tourner l’hyperviseur. Une licence est un accord entre la personne qui l’a achetée et Microsoft, et votre plateforme n’en est pas partie, mais c’est elle qui déplace la clé, et cela fait de la question la vôtre, que vous l’ayez demandé ou non.
Écrivez la réponse avant que le bouton de téléversement fasse quoi que ce soit. Une machine, une VM, la propre licence du client et sa parole qu’elle couvre ceci. Ça ne coûte rien, et ça vous protège autant que lui. Le logiciel déplacera n’importe quelle clé qu’on lui tend. Savoir s’il aurait dû est une question à laquelle seule une personne peut répondre, alors assurez-vous qu’une personne y ait répondu.
Microsoft Learn, OA 3.0 on the factory floor — « injects the product keys into the firmware » ;
/Validatevérifie « that the MSDM table exists ». ↩︎Microsoft Learn, Validate an OEM Activation key — « is activated by using the OA3 DPK in the firmware ». ↩︎
Microsoft, Microsoft Software Licensing Tables (SLIC and MSDM) — table 2, décalage 36 : « Proprietary data structure that contains all the licensing data necessary to enable Windows activation. » ↩︎
QEMU, System Emulation, Invocation — les champs de
-smbiospar type ;-acpitable« For file=, take whole ACPI table from the specified files, including all ACPI headers » ;-boot« Currently Seabios for X86 system support it. » ↩︎QEMU v11.0.3,
hw/acpi/core.c— « ACPI table has wrong length » est unwarn_report, puis la longueur est écrasée et la somme de contrôle recalculée. ↩︎pve-cluster,
src/pmxcfs/pmxcfs.c— les chemins sousprivsont masqués en0777700, tout le reste en0777750. ↩︎pve-cluster,
src/pmxcfs/memdb.h—#define MEMDB_MAX_FILE_SIZE (1024 * 1024) // 1 MiB. ↩︎Microsoft, Windows 11 OEM licence terms — « you may transfer the license to use the software directly to another user, only with the licensed device » ; les dispositions de transfert « do not apply if you acquired the software in Germany » ou dans les pays listés. Copie archivée ; la page en ligne refuse les requêtes automatisées. ↩︎ ↩︎ ↩︎ ↩︎
Microsoft, small business blog archive — « the OEM Windows license is ’locked’ to the original PC it comes with and cannot be transferred to any other PC. » ↩︎
Microsoft, Services Provider License Agreement — « for service providers and software development companies licensing eligible Microsoft products to provide software services and hosted applications to end customers. » ↩︎
Microsoft Learn, Windows subscription activation for VDA — « VMs must be hosted by a Qualified Multitenant Hoster (QMTH). » ↩︎
Microsoft Learn, Plan for volume activation — les trois modèles d’activation en volume ; les seuils KMS de « at least five computers » pour les serveurs et « at least 25 computers » pour les clients, sur le port TCP 1688 ; l’activation par Active Directory qui a besoin du domaine « at least once every 180 days » ; « the GVLK doesn’t work unless a valid KMS host key can be found » ; les licences en volume « cover upgrades to Windows client operating systems only ». ↩︎ ↩︎ ↩︎ ↩︎
Microsoft Learn, Automatic Virtual Machine Activation in Windows Server — « AVMA requires a Windows Server Datacenter edition with the Hyper-V server host role installed » ; « AVMA doesn’t work with other server virtualization technologies. » ↩︎ ↩︎