Skip to content

SMB

DittoFS speaks SMB 2.0.2 through SMB 3.1.1 over TCP. This page covers everything an operator or end-user needs: supported dialects, how to mount, authentication, encryption and signing configuration, and lease/durable-handle behaviour as seen from clients.

Contributors: protocol internals, wire formats, key derivation details, and cross-protocol coordination live in ../internals/smb-protocol.md.


DittoFS negotiates the highest mutually-supported dialect with each client.

DialectHexKey Features
SMB 2.0.20x0202Basic file operations, credits, HMAC-SHA256 signing
SMB 3.00x0300AES-128-CCM encryption, AES-128-CMAC signing, secure dialect negotiation
SMB 3.0.20x0302VALIDATE_NEGOTIATE_INFO downgrade protection
SMB 3.1.10x0311Preauth integrity (SHA-512), AES-256/128-GCM & -CCM encryption (AES-256-GCM preferred), GMAC signing, negotiate contexts

SMB 3.1.1 is preferred; it provides the strongest security and best cipher performance on AES-NI-capable hardware.

Selecting a dialect (the SMB “version”)

Section titled “Selecting a dialect (the SMB “version”)”

Unlike NFS — where the client states an exact version with vers= — SMB auto-negotiates: the client and server agree on the highest dialect both support, and DittoFS always offers up to 3.1.1. So most clients need no version flag at all.

You only pin a dialect when troubleshooting or forcing weaker/stronger crypto:

ClientHow to pin a dialectDefault behaviour
Linux (mount.cifs)-o vers=3.1.1 (also 2.0, 2.1, 3.0)Modern cifs-utils negotiates ≥ 2.1 automatically.
macOS (mount_smbfs)No per-mount dialect flag — always negotiates the best.Negotiates up to 3.1.1.
Windows (net use / Explorer)No per-mount flag (tune via the SMB client service).Negotiates up to 3.1.1.
dfsctl share mount --protocol smbNo flag; requests vers=2.1 on Linux, lets macOS negotiate.Wrapper picks safe defaults — see Mounting.

seal (on Linux mount.cifs) forces SMB3 encryption and therefore a 3.x dialect; pair it with vers=3.1.1 to negotiate the strongest AEAD cipher the client offers — DittoFS prefers AES-256-GCM, then AES-256-CCM, then the AES-128 variants. The matching dfsctl convenience wrapper is shown under Mounting SMB Shares.

Session and negotiation:

CommandStatusNotes
NEGOTIATEImplementedMulti-dialect (2.0.2 through 3.1.1), negotiate contexts
SESSION_SETUPImplementedNTLM and Kerberos via SPNEGO, key derivation
LOGOFFImplemented
TREE_CONNECTImplementedShare-level permissions, per-share encryption
TREE_DISCONNECTImplemented

File operations:

CommandStatusNotes
CREATEImplementedFiles and directories, lease V2 request/grant, durable handle create contexts
CLOSEImplemented
FLUSHImplementedFlushes data to block store
READImplementedWith cache support
WRITEImplementedWith cache support
QUERY_INFOImplementedMultiple info classes
SET_INFOImplementedAttributes, timestamps, rename, delete
QUERY_DIRECTORYImplementedWith pagination
CHANGE_NOTIFYPartialAccepts watches, async delivery via notification queue
IOCTLImplementedVALIDATE_NEGOTIATE_INFO, FSCTL_PIPE_WAIT, server-side copy (SRV_REQUEST_RESUME_KEY + SRV_COPYCHUNK)
LOCKImplementedShared and exclusive byte-range locks

SMB3 advanced features:

