This service will be undergoing maintenance at 00:00 UTC, 2016-09-28. It is expected to last about 1 hours
Bug 561 - lpd doesn't print when user doesn't exist causes DoS
lpd doesn't print when user doesn't exist causes DoS
Status: CLOSED CURRENTRELEASE
Product: Red Hat Linux
Classification: Retired
Component: lpr (Show other bugs)
5.1
i386 Linux
medium Severity medium
: ---
: ---
Assigned To: Cristian Gafton
:
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 1998-12-22 12:31 EST by kksocha
Modified: 2010-11-10 01:50 EST (History)
0 users

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 1999-02-17 12:08:05 EST
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:


Attachments (Terms of Use)

  None (edit)
Description kksocha 1998-12-22 12:31:05 EST
The problem was reported back on July 28, 1998 by Martin
Lacasse
mdlacas@erenj.com to RedHat or more specificlly
msf@redhat.com  and
ewt@redhat.com for lpr-0.31-1 and yet lpr-0.33-1 was
released with the
same problem.

THE CAUSE
When a remote user prints to a queue that is _not_
restricted (no rs
capability in printcap) on a print server they don't have an
account on,
lpd will try to fork the filter as that user.  By this time
lpd has
already established a connection to the printer, not being
able to fork
because user is not found the connection presist tying up
the printer so
that other TCP connection are refused hence the first denial
of
service.  Also the job remains at the top of the queue
jaming the queue
so other request are accepted but not printed hence the
second denial of
service.  Also in looking at the code to hack a fix I
noticed that
althought lpd attempts not to run the filters as root if the
remote or
local user is root the filters are run as root.  This leaves
the filters
open to race condition and the like on poorly writen
filters.

THE FIX
Attached is a patch to printjob.c where the problem was
found.   The fix
was if user did not exist or is root effectively become lp
to fork the
filters other wise fork the filter as the user.

THE PROOF
First let me appologize for the log prompts, but I like to
know when,
what, whence, who and where at a glance.
Also the hostnames and IPs have been xxxed out to hide their
IDs,
security you know.

#testprint user _does_not_ exist on server
#kksocha user does exist on the server
#both printer job are remote request
#the printcap _does_not_ have a 'rs' for this queue

#as you can see a small 124 byte print request has be made
by testprint
user
#and the lpd has a connection hence the sending to reported
by lpq

   Mon Dec 14 16:33:05 </dev/ttyp0>
/usr/src/redhat/BUILD/lpr-0.33/lpd
   [root@xxprint1]$ lpq -Pxxx01-cc103
   xxprint1.erenj.com: sending to xxx01-cc103
   Rank   Owner      Job
Files                                 Total
Size
   1st    testprint  11
.bashrc                               124
bytes
   2nd    kksocha    12
.bashrc                               682
bytes
   0 bytes

   connection to xxx01-cc103 is down

#any attempted to connet to the printer port of the printer
is refused
as long as lpd is sending
#however lpd can't fork the filter to actually send the data
because the
user _does_not_ exist
#oh port 9100 is also refused but I don't show that here

   Mon Dec 14 16:33:53 </dev/ttyp0>
/usr/src/redhat/BUILD/lpr-0.33/lpd
   [root@xxprint1]$ telnet xxx01-cc103 printer
   Trying xxx.xxx.1.167...
   telnet: Unable to connect to remote host: Connection
refused

#over 2 minutes later lpd is still trying to  send 124 bytes
to printer
which is more then enough
#time as will be proven later with the second larger job in
the queue

   Mon Dec 14 16:35:36 </dev/ttyp0>
/usr/src/redhat/BUILD/lpr-0.33/lpd
   [root@xxprint1]$ lpq -Pxxx01-cc103
   xxprint1.erenj.com: sending to xxx01-cc103
   Rank   Owner      Job
Files                                 Total
Size
   1st    testprint  11
.bashrc                               124
bytes
   2nd    kksocha    12
.bashrc                               682
bytes
   0 bytes

   connection to xxx01-cc103 is down

#remove the job that is jamming the queue and tying up the
connection to
the printer

   Mon Dec 14 16:36:12 </dev/ttyp0>
