--- cups-1.3.0-selinux/config.h.in 2007-08-14 18:24:58.000000000 -0400 +++ cups-1.3.0-seaudit/config.h.in 2007-08-14 18:31:23.000000000 -0400 @@ -542,6 +542,12 @@ #undef HAVE_SELINUX +/* + * Do we have auditing? + */ + +#undef HAVE_AUDIT + #endif /* !_CUPS_CONFIG_H_ */ --- cups-1.3.0-selinux/config-scripts/cups-lspp.m4 2007-08-14 18:24:58.000000000 -0400 +++ cups-1.3.0-seaudit/config-scripts/cups-lspp.m4 2007-08-14 18:31:23.000000000 -0400 @@ -31,3 +31,19 @@ ;; esac fi + +dnl Is there an auditing framework which could be used +AC_ARG_ENABLE(audit, [ --enable-audit enable support for auditing, default=auto]) +if test x"$enable_audit" != xno; then + case "$uname" in + Linux) + AC_CHECK_LIB(audit,audit_log_user_message, [LIBAUDIT="-laudit" AC_SUBST(LIBAUDIT) + AC_CHECK_HEADER(libaudit.h) + AC_DEFINE(HAVE_AUDIT)]) + ;; + *) + # All others + ;; + esac +fi + --- cups-1.3.0-selinux/Makedefs.in 2007-08-14 18:24:58.000000000 -0400 +++ cups-1.3.0-seaudit/Makedefs.in 2007-08-14 18:31:23.000000000 -0400 @@ -132,7 +132,7 @@ LEGACY_BACKENDS = @LEGACY_BACKENDS@ LIBCUPSORDER = @LIBCUPSORDER@ LIBCUPSIMAGEORDER = @LIBCUPSIMAGEORDER@ -LINKCUPS = @LINKCUPS@ $(SSLLIBS) @LIBSELINUX@ +LINKCUPS = @LINKCUPS@ $(SSLLIBS) @LIBSELINUX@ @LIBAUDIT@ LINKCUPSIMAGE = @LINKCUPSIMAGE@ LIBS = $(LINKCUPS) $(COMMONLIBS) OPTIM = @OPTIM@ --- cups-1.3.0-selinux/scheduler/client.c 2007-08-14 18:24:59.000000000 -0400 +++ cups-1.3.0-seaudit/scheduler/client.c 2007-08-14 18:31:23.000000000 -0400 @@ -17,6 +17,7 @@ * * Contents: * + * client_pid_to_auid() - Determine the client's login uid. * cupsdAcceptClient() - Accept a new client. * cupsdCloseAllClients() - Close all remote clients immediately. * cupsdCloseClient() - Close a remote client. @@ -81,6 +82,9 @@ #include #endif /* HAVE_SELINUX */ +#ifdef HAVE_AUDIT +#include +#endif /* HAVE_AUDIT */ /* * Local functions... @@ -111,6 +115,48 @@ static void write_pipe(cupsd_client_t *con); +#ifdef HAVE_AUDIT +/* + * 'client_pid_to_auid()' - Using the client's pid, read /proc and determine the loginuid. + */ + +uid_t +client_pid_to_auid(pid_t clipid) +{ + uid_t uid; + int len, in; + char buf[16] = {0}; + char fname[32] = {0}; + + snprintf(fname, 32, "/proc/%d/loginuid", clipid); + in = open(fname, O_RDONLY); + + if (in < 0) + return -1; + + errno = 0; + + do { + len = read(in, buf, sizeof(buf)); + } while (len < 0 && errno == EINTR); + + close(in); + + if (len < 0 || len >= sizeof(buf)) + return -1; + + errno = 0; + buf[len] = 0; + uid = strtol(buf, 0, 10); + + if (errno != 0) + return -1; + else + return uid; +} +#endif /* HAVE_AUDIT */ + + /* * 'cupsdAcceptClient()' - Accept a new client. */ @@ -128,9 +174,11 @@ 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 */ +#ifdef HAVE_AUDIT + struct ucred cr; /* Socket peer credentials struct */ + unsigned int cl; /* Size of credentials struct */ +#endif /* HAVE_AUDIT */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, @@ -370,8 +418,34 @@ #endif /* AF_INET6 */ #ifdef AF_LOCAL if (con->http.hostaddr->addr.sa_family == AF_LOCAL) + { cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s (Domain)", con->http.fd, con->http.hostname); + +#ifdef HAVE_AUDIT + cl=sizeof(cr); + + if (getsockopt(con->http.fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl) == 0) + { + /* + * Use the pid of the socket connection to determine the audit uid + */ + if ((con->auid = client_pid_to_auid(cr.pid)) == -1) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "cupsdAcceptClient: unable to determine client auid for client pid=%d", + cr.pid); + } + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAcceptClient: peer's pid=%d, uid=%d, gid=%d, auid=%d", + cr.pid, cr.uid, cr.gid, con->auid); + } + else + { + cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdAcceptClient: getsockopt() failed"); + con->auid = -1; + } +#endif /* HAVE_AUDIT */ + } else #endif /* AF_LOCAL */ cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv4)", --- cups-1.3.0-selinux/scheduler/client.h 2007-08-14 18:24:59.000000000 -0400 +++ cups-1.3.0-seaudit/scheduler/client.h 2007-08-14 18:31:23.000000000 -0400 @@ -68,7 +68,9 @@ #ifdef HAVE_SELINUX security_context_t scon; /* Security context of connection */ #endif /* HAVE_SELINUX */ - +#ifdef HAVE_AUDIT + uid_t auid; /* Audit loginuid of the client */ +#endif /* HAVE_AUDIT */ }; #define HTTP(con) &((con)->http) @@ -116,6 +118,9 @@ * Prototypes... */ +#ifdef HAVE_AUDIT +extern uid_t client_pid_to_auid(pid_t clipid); +#endif /* HAVE_AUDIT */ extern void cupsdAcceptClient(cupsd_listener_t *lis); extern void cupsdCloseAllClients(void); extern int cupsdCloseClient(cupsd_client_t *con); --- cups-1.3.0-selinux/scheduler/conf.c 2007-08-14 18:24:37.000000000 -0400 +++ cups-1.3.0-seaudit/scheduler/conf.c 2007-08-14 18:33:07.000000000 -0400 @@ -37,6 +37,9 @@ #include #include #include +#ifdef HAVE_AUDIT +#include +#endif /* HAVE_AUDIT */ /* @@ -349,6 +352,9 @@ *old_requestroot; /* Old RequestRoot */ const char *tmpdir; /* TMPDIR environment variable */ struct stat tmpinfo; /* Temporary directory info */ +#ifdef HAVE_AUDIT + char *audit_message; /* Audit message string */ +#endif /* HAVE_AUDIT */ /* @@ -885,7 +891,19 @@ cupsdClearString(&Classification); if (Classification) + { cupsdLogMessage(CUPSD_LOG_INFO, "Security set to \"%s\"", Classification); +#ifdef HAVE_AUDIT + if (AuditLog != -1) + { + audit_message = NULL; + cupsdSetStringf(&audit_message, "[Config] Classification=%s", Classification); + audit_log_user_message(AuditLog, AUDIT_LABEL_LEVEL_CHANGE, audit_message, + ServerName, NULL, NULL, 1); + free(audit_message); + } +#endif /* HAVE_AUDIT */ + } /* * Update the MaxClientsPerHost value, as needed... @@ -933,6 +951,24 @@ cupsd_policy_t *p; /* New policy */ cupsd_location_t *po; /* New policy operation */ +#ifdef HAVE_AUDIT + if (AuditLog != -1) + { + /* + * Audit security related configuration settings + */ + if (ClassifyOverride) + audit_log_user_message(AuditLog, AUDIT_USYS_CONFIG, + "[Config] ClassifyOverride=enabled Users can override print banners", + ServerName, NULL, NULL, 1); + + if (!EnforceSELinux) + audit_log_user_message(AuditLog, AUDIT_USYS_CONFIG, + "[Config] EnforceSELinux=disabled SELinux controls are not in effect", + ServerName, NULL, NULL, 1); + + } +#endif /* HAVE_AUDIT */ if (DefaultPolicy) cupsdLogMessage(CUPSD_LOG_ERROR, "Default policy \"%s\" not found!", --- cups-1.3.0-selinux/scheduler/conf.h 2007-08-14 18:24:37.000000000 -0400 +++ cups-1.3.0-seaudit/scheduler/conf.h 2007-08-14 18:31:23.000000000 -0400 @@ -205,6 +205,12 @@ #endif /* HAVE_AUTHORIZATION_H */ +#ifdef HAVE_AUDIT +VAR int AuditLog VALUE(-1); + /* File descriptor for audit */ +#endif /* HAVE_AUDIT */ + + /* * Prototypes... */ --- cups-1.3.0-selinux/scheduler/ipp.c 2007-08-14 18:24:59.000000000 -0400 +++ cups-1.3.0-seaudit/scheduler/ipp.c 2007-08-14 18:31:23.000000000 -0400 @@ -127,6 +127,9 @@ #include #include #endif /* HAVE_SELINUX */ +#ifdef HAVE_AUDIT +#include +#endif /* HAVE_AUDIT */ /* * Local functions... @@ -1256,6 +1259,13 @@ security_class_t tclass = 0; /* Object class for the SELinux check */ access_vector_t avr; /* Access method being requested */ #endif /* HAVE_SELINUX */ +#ifdef HAVE_AUDIT + int override = 0; /* Was a banner overrode on a job */ + char *audit_message; /* Audit message string */ + char *userheader = NULL; /* User supplied job-sheets[0] */ + char *userfooter = NULL; /* User supplied job-sheets[1] */ +#endif /* HAVE_AUDIT */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))", @@ -1514,6 +1524,21 @@ if (avc_has_perm_noaudit(clisid, psid, tclass, avr, &avcref, NULL) != 0) { +#ifdef HAVE_AUDIT + /* + * The access check failed, so cancel the job and send an audit message + */ + if (AuditLog != -1) + { + audit_message = NULL; + cupsdSetStringf(&audit_message, "job=%d auid=%u acct=%s obj=%s refused" + " unable to access printer=%s", job->id, con->auid, + con->username, con->scon, printer->name); + audit_log_user_message(AuditLog, AUDIT_USER_LABELED_EXPORT, audit_message, + ServerName, NULL, NULL, 0); + free(audit_message); + } +#endif /* HAVE_AUDIT */ send_ipp_status(con, IPP_NOT_AUTHORIZED, _("SELinux prohibits access to the printer")); return (NULL); } @@ -1762,6 +1787,23 @@ attr->values[0].string.text = _cupsStrAlloc(printer->job_sheets[0]); attr->values[1].string.text = _cupsStrAlloc(printer->job_sheets[1]); } +#ifdef HAVE_AUDIT + else + { + if (Classification != NULL && + ((strcmp(attr->values[0].string.text, Classification) != 0) || + (attr->num_values > 1 && (strcmp(attr->values[1].string.text, Classification) != 0)))) + { + /* + * Since the user is trying to override, capture the values + */ + userheader = strdup(attr->values[0].string.text); + + if (attr->num_values > 1) + userfooter = strdup(attr->values[1].string.text); + } + } +#endif /* HAVE_AUDIT */ job->job_sheets = attr; @@ -1792,6 +1834,9 @@ "job-sheets=\"%s,none\", " "job-originating-user-name=\"%s\"", job->id, Classification, job->username); +#ifdef HAVE_AUDIT + override = 1; +#endif /* HAVE_AUDIT */ } else if (attr->num_values == 2 && strcmp(attr->values[0].string.text, @@ -1810,6 +1855,9 @@ "job-originating-user-name=\"%s\"", job->id, attr->values[0].string.text, attr->values[1].string.text, job->username); +#ifdef HAVE_AUDIT + override = 1; +#endif /* HAVE_AUDIT */ } else if (strcmp(attr->values[0].string.text, Classification) && strcmp(attr->values[0].string.text, "none") && @@ -1830,6 +1878,9 @@ "job-originating-user-name=\"%s\"", job->id, attr->values[0].string.text, attr->values[1].string.text, job->username); +#ifdef HAVE_AUDIT + override = 1; +#endif /* HAVE_AUDIT */ } } else if (strcmp(attr->values[0].string.text, Classification) && @@ -1870,9 +1921,50 @@ "job-sheets=\"%s\", " "job-originating-user-name=\"%s\"", job->id, Classification, job->username); +#ifdef HAVE_AUDIT + override = 1; +#endif /* HAVE_AUDIT */ } } +#ifdef HAVE_AUDIT + if (AuditLog != -1 && (userheader || userfooter)) + { + audit_message = NULL; + + if (!override) + { + /* + * The user overrode the banner, so audit it + */ + cupsdSetStringf(&audit_message, "job=%d user supplied job-sheets=%s,%s" + " using banners=%s,%s", + job->id, userheader, userfooter, attr->values[0].string.text, + (attr->num_values > 1) ? attr->values[1].string.text : "(null)"); + audit_log_user_message(AuditLog, AUDIT_LABEL_OVERRIDE, audit_message, + ServerName, NULL, NULL, 1); + } + else + { + /* + * The user tried to override the banner, audit the failure + */ + cupsdSetStringf(&audit_message, "job=%d user supplied job-sheets=%s,%s" + " ignored banners=%s,%s", + job->id, userheader, userfooter, attr->values[0].string.text, + (attr->num_values > 1) ? attr->values[1].string.text : "(null)"); + audit_log_user_message(AuditLog, AUDIT_LABEL_OVERRIDE, audit_message, + ServerName, NULL, NULL, 0); + } + free(audit_message); + } + + if (userheader) + free(userheader); + if (userfooter) + free(userfooter); +#endif /* HAVE_AUDIT */ + /* * See if we need to add the starting sheet... */ --- cups-1.3.0-selinux/scheduler/job.c 2007-08-14 18:24:59.000000000 -0400 +++ cups-1.3.0-seaudit/scheduler/job.c 2007-08-14 18:31:23.000000000 -0400 @@ -76,6 +76,9 @@ #include #include #endif /* HAVE_SELINUX */ +#ifdef HAVE_AUDIT +#include +#endif /* HAVE_AUDIT */ /* @@ -2560,6 +2563,9 @@ security_class_t tclass = 0; /* Object class for the SELinux check */ access_vector_t avr; /* Access method being requested */ #endif /* HAVE_SELINUX */ +#ifdef HAVE_AUDIT + char *audit_message = NULL; /* Audit message string */ +#endif /* HAVE_AUDIT */ @@ -2938,6 +2944,18 @@ */ cupsdCancelJob(job, 0, IPP_JOB_ABORTED); +#ifdef HAVE_AUDIT + if (AuditLog != -1) + { + audit_message = NULL; + cupsdSetStringf(&audit_message, "job=%d auid=%u acct=%s obj=%s canceled" + " unable to access printer=%s", job->id, + job->auid, (job->username)?job->username:"?", job->scon, printer->name); + audit_log_user_message(AuditLog, AUDIT_USER_LABELED_EXPORT, audit_message, + ServerName, NULL, NULL, 0); + free(audit_message); + } +#endif /* HAVE_AUDIT */ return ; } } @@ -3281,6 +3299,34 @@ envp[envc ++] = classification; } +#ifdef HAVE_AUDIT + if (job->scon == NULL) + { + if (AuditLog != -1 && !banner_page) + { + audit_message = NULL; + cupsdSetStringf(&audit_message, "job=%d auid=%u acct=%s printer=%s title=%s", + job->id, job->auid, job->username, printer->name, title); + audit_log_user_message(AuditLog, AUDIT_USER_UNLABELED_EXPORT, audit_message, + ServerName, NULL, NULL, 1); + free(audit_message); + } + } + else + { + if ((AuditLog != -1) && !banner_page) + { + audit_message = NULL; + cupsdSetStringf(&audit_message, + "job=%d auid=%u acct=%s printer=%s title=%s obj=%s", + job->id, job->auid, job->username, printer->name, title, job->scon); + audit_log_user_message(AuditLog, AUDIT_USER_LABELED_EXPORT, audit_message, + ServerName, NULL, NULL, 1); + free(audit_message); + } + } +#endif /* HAVE_AUDIT */ + if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)) { snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest); --- cups-1.3.0-selinux/scheduler/job.h 2007-08-14 18:24:59.000000000 -0400 +++ cups-1.3.0-seaudit/scheduler/job.h 2007-08-14 18:31:23.000000000 -0400 @@ -67,7 +67,9 @@ #ifdef HAVE_SELINUX security_context_t scon; /* Security context of job */ #endif /* HAVE_SELINUX */ - +#ifdef HAVE_AUDIT + uid_t auid; /* Audit loginuid for this job */ +#endif /* HAVE_AUDIT */ } cupsd_job_t; --- cups-1.3.0-selinux/scheduler/main.c 2007-08-14 18:24:37.000000000 -0400 +++ cups-1.3.0-seaudit/scheduler/main.c 2007-08-14 18:31:23.000000000 -0400 @@ -73,6 +73,9 @@ # include #endif /* __APPLE__ && HAVE_DLFCN_H */ +#ifdef HAVE_AUDIT +#include +#endif /* HAVE_AUDIT */ /* * Local functions... @@ -150,6 +153,9 @@ int launchd_idle_exit; /* Idle exit on select timeout? */ #endif /* HAVE_LAUNCHD */ +#if HAVE_AUDIT + auditfail_t failmode; /* Action to take if audit_open fails */ +#endif /* HAVE_AUDIT */ /* @@ -358,6 +364,25 @@ #endif /* DEBUG */ } +#ifdef HAVE_AUDIT + if ((AuditLog = audit_open()) < 0 ) + { + if (get_auditfail_action(&failmode) == 0) + { + if (failmode == FAIL_LOG) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to connect to audit subsystem."); + AuditLog = -1; + } + else if (failmode == FAIL_TERMINATE) + { + fprintf(stderr, "cupsd: unable to start auditing, terminating"); + return -1; + } + } + } +#endif /* HAVE_AUDIT */ + /* * Set the timezone info... */ @@ -1059,6 +1084,11 @@ cupsdStopSelect(); +#ifdef HAVE_AUDIT + if (AuditLog != -1) + audit_close(AuditLog); +#endif /* HAVE_AUDIT */ + return (!stop_scheduler); } --- cups-1.3.0-selinux/scheduler/printers.c 2007-08-14 18:24:37.000000000 -0400 +++ cups-1.3.0-seaudit/scheduler/printers.c 2007-08-14 18:31:23.000000000 -0400 @@ -57,6 +57,10 @@ #include "cupsd.h" #include +#ifdef HAVE_AUDIT +#include +#include +#endif /* HAVE_AUDIT */ /* * Local functions... @@ -1690,6 +1694,13 @@ { /* No authentication */ "none" }; +#ifdef HAVE_AUDIT + char *audit_message; /* Audit message string */ + char *printerfile; /* Path to a local printer dev */ + char *rangestr; /* Printer's range if its available */ + security_context_t devcon; /* Printer SELinux context */ + context_t printercon; /* context_t for the printer */ +#endif /* HAVE_AUDIT */ DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name, @@ -1833,6 +1844,44 @@ attr->values[1].string.text = _cupsStrAlloc(Classification ? Classification : p->job_sheets[1]); } +#ifdef HAVE_AUDIT + if (AuditLog != -1) + { + char uri[HTTP_MAX_URI]; + audit_message = NULL; + rangestr = NULL; + printercon = 0; + printerfile = strstr(p->device_uri, "/dev/"); + if (printerfile == NULL && (strncmp(p->device_uri, "file:/", 6) == 0)) + printerfile = strdup(p->device_uri + strlen("file:/")); + + if (printerfile != NULL) + { + if (getfilecon(printerfile, &devcon) == -1) + cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdSetPrinterAttrs: Unable to get printer context"); + else + { + printercon = context_new(devcon); + freecon(devcon); + } + } + + if (printercon && context_range_get(printercon)) + rangestr = strdup(context_range_get(printercon)); + else + rangestr = strdup("unknown"); + + cupsdSanitizeURI(p->device_uri, uri, sizeof(uri)); + cupsdSetStringf(&audit_message, "printer=%s uri=%s banners=%s,%s range=%s", + p->name, uri, p->job_sheets[0], p->job_sheets[1], rangestr); + audit_log_user_message(AuditLog, AUDIT_LABEL_LEVEL_CHANGE, audit_message, + ServerName, NULL, NULL, 1); + if (printercon) + context_free(printercon); + free(rangestr); + free(audit_message); + } +#endif /* HAVE_AUDIT */ } p->raw = 0;