FeatureStatusNotes
Multi-Dialect NegotiationImplemented2.0.2, 3.0, 3.0.2, 3.1.1
Negotiate ContextsImplementedPREAUTH_INTEGRITY, ENCRYPTION, SIGNING
Preauth Integrity HashImplementedSHA-512 chain over raw wire bytes
AES-GCM EncryptionImplementedDefault for 3.1.1
AES-CCM EncryptionImplementedDefault for 3.0/3.0.2
AES-256-GCM/CCMImplemented256-bit variants
AES-CMAC SigningImplementedDefault for 3.0+
AES-GMAC SigningImplementedPreferred for 3.1.1
SP800-108 KDFImplementedKey derivation for signing/encryption
VALIDATE_NEGOTIATE_INFOImplementedDowngrade protection for 3.0/3.0.2
Leases V2ImplementedParentLeaseKey, epoch tracking
Directory LeasesImplementedRead-caching for directory listings
Durable Handles V1ImplementedDHnQ/DHnC with batch oplock
Durable Handles V2ImplementedDH2Q/DH2C with CreateGuid
Durable Handle ScavengerImplementedTimeout-based cleanup
Kerberos via SPNEGOImplementedShared keytab with NFS adapter
Compound RequestsImplementedCREATE+QUERY_INFO+CLOSE
Credit ManagementImplementedAdaptive flow control
Parallel RequestsImplementedPer-connection concurrency
Byte-Range LockingImplementedShared/exclusive locks
OplocksImplementedLevel II, Exclusive, Batch
Cross-Protocol CoordinationImplementedBidirectional lease/delegation breaks

Features not supported:

FeatureNotes
SMB1Legacy protocol, security risk
CompressionSMB 3.1.1 compression contexts not implemented
MultichannelMultiple TCP connections per session
Persistent HandlesCluster-aware handles (requires shared state)
RDMARemote Direct Memory Access transport
QUICUDP-based transport (SMB over QUIC)
SACL / auditingAudit ACEs round-trip (read/write/Auditing tab) but are not enforced — no audit events are emitted (owner/group/DACL are supported — see Access Control)
DFSDistributed File System referrals

DittoFS listens on port 12445 by default (port 445 requires root). All examples below use that port. To serve the standard SMB port 445 in production (so clients connect with no port suffix), see Running on standard ports (production).

dfsctl share mount handles platform-specific mount options automatically:

Terminal window
# macOS - Mount to user directory (recommended, no sudo needed)
mkdir -p ~/mnt/dittofs
dfsctl share mount --protocol smb /export ~/mnt/dittofs
# macOS - Mount to system directory (requires sudo)
sudo dfsctl share mount --protocol smb /export /mnt/smb
# Linux - Mount with sudo (owner set to your user automatically)
sudo dfsctl share mount --protocol smb /export /mnt/smb
# Unmount
sudo umount /mnt/smb # or: diskutil unmount ~/mnt/dittofs (macOS)

macOS restricts SMB mounts so that only the mount owner can access files, regardless of Unix permissions. Even with 0777 permissions, non-owner users get “Permission denied”. Apple confirmed this is “works as intended”.

How dfsctl handles this: When you run sudo dfsctl share mount, it automatically uses sudo -u $SUDO_USER to mount as your user (not root):

Terminal window
# Works correctly - mount owned by your user
sudo dfsctl share mount --protocol smb /export /mnt/share

Alternative - mount without sudo (to a user-owned directory):

Terminal window
mkdir -p ~/mnt/dittofs
dfsctl share mount --protocol smb /export ~/mnt/dittofs
# No sudo needed; directory permissions default to 0755

From Command Prompt or PowerShell:

Terminal window
net use Z: \\server\export /user:username password
# Explicit port:
net use Z: \\server@12445\export /user:username password
# Disconnect
net use Z: /delete

From Explorer: right-click “This PC” > “Map network drive”, set path to \\server@12445\export.

