# HG changeset patch # User Sonam Gupta # Date 1357624407 28800 # Node ID 6ab0c0792118c35deb93ef1d96454e92b3b443c5 # Parent d79c1a1cf6817a57c40e16b81143479c7b30f674 15698471 cups should audit printing information in TX environments 14850597 TX printing is reported to print jobs even when request authorization fail. diff -r d79c1a1cf681 -r 6ab0c0792118 components/cups/Makefile --- a/components/cups/Makefile Sat Jan 05 09:19:55 2013 -0800 +++ b/components/cups/Makefile Mon Jan 07 21:53:27 2013 -0800 @@ -20,7 +20,7 @@ # # -# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. # include ../../make-rules/shared-macros.mk @@ -63,6 +63,7 @@ CONFIGURE_OPTIONS += --enable-dbus CONFIGURE_OPTIONS += --enable-threads CONFIGURE_OPTIONS += --enable-64bit +CONFIGURE_OPTIONS += --enable-auditing CONFIGURE_OPTIONS += ARCH64FLAGS=-m64 CONFIGURE_OPTIONS += DSO64FLAGS="$(LDLIBS) -G" diff -r d79c1a1cf681 -r 6ab0c0792118 components/cups/patches/18-audit-tx.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/cups/patches/18-audit-tx.patch Mon Jan 07 21:53:27 2013 -0800 @@ -0,0 +1,1260 @@ +--- 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 + #endif /* HAVE_AUTHORIZATION_H */ + ++#ifdef HAVE_ADT ++#include ++#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 ++#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 + #include + ++#if defined(HAVE_TSOL) ++#include ++#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 ++#include "cupsd.h" ++#include ++#include ++#include ++ ++#define PRINT_UNLABELED_AUTH "solaris.print.unlabeled" ++#define PRINT_NOBANNER_AUTH "solaris.print.nobanner" ++ ++/* ++ * Maximum length of string to hold requested 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; - ++ * job_ID_remote = remote request_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: -id | ++ * Remote JOB_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; - ++ * 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; - ++ * 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 = -; ++ * -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; irequest, "requesting-user-name", IPP_TAG_NAME); ++ if (uattr) { ++ /* check for 'nolabels' in request */ ++ if (ippFindAttribute(con->request, "labels", IPP_TAG_ZERO) != NULL) { ++ /* check if user is authorized */ ++ if (chkauthattr("solaris.print.unlabeled", uattr->values[0].string.text) == 0) { ++ /* ++ * print request will fail since user ++ * does not have the required authorization. ++ */ ++ cups_audit_print_request_denial(con, NULL, printer, -1, EACCES); ++ send_http_error(con, HTTP_UNAUTHORIZED, printer); ++ return (NULL); ++ } ++ } ++ ++ /* ++ * check for nobanner. ++ * '-o job-sheets=none,none' means ++ * user does not want to print banner and trailer page. ++ */ ++ if ((attr = ippFindAttribute(con->request, "job-sheets", ++ IPP_TAG_ZERO)) != NULL) { ++ if (strcmp(attr->values[0].string.text, "none") == 0) { ++ /* ++ * User wants no banner. ++ * Check if user is authorized. ++ */ ++ if (chkauthattr("solaris.print.nobanner", uattr->values[0].string.text) == 0) { ++ /* ++ * print request will fail since user ++ * does not have the required authorization. ++ */ ++ cups_audit_print_request_denial(con, NULL, printer, -1, EACCES); ++ send_http_error(con, HTTP_UNAUTHORIZED, printer); ++ return (NULL); ++ } ++ } ++ } ++ } ++ } ++#endif /* HAVE_TSOL */ ++ + /* + * See if the printer is accepting jobs... + */