Skip to content

strip CR/LF from email header values in mailto notifier#1626

Closed
aizu-m wants to merge 1 commit into
OpenPrinting:masterfrom
aizu-m:mailto-header-newlines
Closed

strip CR/LF from email header values in mailto notifier#1626
aizu-m wants to merge 1 commit into
OpenPrinting:masterfrom
aizu-m:mailto-header-newlines

Conversation

@aizu-m

@aizu-m aizu-m commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Found while reading the notification path, tracing how event attributes end up on the wire.

job-name is an IPP name value chosen by whoever submits the print job. cupsNotifySubject() drops it straight into the subject text:

cups/notify.c:96
snprintf(buffer, sizeof(buffer), "%s %s-%d (%s) %s", prefix,
         printer_name->..., job_id->..., job_name->..., state);

email_message() then writes that subject into an RFC 5322 header field with CRLF line endings and no sanitising:

notifier/mailto.c:288
cupsFilePrintf(fp, "Subject: %s %s%s", mailtoSubject, subject, nl);

So a job submitted with

job-name = "report.pdf\r\nBcc: attacker@evil.example\r\nX-Injected: yes"

yields this on the SMTP stream:

Subject: ... Print Job: office-42 (report.pdf
Bcc: attacker@evil.example
X-Injected: yes) completed

The bare CR/LF start new header lines, so the printing user controls extra recipients and injected content in every notification mail. The recipient address and the reply-to value (decoded from notify-user-data) land in the To:/Sender:/Reply-To: headers by the same route.

Conclusion: the header writer must not emit a CR or LF inside a field value. The patch replaces them with spaces in the subject, recipient, and reply-to strings before they reach the cupsFilePrintf header lines. After it the whole job-name stays on the single Subject line.

@michaelrsweet michaelrsweet self-assigned this Jun 22, 2026
@michaelrsweet michaelrsweet added the bug Something isn't working label Jun 22, 2026
@michaelrsweet michaelrsweet added this to the v2.4.x milestone Jun 22, 2026
@michaelrsweet

Copy link
Copy Markdown
Member

This isn't the correct fix.

Incoming jobs should have the job-name attribute validated - aside from catching invalid chars in a name value (the name syntax doesn't allow control chars) we also need to make sure any Unicode/UTF-8 is valid.

@michaelrsweet

Copy link
Copy Markdown
Member

OK, so reviewing the current cupsd 2.4.x/2.5 code shows that all of the code paths for job creation and validation make sure that the "job-name" attribute is a single-valued name string with the correct syntax/characters.

What version of CUPS are you testing with?

@aizu-m

aizu-m commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

You're right, I went back through the intake path.

Testing was against master (2.5b1, CUPS_VERSION 2.0500).

job-name is run through ippValidateAttribute() in both add_job() and validate_job() (scheduler/ipp.c:1479 and :11471). For IPP_TAG_NAME/NAMELANG the validator breaks on every byte below 0x20 (cups/ipp.c:3933), so CR and LF are caught as a bad control character. That's deliberately stricter than IPP_TAG_TEXT a few lines up, which allows \r \n \t. In StrictConformance the request is rejected, otherwise the attribute is dropped and replaced with "Untitled". Either way the value never reaches the notifier.

Where I went wrong: my repro called cupsNotifySubject() directly on a hand-built event with a crafted job-name, so it skipped cupsd validation entirely. That's why I saw the injected headers. Through a real submission the control chars don't get that far.

So the sanitising belongs at intake, which already does it, not in the notifier. Closing this.

@aizu-m aizu-m closed this Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants