cups: add a bunch of security fixes in 8.09
authorNicolas Thill <nico@openwrt.org>
Fri, 24 Apr 2009 12:07:08 +0000 (12:07 +0000)
committerNicolas Thill <nico@openwrt.org>
Fri, 24 Apr 2009 12:07:08 +0000 (12:07 +0000)
SVN-Revision: 15379

net/cups/patches/901-cve-2008-1722.patch [new file with mode: 0644]
net/cups/patches/902-cve-2008-3639.patch [new file with mode: 0644]
net/cups/patches/903-cve-2008-3640.patch [new file with mode: 0644]
net/cups/patches/904-cve-2008-3641.patch [new file with mode: 0644]
net/cups/patches/905-cve-2008-5183.patch [new file with mode: 0644]
net/cups/patches/906-cve-2008-5184.patch [new file with mode: 0644]
net/cups/patches/907-cve-2008-5286.patch [new file with mode: 0644]
net/cups/patches/908-cve-2009-0163.patch [new file with mode: 0644]
net/cups/patches/909-cve-2009-0164.patch [new file with mode: 0644]

diff --git a/net/cups/patches/901-cve-2008-1722.patch b/net/cups/patches/901-cve-2008-1722.patch
new file mode 100644 (file)
index 0000000..8fca14c
--- /dev/null
@@ -0,0 +1,72 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-1722
+
+--- a/filter/image-png.c
++++ b/filter/image-png.c
+@@ -3,7 +3,7 @@
+  *
+  *   PNG image routines for the Common UNIX Printing System (CUPS).
+  *
+- *   Copyright 2007 by Apple Inc.
++ *   Copyright 2007-2008 by Apple Inc.
+  *   Copyright 1993-2007 by Easy Software Products.
+  *
+  *   These coded instructions, statements, and computer programs are the
+@@ -170,16 +170,56 @@ _cupsImageReadPNG(
+     * Interlaced images must be loaded all at once...
+     */
++    size_t bufsize;                   /* Size of buffer */
++
++
+     if (color_type == PNG_COLOR_TYPE_GRAY ||
+       color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+-      in = malloc(img->xsize * img->ysize);
++    {
++      bufsize = img->xsize * img->ysize;
++
++      if ((bufsize / img->ysize) != img->xsize)
++      {
++      fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
++              (unsigned)width, (unsigned)height);
++      fclose(fp);
++      return (1);
++      }
++    }
+     else
+-      in = malloc(img->xsize * img->ysize * 3);
++    {
++      bufsize = img->xsize * img->ysize * 3;
++
++      if ((bufsize / (img->ysize * 3)) != img->xsize)
++      {
++      fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
++              (unsigned)width, (unsigned)height);
++      fclose(fp);
++      return (1);
++      }
++    }
++
++    in = malloc(bufsize);
+   }
+   bpp = cupsImageGetDepth(img);
+   out = malloc(img->xsize * bpp);
++  if (!in || !out)
++  {
++    fputs("DEBUG: Unable to allocate memory for PNG image!\n", stderr);
++
++    if (in)
++      free(in);
++
++    if (out)
++      free(out);
++
++    fclose(fp);
++
++    return (1);
++  }
++
+  /*
+   * Read the image, interlacing as needed...
+   */
diff --git a/net/cups/patches/902-cve-2008-3639.patch b/net/cups/patches/902-cve-2008-3639.patch
new file mode 100644 (file)
index 0000000..5910761
--- /dev/null
@@ -0,0 +1,40 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-3639
+
+--- a/filter/image-sgilib.c
++++ b/filter/image-sgilib.c
+@@ -640,13 +640,14 @@ read_rle8(FILE           *fp,            /* I - Fi
+     if (ch & 128)
+     {
+       for (i = 0; i < count; i ++, row ++, xsize --, length ++)
+-        *row = getc(fp);
++        if (xsize > 0)
++        *row = getc(fp);
+     }
+     else
+     {
+       ch = getc(fp);
+       length ++;
+-      for (i = 0; i < count; i ++, row ++, xsize --)
++      for (i = 0; i < count && xsize > 0; i ++, row ++, xsize --)
+         *row = ch;
+     }
+   }
+@@ -685,14 +686,15 @@ read_rle16(FILE           *fp,           /* I - F
+     if (ch & 128)
+     {
+       for (i = 0; i < count; i ++, row ++, xsize --, length ++)
+-        *row = getshort(fp);
++        if (xsize > 0)
++        *row = getshort(fp);
+     }
+     else
+     {
+       ch = getshort(fp);
+       length ++;
+-      for (i = 0; i < count; i ++, row ++, xsize --)
+-        *row = ch;
++      for (i = 0; i < count && xsize > 0; i ++, row ++, xsize --)
++      *row = ch;
+     }
+   }
diff --git a/net/cups/patches/903-cve-2008-3640.patch b/net/cups/patches/903-cve-2008-3640.patch
new file mode 100644 (file)
index 0000000..6f218b9
--- /dev/null
@@ -0,0 +1,83 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-3640
+
+--- a/filter/textcommon.c
++++ b/filter/textcommon.c
+@@ -3,7 +3,7 @@
+  *
+  *   Common text filter routines for the Common UNIX Printing System (CUPS).
+  *
+- *   Copyright 2007 by Apple Inc.
++ *   Copyright 2007-2008 by Apple Inc.
+  *   Copyright 1997-2007 by Easy Software Products.
+  *
+  *   These coded instructions, statements, and computer programs are the
+@@ -605,14 +605,38 @@ TextMain(const char *name,       /* I - Name o
+                 !strcasecmp(val, "yes");
+   if ((val = cupsGetOption("columns", num_options, options)) != NULL)
++  {
+     PageColumns = atoi(val);
++    if (PageColumns < 1)
++    {
++      _cupsLangPrintf(stderr, _("ERROR: Bad columns value %d!\n"), PageColumns);
++      return (1);
++    }
++  }
++
+   if ((val = cupsGetOption("cpi", num_options, options)) != NULL)
++  {
+     CharsPerInch = atof(val);
++    if (CharsPerInch <= 0.0)
++    {
++      _cupsLangPrintf(stderr, _("ERROR: Bad cpi value %f!\n"), CharsPerInch);
++      return (1);
++    }
++  }
++
+   if ((val = cupsGetOption("lpi", num_options, options)) != NULL)
++  {
+     LinesPerInch = atof(val);
++    if (LinesPerInch <= 0.0)
++    {
++      _cupsLangPrintf(stderr, _("ERROR: Bad lpi value %f!\n"), LinesPerInch);
++      return (1);
++    }
++  }
++
+   if (PrettyPrint)
+     PageTop -= 216.0f / LinesPerInch;
+--- a/filter/texttops.c
++++ b/filter/texttops.c
+@@ -173,6 +173,14 @@ WriteProlog(const char *title,            /* I - T
+   SizeColumns = (PageRight - PageLeft) / 72.0 * CharsPerInch;
+   SizeLines   = (PageTop - PageBottom) / 72.0 * LinesPerInch;
++  if (SizeColumns <= 0 || SizeColumns > 32767 ||
++      SizeLines <= 0 || SizeLines > 32767)
++  {
++    _cupsLangPrintf(stderr, _("ERROR: Unable to print %dx%d text page!\n"),
++                    SizeColumns, SizeLines);
++    exit(1);
++  }
++
+   Page    = calloc(sizeof(lchar_t *), SizeLines);
+   Page[0] = calloc(sizeof(lchar_t), SizeColumns * SizeLines);
+   for (i = 1; i < SizeLines; i ++)
+@@ -187,6 +195,13 @@ WriteProlog(const char *title,            /* I - T
+   else
+     ColumnWidth = SizeColumns;
++  if (ColumnWidth <= 0)
++  {
++    _cupsLangPrintf(stderr, _("ERROR: Unable to print %d text columns!\n"),
++                    PageColumns);
++    exit(1);
++  }
++
+  /*
+   * Output the DSC header...
+   */
diff --git a/net/cups/patches/904-cve-2008-3641.patch b/net/cups/patches/904-cve-2008-3641.patch
new file mode 100644 (file)
index 0000000..97addd4
--- /dev/null
@@ -0,0 +1,129 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-3641
+
+--- a/filter/hpgl-attr.c
++++ b/filter/hpgl-attr.c
+@@ -197,8 +197,18 @@ NP_number_pens(int     num_params,        /* I 
+   if (num_params == 0)
+     PenCount = 8;
+-  else if (num_params == 1 && params[0].value.number <= 1024)
+-    PenCount = (int)params[0].value.number;
++  else if (num_params == 1)
++  {
++    if (params[0].value.number < 1 || params[0].value.number > MAX_PENS)
++    {
++      fprintf(stderr,
++            "DEBUG: HP-GL/2 \'NP\' command with invalid number of "
++            "pens (%d)!\n", (int)params[0].value.number);
++      PenCount = 8;
++    }
++    else
++      PenCount = (int)params[0].value.number;
++  }
+   else
+     fprintf(stderr,
+             "DEBUG: HP-GL/2 \'NP\' command with invalid number of "
+@@ -235,7 +245,7 @@ PC_pen_color(int     num_params,   /* I - 
+   if (num_params == 0)
+   {
+-    for (i = 0; i <= PenCount; i ++)
++    for (i = 0; i < PenCount; i ++)
+       if (i < 8)
+       {
+         Pens[i].rgb[0] = standard_colors[i][0];
+@@ -256,7 +266,14 @@ PC_pen_color(int     num_params,  /* I - 
+   }
+   else if (num_params == 1 || num_params == 4)
+   {
+-    i = (int)params[0].value.number;
++    i = (int)params[0].value.number - 1;
++
++    if (i < 0 || i >= PenCount)
++    {
++      fprintf(stderr,
++              "DEBUG: HP-GL/2 \'PC\' command with invalid pen (%d)!\n", i + 1);
++      return;
++    }
+     if (num_params == 1)
+     {
+@@ -330,7 +347,15 @@ PW_pen_width(int     num_params,  /* I - 
+   if (num_params == 2)
+   {
+-    pen = (int)params[1].value.number;
++    pen = (int)params[1].value.number - 1;
++
++    if (pen < 0 || pen >= PenCount)
++    {
++      fprintf(stderr,
++              "DEBUG: HP-GL/2 \'PW\' command with invalid pen (%d)!\n",
++            pen + 1);
++      return;
++    }
+     Pens[pen].width = w;
+@@ -345,7 +370,7 @@ PW_pen_width(int     num_params,   /* I - 
+     * Set width for all pens...
+     */
+-    for (pen = 0; pen <= PenCount; pen ++)
++    for (pen = 0; pen < PenCount; pen ++)
+       Pens[pen].width = w;
+     if (PageDirty)
+@@ -399,14 +424,16 @@ SP_select_pen(int     num_params,        /* I -
+               param_t *params)                /* I - Parameters */
+ {
+   if (num_params == 0)
+-    PenNumber = 1;
+-  else if (params[0].value.number <= PenCount)
+-    PenNumber = (int)params[0].value.number;
+-  else
++    PenNumber = 0;
++  else if (num_params > 1)
+     fprintf(stderr,
+-            "DEBUG: HP-GL/2 \'SP\' command with invalid number or value "
+-          "of parameters (%d, %d)!\n", num_params,
++            "DEBUG: HP-GL/2 \'SP\' command with invalid number of parameters "
++          "(%d)!\n", num_params);
++  else if (params[0].value.number <= 0 || params[0].value.number >= PenCount)
++    fprintf(stderr, "DEBUG: HP-GL/2 \'SP\' command with invalid pen (%d)!\n",
+           (int)params[0].value.number);
++  else
++    PenNumber = (int)params[0].value.number - 1;
+   if (PageDirty)
+     printf("%.3f %.3f %.3f %.2f SP\n", Pens[PenNumber].rgb[0],
+--- a/filter/hpgltops.h
++++ b/filter/hpgltops.h
+@@ -26,6 +26,14 @@
+ #  define M_PI        3.14159265358979323846
+ #endif /* M_PI */
++
++/*
++ * Maximum number of pens we emulate...
++ */
++
++#define MAX_PENS      1024
++
++
+ /*
+  * Parameter value structure...
+  */
+@@ -108,10 +116,10 @@ VAR float        PenPosition[2]  VALUE2(0.0f, 0.
+                                               /* Current pen position */
+               PenScaling      VALUE(1.0f),    /* Pen width scaling factor */
+               PenWidth        VALUE(1.0f);    /* Default pen width */
+-VAR pen_t     Pens[1024];                     /* State of each pen */
++VAR pen_t     Pens[MAX_PENS];                 /* State of each pen */
+ VAR int               PenMotion       VALUE(0),       /* 0 = absolute, 1 = relative */
+               PenValid        VALUE(0),       /* 1 = valid position, 0 = undefined */
+-              PenNumber       VALUE(1),       /* Current pen number */
++              PenNumber       VALUE(0),       /* Current pen number */
+               PenCount        VALUE(8),       /* Number of pens */
+               PenDown         VALUE(0),       /* 0 = pen up, 1 = pen down */
+               PolygonMode     VALUE(0),       /* Drawing polygons? */
diff --git a/net/cups/patches/905-cve-2008-5183.patch b/net/cups/patches/905-cve-2008-5183.patch
new file mode 100644 (file)
index 0000000..611312c
--- /dev/null
@@ -0,0 +1,168 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-5183
+
+--- a/scheduler/ipp.c
++++ b/scheduler/ipp.c
+@@ -2027,24 +2027,25 @@ add_job_subscriptions(
+     if (mask == CUPSD_EVENT_NONE)
+       mask = CUPSD_EVENT_JOB_COMPLETED;
+-    sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job, recipient,
+-                               0);
++    if ((sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job,
++                                    recipient, 0)) != NULL)
++    {
++      sub->interval = interval;
+-    sub->interval = interval;
++      cupsdSetString(&sub->owner, job->username);
+-    cupsdSetString(&sub->owner, job->username);
++      if (user_data)
++      {
++      sub->user_data_len = user_data->values[0].unknown.length;
++      memcpy(sub->user_data, user_data->values[0].unknown.data,
++             sub->user_data_len);
++      }
+-    if (user_data)
+-    {
+-      sub->user_data_len = user_data->values[0].unknown.length;
+-      memcpy(sub->user_data, user_data->values[0].unknown.data,
+-             sub->user_data_len);
++      ippAddSeparator(con->response);
++      ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
++                  "notify-subscription-id", sub->id);
+     }
+-    ippAddSeparator(con->response);
+-    ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
+-                  "notify-subscription-id", sub->id);
+-
+     if (attr)
+       attr = attr->next;
+   }
+@@ -5417,7 +5418,12 @@ create_subscription(
+     else
+       job = NULL;
+-    sub = cupsdAddSubscription(mask, printer, job, recipient, 0);
++    if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL)
++    {
++      send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS,
++                    _("There are too many subscriptions."));
++      return;
++    }
+     if (job)
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
+--- a/scheduler/subscriptions.c
++++ b/scheduler/subscriptions.c
+@@ -341,8 +341,54 @@ cupsdAddSubscription(
+   * Limit the number of subscriptions...
+   */
+-  if (cupsArrayCount(Subscriptions) >= MaxSubscriptions)
++  if (MaxSubscriptions > 0 && cupsArrayCount(Subscriptions) >= MaxSubscriptions)
++  {
++    cupsdLogMessage(CUPSD_LOG_DEBUG,
++                    "cupsdAddSubscription: Reached MaxSubscriptions %d",
++                  MaxSubscriptions);
+     return (NULL);
++  }
++
++  if (MaxSubscriptionsPerJob > 0 && job)
++  {
++    int       count;                          /* Number of job subscriptions */
++
++    for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
++             count = 0;
++         temp;
++       temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
++      if (temp->job == job)
++        count ++;
++
++    if (count >= MaxSubscriptionsPerJob)
++    {
++      cupsdLogMessage(CUPSD_LOG_DEBUG,
++                    "cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
++                    "for job #%d", MaxSubscriptionsPerJob, job->id);
++      return (NULL);
++    }
++  }
++
++  if (MaxSubscriptionsPerPrinter > 0 && dest)
++  {
++    int       count;                          /* Number of printer subscriptions */
++
++    for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
++             count = 0;
++         temp;
++       temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
++      if (temp->dest == dest)
++        count ++;
++
++    if (count >= MaxSubscriptionsPerPrinter)
++    {
++      cupsdLogMessage(CUPSD_LOG_DEBUG,
++                    "cupsdAddSubscription: Reached "
++                    "MaxSubscriptionsPerPrinter %d for %s",
++                    MaxSubscriptionsPerPrinter, dest->name);
++      return (NULL);
++    }
++  }
+  /*
+   * Allocate memory for this subscription...
+@@ -758,7 +804,6 @@ cupsdLoadAllSubscriptions(void)
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Syntax error on line %d of subscriptions.conf.",
+                     linenum);
+-      break;
+     }
+     else if (!strcasecmp(line, "Events"))
+     {
+--- a/test/4.4-subscription-ops.test
++++ b/test/4.4-subscription-ops.test
+@@ -116,6 +116,32 @@
+       EXPECT notify-events
+       DISPLAY notify-events
+ }
++{
++      # The name of the test...
++      NAME "Check MaxSubscriptions limits"
++
++      # The operation to use
++      OPERATION Create-Printer-Subscription
++      RESOURCE /
++
++      # The attributes to send
++      GROUP operation
++      ATTR charset attributes-charset utf-8
++      ATTR language attributes-natural-language en
++      ATTR uri printer-uri $method://$hostname:$port/printers/Test1
++
++        GROUP subscription
++      ATTR uri notify-recipient-uri testnotify://
++      ATTR keyword notify-events printer-state-changed
++      ATTR integer notify-lease-duration 5
++
++      # What statuses are OK?
++      STATUS client-error-too-many-subscriptions
++
++      # What attributes do we expect?
++      EXPECT attributes-charset
++      EXPECT attributes-natural-language
++}
+ #
+ # End of "$Id: 4.4-subscription-ops.test 6635 2007-07-09 20:34:48Z mike $"
+--- a/test/run-stp-tests.sh
++++ b/test/run-stp-tests.sh
+@@ -303,6 +303,7 @@ FontPath /tmp/cups-$user/share/fonts
+ DocumentRoot $root/doc
+ RequestRoot /tmp/cups-$user/spool
+ TempDir /tmp/cups-$user/spool/temp
++MaxSubscriptions 3
+ MaxLogSize 0
+ AccessLog /tmp/cups-$user/log/access_log
+ ErrorLog /tmp/cups-$user/log/error_log
diff --git a/net/cups/patches/906-cve-2008-5184.patch b/net/cups/patches/906-cve-2008-5184.patch
new file mode 100644 (file)
index 0000000..b2fa47f
--- /dev/null
@@ -0,0 +1,58 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-5184
+
+--- a/cgi-bin/admin.c
++++ b/cgi-bin/admin.c
+@@ -309,6 +309,16 @@ do_add_rss_subscription(http_t *http)     /*
+   }
+  /*
++  * Make sure we have a username...
++  */
++
++  if ((user = getenv("REMOTE_USER")) == NULL)
++  {
++    puts("Status: 401\n");
++    exit(0);
++  }
++
++ /*
+   * Validate the subscription name...
+   */
+@@ -352,9 +362,6 @@ do_add_rss_subscription(http_t *http)      /*
+     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+                  NULL, printer_uri);
+-  if ((user = getenv("REMOTE_USER")) == NULL)
+-    user = "guest";
+-
+   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+                NULL, user);
+@@ -1269,6 +1276,16 @@ do_cancel_subscription(http_t *http)/* I
+   }
+  /*
++  * Require a username...
++  */
++
++  if ((user = getenv("REMOTE_USER")) == NULL)
++  {
++    puts("Status: 401\n");
++    exit(0);
++  }
++
++ /*
+   * Cancel the subscription...
+   */
+@@ -1279,9 +1296,6 @@ do_cancel_subscription(http_t *http)/* I
+   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
+                 "notify-subscription-id", id);
+-  if ((user = getenv("REMOTE_USER")) == NULL)
+-    user = "guest";
+-
+   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+                NULL, user);
diff --git a/net/cups/patches/907-cve-2008-5286.patch b/net/cups/patches/907-cve-2008-5286.patch
new file mode 100644 (file)
index 0000000..0c07792
--- /dev/null
@@ -0,0 +1,22 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-5286
+
+--- a/filter/image-png.c
++++ b/filter/image-png.c
+@@ -178,7 +178,7 @@ _cupsImageReadPNG(
+     {
+       bufsize = img->xsize * img->ysize;
+-      if ((bufsize / img->ysize) != img->xsize)
++      if ((bufsize / img->xsize) != img->ysize)
+       {
+       fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
+               (unsigned)width, (unsigned)height);
+@@ -190,7 +190,7 @@ _cupsImageReadPNG(
+     {
+       bufsize = img->xsize * img->ysize * 3;
+-      if ((bufsize / (img->ysize * 3)) != img->xsize)
++      if ((bufsize / (img->xsize * 3)) != img->ysize)
+       {
+       fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
+               (unsigned)width, (unsigned)height);
diff --git a/net/cups/patches/908-cve-2009-0163.patch b/net/cups/patches/908-cve-2009-0163.patch
new file mode 100644 (file)
index 0000000..ccf5769
--- /dev/null
@@ -0,0 +1,15 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-0163
+
+--- a/filter/image-private.h
++++ b/filter/image-private.h
+@@ -41,8 +41,8 @@
+ #  define CUPS_IMAGE_MAX_WIDTH        0x07ffffff
+                                       /* 2^27-1 to allow for 15-channel data */
+-#  define CUPS_IMAGE_MAX_HEIGHT       0x7fffffff
+-                                      /* 2^31-1 */
++#  define CUPS_IMAGE_MAX_HEIGHT       0x3fffffff
++                                      /* 2^30-1 */
+ #  define CUPS_TILE_SIZE      256     /* 256x256 pixel tiles */
+ #  define CUPS_TILE_MINIMUM   10      /* Minimum number of tiles */
diff --git a/net/cups/patches/909-cve-2009-0164.patch b/net/cups/patches/909-cve-2009-0164.patch
new file mode 100644 (file)
index 0000000..0cbd067
--- /dev/null
@@ -0,0 +1,575 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-0164
+
+--- a/cups/http-addr.c
++++ b/cups/http-addr.c
+@@ -149,7 +149,7 @@ httpAddrLocalhost(
+ #endif /* AF_LOCAL */
+   if (addr->addr.sa_family == AF_INET &&
+-      ntohl(addr->ipv4.sin_addr.s_addr) == 0x7f000001)
++      (ntohl(addr->ipv4.sin_addr.s_addr) & 0xff000000) == 0x7f000000)
+     return (1);
+   return (0);
+--- a/doc/help/ref-cupsd-conf.html.in
++++ b/doc/help/ref-cupsd-conf.html.in
+@@ -2477,6 +2477,37 @@ administrator email address is <CODE>roo
+ HREF="#ServerName"><CODE>ServerName</CODE></A>.</P>
++<H2 CLASS="title"><SPAN CLASS="info">CUPS 1.3.10</SPAN><A NAME="ServerAlias">ServerAlias</A></H2>
++
++<H3>Examples</H3>
++
++<PRE CLASS="command">
++ServerAlias althost
++ServerAlias althost.foo.com
++ServerAlias althost.bar.com
++ServerAlias *
++</PRE>
++
++<H3>Description</H3>
++
++<P>The <CODE>ServerAlias</CODE> directive specifies alternate names that the
++server is known by. By default it contains a list of all aliases associated
++with the <A HREF="#ServerName"><CODE>ServerName</CODE></A>. The special name
++"*" can be used to allow any hostname when accessing CUPS via an external
++network interfaces.</P>
++
++<BLOCKQUOTE><B>Note</B>
++
++<P>The <CODE>ServerAlias</CODE> directive is used for HTTP Host header
++validation when clients connect to the scheduler from external interfaces.
++Using the special name "*" can expose your system to known browser-based
++DNS rebinding attacks, even when accessing sites through a firewall. If the
++auto-discovery of alternate names does not work, we recommend listing each
++alternate name with a ServerAlias directive instead of using "*".</P>
++
++</BLOCKQUOTE>
++
++
+ <H2 CLASS="title"><A NAME="ServerBin">ServerBin</A></H2>
+ <H3>Examples</H3>
+--- a/man/cupsd.conf.man.in
++++ b/man/cupsd.conf.man.in
+@@ -540,6 +540,11 @@ ServerAdmin user@domain.com
+ .br
+ Specifies the email address of the server administrator.
+ .TP 5
++ServerAlias hostname
++.br
++Specifies an alternate name that the server is known by. The special name "*"
++allows any name to be used.
++.TP 5
+ ServerBin directory
+ .br
+ Specifies the directory where backends, CGIs, daemons, and filters may
+--- a/scheduler/client.c
++++ b/scheduler/client.c
+@@ -100,6 +100,7 @@ static int         make_certificate(cupsd_clien
+ #endif /* HAVE_SSL */
+ static int            pipe_command(cupsd_client_t *con, int infile, int *outfile,
+                                    char *command, char *options, int root);
++static int            valid_host(cupsd_client_t *con);
+ static int            write_file(cupsd_client_t *con, http_status_t code,
+                                  char *filename, char *type,
+                                  struct stat *filestats);
+@@ -259,16 +260,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)
+     * Map accesses from the same host to the server name.
+     */
+-    for (addr = ServerAddrs; addr; addr = addr->next)
+-      if (httpAddrEqual(con->http.hostaddr, &(addr->addr)))
+-        break;
+-
+-    if (addr)
+-    {
+-      strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname));
+-      hostname = con->http.hostname;
+-    }
+-    else if (HostNameLookups)
++    if (HostNameLookups)
+       hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname,
+                                 sizeof(con->http.hostname));
+     else
+@@ -1066,6 +1058,23 @@ cupsdReadClient(cupsd_client_t *con)    /* 
+       return;
+       }
+     }
++    else if (!valid_host(con))
++    {
++     /*
++      * Access to localhost must use "localhost" or the corresponding IPv4
++      * or IPv6 values in the Host: field.
++      */
++
++      cupsdLogMessage(CUPSD_LOG_WARN,
++                      "Request from \"%s\" using invalid Host: field \"%s\"",
++                    con->http.hostname, con->http.fields[HTTP_FIELD_HOST]);
++
++      if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
++      {
++      cupsdCloseClient(con);
++      return;
++      }
++    }
+     else if (con->operation == HTTP_OPTIONS)
+     {
+      /*
+@@ -4760,6 +4769,137 @@ pipe_command(cupsd_client_t *con,      /* I -
+ /*
++ * 'valid_host()' - Is the Host: field valid?
++ */
++
++static int                            /* O - 1 if valid, 0 if not */
++valid_host(cupsd_client_t *con)               /* I - Client connection */
++{
++  cupsd_alias_t       *a;                     /* Current alias */
++  cupsd_netif_t       *netif;                 /* Current network interface */
++  const char  *host,                  /* Host field */
++              *end;                   /* End character */
++
++
++  host = con->http.fields[HTTP_FIELD_HOST];
++
++  if (httpAddrLocalhost(con->http.hostaddr))
++  {
++   /*
++    * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical
++    * addresses when accessing CUPS via the loopback interface...
++    */
++
++    return (!strcasecmp(host, "localhost") ||
++            !strncasecmp(host, "localhost:", 10) ||
++          !strcasecmp(host, "localhost.") ||
++            !strncasecmp(host, "localhost.:", 11) ||
++#ifdef __linux
++          !strcasecmp(host, "localhost.localdomain") ||
++            !strncasecmp(host, "localhost.localdomain:", 22) ||
++#endif /* __linux */
++            !strcmp(host, "127.0.0.1") ||
++          !strncmp(host, "127.0.0.1:", 10) ||
++          !strcmp(host, "[::1]") ||
++          !strncmp(host, "[::1]:", 6));
++  }
++
++#ifdef HAVE_DNSSD
++ /*
++  * Check if the hostname is something.local (Bonjour); if so, allow it.
++  */
++
++  if ((end = strrchr(host, '.')) != NULL &&
++      (!strcasecmp(end, ".local") || !strncasecmp(end, ".local:", 7)))
++    return (1);
++#endif /* HAVE_DNSSD */
++
++ /*
++  * Check for (alias) name matches...
++  */
++
++  for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias);
++       a;
++       a = (cupsd_alias_t *)cupsArrayNext(ServerAlias))
++  {
++   /*
++    * "ServerAlias *" allows all host values through...
++    */
++
++    if (!strcmp(a->name, "*"))
++      return (1);
++
++    if (!strncasecmp(host, a->name, a->namelen))
++    {
++     /*
++      * Prefix matches; check the character at the end - it must be either
++      * ":" or nul...
++      */
++
++      end = host + a->namelen;
++
++      if (!*end || *end == ':')
++        return (1);
++    }
++  }
++
++ /*
++  * Check for interface hostname matches...
++  */
++
++  for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
++       netif;
++       netif = (cupsd_netif_t *)cupsArrayNext(NetIFList))
++  {
++    if (!strncasecmp(host, netif->hostname, netif->hostlen))
++    {
++     /*
++      * Prefix matches; check the character at the end - it must be either
++      * ":" or nul...
++      */
++
++      end = host + netif->hostlen;
++
++      if (!*end || *end == ':')
++        return (1);
++    }
++  }
++
++ /*
++  * Check if the hostname is an IP address...
++  */
++
++  if (isdigit(*host & 255) || *host == '[')
++  {
++   /*
++    * Possible IPv4/IPv6 address...
++    */
++
++    char      temp[1024],             /* Temporary string */
++              *ptr;                   /* Pointer into temporary string */
++    http_addrlist_t *addrlist;                /* List of addresses */
++
++
++    strlcpy(temp, host, sizeof(temp));
++    if ((ptr = strrchr(temp, ':')) != NULL && !strchr(ptr, ']'))
++      *ptr = '\0';                    /* Strip :port from host value */
++
++    if ((addrlist = httpAddrGetList(temp, AF_UNSPEC, NULL)) != NULL)
++    {
++     /*
++      * Good IPv4/IPv6 address...
++      */
++
++      httpAddrFreeList(addrlist);
++      return (1);
++    }
++  }
++
++  return (0);
++}
++
++
++/*
+  * 'write_file()' - Send a file via HTTP.
+  */
+--- a/scheduler/client.h
++++ b/scheduler/client.h
+@@ -95,8 +95,6 @@ VAR time_t           ListeningPaused VALUE(0);
+                                       /* Time when listening was paused */
+ VAR cups_array_t      *Clients        VALUE(NULL);
+                                       /* HTTP clients */
+-VAR http_addrlist_t   *ServerAddrs    VALUE(NULL);
+-                                      /* Server address(es) */
+ VAR char              *ServerHeader   VALUE(NULL);
+                                       /* Server header in requests */
+ VAR int                       CGIPipes[2]     VALUE2(-1,-1);
+--- a/scheduler/conf.c
++++ b/scheduler/conf.c
+@@ -187,6 +187,9 @@ static const unsigned      zeros[4] =
+ /*
+  * Local functions...
+  */
++
++static void           add_alias(const char *name);
++static void           free_aliases(void);
+ static http_addrlist_t        *get_address(const char *value, int defport);
+ static int            get_addr_and_mask(const char *value, unsigned *ip,
+                                         unsigned *mask);
+@@ -253,7 +256,8 @@ cupsdCheckPermissions(
+         return (-1);
+       }
+-      dir_created = 1;
++      dir_created      = 1;
++      fileinfo.st_mode = mode | S_IFDIR;
+     }
+     else
+       return (create_dir ? -1 : 1);
+@@ -395,12 +399,16 @@ cupsdReadConfiguration(void)
+   cupsdDeleteAllListeners();
++  RemoteAccessEnabled = 0;
++
+  /*
+   * String options...
+   */
+-  cupsdSetString(&ServerName, httpGetHostname(NULL, temp, sizeof(temp)));
+-  cupsdSetStringf(&ServerAdmin, "root@%s", temp);
++  free_aliases();
++
++  cupsdClearString(&ServerName);
++  cupsdClearString(&ServerAdmin);
+   cupsdSetString(&ServerBin, CUPS_SERVERBIN);
+   cupsdSetString(&RequestRoot, CUPS_REQUESTS);
+   cupsdSetString(&CacheDir, CUPS_CACHEDIR);
+@@ -608,15 +616,69 @@ cupsdReadConfiguration(void)
+   RunUser = getuid();
++  cupsdLogMessage(CUPSD_LOG_INFO, "Remote access is %s.",
++                  RemoteAccessEnabled ? "enabled" : "disabled");
++
+  /*
+   * See if the ServerName is an IP address...
+   */
++  if (!ServerName)
++  {
++    if (gethostname(temp, sizeof(temp)))
++    {
++      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get hostname: %s",
++                      strerror(errno));
++      strlcpy(temp, "localhost", sizeof(temp));
++    }
++
++    cupsdSetString(&ServerName, temp);
++    add_alias(temp);
++
++    if (HostNameLookups || RemoteAccessEnabled)
++    {
++      struct hostent  *host;          /* Host entry to get FQDN */
++
++      if ((host = gethostbyname(temp)) != NULL)
++      {
++        if (strcasecmp(temp, host->h_name))
++        {
++        cupsdSetString(&ServerName, host->h_name);
++        add_alias(host->h_name);
++      }
++
++        if (host->h_aliases)
++      {
++          for (i = 0; host->h_aliases[i]; i ++)
++          if (strcasecmp(temp, host->h_aliases[i]))
++            add_alias(host->h_aliases[i]);
++      }
++      }
++    }
++
++   /*
++    * Make sure we have the base hostname added as an alias, too!
++    */
++
++    if ((slash = strchr(temp, '.')) != NULL)
++    {
++      *slash = '\0';
++      add_alias(temp);
++    }
++  }
++
+   for (slash = ServerName; isdigit(*slash & 255) || *slash == '.'; slash ++);
+   ServerNameIsIP = !*slash;
+  /*
++  * Make sure ServerAdmin is initialized...
++  */
++
++  if (!ServerAdmin)
++    cupsdSetStringf(&ServerAdmin, "root@%s", ServerName);
++
++ /*
+   * Use the default system group if none was supplied in cupsd.conf...
+   */
+@@ -1227,6 +1289,52 @@ cupsdReadConfiguration(void)
+ /*
++ * 'add_alias()' - Add a ServerAlias.
++ */
++
++static void
++add_alias(const char *name)           /* I - Name to add */
++{
++  cupsd_alias_t       *a;                     /*  New alias */
++  size_t      namelen;                /* Length of name */
++
++
++  namelen = strlen(name);
++
++  if ((a = (cupsd_alias_t *)malloc(sizeof(cupsd_alias_t) + namelen)) == NULL)
++    return;
++
++  if (!ServerAlias)
++    ServerAlias = cupsArrayNew(NULL, NULL);
++
++  a->namelen = namelen;
++  strcpy(a->name, name);              /* OK since a->name is allocated */
++
++  cupsArrayAdd(ServerAlias, a);
++}
++
++
++/*
++ * 'free_aliases()' - Free all of the ServerAlias entries.
++ */
++
++static void
++free_aliases(void)
++{
++  cupsd_alias_t       *a;                     /* Current alias */
++
++
++  for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias);
++       a;
++       a = (cupsd_alias_t *)cupsArrayNext(ServerAlias))
++    free(a);
++
++  cupsArrayDelete(ServerAlias);
++  ServerAlias = NULL;
++}
++
++
++/*
+  * 'get_address()' - Get an address + port number from a line.
+  */
+@@ -2260,6 +2368,9 @@ read_configuration(cups_file_t *fp)      /* I
+ #endif /* AF_LOCAL */
+       cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d (IPv4)", temp,
+                         ntohs(lis->address.ipv4.sin_port));
++
++        if (!httpAddrLocalhost(&(lis->address)))
++        RemoteAccessEnabled = 1;
+       }
+      /*
+@@ -2993,6 +3104,8 @@ read_configuration(cups_file_t *fp)      /* I
+           break;
+       }
+     }
++    else if (!strcasecmp(line, "ServerAlias"))
++      add_alias(value);
+     else if (!strcasecmp(line, "SetEnv"))
+     {
+      /*
+--- a/scheduler/conf.h
++++ b/scheduler/conf.h
+@@ -46,6 +46,17 @@ typedef enum
+ /*
++ * ServerAlias data...
++ */
++
++typedef struct
++{
++  size_t      namelen;                /* Length of alias name */
++  char                name[1];                /* Alias name */
++} cupsd_alias_t;
++
++
++/*
+  * Globals...
+  */
+@@ -65,7 +76,12 @@ VAR char            *ConfigurationFile      VALUE(NULL)
+                                       /* Directory for request files */
+                       *DocumentRoot           VALUE(NULL);
+                                       /* Root directory for documents */
+-VAR int                       ServerNameIsIP          VALUE(0);
++VAR cups_array_t      *ServerAlias            VALUE(NULL);
++                                      /* Alias names for server */
++VAR int                       RemoteAccessEnabled     VALUE(0),
++                                      /* Are we listening on non-local addresses? */
++                      ServerNameIsIP          VALUE(0);
++                                      /* Is the ServerName an IP address? */
+ VAR int                       NumSystemGroups         VALUE(0);
+                                       /* Number of system group names */
+ VAR char              *SystemGroups[MAX_SYSTEM_GROUPS]
+--- a/scheduler/listen.c
++++ b/scheduler/listen.c
+@@ -143,18 +143,6 @@ cupsdStartListening(void)
+                   cupsArrayCount(Listeners));
+  /*
+-  * Get the server's IP address...
+-  */
+-
+-  if (ServerAddrs)
+-    httpAddrFreeList(ServerAddrs);
+-
+-  if ((ServerAddrs = httpAddrGetList(ServerName, AF_UNSPEC, NULL)) == NULL)
+-    cupsdLogMessage(CUPSD_LOG_ERROR,
+-                    "Unable to find IP address for server name \"%s\"!\n",
+-                  ServerName);
+-
+- /*
+   * Setup socket listeners...
+   */
+--- a/scheduler/network.c
++++ b/scheduler/network.c
+@@ -100,8 +100,8 @@ cupsdNetIFUpdate(void)
+   cupsd_netif_t               *temp;          /* New interface */
+   struct ifaddrs      *addrs,         /* Interface address list */
+                       *addr;          /* Current interface address */
+-  http_addrlist_t     *saddr;         /* Current server address */
+   char                        hostname[1024]; /* Hostname for address */
++  size_t              hostlen;        /* Length of hostname */
+  /*
+@@ -155,7 +155,7 @@ cupsdNetIFUpdate(void)
+     * Try looking up the hostname for the address as needed...
+     */
+-    if (HostNameLookups)
++    if (HostNameLookups || RemoteAccessEnabled)
+       httpAddrLookup((http_addr_t *)(addr->ifa_addr), hostname,
+                      sizeof(hostname));
+     else
+@@ -169,25 +169,16 @@ cupsdNetIFUpdate(void)
+       if (httpAddrLocalhost((http_addr_t *)(addr->ifa_addr)))
+         strcpy(hostname, "localhost");
+       else
+-      {
+-        for (saddr = ServerAddrs; saddr; saddr = saddr->next)
+-        if (httpAddrEqual((http_addr_t *)(addr->ifa_addr), &(saddr->addr)))
+-          break;
+-
+-      if (saddr)
+-          strlcpy(hostname, ServerName, sizeof(hostname));
+-      else
+-          httpAddrString((http_addr_t *)(addr->ifa_addr), hostname,
+-                       sizeof(hostname));
+-      }
++      httpAddrString((http_addr_t *)(addr->ifa_addr), hostname,
++                     sizeof(hostname));
+     }
+    /*
+     * Create a new address element...
+     */
+-    if ((temp = calloc(1, sizeof(cupsd_netif_t) +
+-                          strlen(hostname))) == NULL)
++    hostlen = strlen(hostname);
++    if ((temp = calloc(1, sizeof(cupsd_netif_t) + hostlen)) == NULL)
+       break;
+    /*
+@@ -195,6 +186,7 @@ cupsdNetIFUpdate(void)
+     */
+     strlcpy(temp->name, addr->ifa_name, sizeof(temp->name));
++    temp->hostlen = hostlen;
+     strcpy(temp->hostname, hostname); /* Safe because hostname is allocated */
+     if (addr->ifa_addr->sa_family == AF_INET)
+--- a/scheduler/network.h
++++ b/scheduler/network.h
+@@ -25,6 +25,7 @@ typedef struct cupsd_netif_s         /**** Netw
+   http_addr_t         address,        /* Network address */
+                       mask,           /* Network mask */
+                       broadcast;      /* Broadcast address */
++  size_t              hostlen;        /* Length of hostname */
+   char                        name[32],       /* Network interface name */
+                       hostname[1];    /* Hostname associated with interface */
+ } cupsd_netif_t;