--- cups-1.3.0/config.h.in 2007-08-01 15:02:47.000000000 -0400 +++ cups-1.3.0-selinux/config.h.in 2007-08-14 18:24:58.000000000 -0400 @@ -536,6 +536,12 @@ #undef HAVE_REMOVEFILE +/* + * Do we support SELinux labels? + */ + +#undef HAVE_SELINUX + #endif /* !_CUPS_CONFIG_H_ */ --- cups-1.3.0/configure.in 2007-07-24 19:47:12.000000000 -0400 +++ cups-1.3.0-selinux/configure.in 2007-08-14 18:24:58.000000000 -0400 @@ -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-lspp.m4) INSTALL_LANGUAGES="" UNINSTALL_LANGUAGES="" --- cups-1.3.0/config-scripts/cups-lspp.m4 1969-12-31 19:00:00.000000000 -0500 +++ cups-1.3.0-selinux/config-scripts/cups-lspp.m4 2007-08-14 18:24:58.000000000 -0400 @@ -0,0 +1,33 @@ +dnl +dnl LSPP code for the Common UNIX Printing System (CUPS). +dnl +dnl Copyright 2005-2006 by Hewlett-Packard Development Company, L.P. +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 + +dnl Is SELinux available to be used +AC_ARG_ENABLE(selinux, [ --enable-selinux enable support for SELinux labels, default=auto]) +if test x"$enable_selinux" != xno; then + case "$uname" in + Linux) + AC_CHECK_LIB(selinux,getpeercon, [LIBSELINUX="-lselinux" AC_SUBST(LIBSELINUX) + AC_CHECK_HEADER(selinux/selinux.h) + AC_DEFINE(HAVE_SELINUX)]) + ;; + *) + # All others + ;; + esac +fi --- cups-1.3.0/Makedefs.in 2007-07-18 15:49:45.000000000 -0400 +++ cups-1.3.0-selinux/Makedefs.in 2007-08-14 18:24:58.000000000 -0400 @@ -132,7 +132,7 @@ LEGACY_BACKENDS = @LEGACY_BACKENDS@ LIBCUPSORDER = @LIBCUPSORDER@ LIBCUPSIMAGEORDER = @LIBCUPSIMAGEORDER@ -LINKCUPS = @LINKCUPS@ $(SSLLIBS) +LINKCUPS = @LINKCUPS@ $(SSLLIBS) @LIBSELINUX@ LINKCUPSIMAGE = @LINKCUPSIMAGE@ LIBS = $(LINKCUPS) $(COMMONLIBS) OPTIM = @OPTIM@ --- cups-1.3.0/man/cupsd.conf.man.in 2007-07-11 17:46:42.000000000 -0400 +++ cups-1.3.0-selinux/man/cupsd.conf.man.in 2007-08-14 18:24:58.000000000 -0400 @@ -263,6 +263,12 @@ Specifies the level of encryption that is required for a particular location. .TP 5 +EnforceSELinux Yes +.TP 5 +EnforceSELinux No +.br +Specifies wheather to perform SELinux access checks +.TP 5 ErrorLog filename .TP 5 ErrorLog syslog --- cups-1.3.0/scheduler/cupsd.h 2007-08-01 15:02:47.000000000 -0400 +++ cups-1.3.0-selinux/scheduler/cupsd.h 2007-08-14 18:24:59.000000000 -0400 @@ -176,6 +176,10 @@ /* Apple PrintService quota function */ #endif /* __APPLE__ && HAVE_DLFCN_H */ +#ifdef HAVE_SELINUX +VAR int EnforceSELinux VALUE(TRUE); +#endif /* HAVE_SELINUX */ + --- cups-1.3.0/scheduler/client.c 2007-08-13 13:20:14.000000000 -0400 +++ cups-1.3.0-selinux/scheduler/client.c 2007-08-14 18:24:59.000000000 -0400 @@ -76,6 +76,11 @@ # include #endif /* HAVE_GNUTLS */ +#ifdef HAVE_SELINUX +#include +#include +#endif /* HAVE_SELINUX */ + /* * Local functions... @@ -123,6 +128,9 @@ char *hostname; /* Hostname for address */ http_addr_t temp; /* Temporary address variable */ static time_t last_dos = 0; /* Time of last DoS attack */ +#ifdef HAVE_SELINUX + security_context_t cupsdcon; /* Security Context of cups daemon */ +#endif /* HAVE_SELINUX */ cupsdLogMessage(CUPSD_LOG_DEBUG2, @@ -339,6 +347,20 @@ } } +#ifdef HAVE_SELINUX + /* + * get the context of the peer connection + */ + if (getpeercon(con->http.fd, &con->scon)) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "cupsdAcceptClient: determining SELinux context failed for %s", + con->http.hostname); + con->scon = NULL; + } + else + cupsdLogMessage(CUPSD_LOG_INFO, "cupsdAcceptClient: client SELinux context=%s", con->scon); +#endif /* HAVE_SELINUX */ #ifdef AF_INET6 if (con->http.hostaddr->addr.sa_family == AF_INET6) cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv6)", --- cups-1.3.0/scheduler/client.h 2007-08-13 13:20:14.000000000 -0400 +++ cups-1.3.0-selinux/scheduler/client.h 2007-08-14 18:24:59.000000000 -0400 @@ -16,6 +16,10 @@ #ifdef HAVE_AUTHORIZATION_H # include #endif /* HAVE_AUTHORIZATION_H */ +#ifdef HAVE_SELINUX +#include +#endif /* HAVE_SELINUX */ + /* * HTTP client structure... @@ -61,6 +65,10 @@ #ifdef HAVE_AUTHORIZATION_H AuthorizationRef authref; /* Authorization ref */ #endif /* HAVE_AUTHORIZATION_H */ +#ifdef HAVE_SELINUX + security_context_t scon; /* Security context of connection */ +#endif /* HAVE_SELINUX */ + }; #define HTTP(con) &((con)->http) --- cups-1.3.0/scheduler/ipp.c 2007-08-09 18:19:08.000000000 -0400 +++ cups-1.3.0-selinux/scheduler/ipp.c 2007-08-14 18:24:59.000000000 -0400 @@ -120,6 +120,13 @@ # endif /* HAVE_MEMBERSHIPPRIV_H */ #endif /* __APPLE__ */ +#ifdef HAVE_SELINUX +#include +#include +#include +#include +#include +#endif /* HAVE_SELINUX */ /* * Local functions... @@ -144,6 +151,9 @@ static void cancel_all_jobs(cupsd_client_t *con, ipp_attribute_t *uri); static void cancel_job(cupsd_client_t *con, ipp_attribute_t *uri); static void cancel_subscription(cupsd_client_t *con, int id); +#ifdef HAVE_SELINUX +static int check_context(cupsd_client_t *con, cupsd_job_t *job); +#endif /* HAVE_SELINUX */ static int check_quotas(cupsd_client_t *con, cupsd_printer_t *p); static ipp_attribute_t *copy_attribute(ipp_t *to, ipp_attribute_t *attr, int quickcopy); @@ -1235,6 +1245,17 @@ int kbytes; /* Size of print file */ int i; /* Looping var */ int lowerpagerange; /* Page range bound */ +#ifdef HAVE_SELINUX + char *printerfile; /* device file pointed to by the printer */ + security_id_t clisid; /* SELinux SID for the client */ + security_id_t psid; /* SELinux SID for the printer */ + context_t printercon; /* Printer's context string */ + struct stat printerstat; /* Printer's stat buffer */ + security_context_t devcon; /* Printer's SELinux context */ + struct avc_entry_ref avcref; /* Pointer to the access vector cache */ + security_class_t tclass = 0; /* Object class for the SELinux check */ + access_vector_t avr; /* Access method being requested */ +#endif /* HAVE_SELINUX */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))", @@ -1421,6 +1442,90 @@ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, title = "Untitled"); +#ifdef HAVE_SELINUX + if (!con->scon) + /* + * Perhaps a check here to see if the printer should accept unlabeled jobs + */ + cupsdLogMessage(CUPSD_LOG_DEBUG, "add_job: Connection from unlabeled client to printer \'%s\'", + printer->name); + else + { + /* + * Perform an access check so that if the user gets feedback at enqueue time + */ + printerfile = strstr(printer->device_uri, "/dev/"); + if (printerfile == NULL && (strncmp(printer->device_uri, "file:/", 6) == 0)) + printerfile = strdup(printer->device_uri + strlen("file:/")); + + if (printerfile != NULL) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "add_job: Attempting an access check on printer device %s", + printerfile); + + if (stat(printerfile, &printerstat) < 0) + { + if (errno != ENOENT) + { + send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Unable to stat the printer")); + return (NULL); + } + /* + * The printer does not exist, so for now assume its a FileDevice + */ + tclass = SECCLASS_FILE; + avr = FILE__WRITE; + } + else if (S_ISCHR(printerstat.st_mode)) + { + tclass = SECCLASS_CHR_FILE; + avr = CHR_FILE__WRITE; + } + else if (S_ISREG(printerstat.st_mode)) + { + tclass = SECCLASS_FILE; + avr = FILE__WRITE; + } + + avc_init("cupsd_enqueue", NULL, NULL, NULL, NULL); + avc_entry_ref_init(&avcref); + if (avc_context_to_sid(con->scon, &clisid) != 0) + { + send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Unable to get the SELinux sid of the client")); + return (NULL); + } + if (getfilecon(printerfile, &devcon) == -1) + { + send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Unable to get the SELinux context of the printer")); + return (NULL); + } + printercon = context_new(devcon); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job: printer context \'%s\' client context \'%s\'", + context_str(printercon), con->scon); + context_free(printercon); + + if (avc_context_to_sid(devcon, &psid) != 0) + { + send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Unable to get the SELinux sid of the printer")); + freecon(devcon); + return (NULL); + } + freecon(devcon); + + if (avc_has_perm_noaudit(clisid, psid, tclass, avr, &avcref, NULL) != 0) + { + send_ipp_status(con, IPP_NOT_AUTHORIZED, _("SELinux prohibits access to the printer")); + return (NULL); + } + + if (strncmp(printer->device_uri, "file:/", 6) == 0) + free(printerfile); + } + cupsdLogMessage(CUPSD_LOG_DEBUG, "add_job: client labeled \'%s\' printing to \'%s\'", + con->scon, printer->name); + } +#endif /* HAVE_SELINUX */ + if ((job = cupsdAddJob(priority, printer->name)) == NULL) { send_ipp_status(con, IPP_INTERNAL_ERROR, @@ -1439,6 +1544,18 @@ attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME); +#ifdef HAVE_SELINUX + if (con->scon) + { + job->scon = strdup(con->scon); + + ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "security-context", + NULL, job->scon); + } + else + job->scon = NULL; +#endif /* HAVE_SELINUX */ + if (con->username[0]) { cupsdSetString(&job->username, con->username); @@ -3308,6 +3425,124 @@ } +#ifdef HAVE_SELINUX +/* + * * 'check_context()' - Check SELinux security context of a user and job + * */ + +static int /* O - 1 if OK, 0 if not, -1 on error */ +check_context(cupsd_client_t *con, /* I - Client connection */ + cupsd_job_t *job) /* I - Job */ +{ + int enforcing; /* is SELinux in enforcing mode */ + char filename[1024]; /* Filename of the spool file */ + security_id_t clisid; /* SELinux SID of the client */ + security_id_t jobsid; /* SELinux SID of the job */ + security_id_t filesid; /* SELinux SID of the spool file */ + struct avc_entry_ref avcref; /* AVC entry cache pointer */ + security_class_t tclass; /* SELinux security class */ + access_vector_t avr; /* SELinux access being queried */ + security_context_t spoolfilecon; /* SELinux context of the spool file */ + + + /* + * Check to see if we're enforcing or not + */ + if ((enforcing = security_getenforce()) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Error while determining SELinux enforcement"); + return -1; + } + + if (con->scon == NULL && job->scon == NULL) + { + cupsdLogMessage(CUPSD_LOG_INFO, "check_context: no context information available, allowing action"); + return 1; + } + + if (con->scon == NULL || job->scon == NULL) + { + if (enforcing) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Error, missing context information in enforcing mode"); + return -1; + } + else + { + cupsdLogMessage(CUPSD_LOG_INFO, + "check_context: missing contexts, but selinux=permisive so allow action"); + return 1; + } + } + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_context: client context %s job context %s", + con->scon, job->scon); + + /* + * Initialize the avc engine... + */ + if (avc_init("cupsd", NULL, NULL, NULL, NULL) < 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_context: unable avc_init"); + return -1; + } + if (avc_context_to_sid(con->scon, &clisid) != 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_context: unable to convert \'%s\' to SELinux sid", + con->scon); + return -1; + } + if (avc_context_to_sid(job->scon, &jobsid) != 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_context: unable to convert \'%s\' to SELinux sid", + job->scon); + return -1; + } + avc_entry_ref_init(&avcref); + tclass = SECCLASS_FILE; + avr = FILE__READ; + + /* + * Perform the check with the client as the subject, first with the job as the object + * if that fails then with the spool file as the object... + */ + if (avc_has_perm_noaudit(clisid, jobsid, tclass, avr, &avcref, NULL) != 0) + { + cupsdLogMessage(CUPSD_LOG_INFO, "check_context: SELinux denied access based on the client context"); + + snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id); + if (getfilecon(filename, &spoolfilecon) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_context: Unable to get spoolfile context"); + return -1; + } + if (avc_context_to_sid(spoolfilecon, &filesid) != 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "check_context: Unable to determine the SELinux sid for the spool file"); + freecon(spoolfilecon); + return -1; + } + freecon(spoolfilecon); + if (avc_has_perm_noaudit(clisid, filesid, tclass, avr, &avcref, NULL) != 0) + { + cupsdLogMessage(CUPSD_LOG_INFO, "check_context: SELinux denied access to the spool file"); + return 0; + } + cupsdLogMessage(CUPSD_LOG_INFO, "check_context: SELinux allowed access to the spool file"); + return 1; + } + else + if (enforcing == 0) + cupsdLogMessage(CUPSD_LOG_INFO, "check_context: SELinux is in permissive mode, allowing access"); + else + cupsdLogMessage(CUPSD_LOG_INFO, + "check_context: SELinux allowed access based on the client context"); + + return 1; +} +#endif /* HAVE_SELINUX */ + + /* * 'check_quotas()' - Check quotas for a printer and user. */ @@ -3836,6 +4071,14 @@ char attrname[255], /* Name of attribute */ *s; /* Pointer into name */ ipp_attribute_t *attr; /* Attribute */ +#ifdef HAVE_SELINUX + char *jobrange = NULL; /* SELinux sensitivity range */ + char *jobclearance; /* SELinux low end clearance */ + context_t jobcon; /* SELinux context of the job */ + context_t tmpcon; /* Temp context to set the level */ + security_context_t spoolcon; /* Context of the file in the spool */ +#endif /* HAVE_SELINUX */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_banner(%p[%d], %p[%d], %s)", @@ -3870,6 +4113,77 @@ fchmod(cupsFileNumber(out), 0640); fchown(cupsFileNumber(out), RunUser, Group); +#ifdef HAVE_SELINUX + if (con != NULL && con->scon != NULL && EnforceSELinux) + { + if (getfilecon(filename, &spoolcon) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "copy_banner: Unable to get the context of the banner file %s - %s", + filename, strerror(errno)); + job->num_files --; + return (0); + } + tmpcon = context_new(spoolcon); + jobcon = context_new(con->scon); + freecon(spoolcon); + if (!tmpcon || !jobcon) + { + if (tmpcon) + context_free(tmpcon); + if (jobcon) + context_free(jobcon); + cupsdLogMessage(CUPSD_LOG_ERROR, + "copy_banner: Unable to get the SELinux contexts"); + job->num_files --; + return (0); + } + if (context_range_get(jobcon) != NULL) + jobrange = strdup(context_range_get(jobcon)); + if (jobrange && (jobclearance = strtok(jobrange, "-")) != NULL) + { + if (context_range_set(tmpcon, jobclearance) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "copy_banner: Unable to set the level of the context for file %s - %s", + filename, strerror(errno)); + free(jobrange); + context_free(jobcon); + context_free(tmpcon); + job->num_files --; + return (0); + } + } + else + { + if (context_range_set(tmpcon, (context_range_get(jobcon))) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "copy_banner: Unable to set the level of the context for file %s - %s", + filename, strerror(errno)); + context_free(jobcon); + context_free(tmpcon); + job->num_files --; + return (0); + } + } + free(jobrange); + if (setfilecon(filename, context_str(tmpcon)) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "copy_banner: Unable to set the context of the banner file %s - %s", + filename, strerror(errno)); + context_free(jobcon); + context_free(tmpcon); + job->num_files --; + return (0); + } + cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_banner: %s set to %s", + filename, context_str(tmpcon)); + context_free(jobcon); + context_free(tmpcon); + } +#endif /* HAVE_SELINUX */ /* * Try the localized banner file under the subdirectory... @@ -5685,6 +5999,20 @@ return; } +#ifdef WITH_LSPP + /* + * Check SELinux... + */ + if (EnforceSELinux && (check_context(con, job) != 1)) + { + /* + * Unfortunately we have to lie to the user... + */ + send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid); + return; + } +#endif /* HAVE_SELINUX */ + /* * Copy attributes... */ @@ -5884,6 +6212,11 @@ if (count > 0) ippAddSeparator(con->response); +#ifdef HAVE_SELINUX + if (EnforceSELinux && (check_context(con, job) != 1)) + continue; +#endif /*HAVE_SELINUX */ + count ++; cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count = %d", count); @@ -9881,6 +10214,11 @@ strlcpy(username, get_username(con), userlen); +#ifdef HAVE_SELINUX + if (check_context(con, job) != 1) + return (0); +#endif /* HAVE_SELINUX */ + /* * Check the username against the owner... */ --- cups-1.3.0/scheduler/job.c 2007-08-09 18:19:08.000000000 -0400 +++ cups-1.3.0-selinux/scheduler/job.c 2007-08-14 18:24:59.000000000 -0400 @@ -69,6 +69,14 @@ #include #include +#ifdef HAVE_SELINUX +#include +#include +#include +#include +#include +#endif /* HAVE_SELINUX */ + /* * Local globals... @@ -1077,6 +1085,17 @@ return; } +#ifdef HAVE_SELINUX + if ((attr = ippFindAttribute(job->attrs, "security-context", IPP_TAG_NAME)) != NULL) + cupsdSetString(&job->scon, attr->values[0].string.text); + else + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] No SELinux security-context attribute in control file", + job->id); + job->scon = NULL; + } +#endif /* HAVE_SELINUX */ job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed", IPP_TAG_INTEGER); job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME); @@ -1430,6 +1449,13 @@ { char filename[1024]; /* Job control filename */ cups_file_t *fp; /* Job file */ +#ifdef HAVE_SELINUX + security_context_t spoolcon; /* context of the job control file */ + context_t jobcon; /* contex_t container for job->scon */ + context_t tmpcon; /* Temp context to swap the level */ + char *jobclearance; /* SELinux low end clearance */ + char *jobrange = NULL; /* SELinux sensitivity range */ +#endif /* HAVE_SELINUX */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob(job=%p(%d)): job->attrs=%p", @@ -1448,6 +1474,72 @@ fchmod(cupsFileNumber(fp), 0600); fchown(cupsFileNumber(fp), RunUser, Group); +#ifdef HAVE_SELINUX + if (job->scon != NULL && EnforceSELinux) + { + if (getfilecon(filename, &spoolcon) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to get context of job control file \"%s\" - %s.", + job->id, filename, strerror(errno)); + return; + } + jobcon = context_new(job->scon); + tmpcon = context_new(spoolcon); + freecon(spoolcon); + if (!jobcon || !tmpcon) + { + if (jobcon) + context_free(jobcon); + if (tmpcon) + context_free(tmpcon); + cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Unable to get SELinux contexts", job->id); + return; + } + if (context_range_get(jobcon) != NULL) + jobrange = strdup(context_range_get(jobcon)); + if (jobrange && (jobclearance = strtok(jobrange, "-")) != NULL) + { + if (context_range_set(tmpcon, jobclearance) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to set the range for job control file \"%s\" - %s.", + job->id, filename, strerror(errno)); + free(jobrange); + context_free(tmpcon); + context_free(jobcon); + return; + } + } + else + { + if (context_range_set(tmpcon, (context_range_get(jobcon))) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to set the range for job control file \"%s\" - %s.", + job->id, filename, strerror(errno)); + context_free(tmpcon); + context_free(jobcon); + return; + } + } + free(jobrange); + if (setfilecon(filename, context_str(tmpcon)) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "[Job %d] Unable to set context of job control file \"%s\" - %s.", + job->id, filename, strerror(errno)); + context_free(tmpcon); + context_free(jobcon); + return; + } + cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSaveJob(job=%p): new spool file context=%s", + job, context_str(tmpcon)); + context_free(tmpcon); + context_free(jobcon); + } +#endif /* HAVE_SELINUX */ + job->attrs->state = IPP_IDLE; if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL, @@ -2456,6 +2548,19 @@ /* RIP_MAX_CACHE env variable */ static char *options = NULL;/* Full list of options */ static int optlength = 0; /* Length of option buffer */ +#ifdef HAVE_SELINUX + char *printerfile = NULL; + /* Device file pointed to by the printer */ + security_id_t clisid; /* SELinux SID for the client */ + security_id_t psid; /* SELinux SID for the printer */ + context_t printercon; /* Printer's context string */ + struct stat printerstat; /* Printer's stat buffer */ + security_context_t devcon; /* Printer's SELinux context */ + struct avc_entry_ref avcref; /* Pointer to the access vector cache */ + security_class_t tclass = 0; /* Object class for the SELinux check */ + access_vector_t avr; /* Access method being requested */ +#endif /* HAVE_SELINUX */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] start_job: file = %d/%d", @@ -2746,6 +2851,102 @@ cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] banner_page = %d", job->id, banner_page); +#ifdef HAVE_SELINUX + /* + * If there is a security context on the job make sure that + * SELinux says it jives with the one on the printer... + */ + if (job->scon != NULL && EnforceSELinux) + { + /* + * Perform an access check before printing, but only if the printer starts with /dev/ + */ + printerfile = strstr(printer->device_uri, "/dev/"); + if (printerfile == NULL && (strncmp(printer->device_uri, "file:/", 6) == 0)) + printerfile = strdup(printer->device_uri + strlen("file:/")); + + if (printerfile != NULL) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, + "StartJob: Attempting to check access on printer device %s", printerfile); + if (lstat(printerfile, &printerstat) < 0) + { + if (errno != ENOENT) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "StartJob: Unable to stat the printer"); + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); + return ; + } + /* + * The printer does not exist, so for now assume its a FileDevice + */ + tclass = SECCLASS_FILE; + avr = FILE__WRITE; + } + else if (S_ISCHR(printerstat.st_mode)) + { + tclass = SECCLASS_CHR_FILE; + avr = CHR_FILE__WRITE; + } + else if (S_ISREG(printerstat.st_mode)) + { + tclass = SECCLASS_FILE; + avr = FILE__WRITE; + } + + /* + * If the printer is not a file or a character device, skip the checks + * TODO: allow networked printer to be assigned a range + */ + if (tclass == SECCLASS_FILE || tclass == SECCLASS_CHR_FILE) + { + avc_init("cupsd_dequeue_", NULL, NULL, NULL, NULL); + avc_entry_ref_init(&avcref); + if (avc_context_to_sid(job->scon, &clisid) != 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "StartJob: Unable to determine the SELinux sid for the job"); + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); + return ; + } + if (getfilecon(printerfile, &devcon) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "StartJob: Unable to get the SELinux context of %s", + printerfile); + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); + return ; + } + printercon = context_new(devcon); + cupsdLogMessage(CUPSD_LOG_DEBUG, "StartJob: printer context %s client context %s", + context_str(printercon), job->scon); + context_free(printercon); + + if (avc_context_to_sid(devcon, &psid) != 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "StartJob: Unable to determine the SELinux sid for the printer"); + freecon(devcon); + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); + return ; + } + freecon(devcon); + + if (avc_has_perm(clisid, psid, tclass, avr, &avcref, NULL) != 0) + { + /* + * The access check failed, so cancel the job + */ + cupsdCancelJob(job, 0, IPP_JOB_ABORTED); + + return ; + } + } + + free(printerfile); + } + } +#endif /* HAVE_SELINUX */ + /* * Building the options string is harder than it needs to be, but * for the moment we need to pass strings for command-line args and --- cups-1.3.0/scheduler/job.h 2007-08-01 15:02:47.000000000 -0400 +++ cups-1.3.0-selinux/scheduler/job.h 2007-08-14 18:24:59.000000000 -0400 @@ -13,6 +13,10 @@ * file is missing or damaged, see the license at "http://www.cups.org/". */ +#ifdef HAVE_SELINUX +#include +#endif /* HAVE_SELINUX */ + /* * Job request structure... */ @@ -60,6 +64,10 @@ krb5_ccache ccache; /* Kerberos credential cache */ char *ccname; /* KRB5CCNAME environment variable */ #endif /* HAVE_GSSAPI */ +#ifdef HAVE_SELINUX + security_context_t scon; /* Security context of job */ +#endif /* HAVE_SELINUX */ + } cupsd_job_t;