components/cups/patches/18-audit-tx.patch
branchs11-update
changeset 2490 ee4391c71c07
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/cups/patches/18-audit-tx.patch	Wed Feb 13 01:14:45 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 <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...
+   */