Terminal window
# Using mount_smbfs (built-in)
# Note: -f sets file mode, -d sets directory mode (required for write access with sudo)
sudo mount_smbfs -f 0777 -d 0777 //username:password@localhost:12445/export /mnt/smb
# Mount to home directory (no sudo, user-owned)
mount_smbfs //username:password@localhost:12445/export ~/mnt/smb
# Using open (opens in Finder)
open smb://username:password@localhost:12445/export
# Unmount
sudo umount /mnt/smb
# or
diskutil unmount /mnt/smb
Terminal window
# Using mount.cifs (requires cifs-utils)
# uid/gid options set the owner of mounted files
sudo mount -t cifs //localhost/export /mnt/smb \
-o port=12445,username=testuser,vers=2.0,uid=$(id -u),gid=$(id -g)
# Password will be prompted interactively
# Mount with SMB3 encryption
sudo mount -t cifs //localhost/export /mnt/smb \
-o port=12445,username=testuser,vers=3.1.1,seal,uid=$(id -u),gid=$(id -g)
Terminal window
# Interactive client
smbclient //localhost/export -p 12445 -U testuser
# List shares
smbclient -L localhost -p 12445 -U testuser
# One-liner file operations
smbclient //localhost/export -p 12445 -U testuser -c "ls"
smbclient //localhost/export -p 12445 -U testuser -c "get file.txt"
smbclient //localhost/export -p 12445 -U testuser -c "put localfile.txt"

SMB3 sessions negotiate AES-GCM or AES-CCM encryption automatically. When encryption is active, all messages between client and server are opaque on the wire; packet capture shows only the transform header (52 bytes) and ciphertext.

Cipher suites supported:

CipherDefault ForKey Size
AES-128-CCMSMB 3.0, 3.0.2128-bit
AES-128-GCMSMB 3.1.1128-bit
AES-256-CCM256-bit
AES-256-GCM256-bit

SMB confidentiality is provided by SMB3 in-protocol encryption, not TLS or QUIC. See ./security.md for details.

ModeBehaviour
disabledNo encryption for any session
preferredEncrypt SMB 3.x sessions that support it; allow unencrypted 2.x (default)
requiredReject SMB 2.x clients; encrypt all SMB 3.x sessions

The default (preferred) provides confidentiality for SMB 3.x clients without breaking SMB 2.x compatibility. Set required to force encryption on the whole server, or set encrypt_data on an individual share to enforce it only on sensitive data.

Guest sessions are never encrypted — guest sessions have no session key and cannot participate in key derivation.

adapters:
smb:
encryption:
encryption_mode: preferred # disabled | preferred | required
allowed_ciphers: [] # Empty = all in default order
# Custom cipher preference: [AES-128-GCM, AES-128-CCM]

Per-share encryption:

shares:
- name: sensitive
encrypt_data: true # Enforces encryption for this share regardless of server mode

See ./configuration.md for complete encryption configuration options. See ./security.md for security implications and recommendations.


Signing adds an integrity tag to every SMB2 message so that tampering in transit is detected. Clients may request signing in the NEGOTIATE request (SIGNING_ENABLED), or you can require it server-wide.

Signing algorithms by dialect:

DialectAlgorithm
SMB 2.0.2HMAC-SHA256
SMB 3.0AES-128-CMAC
SMB 3.0.2AES-128-CMAC
SMB 3.1.1AES-128-GMAC (preferred) or AES-128-CMAC

AES-128-GMAC is the preferred algorithm for 3.1.1 — it reuses GCM hardware acceleration. When encryption is active, AEAD already provides integrity; DittoFS still signs to match Windows Server behaviour.

adapters:
smb:
signing:
enabled: true # Advertise signing capability
required: false # Require all clients to sign
# Signing algorithm preference (for 3.1.1 negotiate context)
# Default: [AES-128-GMAC, AES-128-CMAC]
preferred_algorithms: []

SMB 2.1+ clients receive lease grants that allow them to cache file data, writes, and open handles locally without round-tripping to the server on every access.

Three caching flags compose a lease state:

FlagAbbreviationWhat the Client May Do
ReadRCache read data without revalidating against the server
WriteWCache writes and defer flushing to the server
HandleHCache the file handle and defer CLOSE

Common state combinations you’ll see:

StateFlagsTypical Use
NoneNo caching
ReadRShared read caching (multiple clients)
Read-HandleRHRead caching with handle caching
Read-WriteRWExclusive read/write caching
Read-Write-HandleRWHFull exclusive caching (most aggressive)

When a conflicting operation arrives (another client opens the file for write, or a cross- protocol NFS open occurs), the server sends a lease break notification. The client must flush cached data and acknowledge before the conflicting operation proceeds.

Directory leases (SMB 3.0+) allow clients to cache QUERY_DIRECTORY results. Any modification to the directory’s contents triggers an immediate break to None.

adapters:
smb:
leases:
enabled: true # Enable lease support
directory_leases: true # Enable directory leasing
lease_break_timeout: 35s # Time to wait for break acknowledgment

Durable handles let clients survive a transient network disconnection without losing their open files or cached state. The client disconnects, reconnects, and resumes — the server preserves the handle and its state for up to the configured timeout.

Two versions:

V1 (SMB 2.0.2+): Requires a batch oplock on the open file.

V2 (SMB 3.0+): No oplock requirement; the client provides a CreateGuid (16-byte GUID) that enables idempotent reconnection. Multiple reconnect attempts with the same GUID all succeed.

After a successful reconnect, the client must re-request durability — the IsDurable flag is not automatically set on the restored handle.

Ephemeral state caveat: Durable handles survive disconnection but not server restart. The handle metadata is persisted in the configured store (BadgerDB/PostgreSQL), but in-memory state is lost on restart.

adapters:
smb:
durable_handles:
enabled: true # Enable durable handle support
default_timeout: 60s # Default handle preservation timeout
scavenger_interval: 30s # How often to scan for expired handles
max_handles_per_session: 1000 # Limit per session

The server advertises both Kerberos and NTLM mechanism OIDs in the NEGOTIATE response. Clients with valid Kerberos tickets choose Kerberos for single round-trip authentication; other clients fall back to NTLM.

DittoFS validates Kerberos tickets via SPNEGO during SESSION_SETUP. The SMB adapter shares the Kerberos keytab with the NFS adapter; the server automatically derives the cifs/ service principal from the configured nfs/ principal.

Principal-to-user mapping:

  • A local DittoFS account matching the principal wins.
  • When no local account exists but the LDAP/AD directory resolves the principal (RFC2307 UID/GID + groups), the session is built from that directory identity — AD domain users do not need pre-created local accounts.
  • A valid ticket whose principal resolves to neither a local account nor the directory is rejected (not treated as guest).

Kerberos is the recommended path for AD domain users: single round-trip, mutual authentication, no machine-account provisioning required.

When Kerberos is unavailable (no keytab, client has no valid TGT, DNS resolution fails), the server falls back to NTLM:

  • Local users: the NTLMv2 response is validated against the NT hash stored in the control- plane user store.
  • AD domain users: when a machine account is configured (kerberos.machine_account), DittoFS validates the domain user’s NTLM response by forwarding it to the DC over a sealed NETLOGON secure channel (MS-NRPC NetrLogonSamLogon, sign+seal AES). The DC-returned SID is resolved through the same LDAP/idmap pipeline used for Kerberos, so the user maps to the same UID/GID across NFS-krb5, SMB-krb5, and SMB-NTLM.

Machine account requirement: NETLOGON passthrough requires an offline-provisioned machine account (DITTOFS$) and its shared secret:

Terminal window
dfsctl identity-provider configure kerberos \
--machine-account-enabled \
--machine-account-name DITTOFS$ \
--machine-secret <secret> \
--dc-address <dc>

The secret is stored at-rest and is write-only / redacted in API responses.

Note: end-to-end interoperability of the NETLOGON secure channel against a Samba AD-DC is still being validated — the sealed-schannel AlterContext is currently rejected by the test DC (RPC_S_UNKNOWN_AUTHN_SERVICE); see #1345.

When authentication fails and guest access is enabled, a session is created with guest privileges. Guest sessions cannot sign or encrypt messages (no session key), so they should be limited to read-only public shares.

