Files
OpenVixDiskLib/docs/nfc_auth.md
T

13 KiB

VDDK NFC authentication

This document records how VMware VDDK authenticates for NBD/NFC disk access, and how OpenVixDiskLib (openvixdisklib/nfc_auth.py) reproduces that path. Findings come from VDDK 8.0.2 libraries (libvixDiskLib, libvddkVimAccess, libvim-types), live SOAP calls against vCenter 8.0.1, and a TLS intercept of VixDiskLib_ConnectEx / VixDiskLib_Open. The steps used to obtain those findings are in docs/reverse_engineering_procedure.md.

The goal of this stage is authentication only: a logged-in VIM session plus an authd TLS socket that has completed 200 Connect. Opening a VMDK and reading sectors is docs/nfc_open.md.

Mapping from VDDK

The VDDK wrapper in tests/integration/vixdisklib.py calls VixDiskLib_ConnectEx with UID credentials and VixDiskLib_Open on a datastore path. VDDK does not send the vCenter username and password to ESXi port 902. It:

  1. Logs into vCenter over HTTPS 443 (SOAP / urn:vim25).
  2. Asks vCenter for a one-time NFC ticket.
  3. Connects to the ESXi authd daemon on TCP 902, upgrades to TLS, and presents that ticket.
VDDK call What actually happens
VixDiskLib_InitEx Load plugins, SSL, logging
VixDiskLib_ConnectEx SOAP SessionManager.Login to vCenter
VixDiskLib_Open (read-only) NfcGetVmFiles ticket, then authd handshake, then NFC I/O
VixDiskLib_Open (read-write) NfcRandomAccessOpenDisk ticket (disk key + host)
transport_modes="nbdssl" (default) NBDSSL (vpxa-nfcssl://...@esxi:902, second TLS wrap)
transport_modes="nbd" NBD over NFC (vpxa-nfc://...@esxi:902)
vmxSpec=moref=vm-13098 VM managed object used as the ticket target
snapshot_ref Not consumed by the ticket call itself
VIXDISKLIB_CRED_UID Username/password for VIM only

Lab topology used for capture:

  • vCenter: <vcenter> (VirtualCenter 8.0.1)
  • VM: vm-13098 on host host-13001 (<esxi>)
  • NFC service moref on vCenter: nfcService
  • Authd: <esxi>:902

Stage 1: VIM login

This is a public pyVmomi operation. Reuse pyVim.connect.SmartConnect rather than crafting SOAP.

  • Endpoint: https://<vcenter>:443/sdk
  • Cookie: vmware_soap_session
  • SOAPAction: "urn:vim25/8.0.1.0" (negotiated)

VDDK logs this as Connected to VIM Server / Authenticating user / Logged in!. OpenVixDiskLib keeps that ServiceInstance and its stub for the ticket call.

Direct ESXi login is the same SOAP login against hostd, but the NFC moref and service name differ (ha-nfc instead of nfcService / vpxa-nfc). The lab path is vCenter-mediated.

Stage 2: NFC ticket

Why this is not public pyVmomi

vim.NfcService is omitted from the public vim25 WSDL that pyVmomi ships. vCenter still implements it:

  • Version document: GET /sdk/nfcServiceVersions.xml → namespace urn:nfc, version 7.0.3.2
  • Methods also accept urn:vim25 (that is what VDDK uses)
  • Well-known moref on this vCenter: nfcService

ServiceManager.QueryServiceList does not list NFC. The moref is hardcoded in VDDK as nfcService (vCenter) or ha-nfc (ESXi).

OpenVixDiskLib (openvixdisklib/nfc_auth.py) registers the missing type with pyVmomi.VmomiSupport.CreateManagedType and invokes it on the existing SmartConnect stub, so serialization, cookies, and HostServiceTicket stay in pyVmomi.

Methods VDDK actually calls

Intercepted SOAP for a read-only VixDiskLib_Open of a datastore path:

<NfcGetVmFiles xmlns="urn:vim25">
  <_this type="NfcService">nfcService</_this>
  <vm type="VirtualMachine">vm-13098</vm>
</NfcGetVmFiles>

No disk path, snapshot, or host is in this request. The path ([datastore0] ...-000007.vmdk) is used later on the NFC channel.

A GetVmFiles ticket is not writable. Opening the same path with NFC flags 0x1a returns AIO error 0x0b (VIX_E_FILE_READ_ONLY). Writable ConnectEx(readOnly=FALSE) uses a disk-scoped ticket instead.

libvim-types.so maps vmodl randomAccessOpen to WSDL NfcRandomAccessOpenDisk (same arguments as the read-only sibling):

<NfcRandomAccessOpenDisk xmlns="urn:vim25">
  <_this type="NfcService">nfcService</_this>
  <vm type="VirtualMachine">vm-13098</vm>
  <diskDeviceKey>2000</diskDeviceKey>
  <hostForAccess type="HostSystem">host-13001</hostForAccess>
</NfcRandomAccessOpenDisk>

A disk-scoped read ticket also works and returns the same HostServiceTicket type:

<NfcRandomAccessOpenReadonly xmlns="urn:nfc">
  <_this type="NfcService">nfcService</_this>
  <vm type="VirtualMachine">vm-13098</vm>
  <diskDeviceKey>2000</diskDeviceKey>
  <hostForAccess type="HostSystem">host-13001</hostForAccess>
</NfcRandomAccessOpenReadonly>

diskDeviceKey is VirtualDisk.key from vm.config.hardware.device (2000 for Hard disk 1). Read-only VixDiskLib_Open does not send it: the drop-in uses NfcGetVmFiles and puts the VMDK path (leaf or snapshot parent) only on NFC OPEN_FILE. Writable open resolves a key from the datastore path, matching the current leaf or any parent in backing.parent (for example …-000007.vmdk after the VM has moved on to …-000008.vmdk).

Return value: vim.HostServiceTicket

Public pyVmomi type. Example from this lab:

Field Example Role
host 10.11.12.13 ESXi management / NFC address
port 902 authd TCP port
sslThumbprint BE:22:58:...:76:29 SHA-1 of the ESXi TLS cert
service vpxa-nfc authd PROXY argument
serviceVersion 1.1 NFC hosted by hostd (ESX 3.0+ convention)
sessionId 52cdebc5-b7ee-359a-1dec-76f0bc105ac5 One-time authd SESSION token

Tickets are single-use. Calling GetVmFiles twice issues two tickets; only the one presented to authd is consumed.

Other NfcService methods seen in VDDK

WSDL names are prefixed with Nfc. The vmodl names (from libvim-types.so) include:

WSDL name Parameters (observed / from C++) Notes
NfcGetVmFiles vm VDDK read-only Open path
NfcRandomAccessOpenReadonly vm, diskDeviceKey, hostForAccess Disk-scoped read ticket
NfcRandomAccessOpenDisk vm, diskDeviceKey, hostForAccess Disk-scoped read-write ticket
NfcGetServerNfcLibVersion hostForAccess Lab returned 11
NfcFileManagement requires ds (datastore) File copy, not NBD
NfcSystemManagement host moref Not used for disk open

NfcGetServerNfcLibVersion without hostForAccess fails with A specified parameter was not correct: hostForAccess. Using moref ha-nfc on vCenter fails with ManagedObjectNotFound; nfcService is the correct vCenter object.

Stage 3: authd handshake (TCP 902)

authd is the VMware Authentication Daemon. Plaintext banner from ESXi 8:

220 VMware Authentication Daemon Version 1.10: SSL Required, ServerDaemonProtocol:SOAP, MKSDisplayProtocol:VNC , VMXARGS supported, NFCSSL supported/t, SHA256 supported

SSL is required. Sending commands before wrap_socket closes the connection. After TLS there is no USER / PASS when the client holds a vCenter NFC ticket.

Sequence captured from VDDK

VDDK log line immediately before the socket:

Using proxy/session authentication, sessionId=..., useSSL=0
Plain-text connection is deprecated; use SSL to connect to NFC server

useSSL=0 does not mean skip TLS on 902. It means skip a second NFCSSL wrap after authd TLS. The management channel is still TLS. useSSL=1 (nbdssl) is the same authd commands with PROXY vpxa-nfcssl and a second TLS handshake after 200 Connect.

Intercepted writes/reads after the TLS handshake:

C -> SESSION <sessionId>\r\n
C -> BANNER \r\n
S -> 220 VMware Authentication Daemon Version 1.10: ...\r\n
C -> THUMBPRINT_SHA2 PlainText\r\n
S -> 200 <SHA-256 thumbprint with colons>\r\n
C -> PROXY vpxa-nfc\r\n
S -> 200 Connect ha-nfc\r\n

NBDSSL uses the same SESSION / BANNER / THUMBPRINT_SHA2 PlainText sequence. The ticket still has service=vpxa-nfc; the client rewrites the PROXY argument:

C -> PROXY vpxa-nfcssl\r\n
S -> 200 Connect ha-nfcssl\r\n

After that reply, authd TLS is finished and ha-nfcssl expects a new TLS ClientHello on the same TCP connection (useSSL=1). NFC frames then travel as TLS application data of that second session. NBD (useSSL=0) skips the second wrap and sends NFC as raw TCP instead.

Notes:

  • SESSION does not get a reply of its own. Waiting for a line after SESSION looks like a hang.
  • BANNER is the 7-byte command BANNER plus a trailing space. That space is part of the token; authd strips spaces when matching some commands, so THUMBPRINT_SHA2 <colon-thumbprint> is parsed as one token and returns 501 Invalid arguments. PlainText has no extra spaces/colons and is the argument VDDK sends.
  • PROXY uses ticket.service (vpxa-nfc via vCenter) for NBD. NBDSSL appends ssl (vpxa-nfcssl). The success line names the host-side endpoint (ha-nfc or ha-nfcssl).
  • After 200 Connect, NBD speaks binary NFC on the raw fd. NBDSSL starts a second TLS handshake, then the same NFC protocol.

Commands that are not used for this ticket type

authd also implements FTP-style USER / PASS (and XPAS). Those are for local ESXi credentials. With a vCenter ticket:

Attempt Result
USER / PASS (vCenter account) 530 Login incorrect
USER * / PASS <sessionId> 530 Login incorrect
USER <sessionId> / PASS <sessionId> 530 Login incorrect
SESSIONID <sessionId> 530 Please login with USER and PASS
CONNECT_VPXA <sessionId> (after TLS) 530 Please login with USER and PASS
SESSION <sessionId> then wait for a reply No line until BANNER / PROXY follow

THUMBPRINT / THUMBPRINT_SHA2 with the SHA-1 ticket thumbprint as argument is not what VDDK sends. The SHA-1 value is for verifying the TLS certificate, not for the THUMBPRINT_SHA2 command.

OpenVixDiskLib

Piece Module Reuses pyVmomi?
VIM login openvixdisklib.nfc_auth.connect_vim Yes — SmartConnect
VM / host lookup vim.VirtualMachine Yes
HostServiceTicket return type of ticket call Yes — public data object
NFC ticket openvixdisklib.nfc_auth.get_nfc_ticket Same stub; type registered locally
authd TLS + commands openvixdisklib.nfc_auth.connect_authd No public API
End-to-end openvixdisklib.nfc_auth.authenticate NfcAuthSession

Management SHA-1 thumbprints are read with openvixdisklib.nfc_auth.get_ssl_cert_thumbprint (stdlib ssl and hashlib; no pyOpenSSL). Integration tests call that instead of hard-coding the lab certificate.

Run:

.venv/bin/pytest tests/integration/test_nfc_auth.py

The test completes VIM login and the authd handshake (200 Connect) and asserts an established TLS socket on ticket.host:ticket.port.

What comes after authentication

Authentication stops at 200 Connect ha-nfc (NBD) or 200 Connect ha-nfcssl (NBDSSL). Opening the VMDK and reading or writing sectors is documented in docs/nfc_open.md and implemented in OpenVixDiskLib (openvixdisklib/nfc_open.py). The datastore path is consumed there (and, for writes, as diskDeviceKey on the ticket).