freedomofpress / securedrop-client

a Qt-based GUI for SecureDrop journalists 📰🗞️
GNU Affero General Public License v3.0
41 stars 39 forks source link

Print: Find alternative to PPD #2088

Open deeplow opened 5 months ago

deeplow commented 5 months ago

Description

The workstation's print workflow relies on PPD files, which according to the CUPS's lpadmin manpage:

The following lpadmin options are deprecated:

-i filename

  • This option historically has been used to provide either a System V interface script or (as an implementation side-effect) a PPD file. Note: Interface scripts are not supported by CUPS. PPD files and printer drivers are deprecated and will not be supported in a future version of CUPS.

-P ppd-file

  • Specifies a PostScript Printer Description (PPD) file to use with the printer. Note: PPD files and printer drivers are deprecated and will not be supported in a future version of CUPS.

update: found the respective CUPS issue about the removal of PPD https://github.com/OpenPrinting/cups-sharing/issues/4

This is also warned when running _setup_printer(). Running the same line manually on my system succeeds but shows the following on stderr:

lpadmin: Printer drivers are deprecated and will stop working in a future version of CUPS.

Steps to Reproduce

Not relevant,

Expected Behavior

Use of supported system.

Actual Behavior

Warning about using something that may not be supported in the future.

Comments

legoktm commented 5 months ago

Related: https://github.com/freedomofpress/securedrop-engineering/issues/71 and https://github.com/freedomofpress/securedrop-client/issues/2156.

deeplow commented 5 months ago

This is also warned when running _setup_printer(). Running the same line manually on my system succeeds but shows the following on stderr:

lpadmin: Printer drivers are deprecated and will stop working in a future version of CUPS.

I have confirmed now that this line also appears on the sd-device's logs.

rocodes commented 5 months ago

We have sadly known about this for a while and even tried to suppress those warnings: https://github.com/freedomofpress/securedrop-client/commit/9bcd76a6561889da66b10d999fb0c47329ae8c91 Another good reason to move away from this approach and towards the modern age :)

deeplow commented 3 months ago

I looked a bit into this yesterday. I was able to successfully set up an HP printer without drivers (IPP).

How I got it working

  1. Created debian-12-based a standalone qube (to make testing easier)
  2. Add qubes services: avahi and cups
  3. Start the qube and connect the printer's USB to it
  4. sudo apt install ipp-usb cups
  5. system-config-printer, just add the printer, selecting the "Generic" driver and then "IPP Everywhere". (note, the printer showed up in two places: close to the top of the list and then also under Network printers. In my case, both worked)

That was it. In the final version we may want to automate some of the steps using lp commands, but for now I'd say this already provides satisfactory results.

Research on driverless printing

I found the Debian wiki a very valuable resource. Many of my findings come from there.

How to detect if a printer supports IPP?

Run lsusb -v | grep -A 3 bInterfaceClass.*7

Then check for a value of 4 for bInterfaceProtocol indicates a USB IPP device:

  bInterfaceClass 7 Printer
  bInterfaceSubClass 1 Printer  
  bInterfaceProtocol 4  
  iInterface 0  

The interface is usually referred to as being a 7/1/4 one.

deeplow commented 3 months ago

@rocodes and I have both had successful experiences with setting up driverless printers. The Debian wiki provides a lot of detail on the topic and it was my main resource to get things working. However, none of us managed to fully automate the process (yet).

deeplow commented 1 month ago

Driverless Printing FAQ

Here are some answers to some of driverless printing

Driverless printing vs IPP Everywhere vs. AirPrint

IPP Everywhere == AirPrint == IPP Driverless (they are all the same thing)

Classic drivers + filters are the previous default (called Classic drivers). The driver converts a default format to the printer's specific language.

What does driverless actually mean?

A good explanation is provided here. But the just of it is: The driver is generated on demand depending on the features the printer states are available.

How much time do we have to implement this?

CUPS 3.0 is technically not going to support PPD files. All 3.0 com components are planned to be available in mid-2025. Distro availability may be another while (especially because we rely on Debian). And technically classic drivers (PPD files) will still be available through a "printer application" and all open PPD drivers have been already ported.

What is the printer compatibility like?

Apple maintains a list of compatible printers here. I got the impression that Open Printing also has a list for driverless printers, but I could only find the one for classic drivers.

