Viruses and dangerous attachments: ClamAV, custom scanners, the sandbox, sealed mail
-
Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) calls the anti-virus action that strips attachments Remove all attachments and deliver the text (Delete infected attachments only in 6.3.6's Control Panel), and under it a message whose one part is an attachment, and an S/MIME message signed as one part, now arrive as the action's note alone, where every version to 6.3.6 delivered them as they arrived with what the scanner had found in them; a message the server cannot read is deleted and each of its recipients sent the "message deleted" notice whatever the two notify settings say, while an encrypted message, and one that is one text part, are delivered as they are. In the Control Panel the sidebar has a Virus scanning group, and Ctrl+K takes every old title to its page. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) lets the Linux server reach clamd over its Unix socket:
ClamAVHosttakes the absolute path of clamd'sLocalSocket, such as/var/run/clamav/clamd.ctlfor Debian's and Ubuntu'sclamav-daemon, which listens on nothing else by default, andClamAVPortis then not used; no connection test is offered for it on Linux, so a test message and the log show the scan (ClamAV.md). See the 6.3.8 announcement.hMailServer does not scan for viruses itself. It hands each message to an engine you install (ClamAV, ClamWin or any command-line scanner) and acts on the answer, and nothing described here is enabled on a fresh install. This topic covers the three scanners, what happens when one finds something or cannot run, the blocked-attachment list, and what 6.3.4 added: the sandbox, and the handling of mail sealed end to end. The one thing to know first is the failure mode: a configuration that looks enabled and is inert. A scanner switched on with a wrong host or a missing executable does not refuse mail and does not disable itself; when every scanner has errored the verdict is
NoVirusFound, which from the message's point of view is identical to having been examined and found clean. 6.3.4 fixed one such case in the custom scanner, described below.What runs, and when
Scanning happens on the delivery side, not during the SMTP conversation: the message has already been accepted and spooled by the time any scanner sees it.
Which messages are scanned. For SMTP, a message is flagged when the connecting IP's security range has the virus-protection option; both default ranges ("Internet" and "My computer") have it. For mail this server fetches over POP3 or IMAP, the flag is the external account's own "use anti-virus" setting. A flagged message larger than
AntiVirus.MaximumMessageSizeis not scanned at all, silently; the default is0, no limit.Two passes. The whole spool file is scanned first; then every MIME attachment is written to a temporary file and scanned separately, which is what matters for a scanner that does not decode MIME itself. If the message cannot be parsed for the second pass, error
HM6001is reported and only the whole file was scanned; if one attachment cannot be written to the temp directory,HM6002is reported and that attachment was not examined.Concurrency. At most 10 scans run at once (
MaxRunningScanners). A thread that cannot get a slot waits up to 60 seconds and then scans anyway, writingScanning message without an available virus scanner slot.to the debug log.The read-only overview. Spam & virus filtering → Virus scanning overview in the Control Panel (Windows only) answers the question the settings page cannot: will an infected message be caught here.
The three scanners
All three can be enabled at once. They run in the order ClamWin, custom, ClamAV, stopping at the first that reports a virus. A scanner that errors does not stop the others.
ClamAV (clamd) ClamWin ( clamscan.exe)Custom scanner Switch AntiVirus.ClamAVEnabledAntiVirus.ClamWinEnabledAntiVirus.CustomScannerEnabledDefault off off off Reached by TCP socket, INSTREAMone process per file one process per file Settings AntiVirus.ClamAVHost(localhost),AntiVirus.ClamAVPort(3310)AntiVirus.ClamWinExecutable,AntiVirus.ClamWinDBFolderAntiVirus.CustomScannerExecutable"Infected" is a reply matching stream…: <name> FOUNDexit code 1 exit code equal to AntiVirus.CustomScannerReturnValueTimeout ClamMinTimeout15 s toClamMaxTimeout90 sExternalProcessTimeout, 300 sExternalProcessTimeout, 300 sVirus name reported the name clamd returned always Unknownalways UnknownClamAV. Install ClamAV, run
freshclamto download signatures, runclamdlistening on TCP 3310, set the host and port, and press Test ClamAV connection. The button does three things in order:- It sends
nPINGand requiresPONG, so "nothing is listening" is told apart from "something else is listening". - It sends
nVERSIONand reports what answered. - It streams a harmless sample and then the EICAR test string, both from memory, and requires the first to come back clean and the second
FOUND.
A success looks like
ClamAV 1.4.2/27700/Mon Sep 8 09:12:11 2026. Result: stream: Eicar-Signature FOUND; a failure names the step, for exampleUnable to connect to ClamAV server at localhost:3310.The custom scanner. Any program that exits with a distinguishable code will do. Put
%FILE%where the file to scan belongs; without it, the file name is appended as the single quoted argument. The Control Panel ships presets, all of them Windows programs:Preset Command line "Infected" exit code Microsoft Defender "C:\Program Files\Windows Defender\MpCmdRun.exe" -Scan -ScanType 3 -File "%FILE%" -DisableRemediation2 Sophos "C:\Program Files\Sophos\Sophos Anti-Virus\savscan.exe" -ss -archive "%FILE%"3 ESET "C:\Program Files\ESET\ESET Security\ecls.exe" "%FILE%"50 Bitdefender "C:\Program Files\Bitdefender\Endpoint Security\bdscan.exe" "%FILE%"1 Kaspersky "C:\Program Files (x86)\Kaspersky Lab\Kaspersky Endpoint Security\avp.com" SCAN "%FILE%"2 The 6.3.4 fix: a bare
%FILE%. CHANGELOG records that a custom scanner whose command line holds a bare, unquoted%FILE%was handed the message file's path cut at its first space, and the default data directory, underC:\Program Files, has one. The scanner therefore scanned no file, answered with an exit code other than the virus one, and the message was delivered as clean. Since 6.3.4 a%FILE%outside double quotes is replaced by the path in quotes. One already inside them,"%FILE%"as the documented examples above write it, was not affected and is replaced as before. If you run an earlier release with a command line of your own, check it for an unquoted%FILE%. The same fix removes a trailing backslash from ClamWin's database and temporary folders, which escaped the closing quote of--database="..."and took the file to scan with it.The service account must be able to run the scanner. A new 6.3.4 installation on Windows runs the service as
NT SERVICE\hMailServerby default; an upgrade keeps the account it had, LocalSystem unless the installer's page is answered otherwise (docs/ServiceAccount.md; the wiki's "LocalSystemby default" describes earlier releases and upgraded servers). Under the virtual account a custom scanner or ClamWin runs as that account: it reads the message in the temp folder, which is granted, and its own program and signatures, which the Users group can normally read; a scanner that needs more has to be granted it (Installing on Windows covers the account and its grants). Whichever account runs yours, the executable, its database directory and the temp directory must all be reachable by it. The symptom of getting it wrong isUnable to launch the virus scanner…reported asHM5406, once per message.For an engine with no command-line interface, the wiki points to an
OnAcceptMessageevent script or the HTTP filter hook. If you edithm_settingsby hand, note that the custom scanner's return value is stored under the misspelled namecustomviursscannerreturnvalue.Testing it end to end
The test buttons check the scanner. To check the pipeline, send yourself a message with the EICAR test file attached and watch the application log:
Virus found in message from [email protected]. Taking actions SMTPDeliverer - Message 4711: Message will be deleted (contained virus Eicar-Signature).Two things spoil that test on Windows. The host antivirus eats the EICAR file before hMailServer sees it, so exclude the mail store and the temp directory from real-time scanning. And your own outbound mail is not scanned by this server if it never comes back in through SMTP from an unauthenticated range, so send the test from an external address.
What happens when a virus is found
AntiVirus.ActionWhat happens Delete entire e-mail (0, the default) The message is delivered to nobody. Logged as Message will be deleted (contained virus X).Delete infected attachments only (1; Remove all attachments and deliver the text in 6.3.7's Control Panel) The message is rebuilt, not edited: the first textpart is kept, everything else is discarded, theVIRUS_ATTACHMENT_REMOVEDserver message is prepended to the body and the subject is prefixed with theVIRUS_FOUNDmessage. Logged asMessage attachments stripped (contained virus X).Since 6.3.7 a message whose one part is an attachment arrives as the note alone, where every version to 6.3.6 delivered it as it arrived, and one the server cannot read is deleted, each recipient sent the "message deleted" notice whatever the two notify settings sayUnderstand the rebuild before you choose it: an HTML alternative, inline images and every clean attachment go with the infected one.
AntiVirus.NotifySenderandAntiVirus.NotifyReceiverare both off by default, and the wiki's advice is to leave them off: the sender address on a virus is virtually always forged, so a notification is backscatter.When a scanner cannot run
Setting Default Meaning AVFailAction0 0delivers the message anyway.1never delivers unscanned mailAVFailRetryMinutes15 Minutes between scan attempts while a message is held AVFailMaxHolds16 Holds before the message is returned to its sender. 0tells the sender immediatelyThey are on Anti-virus settings → General, card When a scanner cannot run. The wiki describes them as stored in
hMailServer.INI; since 6.3.4 the database is the settings store and a[Settings]line edited in the file is put back, so change them in the Control Panel, over REST (PUT /api/v1/settings/ini/{name}) or, on Linux, withhmailserver --set-setting NAME=VALUE. At the defaults,AVFailAction1 gives a message about four hours to be scanned before its sender is told. The hold counter is the policy's own, not the message's try counter, because incrementing that would make the pass that finally scans the message skip DKIM signing. It lives in memory, so a restart gives every held message a fresh budget, and it is bounded at 100,000 tracked messages; beyond that messages are still held, never delivered unscanned, andHM6195is reported. Every scanner error is reported asHM5406with the failing scanner's own text.Blocked attachments
Spam & virus filtering → Blocked attachments refuses files by name pattern. The master switch is
AntiVirus.EnableAttachmentBlocking, default off. The wiki's starting list:*.exe *.scr *.pif *.bat *.cmd *.com *.vbs *.js *.jse *.wsf *.wsh *.hta *.jar *.msi *.msp *.cpl *.lnk *.reg *.ps1 *.iso *.imgThe attachment is not deleted. Its file name has
.txtappended and its content is replaced by theATTACHMENT_REMOVEDserver message, which names the original file through the%MACRO_FILE%macro, so the recipient receivesinvoice.exe.txtcontaining an explanation. Matching is a case-insensitive wildcard over the declared file name. It does not look inside archives and cannot see the real type of a file whose extension lies: it is a policy control, not a scanner.Since 6.3.4 the rewrite is logged. A rewritten message produces
VirusScanner - Message 1234 from [email protected]: the attachment(s) invoice.exe matched the blocked-attachment list. Their content has been replaced with a notice and the message rewritten.Before this the rewrite left no record anywhere but inside the message itself.Sealed mail: what the list and the scanner do with it
Since 6.3.4. A message encrypted end to end, S/MIME or PGP/MIME, is opaque to the server, which decides that from the body, never from the
Content-Typelabel, so a message only labelled encrypted is blocked, stripped and scanned like any other. What opaque means exactly, and what the rest of the server does with such a message, is in the topic on end-to-end encryption and sealed mail. For the list and the scanner, none of it is a setting:- The blocked-attachment list never rewrites an opaque message. In 6.3.3 and earlier blocking
*.asc,*.p7m,*.p7sor*.*replaced the content of every sealed message with the notice, with no log line, and the plaintext existed nowhere else. It is now delivered as it arrived, and the application log says it was not checked against the list. A signed message is checked like any other, with its detached signature left alone. - The scanner still scans it, and what it examines is ciphertext, so a clean result is not a verdict on the message; the log says its content
COULD NOT BE EXAMINED for viruses. It is deliberately not treated as a scanner failure, so a server withAVFailAction1 does not hold every encrypted message it receives; a policy that unscannable mail must not be delivered belongs in a rule or a Sieve script, not the scanner. - The "delete attachments" action never strips an encrypted message. When the scanner objects to one, the message is left as it arrived and the log ends Set the anti-virus action to delete the message if such a message must not be delivered. That is not a quiet pass: the message still carries what the scanner objected to. To 6.3.6 this held for every opaque message; since 6.3.7 an S/MIME message signed as one part (
application/pkcs7-mimeholding signed-data, which is opaque but not encrypted) is replaced by the action's note, as any other single-part attachment is.
The sandbox
Since 6.3.4 (schema 6091). ClamAV says whether a file is known to be bad. What an unknown file does when it is opened takes a sandbox, a service outside this server which you run or subscribe to. It does nothing until you name one:
SandboxEnabledis off, and cannot be saved on whileSandboxSubmitUrlorSandboxVerdictUrlis empty. Both addresses must be https, or plain http to this machine.After the anti-virus scan and before the rules, a message that no signed-in user of this server submitted, with an attachment whose extension or media type is in
SandboxTypes, is held: in no mailbox, not in the quarantine, and in nobody's digest. The default types are:exe,dll,scr,js,jse,vbs,wsf,hta,msi,bat,cmd,ps1,jar,lnk,iso,img,docm,xlsm,pptm, doc,xls,ppt,rtf,pdf,zip,7z,rarThe sandbox says What happens every file clean delivered, with an X-hMailServer-Sandboxheader readingclean (<n> file(s), <seconds>s)any file malicious, or suspicious at or above SandboxScoreThreshold(70)quarantined with the verdict as the reason; returned to its sender with 5.7.1 while QuarantineEnabledis offan error, once every file has answered, or no answer within SandboxTimeoutMinutes(15)SandboxTimeoutAction:deliver(0, the default) delivers with a header saying so;bounce(1) returns it to its senderThe contract. One POST per file to
SandboxSubmitUrlasmultipart/form-datawith four fields (file,filename,sha256,message_id), then a GET toSandboxVerdictUrleverySandboxPollSeconds(30), answered with JSON such as{"state":"done","verdict":"clean","score":3,"detail":"opened, nothing happened"}. WithSandboxTokenset, every request carriesAuthorization: Bearer <token>; the token is not in the backup archive. No vendor adapter is shipped. The document notes that a service which shares what it is sent, as VirusTotal shares uploads with its subscribers, is a place the attachments of the mail you receive go.What is exempt. What your own signed-in users submit (authenticated SMTP, the webmail, the send API) is not uploaded unless
SandboxInternalMailis on. The document states the threat model of that default: a stolen password used to send attachments to colleagues is not examined by the sandbox, though ClamAV, the blocked-attachment list and compromised-account detection still apply.Limits. An attachment larger than
SandboxMaxSizeKB(20480) is not submitted. A message with more files to submit thanSandboxMaxFiles(10) is not held, andSandboxTimeoutActiondecides it at once. Archives are submitted whole; links are not opened anywhere. A clean or malicious verdict is remembered by the file's SHA-256 forSandboxCacheHours(24), server-wide.What the administrator sees.
GET /api/v1/sandbox/heldlists what is held;POST /api/v1/sandbox/held/{queue_id}/releasedelivers without the verdict andDELETE /api/v1/sandbox/held/{queue_id}discards, both on the audit trail. The Control Deck's queue page has a Held for a sandbox box, andhmctl queue listhas aheldcolumn. The settings are on the Control Panel's Anti-virus page, Sandbox tab (Windows only), and onPUT /api/v1/settings.Rules, and the message store
Since 6.3.4 the mail flow rules can match the attachment's name and, in the outbound content policy, its type read from its bytes, so a rule can hold or refuse what the blocked-attachment list would only rename; that is in the topic on mail flow rules. The message store options added in 6.3.4 change nothing for a scanner: a message is scanned as plain RFC 822 in the queue and only placed in the mailbox in its stored form afterwards. A scanner pointed at the data directory from outside the server reads a compressed or encrypted container, not a message.
Error codes
Code Meaning HM5406An enabled scanner errored; the detail names the scanner and the failure HM6001The message could not be parsed, so only the whole file was scanned HM6002An attachment could not be written to the temp directory for scanning HM6195100,000 messages are already held for want of a scan; further holds are not counted HM7272The sandbox called a message malicious and the quarantine could not store it; it is held again, never delivered Where to read more
- Wiki: https://gitlab.com/hmailserver/hmailserver/-/wikis/Stopping-Viruses
- https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Sandbox.md
- https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SealedMail.md
- Public documentation: https://www.progressiverobot.com/hmailserver-documentation/#11-stopping-viruses, https://www.progressiverobot.com/hmailserver-documentation/#111-clamav and https://www.progressiverobot.com/hmailserver-documentation/#112-blocked-attachments
- Upgrading to 6.3.4 and the 6.3.4 security announcement
- It sends
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login