kerberos:
enabled: true
keytab_path: /etc/dittofs/dittofs.keytab
service_principal: nfs/server.example.com@EXAMPLE.COM

The keytab supports hot-reload: when the file is replaced on disk the server detects the change and loads the new key without restart.

See ./security.md for detailed Kerberos security considerations.


SMB uses the same user/group store as all other DittoFS protocols. A brief example:

users:
- username: alice
password_hash: "$2a$10$..." # bcrypt hash
uid: 1001
gid: 1000
share_permissions:
/export: read-write
groups:
- name: editors
gid: 1000
share_permissions:
/export: read-write
guest:
enabled: false # Disable guest access

Permission levels: none, read, read-write, admin (future).

Resolution order: user explicit permission → group permission → share default.

For the full user management reference (LDAP/AD idmap, password hash format, per-share defaults), see ./configuration.md#user-management.


Terminal window
# Start server with debug logging
DITTOFS_LOGGING_LEVEL=DEBUG ./dfs start
# Mount and test (macOS)
sudo mount_smbfs //testuser:testpass@localhost:12445/export /mnt/smb
cd /mnt/smb
# Test operations
ls -la # QUERY_DIRECTORY
cat readme.txt # READ
echo "test" > new # CREATE + WRITE
mkdir foo # CREATE (directory)
rm new # SET_INFO (delete)
rmdir foo # SET_INFO (delete)
mv file1 file2 # SET_INFO (rename)
Terminal window
# Interactive mode
smbclient //localhost/export -p 12445 -U testuser%testpass
smb: \> ls
smb: \> get file.txt
smb: \> put local.txt
smb: \> mkdir newdir
smb: \> rm file.txt
smb: \> rmdir newdir
smb: \> exit
Terminal window
# Run SMB E2E tests
sudo go test -tags=e2e -v ./test/e2e/ -run TestSMB
# Run interoperability tests (NFS <-> SMB)
sudo go test -tags=e2e -v ./test/e2e/ -run TestInterop
# Run specific test
sudo go test -tags=e2e -v ./test/e2e/ -run TestSMBCreateFileWithContent
# Run SMB Kerberos authentication tests
sudo go test -tags=e2e -v ./test/e2e/ -run TestSMBKerberos
# Run cross-protocol lease/delegation tests
sudo go test -tags=e2e -v ./test/e2e/ -run TestCrossProtocol

  1. Verify the server is running: netstat -an | grep 12445
  2. Check firewall rules
  3. Try explicit port: port=12445 in mount options
  1. Verify the user exists in config
  2. Check that the password hash is valid bcrypt
  3. Enable debug logging to see the authentication flow
  4. Ensure the user has share permissions
  5. For Kerberos: verify the keytab contains the cifs/ service principal and the KDC is reachable
  1. Increase timeout in SMB config
  2. Check block store connectivity (S3, filesystem)
  3. Enable debug logging for detailed timing
Terminal window
# Clear SMB credential cache
security delete-internet-password -s localhost
# Check for stale mounts
mount | grep smb
# Force unmount
sudo umount -f /mnt/smb
Terminal window
# Install cifs-utils if missing
sudo apt-get install cifs-utils # Debian/Ubuntu
sudo yum install cifs-utils # RHEL/CentOS
# Check kernel module
lsmod | grep cifs