:warning: But there's a catch! We want printers with USB support. There is no official list of printers which support IPP-over-USB, some may even not mention it but actually support it. Some even go as far as presenting as having a "WiFi only" sticker over the USB port. All one needs to to remove the sticker and connect.

As a general rule, all support modern HP printers support IPP over USB but the general exception are HP LaserJet printers (this are still from the Samsung printer division that HP bought -- ). For example, HP LaserJet Pro MFP 4101fdn is advertised as "Ethernet / USB only", but is NOT on the AirPrint compatibility list. However, it is supported through classic drivers (via hplip) so it may still support "driverless" printing via backwards compatibility? :thinking:

And as the market moves more towards driverless WiFi-based printing, it is likely that the non-networked will become more scarce. Hopefully the market for security is still enough to sustain some non-networked options.

deeplow commented 1 month ago

Following a team meeting, here are some of the conclusions:

deeplow commented 1 week ago

(Proposal) Mitigating some printer-related risks

We want to add support for driverless printing (IPP) to the workstation. However, recent vulnerabilities in CUPS (linux printing) highlighted just how brittle these tools can be security-wise.

Even though the SecureDrop Workstation does assume that plugged in USBs are not malicious, it could be good to mitigate the risk of a rogue USB stick pretending to be a printer and getting to an RCE.

We can't fully mitigate this risk and are looking for Qubes 4.3 for further device control. However, for the time being a practical mitigation involves separating the process of "adding a printer" from the "using a printer. The vulnerabilities highlighted that the risks were mostly during the custom PPD driver generation, which takes place during the first phase.

This has some usability implications, though. Without this mitigation, the user can just plug in any printer and so long as the printer is IPP-compatible, it will show up in the document print dialog. With this mitigation, the administrator will first need to add the printer and then regular users can print to it. However, because it is just a one time process, it feels like an acceptable usability tradeoff.

Proposal downsides: this doesn't come without its downsides. If the printer were to be malicious, it could use the one-time autodetection and any 0day PPD-related RCE vectors to add a permanent RCE in sd-devices.

Investigation

Printer configs were not remaining after sd-devices-dvm restart. This means that those files are not stored in the user's home and thus, we need them to be kept via bind-dirs

After looking at the files touched by system-config-printer dialog, I have figured out that the following are the key files to preserve:

This means that adding /etc/cups to bind-dirs should suffice to keep printer configurations accross reboots. Lo and behold, it worked.

Implementation

General strategy:

How it would work in practice:

  1. open "print settings" in sd-devices-dvm
  2. attach printer to sd-devices-dvm
  3. "+ add" printer and choose the IPP network-detected one
  4. print test page
  5. shut down sd-devices-dvm
  6. restart sd-devices (if needed)

Implementation notes:

  1. setting up bind-dirs in sd-devices-dvm:
    mkdir /rw/config/bind-dirs.d
    echo "binds+=( '/etc/cups/' ) | sudo tee /rw/config/bind-dirs.d/30_cups.conf
  2. shut down sd-devices-dvm
  3. Add avahi to the qubes-services of sd-devices-dvm (but NOT sd-devices)

Useful commands / tricks

rocodes commented 1 week ago

Hey @deeplow, thanks for preparing this. My opinion (recapping from standup for the rest of the team) is a couple things:

deeplow commented 1 week ago

That makes sense. Given the (understandable) skepticism, let's proceed with driverless printing with complete printer autodetetion.

deeplow commented 1 week ago

Given that we have a decision on how to proceed, I'm moving forward with the implementation already. But I an implementation question.

We currently support two printers (I think). It's a bit unclear exactly which ones these are (https://github.com/freedomofpress/securedrop-client/issues/2087), but looking at the code, it looks like the "br7030" (aka Brother DCP-7030, I think), does NOT support AirPrint.

So much of the code is dependent on loading up manual drivers that it would just be cleaner to scrap all that and just do driverless printing directly, which translates to no code at all. We can do that, but we'd a component which retrofits printers into the IPP world. This can happen for legacy HP printers and legacy Brother printers and this would open up the possibility to have a lot of relatively old hardware also supported (might be easier to procure non-wireless models).

