Index: Makedefs.in
===================================================================
--- Makedefs.in (revision 8801)
+++ Makedefs.in (working copy)
@@ -124,7 +124,7 @@
BACKLIBS = @BACKLIBS@
BANNERTOPS = @BANNERTOPS@
CFLAGS = @CPPFLAGS@ @CFLAGS@
COMMONLIBS = @LIBS@
-CUPSDLIBS = @CUPSDLIBS@
+CUPSDLIBS = @CUPSDLIBS@ @LABELING_LIBS@
CXXFLAGS = @CPPFLAGS@ @CXXFLAGS@
CXXLIBS = @CXXLIBS@
DBUS_NOTIFIER = @DBUS_NOTIFIER@
===================================================================
--- config-scripts/cups-labeling.m4 (revision 0)
+++ config-scripts/cups-labeling.m4 (revision 0)
@@ -0,0 +1,38 @@
+dnl
+dnl Security Labeled environment support for the Common UNIX Printing System (CUPS).
+dnl
+dnl Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; version 2.
+dnl
+dnl This program is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software Foundation,
+dnl Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301 USA
+dnl
+
+AC_ARG_ENABLE(labeling, [ --enable-labeling enable support for labeled environments like Solaris Trusted Extensions and SELinux, default=auto])
+
+if test x"$enable_labeling" != xno; then
+ case "$uname" in
+ SunOS)
+ AC_CHECK_LIB(tsol, str_to_label, [LABELING_LIBS="-zlazyload -lbsm -ltsol -znolazyload" AC_SUBST(LABELING_LIBS)
+ AC_CHECK_HEADER(tsol/label.h)
+ AC_DEFINE(HAVE_TSOL)])
+ ;;
+ Linux)
+ AC_CHECK_LIB(selinux, getpeercon, [LABELING_LIBS="-lselinux" AC_SUBST(LABELING_LIBS)
+ AC_CHECK_HEADER(selinux/selinux.h)
+ AC_DEFINE(HAVE_SELINUX)])
+ ;;
+ *)
+ # All others
+ ;;
+ esac
+fi
Index: data/Makefile
===================================================================
--- data/Makefile (revision 8857)
+++ data/Makefile (working copy)
@@ -22,6 +22,7 @@
BANNERS = \
classified \
confidential \
+ labeled \
secret \
standard \
topsecret \
Index: data/labeled
===================================================================
--- data/labeled (revision 0)
+++ data/labeled (revision 0)
@@ -0,0 +1,4 @@
+#CUPS-BANNER
+Show job-id job-name job-originating-user-name job-originating-host-name job-billing
+Image images/cups.png
+
Index: configure.in
===================================================================
--- configure.in (revision 8857)
+++ configure.in (working copy)
@@ -41,6 +41,7 @@
sinclude(config-scripts/cups-pap.m4)
sinclude(config-scripts/cups-pdf.m4)
sinclude(config-scripts/cups-scripting.m4)
+sinclude(config-scripts/cups-labeling.m4)
INSTALL_LANGUAGES=""
UNINSTALL_LANGUAGES=""
Index: config.h.in
===================================================================
--- config.h.in.orig Thu Aug 12 21:11:46 2010
+++ config.h.in Tue Aug 16 18:03:17 2011
@@ -665,7 +665,17 @@
#undef HAVE_SYS_STATVFS_H
#undef HAVE_SYS_VFS_H
+/*
+ * Do we have Solaris Trusted Extensions support?
+ */
+#undef HAVE_TSOL
+
+/*
+ * Do we have SELinux support?
+ */
+#undef HAVE_SELINUX
+
#endif /* !_CUPS_CONFIG_H_ */
/*
Index: scheduler/ipp.c
===================================================================
--- scheduler/ipp.c.orig Thu Oct 7 01:37:44 2010
+++ scheduler/ipp.c Tue Aug 16 18:03:17 2011
@@ -856,7 +856,52 @@
return (0);
}
+static int
+compare_labels(char *label1, char *label2)
+{
+ int result = 0;
+ if ((label1 != NULL) && (label2 != NULL))
+ {
+#if defined(HAVE_TSOL)
+ if (is_system_labeled())
+ {
+ result = (strcmp(label1, label2) != 0);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "compare_labels(%s, %s): %d",
+ label1, label2, result);
+ }
+#elif defined(HAVE_SELINUX)
+#endif /* HAVE_TSOL || HAVE_SELINUX */
+ }
+
+ return(result);
+}
+
+static char *
+label_to_page_top_bottom_string(char *label)
+{
+ char *result = label;
+
+#if defined(HAVE_TSOL)
+ if (is_system_labeled())
+ {
+ m_label_t *sl = NULL;
+
+ if (str_to_label(label, &sl, USER_CLEAR, L_NO_CORRECTION, NULL) == 0)
+ {
+ (void) label_to_str(sl, &result, PRINTER_TOP_BOTTOM, DEF_NAMES);
+ if (result == NULL)
+ result = label;
+
+ m_label_free(sl);
+ }
+ }
+#endif
+
+ return (result);
+}
+
/*
* 'accept_jobs()' - Accept print jobs to a printer.
*/
@@ -1361,6 +1406,17 @@
filetype, filetype ? filetype->super : "none",
filetype ? filetype->type : "none");
+ /*
+ * Validate that the label associated with the connection is acceptable for
+ * printing on the printer.
+ */
+ if (cupsdInPrinterLabelRange(con->slabel, printer) == 0)
+ {
+ send_ipp_status(con, IPP_NOT_AUTHORIZED, _("label violation."));
+ return (NULL);
+ }
+
+
/*
* Check remote printing to non-shared printer...
*/
@@ -1620,6 +1676,19 @@
return (NULL);
}
+ /*
+ * Add the label to the job...
+ */
+ if (con->slabel)
+ {
+ job->slabel = strdup(con->slabel);
+ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "security-label",
+ NULL, job->slabel);
+ /* used by filters to add header/footer labels to output */
+ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "page-label",
+ NULL, label_to_page_top_bottom_string(job->slabel));
+ }
+
job->dtype = printer->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
CUPS_PRINTER_REMOTE);
job->attrs = con->request;
@@ -3761,6 +3830,15 @@
return;
}
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+ return;
+ }
+
/*
* See if the job has been completed...
*/
@@ -4129,6 +4207,16 @@
return;
}
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+ return;
+ }
+
+
/*
* See if the job is owned by the requesting user...
*/
@@ -6389,6 +6477,16 @@
send_ipp_status(con, IPP_NOT_FOUND, _("Job %d not found!"), jobid);
return;
}
+
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"),
+ jobid);
+ return;
+ }
}
else
job = NULL;
@@ -6936,6 +7034,15 @@
return;
}
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+ return;
+ }
+
/*
* Copy attributes...
*/
@@ -7156,6 +7263,14 @@
cupsdLoadJob(job);
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ continue;
+ }
+
if (!job->attrs)
{
cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d!",
@@ -8212,6 +8327,15 @@
return;
}
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+ return;
+ }
+
/*
* See if the job is owned by the requesting user...
*/
@@ -8466,6 +8590,14 @@
_("Job #%d does not exist!"), jobid);
return;
}
+ else if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+ return;
+ }
else
{
/*
@@ -8488,6 +8620,27 @@
return;
}
+ /*
+ * Validate that the label associated with the connection is acceptable for
+ * printing on the printer.
+ */
+ if (cupsdInPrinterLabelRange(con->slabel, dprinter) == 0)
+ {
+ send_ipp_status(con, IPP_NOT_AUTHORIZED, _("label violation."));
+ return;
+ }
+
+ /*
+ * Validate that the label associated with the job is acceptable for
+ * printing on the printer.
+ */
+ if (cupsdInPrinterLabelRange(job->slabel, dprinter) == 0)
+ {
+ send_ipp_status(con, IPP_NOT_AUTHORIZED, _("label violation."));
+ return;
+ }
+
+
/*
* Now move the job or jobs...
*/
@@ -9318,6 +9471,16 @@
return;
}
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+ return;
+ }
+
+
/*
* See if job is "held"...
*/
@@ -9544,6 +9707,15 @@
return;
}
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+ return;
+ }
+
/*
* See if job is in any of the "completed" states...
*/
@@ -9899,6 +10071,15 @@
return;
}
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+ return;
+ }
+
printer = cupsdFindDest(job->dest);
/*
@@ -10488,6 +10669,15 @@
return;
}
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ /*
+ * If the labels don't match, we can't tell them about it.
+ */
+ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+ return;
+ }
+
/*
* See if the job has been completed...
*/
@@ -10825,7 +11015,6 @@
/*
* Return a list of attributes that can be set via Set-Printer-Attributes.
*/
-
if ((attr = ippFindAttribute(con->request, "printer-location",
IPP_TAG_TEXT)) != NULL)
{
@@ -11584,6 +11773,11 @@
strlcpy(username, get_username(con), userlen);
+ if (compare_labels(con->slabel, job->slabel) != 0)
+ {
+ return (0);
+ }
+
/*
* Check the username against the owner...
*/
Index: scheduler/printers.c
===================================================================
--- scheduler/printers.c.orig Thu Sep 23 00:05:07 2010
+++ scheduler/printers.c Tue Aug 16 18:03:34 2011
@@ -1011,7 +1011,93 @@
p->users = NULL;
}
+#if defined(HAVE_TSOL)
+static char *
+log_tsol_str_to_label_error(char *s, int e)
+{
+ char *mesg = strerror(errno);
+ if (errno == EINVAL)
+ {
+ switch (e) {
+ case M_BAD_STRING:
+ mesg = "bad string";
+ break;
+ case M_BAD_LABEL:
+ mesg = "bad label";
+ break;
+ case M_OUTSIDE_AR:
+ mesg = "outside DIA Accreditation Range";
+ break;
+ }
+ }
+
+ return (mesg);
+}
+#endif /* HAVE_TSOL */
+
+/*
+ * 'cupsdInPrinterLabelRange()' - validate that the supplied slabel is in the
+ * label range of the supplied printer.
+ * 0 - failure, 1 - success
+ */
+int
+cupsdInPrinterLabelRange(char *slabel, cupsd_printer_t *p)
+{
+ /* unlabeled client objects are always in range. */
+ if (slabel == NULL)
+ return (1);
+
+ if (p == NULL) /* this should never happen */
+ return (0);
+
+#if defined(HAVE_TSOL)
+ if (is_system_labeled())
+ {
+ int in_range = 0;
+ int err = 0;
+ m_range_t *range;
+ m_label_t *sl = NULL;
+
+ if (p->device_uri == 0) /* this should never happen */
+ return (0);
+
+ if (str_to_label(slabel, &sl, USER_CLEAR, L_NO_CORRECTION, &err) < 0)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "str_to_label(%s): %s", slabel,
+ log_tsol_str_to_label_error(slabel, err));
+ return (0);
+ }
+
+ if ((range = getdevicerange((const char *)(p->device_uri))) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cupsdInPrinterLabelRange(%s, %s): getdevicerange(%s): %s",
+ slabel, p->name, p->device_uri, strerror(errno));
+ m_label_free(sl);
+ return (0);
+ }
+
+ /* is the client supplied object label is within the printer label range? */
+ in_range = blinrange(sl, range);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdInPrinterLabelRange(%s, %s): %d",
+ slabel, p->name, in_range);
+
+ m_label_free(sl);
+ m_label_free(range->lower_bound);
+ m_label_free(range->upper_bound);
+ free(range);
+
+ return (in_range);
+ }
+#elif defined(HAVE_SELINUX)
+#endif /* HAVE_TSOL || HAVE_SELINUX */
+
+ /* we should probably never get here */
+ return (0);
+}
+
/*
* 'cupsdLoadAllPrinters()' - Load printers from the printers.conf file.
*/
Index: scheduler/job.c
===================================================================
--- scheduler/job.c.orig Sun Oct 17 09:43:56 2010
+++ scheduler/job.c Tue Aug 16 18:03:43 2011
@@ -911,8 +911,14 @@
}
}
- if (Classification && !banner_page)
+ if (job->slabel != NULL)
{
+ snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
+ job->slabel);
+ envp[envc ++] = classification;
+ }
+ else if (Classification && !banner_page)
+ {
if ((attr = ippFindAttribute(job->attrs, "job-sheets",
IPP_TAG_NAME)) == NULL)
snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
@@ -1276,6 +1282,12 @@
job->num_files = 0;
}
+ if (job->slabel)
+ {
+ free(job->slabel);
+ job->slabel = NULL;
+ }
+
if (job->history)
free_job_history(job);
@@ -1562,6 +1574,10 @@
goto error;
}
+ if ((attr = ippFindAttribute(job->attrs, "security-label",
+ IPP_TAG_NAME)) != NULL)
+ cupsdSetString(&job->slabel, attr->values[0].string.text);
+
job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed",
IPP_TAG_INTEGER);
job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
@@ -3977,6 +3993,18 @@
if (!cupsdLoadJob(job))
return;
+ /*
+ * Verify that the job sensitivity label is in range of the printer.
+ */
+ if (cupsdInPrinterLabelRange(job->slabel, printer) == 0) {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "start_job(): job label outside of printer label range");
+ cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
+ "job label is outside of printer label range");
+ cupsdCheckJobs();
+ return;
+ }
+
cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL);
cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
Index: scheduler/printers.h
===================================================================
--- scheduler/printers.h.orig Wed Mar 31 03:37:33 2010
+++ scheduler/printers.h Tue Aug 16 18:03:51 2011
@@ -151,6 +151,8 @@
const char *username);
extern void cupsdFreePrinterUsers(cupsd_printer_t *p);
extern void cupsdFreeQuotas(cupsd_printer_t *p);
+extern int cupsdInPrinterLabelRange(char *slabel,
+ cupsd_printer_t *p);
extern void cupsdLoadAllPrinters(void);
extern void cupsdRenamePrinter(cupsd_printer_t *p,
const char *name);
Index: scheduler/job.h
===================================================================
--- scheduler/job.h (revision 8857)
+++ scheduler/job.h (working copy)
@@ -83,6 +83,8 @@
krb5_ccache ccache; /* Kerberos credential cache */
char *ccname; /* KRB5CCNAME environment variable */
#endif /* HAVE_GSSAPI */
+ char *slabel; /* security context for security
+ labeled environments */
};
typedef struct cupsd_joblog_s /**** Job log message ****/
Index: scheduler/client.c
===================================================================
--- scheduler/client.c.orig Sun Oct 17 09:43:56 2010
+++ scheduler/client.c Tue Aug 16 18:04:07 2011
@@ -90,7 +90,12 @@
# include <tcpd.h>
#endif /* HAVE_TCPD_H */
+#ifdef HAVE_TSOL
+#include <ucred.h>
+#include <tsol/label.h>
+#endif /* HAVE_TSOL */
+
/*
* Local functions...
*/
@@ -123,7 +128,40 @@
struct stat *filestats);
static void write_pipe(cupsd_client_t *con);
+#ifdef HAVE_TSOL
+/*
+ * Retrieve the sensitivity label from the peer connection.
+ */
+static int
+getpeerseclabel(int fd, char **label)
+{
+ if ((fd < 0) || (label == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ *label = NULL; /* default to unlabeled */
+
+ if (is_system_labeled()) {
+ ucred_t *cred = NULL;
+ m_label_t *slabel;
+
+ if (getpeerucred(fd, &cred) == -1)
+ return (-1);
+
+ slabel = ucred_getlabel(cred);
+ *label = NULL;
+ if (label_to_str(slabel, label, M_INTERNAL, DEF_NAMES) != 0)
+ cupsdLogMessage(CUPSD_LOG_ERROR, "getpeercon(%d, 0x%8.8x): %s",
+ fd, (int)label, strerror(errno));
+ ucred_free(cred);
+ }
+
+ return (0);
+}
+#endif /* HAVE_TSOL */
+
+
/*
* 'cupsdAcceptClient()' - Accept a new client.
*/
@@ -391,6 +429,21 @@
}
#endif /* HAVE_TCPD_H */
+#if defined(HAVE_TSOL) || defined(HAVE_SELINUX)
+ /*
+ * Get the sensitivity label from the peer connection.
+ */
+ if (getpeerseclabel(con->http.fd, &con->slabel))
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cupsdAcceptClient: failed to retrieve label from peer: %s",
+ con->http.hostname);
+ con->slabel = NULL;
+ }
+ else
+ cupsdLogMessage(CUPSD_LOG_INFO, "cupsdAcceptClient: label=%s", con->slabel);
+#endif /* HAVE_TSOL || HAVE_SELINUX */
+
#ifdef AF_INET6
if (con->http.hostaddr->addr.sa_family == AF_INET6)
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv6)",
@@ -680,6 +733,12 @@
}
}
+ if (con->slabel != NULL)
+ {
+ free(con->slabel);
+ con->slabel = NULL;
+ }
+
if (!partial)
{
/*
Index: scheduler/client.h
===================================================================
--- scheduler/client.h (revision 8857)
+++ scheduler/client.h (working copy)
@@ -64,6 +64,8 @@
#ifdef HAVE_AUTHORIZATION_H
AuthorizationRef authref; /* Authorization ref */
#endif /* HAVE_AUTHORIZATION_H */
+ char *slabel; /* security context for security
+ labeled environments */
};
#define HTTP(con) &((con)->http)
Index: scheduler/cupsd.h
===================================================================
--- scheduler/cupsd.h (revision 8857)
+++ scheduler/cupsd.h (working copy)
@@ -50,7 +50,19 @@
# include <CoreFoundation/CoreFoundation.h>
#endif /* HAVE_CDSASSL */
+#if defined(HAVE_TSOL)
+#include <tsol/label.h>
+#endif /* HAVE_TSOL */
+#if defined(HAVE_SELINUX)
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <selinux/avc.h>
+#include <selinux/flask.h>
+#include <selinux/av_permissions.h>
+#include <selinux/get_context_list.h>
+#endif /* HAVE_SELINUX */
+
/*
* Some OS's don't have hstrerror(), most notably Solaris...
*/