See Troubleshooting › Cross-Protocol Issues for cross-protocol troubleshooting, including:

  • File locked by another protocol
  • Delegation recall timeouts
  • Lease break storms
  • Stale data after cross-protocol writes

  1. No SMB1 support: Legacy protocol, not implemented for security reasons
  2. No compression: SMB 3.1.1 compression contexts are not implemented
  3. No multichannel: Multiple TCP connections per session not supported
  4. No persistent handles: Cluster-aware handles require shared state infrastructure
  5. No RDMA transport: Remote Direct Memory Access not supported
  6. No QUIC transport: SMB over QUIC (UDP) not supported
  7. SACL audit events not generated: Owner, group, and DACL security descriptors are supported (see Access Control); audit ACEs (SACL) round-trip through read/write and the Windows Auditing tab but are not enforced — no audit log is emitted on a matching access
  8. No DFS referrals: Distributed File System not supported
  1. Ephemeral locks and oplocks: Both byte-range locks and oplocks are in-memory only, lost on server restart
  2. No blocking locks: Lock requests fail immediately if a conflicting lock exists
  3. Single-node only: No clustering or high availability for SMB state
  4. Durable handle state is in-memory: Durable handles survive disconnection but not server restart (BadgerDB/PostgreSQL stores persist handle metadata but in-memory state is lost)
  1. No per-file encryption: Encryption is per-session or per-share only

TermDefinition
AEADAuthenticated Encryption with Associated Data — encryption providing both confidentiality and integrity (AES-GCM, AES-CCM)
ACLAccess Control List — Windows permission model
AES-CCMAES in Counter with CBC-MAC mode — AEAD cipher for SMB 3.0/3.0.2
AES-CMACAES-based Cipher-based Message Authentication Code — signing algorithm for SMB 3.0+
AES-GCMAES in Galois/Counter Mode — AEAD cipher preferred for SMB 3.1.1
AES-GMACAES-GCM used for authentication only (no encryption) — signing algorithm for SMB 3.1.1
AP-REQKerberos Application Request — contains client’s service ticket
AP-REPKerberos Application Reply — provides mutual authentication
ACEAccess Control Entry — a single allow/deny rule inside an ACL
CIFSCommon Internet File System — older name for SMB
CreateGuid16-byte GUID used for idempotent durable handle V2 reconnection
CreditFlow control unit in SMB2
DACLDiscretionary ACL — the access-granting part of a security descriptor
DH2Q/DH2CDurable Handle V2 Request/Reconnect create contexts
DHnQ/DHnCDurable Handle V1 Request/Reconnect create contexts
Durable / persistent handleAn open handle a client can reclaim after a network drop (durable) or server restart (persistent)
DialectSMB protocol version (e.g., 0x0311 = SMB 3.1.1)
EpochMonotonic counter on lease V2 for stale break detection
FileID16-byte handle for open file (8 persistent + 8 volatile)
GUID16-byte globally unique identifier
KDFKey Derivation Function — derives session-specific keys from base key
Lease V2Enhanced lease with ParentLeaseKey and epoch tracking (SMB 3.0+)
KerberosTicket-based network authentication, carried over SMB via SPNEGO
LeaseSMB 2.1+ caching grant (read/write/handle) succeeding oplocks
NetBIOSNetwork Basic Input/Output System — legacy session layer
NT_STATUSWindows error code format
NTLM / NTLMSSPMicrosoft challenge/response authentication; the fallback when Kerberos is unavailable
OplockOpportunistic lock — client caching hint
ParentLeaseKeyLease V2 field linking file lease to parent directory lease
Preauth IntegritySHA-512 hash chain over negotiate/session-setup messages for downgrade protection
SACLSystem ACL — the auditing part of a security descriptor
Security descriptorWindows/SMB structure bundling owner, group, DACL, and SACL (MS-DTYP §2.4.6)
SessionID64-bit identifier for authenticated session
ShareNetwork-accessible folder (like NFS export)
SIDSecurity Identifier — Windows user/group identity
SP800-108NIST key derivation specification using Counter Mode with HMAC-SHA256
SPNEGOSimple and Protected GSSAPI Negotiation Mechanism — wraps NTLM/Kerberos tokens
Transform Header52-byte header wrapping encrypted SMB3 messages (magic 0xFD)
TreeID32-bit identifier for share connection
UTF-16LE16-bit Unicode, little-endian byte order

For plain-language definitions of these terms, see the project-wide Glossary.

  • go-smb2 - SMB2 client in Go
  • Samba - SMB/CIFS implementation for Unix