--- configure.in Thu Jan 3 02:44:26 2013
+++ configure.in Wed Jan 2 21:45:05 2013
@@ -42,6 +42,7 @@
sinclude(config-scripts/cups-pdf.m4)
sinclude(config-scripts/cups-scripting.m4)
sinclude(config-scripts/cups-labeling.m4)
+sinclude(config-scripts/cups-auditing.m4)
INSTALL_LANGUAGES=""
UNINSTALL_LANGUAGES=""
--- config-scripts/cups-auditing.m4 Wed Jan 2 23:23:52 2013
+++ config-scripts/cups-auditing.m4 Wed Jan 2 21:45:18 2013
@@ -0,0 +1,35 @@
+dnl
+dnl Auditing support for the Common UNIX Printing System (CUPS).
+dnl
+dnl Copyright (c) 2013, 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(auditing, [ --enable-auditing enable support for auditing, default=auto])
+
+if test x"$enable_auditing" != xno; then
+ case "$uname" in
+ SunOS)
+ AC_CHECK_LIB(bsm, adt_start_session,
+ [AUDITING_LIBS="-lbsm" AC_SUBST(AUDITING_LIBS)
+ AC_CHECK_HEADER(bsm/adt.h)
+ AC_CHECK_HEADER(bsm/adt_event.h)
+ AC_DEFINE(HAVE_ADT)])
+ ;;
+ *)
+ # All others
+ ;;
+ esac
+fi
--- config.h.in Wed Jan 2 23:21:36 2013
+++ config.h.in Wed Jan 2 21:44:45 2013
@@ -672,6 +672,11 @@
#undef HAVE_TSOL
/*
+ * Do we have Auditing support ?
+ */
+#undef HAVE_ADT
+
+/*
* Do we have SELinux support?
*/
#undef HAVE_SELINUX
--- Makedefs.in Wed Jan 2 23:21:36 2013
+++ Makedefs.in Wed Jan 2 21:46:40 2013
@@ -125,7 +125,7 @@
BANNERTOPS = @BANNERTOPS@
CFLAGS = @CPPFLAGS@ @CFLAGS@
COMMONLIBS = @LIBS@
-CUPSDLIBS = @CUPSDLIBS@ @LABELING_LIBS@
+CUPSDLIBS = @CUPSDLIBS@ @LABELING_LIBS@ @AUDITING_LIBS@
CXXFLAGS = @CPPFLAGS@ @CXXFLAGS@
CXXLIBS = @CXXLIBS@
DBUS_NOTIFIER = @DBUS_NOTIFIER@
--- scheduler/client.h Wed Jan 2 23:21:36 2013
+++ scheduler/client.h Wed Jan 2 21:44:36 2013
@@ -17,6 +17,9 @@
# include <Security/Authorization.h>
#endif /* HAVE_AUTHORIZATION_H */
+#ifdef HAVE_ADT
+#include <bsm/adt.h>
+#endif /* HAVE_ADT */
/*
* HTTP client structure...
@@ -64,8 +67,13 @@
#ifdef HAVE_AUTHORIZATION_H
AuthorizationRef authref; /* Authorization ref */
#endif /* HAVE_AUTHORIZATION_H */
- char *slabel; /* security context for security
+#ifdef HAVE_TSOL
+ char *slabel; /* security context for security
labeled environments */
+#endif /* HAVE_TSOL */
+#ifdef HAVE_ADT
+ adt_session_data_t *ah /* audit handle for print requests */
+#endif /* HAVE_ADT */
};
#define HTTP(con) &((con)->http)
--- scheduler/job.h Wed Jan 2 23:21:36 2013
+++ scheduler/job.h Wed Jan 2 21:46:16 2013
@@ -13,6 +13,10 @@
* file is missing or damaged, see the license at "http://www.cups.org/".
*/
+#ifdef HAVE_ADT
+#include <bsm/adt.h>
+#endif /* HAVE_ADT */
+
/*
* Constants...
*/
@@ -83,8 +87,13 @@
krb5_ccache ccache; /* Kerberos credential cache */
char *ccname; /* KRB5CCNAME environment variable */
#endif /* HAVE_GSSAPI */
- char *slabel; /* security context for security
+#ifdef HAVE_TSOL
+ char *slabel; /* security context for security
labeled environments */
+#endif /* HAVE_TSOL */
+#ifdef HAVE_ADT
+ adt_session_data_t *ah; /* audit handle for print requests */
+#endif /* HAVE_ADT */
};
typedef struct cupsd_joblog_s /**** Job log message ****/
--- scheduler/ipp.c Wed Jan 2 23:21:36 2013
+++ scheduler/ipp.c Wed Jan 2 23:25:46 2013
@@ -1425,12 +1425,20 @@
*media_margin; /* media-*-margin attribute */
ipp_t *unsup_col; /* media-col in unsupported response */
-
cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))",
con, con->http.fd, printer, printer->name,
filetype, filetype ? filetype->super : "none",
filetype ? filetype->type : "none");
+#if defined(HAVE_TSOL)
+ /* Start audit session to audit CUPS print requests */
+ if (is_system_labeled() != 0) {
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "add_job: Start CUPS audit session"
+ " for connection");
+ cups_audit_start_session(con);
+ }
+#endif /* HAVE_TSOL */
+
/*
* Validate that the label associated with the connection is acceptable for
* printing on the printer.
@@ -1437,6 +1445,11 @@
*/
if (cupsdInPrinterLabelRange(con->slabel, printer) == 0)
{
+#if defined(HAVE_TSOL)
+ /* Audit the print request */
+ if (is_system_labeled() != 0)
+ cups_audit_print_request_denial(con, NULL, printer, -1, EACCES);
+#endif /* HAVE_TSOL */
send_ipp_status(con, IPP_NOT_AUTHORIZED, _("label violation."));
return (NULL);
}
@@ -1450,6 +1463,12 @@
strcasecmp(con->http.hostname, "localhost") &&
strcasecmp(con->http.hostname, ServerName))
{
+#if defined(HAVE_TSOL)
+ /* Audit the print request */
+ if (is_system_labeled() != 0)
+ cups_audit_print_request_denial(con, NULL, printer, -1, EPERM);
+#endif /* HAVE_TSOL */
+
send_ipp_status(con, IPP_NOT_AUTHORIZED,
_("The printer or class is not shared!"));
return (NULL);
@@ -1463,6 +1482,11 @@
if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
{
+#if defined(HAVE_TSOL)
+ /* Audit the print request */
+ if (is_system_labeled() != 0)
+ cups_audit_print_request_denial(con, NULL, printer, -1, EPERM);
+#endif /* HAVE_TSOL */
send_http_error(con, status, printer);
return (NULL);
}
@@ -1714,6 +1738,21 @@
NULL, label_to_page_top_bottom_string(job->slabel));
}
+#if defined(HAVE_TSOL)
+ /*
+ * Initialize audit handle for job.
+ * Audit handle for job is same as that for connection.
+ * Connection handle is not available in job.c where job requests
+ * are processed so the same is copied to job handle so that
+ * job requests can be audited.
+ * Here just the handle pointer is being copied. It will stay valid
+ * throughout the processing of a print request.
+ */
+ if (is_system_labeled() != 0)
+ job->ah = con->ah;
+
+#endif /* HAVE_TSOL */
+
job->dtype = printer->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
CUPS_PRINTER_REMOTE);
job->attrs = con->request;
@@ -1851,6 +1890,7 @@
*/
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
+
job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
"job-state", IPP_JOB_STOPPED);
job->state_value = (ipp_jstate_t)job->state->values[0].integer;
@@ -4081,6 +4121,11 @@
if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
NULL)) != HTTP_OK)
{
+#if defined(HAVE_TSOL)
+ /* Audit the print request */
+ if (is_system_labeled() != 0)
+ cups_audit_print_request_denial(con, NULL, printer, -1, EPERM);
+#endif /* HAVE_TSOL */
send_http_error(con, status, printer);
return;
}
--- scheduler/job.c Wed Jan 2 23:21:36 2013
+++ scheduler/job.c Wed Jan 2 21:46:11 2013
@@ -75,6 +75,9 @@
#include <cups/backend.h>
#include <cups/dir.h>
+#if defined(HAVE_TSOL)
+#include <bsm/adt_event.h>
+#endif /* HAVE_TSOL */
/*
* Design Notes for Job Management
@@ -2712,6 +2715,9 @@
const char *message; /* Message for job state */
char buffer[1024]; /* Buffer for formatted messages */
+#if defined(HAVE_TSOL)
+ ipp_attribute_t *attr; /* Current attributes */
+#endif /* HAVE_TSOL */
cupsdLogMessage(CUPSD_LOG_DEBUG2, "finalize_job(job=%p(%d))", job, job->id);
@@ -2986,6 +2992,13 @@
"error_log file for details.";
}
}
+#if defined(HAVE_TSOL)
+ /* Audit the print request. */
+ if (is_system_labeled() != 0) {
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "finalize_job(cups_audit_print_request)");
+ cups_audit_print_request(job, job_state);
+ }
+#endif /* HAVE_TSOL */
/*
* Update the printer and job state.
@@ -4037,6 +4050,12 @@
"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");
+#if defined(HAVE_TSOL)
+ if (is_system_labeled() != 0)
+ cups_audit_print_request_denial(NULL, job, printer,
+ job->state_value, EACCES);
+#endif /*HAVE_TSOL */
+
cupsdCheckJobs();
return;
}
@@ -4586,7 +4605,6 @@
/*
* Handle the end of job stuff...
*/
-
finalize_job(job, 1);
/*
--- scheduler/cups-lpd.c Wed Sep 3 05:57:17 2008
+++ scheduler/cups-lpd.c Wed Jan 2 21:45:33 2013
@@ -348,8 +348,11 @@
ipp_attribute_t *attr; /* IPP attribute */
char uri[HTTP_MAX_URI]; /* Printer URI */
int id; /* Job ID */
+#if defined(HAVE_TSOL)
+ char *hostname;
+ const char uid[11], gid[11]; /* user id and group id */
+#endif /* HAVE_TSOL */
-
/*
* Setup the Create-Job request...
*/
@@ -373,6 +376,29 @@
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
NULL, docname);
+#if defined(HAVE_TSOL)
+ if (is_system_labeled() != 0) {
+ /* Add job-originating-host-name to the request */
+ if ((hostname = cupsGetOption("job-originating-host-name", num_options,
+ options)) != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "job-originating-host-name", NULL, hostname);
+
+ /* Add userID and groupID of the user who submitted the request */
+
+ /*
+ * When uid and gid are added to the attributes using ippAddInteger,
+ * ippFindAttribute from the scheduler is not able to read the attribute,
+ * so using ippAddString to add uid and gid.
+ */
+ snprintf(uid, sizeof(uid), "%d", getuid());
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "userID", NULL, uid);
+
+ snprintf(gid, sizeof(gid), "%d", getgid());
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "groupID", NULL, gid);
+ }
+#endif /* HAVE_TSOL */
+
cupsEncodeOptions(request, num_options, options);
/*
--- scheduler/audit.c Wed Jan 2 23:26:12 2013
+++ scheduler/audit.c Wed Jan 2 21:44:22 2013
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301 USA
+ */
+#include <syslog.h>
+#include "cupsd.h"
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+#include <auth_attr.h>
+
+#define PRINT_UNLABELED_AUTH "solaris.print.unlabeled"
+#define PRINT_NOBANNER_AUTH "solaris.print.nobanner"
+
+/*
+ * Maximum length of string to hold requested job id:
+ * <printer-name>-<job-id>
+ * IPP_MAX_NAME+1+10+1
+ */
+#define REQ_MAX_NAME IPP_MAX_NAME+1+10+1
+
+uid_t uid = ADT_NO_ATTRIB;
+gid_t gid = ADT_NO_ATTRIB;
+
+void cups_audit_start_session(cupsd_client_t *con);
+void cups_audit_print_request_denial(cupsd_client_t *con, cupsd_job_t *job,
+ cupsd_printer_t *printer, ipp_jstate_t job_state, int fail_reason);
+void cups_audit_print_request(cupsd_job_t *job, ipp_jstate_t job_state);
+
+/*
+ * Set up an audit session for auditing the current print request.
+ */
+void
+cups_audit_start_session(cupsd_client_t *con)
+{
+
+ if (adt_start_session(&con->ah, NULL, 0) != 0) {
+ cupsdLogMessage(CUPSD_LOG_EMERG, "cups_audit_start_session(): "
+ "adt_start_session(): %d", errno);
+ con->ah = NULL;
+ return;
+ }
+
+ if (strcasecmp(con->http.hostname, "localhost") == 0) {
+ /* Local Request */
+ ucred_t *cred = NULL;
+
+ if (getpeerucred(con->http.fd, &cred) == -1) {
+ cupsdLogMessage(CUPSD_LOG_EMERG,
+ "cups_audit_start_session(): "
+ "getpeerucred(): %d", errno);
+ (void) adt_end_session(con->ah);
+ con->ah = NULL;
+ return;
+ }
+
+ if (adt_set_from_ucred(con->ah, cred, ADT_NEW) != 0) {
+ cupsdLogMessage(CUPSD_LOG_EMERG,
+ "cups_audit_start_session(): "
+ "adt_set_from_ucred(): %d", errno);
+ (void) adt_end_session(con->ah);
+ con->ah = NULL;
+ return;
+ }
+ } else {
+ /* Remote Request */
+ ipp_attribute_t *userID = NULL, *grpID = NULL;
+ adt_termid_t *termid = NULL;
+
+ if ((userID = ippFindAttribute(con->request, "userID",
+ IPP_TAG_NAME)) != NULL) {
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cups_audit_start_session():"
+ "userID = %s", userID->values[0].string.text);
+ uid = atoi(userID->values[0].string.text);
+ } else {
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cups_audit_start_session():"
+ "userID = NULL");
+ }
+
+ if ((grpID = ippFindAttribute(con->request, "groupID",
+ IPP_TAG_NAME)) != NULL) {
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cups_audit_start_session():"
+ "grpID = %s", grpID->values[0].string.text);
+ gid = atoi(grpID->values[0].string.text);
+ } else {
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cups_audit_start_session():"
+ "grpID = NULL");
+ }
+
+ if (adt_load_termid(con->http.fd, &termid) != 0) {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cups_audit_start_session(): "
+ "adt_load_termid(): %d", errno);
+ }
+ if (adt_set_user(con->ah, uid, gid, uid, gid, termid,
+ ADT_NEW) != 0) {
+ cupsdLogMessage(CUPSD_LOG_EMERG,
+ "cups_audit_start_session(): "
+ "adt_set_user(): %d", errno);
+ (void) adt_end_session(con->ah);
+ con->ah = NULL;
+ free(termid);
+ return;
+ }
+ free(termid);
+ }
+}
+
+/*
+ * Routine for filling in the audit event details for each of the
+ * three types of TX-related printing audit records:
+ * AUE_print_request - audit each file in print request
+ * AUE_print_request_unlabeled - audit use of the solaris.print.nolabels auth
+ * AUE_print_request_nobanner - audit use of the solaris.print.nobanner auth
+ * One AUE_print_request audit record is written for each text file in the
+ * print request. The '-o nolabels' and '-o nobanner' options apply to the
+ * entire print request so one AUE_print_request_unlabeled audit record and one
+ * AUE_print_request_nobanner audit record is written, respectively, for each
+ * file in the print request when these options are supplied.
+ *
+ * event_ID = type of TX-related printing audit record
+ * event = audit event structure
+ * printer = printer name
+ * slabel = Sensitivity Label
+ * job_ID = request_ID; <printe-name>-<job-id>
+ * job_ID_remote = remote request_ID; <printe-name>-<remote-job_id>
+ * job id for the same request differ on client and server
+ * filelist = file to be printed;
+ * In case 'filelist' is 'stdin', temporary filename
+ * created in /var/spool/cups/ is read
+ * ai = struct addrinfo;
+ * status = audit request status;
+ * ADT_FAILURE, ADT_SUCCESS
+ * return_val = return value of audit request;
+ * If user is not authorized then ADT_FAIL_VALUE_AUTH
+ * If print job is successfully completed then ADT_SUCCESS
+ * If job is cancelled then
+ * ADT_FAIL_VALUE_PRINT_JOB_CANCELLED
+ * MAC failures it is EACESS
+ * DAC failures it is EPERM
+ * i = which of the job files is this 000-999 for the CUPS max of
+ * 1000 jobs. See util.c
+ * job_id = print request job id; this is used to get the location
+ * of the temporary file in spool directory which is of the format:
+ * /var/spool/cups/d00550-001
+ * where 550 is the job-id.
+ * remote = true: remote request
+ * false: local request
+ */
+static void
+cups_audit_print_request_data(au_event_t event_ID,
+ adt_event_data_t *event, char *printer, char *slabel,
+ char *job_ID, char *job_ID_remote, char *filelist,
+ struct addrinfo *ai, int status, int return_val, int i,
+ int job_id, boolean_t remote)
+{
+ char tmpfile[IPP_MAX_NAME];
+ char tfile[IPP_MAX_NAME];
+ char r_job_ID[IPP_MAX_NAME];
+ char *file, *f_job_ID = NULL;
+ struct stat attributes;
+ m_label_t *file_label = NULL;
+
+ /*
+ * If 'filelist' is 'stdin', read the temporary filename
+ * created for the file contents in /var/spool/cups/
+ * This will be useful to audit the files printed
+ * via standard input.
+ * Scheduler can't discern the difference between a
+ * file printed via stdin and one printed normally so
+ * the file attributes and label are always included
+ * even though they refer to the temporary file created
+ * in the case of stdin.
+ * The label is going to be the same regardless of wheather
+ * it is of the tmp file that was used for stdin or the real file.
+ */
+ if (remote) {
+ /*
+ * temporary filename is of the format:
+ * /var/spool/cups/d00550-001
+ * where 550 is the job-id.
+ */
+ snprintf(tmpfile, sizeof (tmpfile), "%s/d%05d-%03d",
+ RequestRoot, job_id, i + 1);
+ /*
+ * In case of remote request,
+ * Job_ID string is like:
+ * 'Local JOB_ID: <printer>-id |
+ * Remote JOB_ID: <printer>-<remote-id>'
+ */
+ if (job_ID_remote != NULL) {
+ snprintf(r_job_ID, sizeof (r_job_ID),
+ "Local JOB_ID: %s |"
+ " Remote JOB_ID: %s-%s",
+ job_ID, printer, job_ID_remote);
+ f_job_ID = r_job_ID;
+ } else {
+ f_job_ID = job_ID;
+ }
+
+ } else {
+ f_job_ID = job_ID;
+ if (job_id != -1) {
+ if ((strncmp(filelist, "(stdin)", 7)) == 0) {
+ snprintf(tmpfile, sizeof (tmpfile),
+ "%s/d%05d-%03d", RequestRoot,
+ job_id, i + 1);
+ } else {
+ strlcpy(tmpfile, filelist, sizeof (tmpfile));
+ }
+ } else {
+ strlcpy(tmpfile, filelist, sizeof (tmpfile));
+ }
+ }
+
+ file = tmpfile;
+
+ if (stat(file, &attributes) == -1) {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cups_audit_print_request_data():"
+ "stat(): %d", errno);
+ }
+
+ /*
+ * In case of remote request, filelist contains client document-name.
+ * Append the same to filename before writing to the audit record.
+ */
+ if (remote && (filelist != NULL)) {
+ strlcpy(tfile, "Local file: ", sizeof (tmpfile));
+ strcat(tfile, tmpfile);
+ strcat(tfile, " | Remote file: ");
+ strcat(tfile, filelist);
+ file = tfile;
+ }
+
+ /* extract label from slabel */
+ if (str_to_label(slabel, &file_label, MAC_LABEL,
+ L_NO_CORRECTION, NULL) == 0) {
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cups_audit_print_request_data():"
+ "file_label extracted");
+ } else {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cups_audit_print_request_data():"
+ "file_label not extracted");
+ }
+
+ /* Populate audit event structure */
+ switch (event_ID) {
+ case ADT_print_request_unlabeled:
+ if (remote) {
+ if (ai->ai_family == AF_INET) {
+ event->adt_print_request_unlabeled.
+ remote_host_ip_type = ADT_IPv4;
+
+ (void) memcpy(event->
+ adt_print_request_unlabeled.
+ remote_host_ipaddress,
+ &((struct sockaddr_in *)ai->
+ ai_addr)->sin_addr,
+ sizeof (struct in_addr));
+ } else if (ai->ai_family == AF_INET6) {
+ event->adt_print_request_unlabeled.
+ remote_host_ip_type = ADT_IPv6;
+
+ (void) memcpy(event->
+ adt_print_request_unlabeled.
+ remote_host_ipaddress,
+ &((struct sockaddr_in6 *)ai->
+ ai_addr)->sin6_addr,
+ sizeof (struct in6_addr));
+ }
+ }
+ event->adt_print_request_unlabeled.request_ID = f_job_ID;
+ event->adt_print_request_unlabeled.printer = printer;
+ event->adt_print_request_unlabeled.file = file;
+ event->adt_print_request_unlabeled.file_attrs = &attributes;
+ event->adt_print_request_unlabeled.file_label = file_label;
+ event->adt_print_request_unlabeled.override_auth =
+ PRINT_UNLABELED_AUTH;
+ break;
+ case ADT_print_request_nobanner:
+ if (remote) {
+ if (ai->ai_family == AF_INET) {
+ event->adt_print_request_nobanner.
+ remote_host_ip_type = ADT_IPv4;
+
+ (void) memcpy(event->
+ adt_print_request_nobanner.
+ remote_host_ipaddress,
+ &((struct sockaddr_in *)ai->
+ ai_addr)->sin_addr,
+ sizeof (struct in_addr));
+ } else if (ai->ai_family == AF_INET6) {
+ event->adt_print_request_nobanner.
+ remote_host_ip_type = ADT_IPv6;
+
+ (void) memcpy(event->
+ adt_print_request_nobanner.
+ remote_host_ipaddress,
+ &((struct sockaddr_in6 *)ai->
+ ai_addr)->sin6_addr,
+ sizeof (struct in6_addr));
+ }
+ }
+ event->adt_print_request_nobanner.request_ID = f_job_ID;
+ event->adt_print_request_nobanner.printer = printer;
+ event->adt_print_request_nobanner.file = file;
+ event->adt_print_request_nobanner.file_attrs = &attributes;
+ event->adt_print_request_nobanner.file_label = file_label;
+ event->adt_print_request_nobanner.override_auth =
+ PRINT_NOBANNER_AUTH;
+ break;
+ case ADT_print_request:
+ if (remote) {
+ if (ai->ai_family == AF_INET) {
+ event->adt_print_request.
+ remote_host_ip_type = ADT_IPv4;
+
+ (void) memcpy(event->
+ adt_print_request.
+ remote_host_ipaddress,
+ &((struct sockaddr_in *)ai->
+ ai_addr)->sin_addr,
+ sizeof (struct in_addr));
+ } else if (ai->ai_family == AF_INET6) {
+ event->adt_print_request.
+ remote_host_ip_type = ADT_IPv6;
+
+ (void) memcpy(event->
+ adt_print_request.
+ remote_host_ipaddress,
+ &((struct sockaddr_in6 *)ai->
+ ai_addr)->sin6_addr,
+ sizeof (struct in6_addr));
+ }
+ }
+ event->adt_print_request.request_ID = f_job_ID;
+ event->adt_print_request.printer = printer;
+ event->adt_print_request.file = file;
+ event->adt_print_request.file_attrs = &attributes;
+ event->adt_print_request.file_label = file_label;
+ break;
+ }
+
+ if (adt_put_event(event, status, return_val) != 0) {
+ cupsdLogMessage(CUPSD_LOG_EMERG,
+ "cups_audit_print_request_data():"
+ "adt_put_event(): %d", errno);
+ }
+}
+
+/*
+ * To populate the audit events, cups_audit_print_request() is called.
+ * cups_audit_print_request() calls cups_audit_print_request_common()
+ * which in turn calls cups_audit_print_request_sub() which finally
+ * calls cups_audit_print_request_data().
+ *
+ * event_ID = type of TX-related printing audit record
+ * ADT_print_request_unlabeled
+ * ADT_print_request_nobanner
+ * ADT_print_request
+ * ah = Audit session handle
+ * printer = printer name
+ * job_ID = request_ID; <printe-name>-<job-id>
+ * job_id = job->id; Job identification number.
+ * -1 in case of denial request when
+ * a request is denied before job gets created.
+ * -1 in case of denial request when
+ * a request is denied before job gets created.
+ * job_ID_remote = remote request_ID; <printe-name>-<remote-job_id>
+ * local job_id and remote job_id are different
+ * job_state = ipp_jstate_t enum:
+ * IPP_JOB_PENDING = 3, Job is waiting to be printed
+ * IPP_JOB_HELD, Job is held for printing
+ * IPP_JOB_PROCESSING, Job is currently printing
+ * IPP_JOB_STOPPED, Job has been stopped
+ * IPP_JOB_CANCELED, Job has been canceled
+ * IPP_JOB_ABORTED, Job has aborted due to error
+ * IPP_JOB_COMPLETED Job has completed successfully
+ * current job state
+ * -1 in case of denial request when
+ * a request is denied before job gets created.
+ * job_state_value = ipp_jstate_t; cached job state
+ * -1 in case of denial request when
+ * a request is denied before job gets created.
+ * hostname = name of connected host
+ * document_list = list of files to be printed
+ * job_name = job title; usually the first filename in filelist
+ * slabel = Sensitivity Label
+ * fail_reason = failure reason; EPERM, EACESS
+ * authorized = true: user has required autorization to print
+ * false: user is not authorized to print
+ * num_files = 0 for initial MAC/DAC
+ * 1 - 999 for number of files printed in a job.
+ * wants_nobanner = true: user does not want to print banner and trailer page
+ * false: user wants to print banner and trailer page
+ */
+static void
+cups_audit_print_request_sub(au_event_t event_ID, adt_session_data_t *ah,
+ char *printer, char *job_ID, int job_id, char *job_ID_remote,
+ ipp_jstate_t job_state, ipp_jstate_t job_state_value, char *hostname,
+ char *document_list, char *job_name, char *slabel, int fail_reason,
+ int authorized, int num_files, boolean_t wants_nobanner)
+{
+ adt_event_data_t *event;
+ char *dlist, *filelist;
+ int status, return_val;
+ struct addrinfo *ai = NULL;
+ boolean_t remote = B_FALSE;
+
+ if ((event = adt_alloc_event(ah, event_ID)) == NULL) {
+ cupsdLogMessage(CUPSD_LOG_EMERG,
+ "cups_audit_print_request_sub():"
+ "adt_alloc_event(): %d", errno);
+ return;
+ }
+
+ if ((job_id == -1) && (job_state == -1) && (job_state_value == -1)) {
+ status = ADT_FAILURE;
+ return_val = fail_reason;
+ } else {
+ if (authorized != 1) {
+ status = ADT_FAILURE;
+ return_val = ADT_FAIL_VALUE_AUTH;
+ } else {
+ if (job_state == IPP_JOB_COMPLETED) {
+ status = ADT_SUCCESS;
+ return_val = ADT_SUCCESS;
+ } else {
+ status = ADT_FAILURE;
+ if ((fail_reason == 0) &&
+ (job_state_value == IPP_JOB_CANCELED)) {
+ return_val =
+ ADT_FAIL_VALUE_PRINT_JOB_CANCELLED;
+ } else if (fail_reason == 0) {
+ return_val =
+ ADT_FAIL_VALUE_PRINT_JOB_FAILED;
+ } else {
+ return_val = fail_reason;
+ }
+ }
+ }
+ }
+
+ if (hostname != NULL) {
+ int err;
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cups_audit_print_request_sub():"
+ "host = %s", hostname);
+ if ((err = getaddrinfo(hostname,
+ NULL, NULL, &ai)) != 0) {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cups_audit_print_request_sub():"
+ "getaddrinfo(%s) failed[%s]",
+ hostname, gai_strerror(err));
+
+ }
+ if (strcasecmp(hostname, "localhost") != 0) {
+ remote = B_TRUE;
+ }
+ } else {
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cups_audit_print_request_sub():"
+ "host is NULL");
+ }
+
+ if (document_list == NULL) {
+ dlist = job_name;
+ } else {
+ dlist = document_list;
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cups_audit_print_request_sub():"
+ "document-names is %s ", dlist);
+ }
+
+ /*
+ * document-names=f1,f2,f3...
+ * extract the files from it.
+ */
+ if (dlist != NULL) {
+ filelist = strtok(dlist, ",");
+ }
+
+ if (remote) {
+ /* Remote Request */
+ int i = 1;
+ int max_files = (num_files - 1);
+
+ /*
+ * remote root always sends banner and trailer files
+ * to the server. Whereas in case of non-root if
+ * nobanner is set it sends only the job file to
+ * the server.
+ */
+ if ((uid != 0) && (gid != 0) && wants_nobanner) {
+ i = 0;
+ max_files = num_files;
+ }
+
+ for (; i < max_files; i++) {
+ cups_audit_print_request_data(event_ID,
+ event, printer, slabel, job_ID, job_ID_remote,
+ filelist, ai, status, return_val,
+ i, job_id, remote);
+
+ if (filelist != NULL) {
+ filelist = strtok(NULL, ",");
+ }
+ }
+ } else {
+ /* Local Request */
+ while (filelist != NULL) {
+ cups_audit_print_request_data(event_ID,
+ event, printer, slabel, job_ID, job_ID_remote,
+ filelist, ai, status, return_val,
+ 0, job_id, remote);
+ filelist = strtok(NULL, ",");
+ }
+ }
+
+ if (ai != NULL) {
+ freeaddrinfo(ai);
+ }
+
+ adt_free_event(event);
+}
+
+/*
+ * To populate the audit events, cups_audit_print_request() is called.
+ * cups_audit_print_request() calls cups_audit_print_request_common()
+ * which in turns calls cups_audit_print_request_sub() which finally
+ * calls cups_audit_print_request_data().
+ *
+ * ah = Audit session handle
+ * request_ID = <printer-name>-<job_id>;
+ * <printer-name>-none in case of denial request when
+ * a request is denied before job gets created.
+ * job_id = job->id; Job identification number.
+ * -1 in case of denial request when
+ * a request is denied before job gets created.
+ * job_state = ipp_jstate_t enum:
+ * IPP_JOB_PENDING = 3, Job is waiting to be printed
+ * IPP_JOB_HELD, Job is held for printing
+ * IPP_JOB_PROCESSING, Job is currently printing
+ * IPP_JOB_STOPPED, Job has been stopped
+ * IPP_JOB_CANCELED, Job has been canceled
+ * IPP_JOB_ABORTED, Job has aborted due to error
+ * IPP_JOB_COMPLETED Job has completed successfully
+ * current job state
+ * -1 in case of denial request when
+ * a request is denied before job gets created.
+ * job_state_value = ipp_jstate_t; cached job state
+ * -1 in case of denial request when
+ * a request is denied before job gets created.
+ * ipp = ipp_t; Request information
+ * printer = cupsd_printer_t
+ * slabel = Sensitivity Label
+ * username = Username from Authorization
+ * hname = name of connected host
+ * fail_reason = failure reason; EPERM, EACESS
+ * num_files = 0 for initial MAC/DAC
+ * 1 - 999 for number of files printed in a job.
+ * denial_request = false if cups_audit_print_request()
+ * true if cups_audit_print_request_denial()
+ */
+static void
+cups_audit_print_request_common(adt_session_data_t *ah,
+ char *request_ID, int job_id, ipp_jstate_t job_state,
+ ipp_jstate_t job_state_value, ipp_t *ipp, cupsd_printer_t *printer,
+ char *slabel, char *username, char *hname, int fail_reason,
+ int num_files, boolean_t denial_request)
+{
+ int authorized = 0;
+ boolean_t wants_nobanner = B_FALSE;
+ boolean_t wants_nolabels = B_FALSE;
+ ipp_attribute_t *attr = NULL;
+ char *document_list = NULL, *job_name = NULL;
+ char *hostname = NULL, *job_id_remote = NULL;
+
+ /*
+ * Read 'document-names', 'job-name',
+ * 'job-id-remote' and 'job-originating-host-name'
+ * from request.
+ */
+ if ((attr = ippFindAttribute(ipp, "document-names",
+ IPP_TAG_NAME)) != NULL) {
+ document_list = attr->values[0].string.text;
+ }
+
+ if ((attr = ippFindAttribute(ipp, "job-name", IPP_TAG_NAME)) != NULL) {
+ job_name = attr->values[0].string.text;
+ }
+
+ /* If input hostname is NULL, then read hostname from job */
+ if (hname == NULL) {
+ if ((attr = ippFindAttribute(ipp, "job-originating-host-name",
+ IPP_TAG_NAME)) != NULL) {
+ hostname = attr->values[0].string.text;
+ }
+ } else {
+ hostname = hname;
+ }
+
+ if ((attr = ippFindAttribute(ipp, "job-id-remote",
+ IPP_TAG_NAME)) != NULL) {
+ job_id_remote = attr->values[0].string.text;
+ }
+
+ /*
+ * '-o job-sheets=none,none' means
+ * user does not want to print banner and trailer page.
+ * In trusted system, '-o job-sheets=none' and '-o job-sheets=none,none'
+ * means same.
+ */
+ if ((attr = ippFindAttribute(ipp, "job-sheets",
+ IPP_TAG_ZERO)) != NULL) {
+ if (strcmp(attr->values[0].string.text, "none") == 0) {
+ wants_nobanner = B_TRUE;
+ }
+ }
+
+ if (wants_nobanner) {
+ authorized = chkauthattr(PRINT_NOBANNER_AUTH, username);
+
+ /*
+ * In case of normal print request
+ * fail_reason is based on authorized.
+ */
+ if (!denial_request) {
+ if (authorized) {
+ fail_reason = 0;
+ } else {
+ fail_reason = ADT_FAIL_VALUE_AUTH;
+ }
+ }
+
+ cups_audit_print_request_sub(
+ ADT_print_request_nobanner, ah, printer->name,
+ request_ID, job_id, job_id_remote, job_state,
+ job_state_value, hostname, document_list,
+ job_name, slabel, fail_reason,
+ authorized, num_files, wants_nobanner);
+ }
+
+ /* chk for 'nolabels' in request */
+ if (ippFindAttribute(ipp, "labels", IPP_TAG_ZERO) != NULL) {
+ wants_nolabels = B_TRUE;
+
+ authorized = chkauthattr(PRINT_UNLABELED_AUTH, username);
+
+ /*
+ * In case of normal print request
+ * fail_reason is based on authorized.
+ */
+ if (!denial_request) {
+ if (authorized) {
+ fail_reason = 0;
+ } else {
+ fail_reason = ADT_FAIL_VALUE_AUTH;
+ }
+ }
+
+ cups_audit_print_request_sub(
+ ADT_print_request_unlabeled, ah, printer->name,
+ request_ID, job_id, job_id_remote, job_state,
+ job_state_value, hostname, document_list,
+ job_name, slabel, fail_reason,
+ authorized, num_files, wants_nobanner);
+ }
+
+ if (!wants_nolabels && !wants_nobanner) {
+ /*
+ * In case of normal print request
+ * fail_reason is based on authorized.
+ */
+ if (!denial_request) {
+ fail_reason = 0;
+ }
+
+ cups_audit_print_request_sub(
+ ADT_print_request, ah, printer->name,
+ request_ID, job_id, job_id_remote, job_state,
+ job_state_value, hostname, document_list,
+ job_name, slabel, fail_reason,
+ 1, num_files, wants_nobanner);
+ }
+
+ (void) adt_end_session(ah);
+}
+
+/*
+ * Routine for auditing print requests which have been validated.
+ * This means user has all of the required authorizations, the
+ * ability to print to the designated printer and the printer is
+ * within the user's label range.
+ *
+ * cupsd_job_[s]t int id Job ID
+ * ipp_jstate_t state_value Cached job-state
+ * char *slabel Sensitivity Label
+ * adt_session_data *ah Audit session handle
+ * char *username Printing user
+ * int num_files Number of files in job
+ * ipp_t *attrs Job attributes
+ * place to get attributes of job such as
+ * document-names, job-name, job-originating-host-name,
+ * job-id-remote, job-sheets, labels
+ * cupsd_printer_t *printer Printer this job is assigned to
+ * char *name Printer name
+ */
+void
+cups_audit_print_request(cupsd_job_t *job, ipp_jstate_t job_state)
+{
+ char request_ID[REQ_MAX_NAME];
+
+ snprintf(request_ID, sizeof (request_ID), "%s-%d",
+ job->printer->name, job->id);
+
+ cups_audit_print_request_common(job->ah, request_ID,
+ job->id, job_state, job->state_value, job->attrs,
+ job->printer, job->slabel, job->username, NULL, 0,
+ job->num_files, B_FALSE);
+}
+
+/*
+ * Routine for auditing print request which have been denied due
+ * to lack of authorization or the printer access list is configured
+ * to deny access to the user.
+ * 'fail_reason' will be either EPERM or EACCES based on DAC or MAC
+ * failure resp.
+ *
+ * cupsd_client_[s]t
+ * adt_session_data_t *ah Audit session handle
+ * ipp_t *request IPP request information
+ * appears equivalent to job->attrs above.
+ * char *slabel Sesnitivity Label
+ * char *username Username from Authorization
+ * http_t http HTTP client connection
+ * char *hostname name of connected host
+ *
+ */
+void
+cups_audit_print_request_denial(cupsd_client_t *con, cupsd_job_t *job,
+ cupsd_printer_t *printer, ipp_jstate_t job_state, int fail_reason)
+{
+ char request_ID[REQ_MAX_NAME];
+
+ /*
+ * If connection is not NULL, audit based on connection and
+ * not job.
+ */
+ if (con != NULL) {
+ sprintf(request_ID, "%s-none", printer->name);
+ cups_audit_print_request_common(con->ah, request_ID,
+ -1, -1, -1, con->request, printer, con->slabel,
+ con->username, con->http.hostname, fail_reason, 0, B_TRUE);
+ } else {
+ sprintf(request_ID, "%s-%d", job->printer->name, job->id);
+ cups_audit_print_request_common(job->ah, request_ID,
+ job->id, job_state, job->state_value, job->attrs,
+ printer, job->slabel, job->username, NULL, fail_reason,
+ job->num_files, B_TRUE);
+ }
+}
--- scheduler/Makefile Wed Jan 2 23:21:35 2013
+++ scheduler/Makefile Wed Jan 2 21:46:49 2013
@@ -16,6 +16,7 @@
include ../Makedefs
CUPSDOBJS = \
+ audit.o \
auth.o \
banners.o \
cert.o \
--- backend/ipp.c Thu Jan 14 14:40:19 2010
+++ backend/ipp.c Wed Jan 2 21:46:02 2013
@@ -893,6 +893,15 @@
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
NULL, uri);
+#if defined(HAVE_TSOL)
+ if (is_system_labeled() != 0) {
+ /* Keep a track of the remote job-id */
+ if (argv[1][0])
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "job-id-remote", NULL, argv[1]);
+ }
+#endif /* HAVE_TSOL */
+
fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
if (argv[2][0])
--- cups/util.c Thu Jan 3 00:07:27 2013
+++ cups/util.c Thu Jan 3 02:00:02 2013
@@ -220,8 +220,11 @@
*response; /* Create-Job response */
ipp_attribute_t *attr; /* job-id attribute */
int job_id = 0; /* job-id value */
+#if defined(HAVE_TSOL)
+ const char *filelist; /* list of input files */
+ const char uid[11], gid[11]; /* user id and group id */
+#endif /* HAVE_TSOL */
-
DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", "
"num_options=%d, options=%p)",
http, name, title, num_options, options));
@@ -257,6 +260,28 @@
if (title)
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
title);
+
+#if defined(HAVE_TSOL)
+ if (is_system_labeled() != 0) {
+ if ((filelist = cupsGetOption("filelist", num_options, options)) != NULL)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-names",
+ NULL, filelist);
+
+ /* Add userID and groupID of the user who submitted the request */
+
+ /*
+ * When uid and gid are added to the attributes using ippAddInteger,
+ * ippFindAttribute from the scheduler is not able to read the attribute,
+ * so using ippAddString to add uid and gid.
+ */
+ snprintf(uid, sizeof(uid), "%d", getuid());
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "userID", NULL, uid);
+
+ snprintf(gid, sizeof(gid), "%d", getgid());
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "groupID", NULL, gid);
+ }
+#endif /* HAVE_TSOL */
+
cupsEncodeOptions(request, num_options, options);
/*
@@ -1462,8 +1487,12 @@
_cups_globals_t *cg = _cupsGlobals(); /* Global data */
ipp_status_t cancel_status; /* Status code to preserve */
char *cancel_message; /* Error message to preserve */
+#if defined(HAVE_TSOL)
+ int listlen = 0;
+ char *filelist; /* file list */
+ char resolved_path[PATH_MAX];
+#endif /* HAVE_TSOL */
-
DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, "
"files=%p, title=\"%s\", num_options=%d, options=%p)",
http, name, num_files, files, title, num_options, options));
@@ -1479,6 +1508,56 @@
return (0);
}
+#if defined(HAVE_TSOL)
+ if (is_system_labeled() != 0) {
+ /*
+ * Read the list of files and add to options.
+ * In options it will be stored like 'filelist=f1,f2,f3'
+ */
+
+ /*
+ * Maximum number of files to be printed should not be
+ * more than 1000.
+ */
+ if (num_files > 1000)
+ num_files = 1000;
+
+ for(i=0; i<num_files; i++) {
+ if (realpath(files[i], resolved_path) != NULL) {
+ listlen = listlen + strlen(resolved_path);
+ } else
+ listlen = listlen + strlen(files[i]);
+ }
+
+ /*
+ * Add num_files to listlen to make room for ','
+ * and NULL terminated string.
+ */
+ listlen = listlen + num_files;
+ filelist = malloc(listlen);
+
+ if (filelist != NULL) {
+ if (realpath(files[0], resolved_path) != NULL)
+ strlcpy(filelist, resolved_path, listlen);
+ else
+ strlcpy(filelist, files[0], listlen);
+
+ for (i=1; i<num_files; i++)
+ {
+ strlcat(filelist, ",", listlen);
+ if (realpath(files[i], resolved_path) != NULL)
+ strlcat(filelist, resolved_path, listlen);
+ else
+ strlcat(filelist, files[i], listlen);
+ }
+
+ num_options = cupsAddOption("filelist", filelist, num_options, &options);
+
+ free(filelist);
+ }
+ }
+#endif /* HAVE_TSOL */
+
/*
* Create the print job...
*/