Index: Makedefs.in =================================================================== --- Makedefs.in (revision 8960) +++ Makedefs.in (working copy) @@ -125,7 +125,7 @@ BANNERTOPS = @BANNERTOPS@ CFLAGS = @CPPFLAGS@ @CFLAGS@ COMMONLIBS = @LIBS@ -CUPSDLIBS = @CUPSDLIBS@ +CUPSDLIBS = @CUPSDLIBS@ @LABELING_LIBS@ CXXFLAGS = @CPPFLAGS@ @CXXFLAGS@ CXXLIBS = @CXXLIBS@ DBUS_NOTIFIER = @DBUS_NOTIFIER@ Index: backend/usb-unix.c =================================================================== --- backend/usb-unix.c (revision 8960) +++ backend/usb-unix.c (working copy) @@ -30,7 +30,11 @@ #include +#ifdef __sun +#include +#endif + /* * Local functions... */ Index: config-scripts/cups-labeling.m4 =================================================================== --- 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 2009 by Sun Microsystems, Inc. +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 Trusted Solaris 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 8960) +++ 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 job-label +Image images/cups.png + Index: configure.in =================================================================== --- configure.in (revision 8960) +++ 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 (revision 8960) +++ config.h.in (working copy) @@ -625,7 +625,17 @@ #undef HAVE_TCPD_H +/* + * Do we have Trusted Solaris support? + */ +#undef HAVE_TSOL + +/* + * Do we have SELinux support? + */ +#undef HAVE_SELINUX + #endif /* !_CUPS_CONFIG_H_ */ /* Index: scheduler/ipp.c =================================================================== --- scheduler/ipp.c (revision 8960) +++ scheduler/ipp.c (working copy) @@ -853,7 +853,58 @@ 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); +} + +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); + + cupsdLogMessage(CUPSD_LOG_DEBUG, "label_to_top_bottom_string(%s): %s", + label, (result ? result: "NULL")); + } + } +#endif + + if (result != NULL) + result = strdup(result); + + return (result); +} + /* * 'accept_jobs()' - Accept print jobs to a printer. */ @@ -1344,6 +1395,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... */ @@ -1558,6 +1620,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; @@ -3682,6 +3757,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... */ @@ -4050,6 +4134,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... */ @@ -6286,6 +6380,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; @@ -6825,6 +6929,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... */ @@ -7045,6 +7158,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", @@ -8101,6 +8222,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... */ @@ -8355,6 +8485,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 { /* @@ -8377,6 +8515,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... */ @@ -9204,6 +9363,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"... */ @@ -9430,6 +9599,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... */ @@ -9785,6 +9963,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); /* @@ -10361,6 +10548,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... */ @@ -10698,7 +10894,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) { @@ -11441,6 +11636,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 (revision 8960) +++ scheduler/printers.c (working copy) @@ -906,7 +906,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 (revision 8960) +++ scheduler/job.c (working copy) @@ -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); @@ -1967,6 +1983,8 @@ fchmod(cupsFileNumber(fp), 0600); fchown(cupsFileNumber(fp), RunUser, Group); + /* we should probably relabel the control file here */ + job->attrs->state = IPP_IDLE; if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, @@ -3798,13 +3816,25 @@ return; } + 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; + } + + /* * Update the printer and job state to "processing"... */ - if (!cupsdLoadJob(job)) - return; - cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL); cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0); Index: scheduler/printers.h =================================================================== --- scheduler/printers.h (revision 8960) +++ scheduler/printers.h (working copy) @@ -147,6 +147,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 8960) +++ 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 (revision 8960) +++ scheduler/client.c (working copy) @@ -84,7 +84,12 @@ # include #endif /* HAVE_TCPD_H */ +#ifdef HAVE_TSOL +#include +#include +#endif /* HAVE_TSOL */ + /* * Local functions... */ @@ -117,7 +122,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. */ @@ -385,6 +423,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)", @@ -674,6 +727,12 @@ } } + if (con->slabel != NULL) + { + free(con->slabel); + con->slabel = NULL; + } + if (!partial) { /* Index: scheduler/client.h =================================================================== --- scheduler/client.h (revision 8960) +++ 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 8960) +++ scheduler/cupsd.h (working copy) @@ -50,7 +50,19 @@ # include #endif /* HAVE_CDSASSL */ +#if defined(HAVE_TSOL) +#include +#endif /* HAVE_TSOL */ +#if defined(HAVE_SELINUX) +#include +#include +#include +#include +#include +#include +#endif /* HAVE_SELINUX */ + /* * Some OS's don't have hstrerror(), most notably Solaris... */