//
// Authentication functions for CUPS.
//
-// Copyright © 2020-2025 by OpenPrinting.
+// Copyright © 2020-2026 by OpenPrinting.
// Copyright © 2007-2019 by Apple Inc.
// Copyright © 1997-2007 by Easy Software Products.
//
static bool // O - `true` if authorized, `false` otherwise
cups_do_local_auth(http_t *http) // I - HTTP connection to server
{
-#if !_WIN32 && !__EMX__
+#if !_WIN32 && !__EMX__ && defined(AF_LOCAL)
int pid; // Current process ID
FILE *fp; // Certificate file
char trc[16], // Try Root Certificate parameter
DEBUG_printf("7cups_local_auth(http=%p) hostaddr=%s, hostname=\"%s\"", (void *)http, httpAddrString(http->hostaddr, filename, sizeof(filename)), http->hostname);
// See if we are accessing localhost...
- if (!httpAddrIsLocalhost(httpGetAddress(http)))
+ if (httpAddrGetFamily(httpGetAddress(http)) != AF_LOCAL)
{
DEBUG_puts("8cups_local_auth: Not a local connection, returning false.");
return (false);
}
# endif // HAVE_AUTHORIZATION_H
-# if defined(SO_PEERCRED) && defined(AF_LOCAL)
+# if defined(SO_PEERCRED)
// See if we can authenticate using the peer credentials provided over a
// domain socket; if so, specify "PeerCred username" as the authentication
// information...
- if (http->hostaddr->addr.sa_family == AF_LOCAL &&
- !getenv("GATEWAY_INTERFACE") && // Not via CGI programs...
+ if (!getenv("GATEWAY_INTERFACE") && // Not via CGI programs...
cups_auth_find(www_auth, "PeerCred"))
{
// Verify that the current cupsGetUser() matches the current UID...
return (true);
}
}
-#endif // !_WIN32 && !__EMX__
+#endif // !_WIN32 && !__EMX__ && defined(AF_LOCAL)
DEBUG_puts("8cups_do_local_auth: Returning false.");
<p style="margin-left: 2.5em; text-indent: -2.5em;"><strong>FileDevice Yes</strong><br>
</p>
<p style="margin-left: 2.5em; text-indent: -2.5em;"><strong>FileDevice No</strong><br>
+</p>
+ <p style="margin-left: 2.5em; text-indent: -2.5em;"><strong>FileDevice ^</strong><em>PATH-REGEX</em><br>
Specifies whether the file pseudo-device can be used for new printer queues.
The URI "file:///dev/null" is always allowed.
File devices cannot be used with "raw" print queues - a PPD file is required.
-The specified file is overwritten for every print job.
-Writing to directories is not supported.
+The specified file must already exist and is opened for writing in exclusive mode.
+Creating new files or writing to directories is not supported.
+Specifying a value of "Yes" allows access to TTY devices of the form "/dev/ttySOMETHING".
+Specifying a regular expression allows access to TTY devices whose paths match the specified regular expression.
</p>
<p style="margin-left: 2.5em; text-indent: -2.5em;"><strong>Printcap </strong><em>filename</em><br>
Specifies a file that is filled with a list of local print queues.
.\"
.\" cups-files.conf man page for CUPS.
.\"
-.\" Copyright © 2020-2025 by OpenPrinting.
+.\" Copyright © 2020-2026 by OpenPrinting.
.\" Copyright © 2007-2019 by Apple Inc.
.\" Copyright © 1997-2006 by Easy Software Products.
.\"
.\" Licensed under Apache License v2.0. See the file "LICENSE" for more
.\" information.
.\"
-.TH cups-files.conf 5 "CUPS" "2025-10-08" "OpenPrinting"
+.TH cups-files.conf 5 "CUPS" "2026-03-19" "OpenPrinting"
.SH NAME
cups\-files.conf \- file and directory configuration file for cups
.SH DESCRIPTION
\fBFileDevice Yes\fR
.TP 5
\fBFileDevice No\fR
+.TP 5
+\fBFileDevice ^\fIPATH-REGEX\fR
Specifies whether the file pseudo-device can be used for new printer queues.
The URI "file:///dev/null" is always allowed.
File devices cannot be used with "raw" print queues - a PPD file is required.
-The specified file is overwritten for every print job.
-Writing to directories is not supported.
+The specified file must already exist and is opened for writing in exclusive mode.
+Creating new files or writing to directories is not supported.
+Specifying a value of "Yes" allows access to TTY devices of the form "/dev/ttySOMETHING".
+Specifying a regular expression allows access to TTY devices whose paths match the specified regular expression.
.\"#Printcap
.TP 5
\fBPrintcap \fIfilename\fR
}
#ifdef HAVE_AUTHORIZATION_H
else if (!strncmp(authorization, "AuthRef ", 8) &&
- httpAddrLocalhost(httpGetAddress(con->http)))
+ httpAddrGetFamily(httpGetAddress(con->http)) == AF_LOCAL)
{
OSStatus status; /* Status */
char authdata[HTTP_MAX_VALUE];
#endif /* HAVE_AUTHORIZATION_H */
#if defined(SO_PEERCRED) && defined(AF_LOCAL)
else if (PeerCred != CUPSD_PEERCRED_OFF && !strncmp(authorization, "PeerCred ", 9) &&
- con->http->hostaddr->addr.sa_family == AF_LOCAL && con->best)
+ httpAddrGetFamily(httpGetAddress(con->http)) == AF_LOCAL && con->best)
{
/*
* Use peer credentials from domain socket connection...
}
#endif /* SO_PEERCRED && AF_LOCAL */
else if (!strncmp(authorization, "Local", 5) &&
- httpAddrLocalhost(httpGetAddress(con->http)))
+ httpAddrGetFamily(httpGetAddress(con->http)) == AF_LOCAL)
{
/*
* Get Local certificate authentication data...
cupsCopyString(auth_str, "Negotiate", sizeof(auth_str));
}
- if (con->best && !con->is_browser && !_cups_strcasecmp(httpGetHostname(con->http, NULL, 0), "localhost"))
+ if (con->best && !con->is_browser && httpAddrGetFamily(httpGetAddress(con->http)) == AF_LOCAL)
{
/*
* Add a "trc" (try root certification) parameter for local
auth_size = sizeof(auth_str) - (size_t)(auth_key - auth_str);
#if defined(SO_PEERCRED) && defined(AF_LOCAL)
- if (PeerCred != CUPSD_PEERCRED_OFF && httpAddrGetFamily(httpGetAddress(con->http)) == AF_LOCAL)
+ if (PeerCred != CUPSD_PEERCRED_OFF)
{
cupsCopyString(auth_key, ", PeerCred", auth_size);
auth_key += 10;
{ "DataDir", &DataDir, CUPSD_VARTYPE_STRING },
{ "DocumentRoot", &DocumentRoot, CUPSD_VARTYPE_STRING },
{ "ErrorLog", &ErrorLog, CUPSD_VARTYPE_STRING },
- { "FileDevice", &FileDevice, CUPSD_VARTYPE_BOOLEAN },
{ "LogFilePerm", &LogFilePerm, CUPSD_VARTYPE_PERM },
{ "OAuthScopes", &OAuthScopes, CUPSD_VARTYPE_STRING },
{ "OAuthServer", &OAuthServer, CUPSD_VARTYPE_STRING },
old_remote_port = RemotePort;
RemotePort = 0;
+ /*
+ * FileDevice...
+ */
+
+ if (FileDevice)
+ {
+ /*
+ * Free file: device path matching regular expression...
+ */
+
+ regfree(FileDevice);
+ free(FileDevice);
+ FileDevice = NULL;
+ }
+
/*
* String options...
*/
JobKillDelay = DEFAULT_TIMEOUT;
JobRetryLimit = 5;
JobRetryInterval = 300;
- FileDevice = FALSE;
FilterLevel = 0;
FilterLimit = 0;
FilterNice = 0;
{
FatalErrors = parse_fatal_errors(value);
}
+ else if (!_cups_strcasecmp(line, "FileDevice") && value)
+ {
+ /*
+ * FileDevice BOOLEAN
+ * FileDevice ^PATH-REGEX
+ */
+
+ int r; /* Regular expression status */
+ char rerror[1024]; /* Regular expression error */
+
+ if (FileDevice)
+ {
+ /*
+ * Free existing regular expression...
+ */
+
+ regfree(FileDevice);
+ free(FileDevice);
+ FileDevice = NULL;
+ }
+
+ if (!_cups_strcasecmp(value, "false") || !_cups_strcasecmp(value, "no") || !_cups_strcasecmp(value, "off"))
+ {
+ /*
+ * Do nothing more, no file: device support beyond /dev/null...
+ */
+
+ continue;
+ }
+ else if (!_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "yes"))
+ {
+ /*
+ * Enable file: device support for /dev/ttySOMETHING...
+ */
+
+ value = "^/dev/tty[A-Za-z0-9._]+$";
+ }
+ else if (*value != '^')
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unsupported FileDevice \"%s\" on line %d of %s.", value, linenum, CupsFilesFile);
+ if (FatalErrors & CUPSD_FATAL_CONFIG)
+ return (0);
+ }
+
+ /*
+ * Try compiling the regular expression in "value"...
+ */
+
+ if ((FileDevice = calloc(1, sizeof(regex_t))) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create FileDevice regular expression \"%s\" on line %d of %s.", value, linenum, CupsFilesFile);
+ if (FatalErrors & CUPSD_FATAL_CONFIG)
+ return (0);
+ }
+
+ if ((r = regcomp(FileDevice, value, REG_EXTENDED | REG_NOSUB)) != 0)
+ {
+ regerror(r, FileDevice, rerror, sizeof(rerror));
+ free(FileDevice);
+ FileDevice = NULL;
+
+ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to compile FileDevice regular expression \"%s\" on line %d of %s: %s", value, linenum, CupsFilesFile, rerror);
+
+ if (FatalErrors & CUPSD_FATAL_CONFIG)
+ return (0);
+ }
+ }
else if (!_cups_strcasecmp(line, "Group") && value)
{
/*
/*
* Configuration file definitions for the CUPS scheduler.
*
- * Copyright © 2020-2025 by OpenPrinting.
+ * Copyright © 2020-2026 by OpenPrinting.
* Copyright © 2007-2018 by Apple Inc.
* Copyright © 1997-2007 by Easy Software Products, all rights reserved.
*
/* Do we do reverse lookups? */
Timeout VALUE(DEFAULT_TIMEOUT),
/* Timeout during requests */
- KeepAlive VALUE(TRUE),
+ KeepAlive VALUE(TRUE);
/* Support the Keep-Alive option? */
- FileDevice VALUE(FALSE),
+VAR regex_t *FileDevice VALUE(NULL);
/* Allow file: devices? */
- FilterLimit VALUE(0),
+VAR int FilterLimit VALUE(0),
/* Max filter cost at any time */
FilterLevel VALUE(0),
/* Current filter level */
* See if the administrator has enabled file devices...
*/
- if (!FileDevice && strcmp(resource, "/dev/null"))
+ if (strcmp(resource, "/dev/null"))
{
- /*
- * File devices are disabled and the URL is not file:/dev/null...
- */
+ if (FileDevice && regexec(FileDevice, resource, /*nmatch*/0, /*pmatch*/NULL, /*eflags*/0))
+ {
+ /*
+ * File devices are enabled but the path is not allowed...
+ */
- send_ipp_status(con, IPP_STATUS_ERROR_NOT_POSSIBLE,
- _("File device URIs have been disabled. "
- "To enable, see the FileDevice directive in "
- "\"%s/cups-files.conf\"."),
- ServerRoot);
- if (!modify)
- cupsdDeletePrinter(printer, 0);
+ send_ipp_status(con, IPP_STATUS_ERROR_NOT_POSSIBLE, _("File device URI \"%s\" is not allowed."), ippGetString(attr, 0, NULL));
+ if (!modify)
+ cupsdDeletePrinter(printer, 0);
- return;
+ return;
+ }
+ else if (!FileDevice)
+ {
+ /*
+ * File devices are disabled and the URL is not file:///dev/null...
+ */
+
+ send_ipp_status(con, IPP_STATUS_ERROR_NOT_POSSIBLE, _("File device URIs have been disabled. To enable, see the FileDevice directive in \"%s/cups-files.conf\"."), ServerRoot);
+ if (!modify)
+ cupsdDeletePrinter(printer, 0);
+
+ return;
+ }
}
}
else
* Require local access to create a local printer...
*/
- if (!httpAddrLocalhost(httpGetAddress(con->http)))
+ if (httpAddrGetFamily(httpGetAddress(con->http)) != AF_LOCAL)
{
send_ipp_status(con, IPP_STATUS_ERROR_FORBIDDEN, _("Only local users can create a local printer."));
return;
ptr = ippGetString(device_uri, 0, NULL);
- if (!ptr || !ptr[0])
+ if (!ptr || !ptr[0] || (strncmp(ptr, "ipp://", 6) && strncmp(ptr, "ipps://", 7)))
{
- send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" has empty value."), "device-uri");
+ send_ipp_status(con, IPP_STATUS_ERROR_NOT_POSSIBLE, _("Bad device-uri \"%s\"."), ptr);
return;
}
}
else
{
+ char scheme[32], /* URI scheme */
+ userpass[32], /* URI username:password */
+ host[256], /* URI hostname */
+ resource[1024]; /* URI resource path (filename) */
+ int port; /* URI port number */
+
+ httpSeparateURI(HTTP_URI_CODING_ALL, job->printer->device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource));
+
job->print_pipes[0] = -1;
- if (!strcmp(job->printer->device_uri, "file:/dev/null") ||
- !strcmp(job->printer->device_uri, "file:///dev/null"))
- job->print_pipes[1] = -1;
- else
+ job->print_pipes[1] = -1;
+
+ if (strcmp(resource, "/dev/null"))
{
- if (!strncmp(job->printer->device_uri, "file:/dev/", 10))
- job->print_pipes[1] = open(job->printer->device_uri + 5,
- O_WRONLY | O_EXCL);
- else if (!strncmp(job->printer->device_uri, "file:///dev/", 12))
- job->print_pipes[1] = open(job->printer->device_uri + 7,
- O_WRONLY | O_EXCL);
- else if (!strncmp(job->printer->device_uri, "file:///", 8))
- job->print_pipes[1] = open(job->printer->device_uri + 7,
- O_WRONLY | O_CREAT | O_TRUNC, 0600);
- else
- job->print_pipes[1] = open(job->printer->device_uri + 5,
- O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (!FileDevice)
+ {
+ abort_message = "Stopping job because file: output is disabled.";
- if (job->print_pipes[1] < 0)
+ goto abort_job;
+ }
+ else if (regexec(FileDevice, resource, /*nmatch*/0, /*pmatch*/NULL, /*eflags*/0))
{
- abort_message = "Stopping job because the scheduler could not "
- "open the output file.";
+ abort_message = "Stopping job because file: output is not allowed to the specified path.";
goto abort_job;
}
+ else if ((job->print_pipes[1] = open(resource, O_WRONLY | O_EXCL)) < 0)
+ {
+ abort_message = "Stopping job because the scheduler could not open the output file.";
- fcntl(job->print_pipes[1], F_SETFD,
- fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
+ goto abort_job;
+ }
+ else
+ {
+ /*
+ * Close this file on execute...
+ */
+
+ fcntl(job->print_pipes[1], F_SETFD, fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
+ }
}
}
}
#
# Verify that the CUPS printer operations work.
#
-# Copyright © 2020-2024 by OpenPrinting.
+# Copyright © 2020-2026 by OpenPrinting.
# Copyright © 2007-2019 by Apple Inc.
# Copyright © 2001-2006 by Easy Software Products. All rights reserved.
#
ATTR uri printer-uri $scheme://$hostname:$port/printers/Test2
GROUP printer
- ATTR uri device-uri file:/tmp/Test2
+ ATTR uri device-uri file:///dev/null
ATTR enum printer-state 3
ATTR boolean printer-is-accepting-jobs true
ATTR uri printer-uri $scheme://$hostname:$port/printers/Test1
GROUP printer
- ATTR uri device-uri file:/tmp/Test1
+ ATTR uri device-uri file:///dev/null
ATTR enum printer-state 3
ATTR boolean printer-is-accepting-jobs true
ATTR text printer-info "Test Printer 1"
#
# Test the lpadmin command.
#
-# Copyright © 2020-2024 by OpenPrinting.
+# Copyright © 2020-2026 by OpenPrinting.
# Copyright © 2007-2018 by Apple Inc.
# Copyright © 1997-2005 by Easy Software Products, all rights reserved.
#
echo "Add Printer Test"
echo ""
-echo " lpadmin -p Test3 -v file:/dev/null -E -m drv:///sample.drv/deskjet.ppd"
-$runcups $VALGRIND ../systemv/lpadmin -p Test3 -v file:/dev/null -E -m drv:///sample.drv/deskjet.ppd 2>&1
+echo " lpadmin -p Test3 -v file:///dev/null -E -m drv:///sample.drv/deskjet.ppd"
+$runcups $VALGRIND ../systemv/lpadmin -p Test3 -v file:///dev/null -E -m drv:///sample.drv/deskjet.ppd 2>&1
if test $? != 0; then
echo " FAILED"
exit 1
echo "Modify Printer Test"
echo ""
-echo " lpadmin -p Test3 -v file:/tmp/Test3 -o PageSize=A4"
-$runcups $VALGRIND ../systemv/lpadmin -p Test3 -v file:/tmp/Test3 -o PageSize=A4 2>&1
+echo " lpadmin -p Test3 -v file:///dev/null -o PageSize=A4"
+$runcups $VALGRIND ../systemv/lpadmin -p Test3 -v file:///dev/null -o PageSize=A4 2>&1
if test $? != 0; then
echo " FAILED"
exit 1
echo "Add a printer for cupSNMP/IPPSupplies test"
echo ""
-echo " lpadmin -p Test4 -E -v file:/dev/null -m drv:///sample.drv/zebra.ppd"
-$runcups $VALGRIND ../systemv/lpadmin -p Test4 -E -v file:/dev/null -m drv:///sample.drv/zebra.ppd 2>&1
+echo " lpadmin -p Test4 -E -v file:///dev/null -m drv:///sample.drv/zebra.ppd"
+$runcups $VALGRIND ../systemv/lpadmin -p Test4 -E -v file:///dev/null -m drv:///sample.drv/zebra.ppd 2>&1
if test $? != 0; then
echo " FAILED"
exit 1