/usr/src/redhat/BUILD/lpr-0.33/lpd
   [root@xxrint1]$ lprm -Pxxx01-cc103 11
   dfA011Aa07949 dequeued
   cfA011dXXXXXXenj.com dequeued
   dfA011Aa14026 dequeued
   cfA011xxnk.erenj.com dequeued

#lpd is now sending the second job to the printer (and their
are already
no entries)

   Mon Dec 14 16:36:40 </dev/ttyp0>
/usr/src/redhat/BUILD/lpr-0.33/lpd
   [root@xxprint1]$ lpq -Pxxx01-cc103
   xxprint1.erenj.com: sending to xxx01-cc103
   Rank   Owner      Job
Files                                 Total
Size
   1st    kksocha    12
.bashrc                               682
bytes
   0 bytes

   JetDirect lpd: no entries

#only 7 seconds to send 682 bytes to the printer

   Mon Dec 14 16:36:47 </dev/ttyp0>
/usr/src/redhat/BUILD/lpr-0.33/lpd
   [root@xxprint1]$ lpq -Pxxx01-cc103
   JetDirect lpd: no entries

#oh look we can now connet to the printer port of the
printer

   Mon Dec 14 16:36:56 </dev/ttyp0>
/usr/src/redhat/BUILD/lpr-0.33/lpd
   [root@xxprint1]$ telnet xxx01-cc103 printer
   Trying xxx.xxx.1.167...
   Connected to xxx01-cc103.erenj.com.
   Escape character is '^]'.
   ^]
   telnet> close
   Connection closed.

#after my hack every thing worked fine including not fork
filters as
root

Thanks for your attention..........

81a82
> static char   euser[32];              /* effective user */
368a370,383
>                       } else {                        /*
not restricted */
>                               struct passwd *dude;
>                               dude = getpwnam(logname);
>                               if (dude == NULL ||
dude->pw_uid == 0) {
>                                       /* if user doesn't
exist will cause a DoS so set    */
>                                       /* effective user to
lp, also we don't want to be   */
>                                       /* root either to
prevent race condition and buffer */
>                                       /* overrun and the
like in poorly designed filters  */
>                                       /* don't change
logname so mail & banner are right  */
>                                       strncpy(euser, "lp",
3);
>                               } else {
>                                       /* since the user
does exist run filter as user */
>                                       strncpy(euser,
logname, sizeof(logname));
>                               }
526c541
<       if (getpwnam(logname) == (struct passwd *)0 ||
!*logname)
---
>       if (getpwnam(euser) == (struct passwd *)0 ||
!*euser)
553c568
<               if ((prchild=doforkuser(DORETURN,logname))
== 0) { /* child */
---
>               if ((prchild=doforkuser(DORETURN,euser)) ==
0) { /* child */
671c686
<       if ((child = doforkuser(DORETURN,logname)) == 0)
{      /* child */
---
>       if ((child = doforkuser(DORETURN,euser)) == 0)
{        /* child */
810a826
>                       logname[sizeof(logname) - 1] = '\0';
815a832,845
>                       } else {                        /*
not restricted */
>                               struct passwd *dude;
>                               dude = getpwnam(logname);
>                               if (dude == NULL ||
dude->pw_uid == 0) {
>                                       /* if user doesn't
exist will cause a DoS so set    */
>                                       /* effective user to
lp, also we don't want to be   */
>                                       /* root either to
prevent race condition and buffer */
>                                       /* overrun and the
like in poorly designed filters  */
>                                       /* don't change
logname so mail & banner are right  */
>                                       strncpy(euser, "lp",
3);
>                               } else {
>                                       /* since the user
does exist run filter as user */
>                                       strncpy(euser,
logname, sizeof(logname));
>                               }
851c881
<                           if ((ifchild =
doforkuser(DORETURN,logname)) == 0) { /*child*/
---
>                           if ((ifchild =
doforkuser(DORETURN,euser)) == 0) { /*child*/
Comment 1 David Lawrence 1998-12-22 13:40:59 EST
This has been verified to be a problem. I am assigning it to a
developer for further review.
Comment 2 Bill Nottingham 1999-02-17 12:08:59 EST
fixed in lpr-0.34-1

------- Email Received From  "Kevin K. Sochacki" <kksocha@erenj.com> 02/26/99 10:18 -------

Note You need to log in before you can comment on or make changes to this bug.