From: Michael R Sweet Date: Fri, 21 Nov 2025 06:29:53 +0000 (+0100) Subject: Fix various issues in cupsd X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=524749b0449b49d8967d4f777854259bf22b278a;p=thirdparty%2Fcups.git Fix various issues in cupsd Various issues were found by @SilverPlate3, recognized as CVE-2025-61915: - out of bound write when handling IPv6 addresses, - cupsd crash caused by null dereference when ErrorPolicy value is empty, On the top of that, Mike Sweet noticed vulnerability via domain socket, exploitable locally if attacker has access to domain socket and knows username of user within a group which is present in CUPS system groups: - rewrite of cupsd.conf via PeerCred authorization via domain socket The last vulnerability is fixed by introducing PeerCred directive for cups-files.conf, which controls whether PeerCred is enabled/disabled for user in CUPS system groups. Fixes CVE-2025-61915 --- diff --git a/conf/cups-files.conf.in b/conf/cups-files.conf.in index f96f745ae1..6db1392977 100644 --- a/conf/cups-files.conf.in +++ b/conf/cups-files.conf.in @@ -22,6 +22,9 @@ SystemGroup @CUPS_SYSTEM_GROUPS@ @CUPS_SYSTEM_AUTHKEY@ +# Are Unix domain socket peer credentials used for authorization? +PeerCred @CUPS_PEER_CRED@ + # User that is substituted for unauthenticated (remote) root accesses... #RemoteRoot remroot diff --git a/config-scripts/cups-defaults.m4 b/config-scripts/cups-defaults.m4 index 9fd3c4e4b6..e2fdee6a10 100644 --- a/config-scripts/cups-defaults.m4 +++ b/config-scripts/cups-defaults.m4 @@ -1,7 +1,7 @@ dnl dnl Default cupsd configuration settings for CUPS. dnl -dnl Copyright © 2020-2024 by OpenPrinting. +dnl Copyright © 2020-2025 by OpenPrinting. dnl Copyright © 2007-2018 by Apple Inc. dnl Copyright © 2006-2007 by Easy Software Products, all rights reserved. dnl @@ -95,6 +95,15 @@ AC_ARG_WITH([log_level], AS_HELP_STRING([--with-log-level], [set default LogLeve AC_SUBST([CUPS_LOG_LEVEL]) AC_DEFINE_UNQUOTED([CUPS_DEFAULT_LOG_LEVEL], ["$CUPS_LOG_LEVEL"], [Default LogLevel value.]) +dnl Default PeerCred +AC_ARG_WITH([peer_cred], AS_HELP_STRING([--with-peer-cred], [set default PeerCred value (on/off/root-only), default=on]), [ + CUPS_PEER_CRED="$withval" +], [ + CUPS_PEER_CRED="on" +]) +AC_SUBST([CUPS_PEER_CRED]) +AC_DEFINE_UNQUOTED([CUPS_DEFAULT_PEER_CRED], ["$CUPS_PEER_CRED"], [Default PeerCred value.]) + dnl Default AccessLogLevel AC_ARG_WITH(access_log_level, [ --with-access-log-level set default AccessLogLevel value, default=none], CUPS_ACCESS_LOG_LEVEL="$withval", diff --git a/config.h.in b/config.h.in index ff0b324e21..06c8428783 100644 --- a/config.h.in +++ b/config.h.in @@ -86,6 +86,13 @@ #define CUPS_DEFAULT_ERROR_POLICY "stop-printer" +/* + * Default PeerCred value... + */ + +#define CUPS_DEFAULT_PEER_CRED "on" + + /* * Default MaxCopies value... */ diff --git a/configure b/configure index 79efebb9c8..8f19056edf 100755 --- a/configure +++ b/configure @@ -669,6 +669,7 @@ CUPS_BROWSING CUPS_SYNC_ON_CLOSE CUPS_PAGE_LOG_FORMAT CUPS_ACCESS_LOG_LEVEL +CUPS_PEER_CRED CUPS_LOG_LEVEL CUPS_FATAL_ERRORS CUPS_ERROR_POLICY @@ -917,6 +918,7 @@ with_max_log_size with_error_policy with_fatal_errors with_log_level +with_peer_cred with_access_log_level enable_page_logging enable_sync_on_close @@ -1648,6 +1650,8 @@ Optional Packages: --with-error-policy set default ErrorPolicy value, default=stop-printer --with-fatal-errors set default FatalErrors value, default=config --with-log-level set default LogLevel value, default=warn + --with-peer-cred set default PeerCred value (on/off/root-only), + default=on --with-access-log-level set default AccessLogLevel value, default=none --with-local-protocols set default BrowseLocalProtocols, default="" --with-cups-user set default user for CUPS @@ -11290,6 +11294,24 @@ printf "%s\n" "#define CUPS_DEFAULT_LOG_LEVEL \"$CUPS_LOG_LEVEL\"" >>confdefs.h +# Check whether --with-peer_cred was given. +if test ${with_peer_cred+y} +then : + withval=$with_peer_cred; + CUPS_PEER_CRED="$withval" + +else $as_nop + + CUPS_PEER_CRED="on" + +fi + + + +printf "%s\n" "#define CUPS_DEFAULT_PEER_CRED \"$CUPS_PEER_CRED\"" >>confdefs.h + + + # Check whether --with-access_log_level was given. if test ${with_access_log_level+y} then : diff --git a/doc/help/man-cups-files.conf.html b/doc/help/man-cups-files.conf.html index 0ceca13162..f8d4188ada 100644 --- a/doc/help/man-cups-files.conf.html +++ b/doc/help/man-cups-files.conf.html @@ -157,6 +157,16 @@ The server name may be included in filenames using the string "%s", fo

PassEnv variable [ ... variable ]
Passes the specified environment variable(s) to child processes. Note: the standard CUPS filter and backend environment variables cannot be overridden using this directive. +

+

PeerCred off
+

+

PeerCred on
+

+

PeerCred root-only
+Specifies whether peer credentials are used for authorization when communicating over the UNIX domain socket. +When on, the peer credentials of any user are accepted for authorization. +The value off disables the use of peer credentials entirely, while the value root-only allows peer credentials only for the root user. +Note: for security reasons, the on setting is reduced to root-only for authorization of PUT requests.

RemoteRoot username
Specifies the username that is associated with unauthenticated accesses by clients claiming to be the root user. @@ -279,6 +289,6 @@ command is used instead. CUPS Online Help (http://localhost:631/help)

Copyright

-

Copyright © 2020-2024 by OpenPrinting. +

Copyright © 2020-2025 by OpenPrinting. diff --git a/man/cups-files.conf.5 b/man/cups-files.conf.5 index 619d625eb9..3caa50e855 100644 --- a/man/cups-files.conf.5 +++ b/man/cups-files.conf.5 @@ -1,14 +1,14 @@ .\" .\" cups-files.conf man page for CUPS. .\" -.\" Copyright © 2020-2024 by OpenPrinting. +.\" Copyright © 2020-2025 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" "2024-04-16" "OpenPrinting" +.TH cups-files.conf 5 "CUPS" "2025-10-08" "OpenPrinting" .SH NAME cups\-files.conf \- file and directory configuration file for cups .SH DESCRIPTION @@ -170,6 +170,17 @@ The default is "/var/log/cups/page_log". \fBPassEnv \fIvariable \fR[ ... \fIvariable \fR] Passes the specified environment variable(s) to child processes. Note: the standard CUPS filter and backend environment variables cannot be overridden using this directive. +.\"#PeerCred +.TP 5 +\fBPeerCred off\fR +.TP 5 +\fBPeerCred on\fR +.TP 5 +\fBPeerCred root-only\fR +Specifies whether peer credentials are used for authorization when communicating over the UNIX domain socket. +When \fBon\fR, the peer credentials of any user are accepted for authorization. +The value \fBoff\fR disables the use of peer credentials entirely, while the value \fBroot-only\fR allows peer credentials only for the root user. +Note: for security reasons, the \fBon\fR setting is reduced to \fBroot-only\fR for authorization of PUT requests. .\"#RemoteRoot .TP 5 \fBRemoteRoot \fIusername\fR @@ -293,4 +304,4 @@ command is used instead. .BR subscriptions.conf (5), CUPS Online Help (http://localhost:631/help) .SH COPYRIGHT -Copyright \[co] 2020-2024 by OpenPrinting. +Copyright \[co] 2020-2025 by OpenPrinting. diff --git a/scheduler/auth.c b/scheduler/auth.c index 3f537f7693..802659481a 100644 --- a/scheduler/auth.c +++ b/scheduler/auth.c @@ -462,7 +462,7 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */ } #endif /* HAVE_AUTHORIZATION_H */ #if defined(SO_PEERCRED) && defined(AF_LOCAL) - else if (!strncmp(authorization, "PeerCred ", 9) && + else if (PeerCred != CUPSD_PEERCRED_OFF && !strncmp(authorization, "PeerCred ", 9) && con->http->hostaddr->addr.sa_family == AF_LOCAL && con->best) { /* @@ -505,6 +505,12 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */ } #endif /* HAVE_AUTHORIZATION_H */ + if ((PeerCred == CUPSD_PEERCRED_ROOTONLY || httpGetState(con->http) == HTTP_STATE_PUT_RECV) && strcmp(authorization + 9, "root")) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "User \"%s\" is not allowed to use peer credentials.", authorization + 9); + return; + } + if ((pwd = getpwnam(authorization + 9)) == NULL) { cupsdLogClient(con, CUPSD_LOG_ERROR, "User \"%s\" does not exist.", authorization + 9); diff --git a/scheduler/auth.h b/scheduler/auth.h index 37f458b581..e1421cb1e8 100644 --- a/scheduler/auth.h +++ b/scheduler/auth.h @@ -48,6 +48,10 @@ #define CUPSD_AUTH_LIMIT_ALL 127 /* Limit all requests */ #define CUPSD_AUTH_LIMIT_IPP 128 /* Limit IPP requests */ +#define CUPSD_PEERCRED_OFF 0 /* Don't allow PeerCred authorization */ +#define CUPSD_PEERCRED_ON 1 /* Allow PeerCred authorization for all users */ +#define CUPSD_PEERCRED_ROOTONLY 2 /* Allow PeerCred authorization for root user */ + #define IPP_ANY_OPERATION (ipp_op_t)0 /* Any IPP operation */ #define IPP_BAD_OPERATION (ipp_op_t)-1 @@ -115,6 +119,9 @@ VAR http_encryption_t DefaultEncryption VALUE(HTTP_ENCRYPTION_REQUIRED); VAR cups_array_t *Locations VALUE(NULL); /* Authorization locations */ +VAR int PeerCred VALUE(CUPSD_PEERCRED_ON); + /* Allow PeerCred authorization? */ + VAR cups_array_t *OAuthGroups VALUE(NULL); /* OAuthGroup entries */ VAR cups_json_t *OAuthJWKS VALUE(NULL), diff --git a/scheduler/client.c b/scheduler/client.c index 065e873ee8..f6166091e8 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -2214,7 +2214,7 @@ cupsdSendHeader( auth_size = sizeof(auth_str) - (size_t)(auth_key - auth_str); #if defined(SO_PEERCRED) && defined(AF_LOCAL) - if (httpAddrGetFamily(httpGetAddress(con->http)) == AF_LOCAL) + if (PeerCred != CUPSD_PEERCRED_OFF && httpAddrGetFamily(httpGetAddress(con->http)) == AF_LOCAL) { cupsCopyString(auth_key, ", PeerCred", auth_size); auth_key += 10; diff --git a/scheduler/conf.c b/scheduler/conf.c index 387c02a6c8..33bbf0514f 100644 --- a/scheduler/conf.c +++ b/scheduler/conf.c @@ -44,6 +44,7 @@ typedef enum { CUPSD_VARTYPE_INTEGER, /* Integer option */ CUPSD_VARTYPE_TIME, /* Time interval option */ + CUPSD_VARTYPE_NULLSTRING, /* String option or NULL/empty string */ CUPSD_VARTYPE_STRING, /* String option */ CUPSD_VARTYPE_BOOLEAN, /* Boolean option */ CUPSD_VARTYPE_PATHNAME, /* File/directory name option */ @@ -65,7 +66,7 @@ typedef struct static const cupsd_var_t cupsd_vars[] = { { "AutoPurgeJobs", &JobAutoPurge, CUPSD_VARTYPE_BOOLEAN }, - { "BrowseDNSSDSubTypes", &DNSSDSubTypes, CUPSD_VARTYPE_STRING }, + { "BrowseDNSSDSubTypes", &DNSSDSubTypes, CUPSD_VARTYPE_NULLSTRING }, { "BrowseWebIF", &BrowseWebIF, CUPSD_VARTYPE_BOOLEAN }, { "Browsing", &Browsing, CUPSD_VARTYPE_BOOLEAN }, { "Classification", &Classification, CUPSD_VARTYPE_STRING }, @@ -114,7 +115,7 @@ static const cupsd_var_t cupsd_vars[] = { "MaxSubscriptionsPerPrinter",&MaxSubscriptionsPerPrinter, CUPSD_VARTYPE_INTEGER }, { "MaxSubscriptionsPerUser", &MaxSubscriptionsPerUser, CUPSD_VARTYPE_INTEGER }, { "MultipleOperationTimeout", &MultipleOperationTimeout, CUPSD_VARTYPE_TIME }, - { "PageLogFormat", &PageLogFormat, CUPSD_VARTYPE_STRING }, + { "PageLogFormat", &PageLogFormat, CUPSD_VARTYPE_NULLSTRING }, { "PreserveJobFiles", &JobFiles, CUPSD_VARTYPE_TIME }, { "PreserveJobHistory", &JobHistory, CUPSD_VARTYPE_TIME }, { "ReloadTimeout", &ReloadTimeout, CUPSD_VARTYPE_TIME }, @@ -811,6 +812,13 @@ cupsdReadConfiguration(void) IdleExitTimeout = 60; #endif /* HAVE_ONDEMAND */ + if (!strcmp(CUPS_DEFAULT_PEER_CRED, "off")) + PeerCred = CUPSD_PEERCRED_OFF; + else if (!strcmp(CUPS_DEFAULT_PEER_CRED, "root-only")) + PeerCred = CUPSD_PEERCRED_ROOTONLY; + else + PeerCred = CUPSD_PEERCRED_ON; + /* * Setup environment variables... */ @@ -1899,7 +1907,7 @@ get_addr_and_mask(const char *value, /* I - String from config file */ family = AF_INET6; - for (i = 0, ptr = value + 1; *ptr && i < 8; i ++) + for (i = 0, ptr = value + 1; *ptr && i >= 0 && i < 8; i ++) { if (*ptr == ']') break; @@ -2045,7 +2053,7 @@ get_addr_and_mask(const char *value, /* I - String from config file */ #ifdef AF_INET6 if (family == AF_INET6) { - if (i > 128) + if (i < 0 || i > 128) return (0); i = 128 - i; @@ -2079,7 +2087,7 @@ get_addr_and_mask(const char *value, /* I - String from config file */ else #endif /* AF_INET6 */ { - if (i > 32) + if (i < 0 || i > 32) return (0); mask[0] = 0xffffffff; @@ -2996,7 +3004,17 @@ parse_variable( cupsdSetString((char **)var->ptr, temp); break; + case CUPSD_VARTYPE_NULLSTRING : + cupsdSetString((char **)var->ptr, value); + break; + case CUPSD_VARTYPE_STRING : + if (!value) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value for %s on line %d of %s.", line, linenum, filename); + return (0); + } + cupsdSetString((char **)var->ptr, value); break; } @@ -3505,9 +3523,10 @@ read_cupsd_conf(cups_file_t *fp) /* I - File to read from */ line, value ? " " : "", value ? value : "", linenum, ConfigurationFile, CupsFilesFile); } - else - parse_variable(ConfigurationFile, linenum, line, value, - sizeof(cupsd_vars) / sizeof(cupsd_vars[0]), cupsd_vars); + else if (!parse_variable(ConfigurationFile, linenum, line, value, + sizeof(cupsd_vars) / sizeof(cupsd_vars[0]), cupsd_vars) && + (FatalErrors & CUPSD_FATAL_CONFIG)) + return (0); } return (1); @@ -3720,6 +3739,31 @@ read_cups_files_conf(cups_file_t *fp) /* I - File to read from */ break; } } + else if (!_cups_strcasecmp(line, "PeerCred") && value) + { + /* + * PeerCred {off,on,root-only} + */ + + if (!_cups_strcasecmp(value, "off")) + { + PeerCred = CUPSD_PEERCRED_OFF; + } + else if (!_cups_strcasecmp(value, "on")) + { + PeerCred = CUPSD_PEERCRED_ON; + } + else if (!_cups_strcasecmp(value, "root-only")) + { + PeerCred = CUPSD_PEERCRED_ROOTONLY; + } + else + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown PeerCred \"%s\" on line %d of %s.", value, linenum, CupsFilesFile); + if (FatalErrors & CUPSD_FATAL_CONFIG) + return (0); + } + } else if (!_cups_strcasecmp(line, "PrintcapFormat") && value) { /* diff --git a/test/run-stp-tests.sh b/test/run-stp-tests.sh index e4330bf9f4..65e1d37932 100755 --- a/test/run-stp-tests.sh +++ b/test/run-stp-tests.sh @@ -497,7 +497,7 @@ fi cat >$BASE/cups-files.conf <