The downside of this is that both "printer applications" are only available as snaps and as far as I can tell, they haven't been packaged for Debian. They appear to be pretty trivial C applications, but I don't know if we want to venture into packaging other's code.

So my question is: how do we proceed:

Personally I'm inclined for Option A just because it's not that much extra work to get around the code and we don't upset anyone's workflows. Even though I think Option C would be ideal if the respective printer applications were available out of the box in Debian. We would not only be adding support for driverless printing, while also (1) simplifying the print code a lot and (2) adding support for so many more legacy printers.

rocodes commented 1 week ago

Yeah I'm with you, I would say A or B, (and if A, that we make a plan to do B and drop all the ppd driver stuff in a followup release). We have a small number of pilot users and can reach out individually to find out if anyone is still using an older printer. (I have started reaching out already to speed things along.)

It looks like to me, you would primarily be making changes to _check_printer_setup and its helper functions. So I'd lean towards making that existing setup method a self contained setup_legacy fallback option if no driverless detection works, and that way we can choose to either keep or deprecate it down the line.

cfm commented 1 week ago

Thanks for writing this up, @deeplow. Strictly speaking I think the opportunity for option B was the breaking v1.0.0 release. For a non-breaking v1.y.0 that leaves us options A or C, and I hear your concerns about the packaging implications of C. So another vote for A.

deeplow commented 1 week ago

While implementing this I'm running into an issue where the ipp info is sometimes being shown on lpinfo -v and other times not. It seems to be inconsistent and is really messing up with the printer mechanism (which does various calls and assumes nothing has changed from the preflight to the print itself).

Example of this happening: ``` user@sd-devices:~$ sudo lpinfo -v file cups-brf:/ network https network socket network ipp network beh network lpd network http network ipps direct usb://HP/OfficeJet%20Pro%208120e%20series?serial=XXXXXXXX&interface=1 user@sd-devices:~$ sudo lpinfo -v file cups-brf:/ network lpd network beh network http network socket network https network ipps network ipp direct usb://HP/OfficeJet%20Pro%208120e%20series?serial=XXXXXXX&interface=1 network ipp://HP%20OfficeJet%20Pro%208120e%20series(USB)._ipp._tcp.local/ ```

One command was run right after the other and yet in the second one it shows ipp://.

First we have to confirm that this is a problem that goes beyond my setup. Then if this is indeed a bigger issue we have several ways to approach this:

🙋‍♂️ Ask: if someone has an IPP-capable printer, could you run lpinfo -v serveral times and see if the results differ


Investigating inconsistent results in lpinfo -v for IPP printers

I went with investigation route 1. a bit already and was investigating if ipp-usb was the culprit:

  1. I stopped the ipp-usb service with sudo system stop ipp-usb
  2. sudo ipp-usb debug
  3. In other tab run lpinfo -v and see if there when the device is not detected if the ipp-usb command ran previously shows any errors or exits (spoiler: it didn't)
  4. Repeat the above step but monitoring the output of ss -tupln to see if somehow during the non-detections the ipp server is not up (spoiler: it is always running)

Then I also opened the gnome's print dialog (via gedit) on sd-devices and it systematically listed the printer.

After this, I would say that the problem appears to be with lpinfo -v which is not properly finding IPP devices.

rocodes commented 1 week ago

@deeplow: re printer detection via lpinfo: can you post the exact steps you are doing to configure the printer, either in a draft PR or a gist somewhere? I did run into this specific issue (not always reliably seeing the printer via lpinfo even after it was configured) and I left a couple notes in the driverless printing wiki page I shared a while back on different printer discovery mechanisms. (I also see that there are some discovery steps needed with lpadmin before lpinfo works, but don't want to suggest paths you have already tried.)

deeplow commented 1 week ago

@deeplow: re printer detection via lpinfo: can you post the exact steps you are doing to configure the printer, either in a draft PR or a gist somewhere?

I added the qubes service avahi and then plugged in the printer and ran lpinfo -v. As simple as that.

I saw on your notes the command driverless list, but that didn't work for me. It just exited with no errors (but without finding the printer, which even lpinfo -v could find).

On your intructions you're creating a print queue via lpadmin. I was originally doing the same, but then I found that with driverless printing the dialog automatically shows the printers it finds. So if we do this we get two printers: sdw-printer and another one which was the printer it detected (both map to the same actually).