@jamesh, @jdstrand, I have started the CUPS patch now (see below, is there really no way to add attachments in this forum?). I have found out that all authentication is done at a central place in cupsd, in the cupsdIsAuthorized() function in the file scheduler/auth.c
.
To define which of the many IPP operations which CUPS supports are administrative operations (the ones which should only be allowed through the “cups-control” interface and not through the “cups” interface) I have taken the ones which are only allowed by the pseudo-group @SYSTEM
in the policies in /etc/cups/cupsd.conf
. This way I can simply check in the cupsdIsAuthorized() function whether the authorization is done through @SYSTEM
and if so, call an extra function, which I have called cupsdCheckAdminTask() and only if this one tests positive, allow the operation.
In cupsdCheckAdminTask() I check whether the client connects via domain socket (network address family AF_LOCAL
) and if not, the function simply passes. If the client connects through a domain socket I poll its peer credentials and so get the PID of the client process. After the line
/* Examine client process here */
one only needs to insert the checking for whether this process is from a Snap and whether this Snap plugs with “cups-control”.
Till
diff --git a/scheduler/auth.c b/scheduler/auth.c
index 4fbad6e24..466a0e529 100644
--- a/scheduler/auth.c
+++ b/scheduler/auth.c
@@ -43,6 +43,7 @@
# include <sys/ucred.h>
typedef struct xucred cupsd_ucred_t;
# define CUPSD_UCRED_UID(c) (c).cr_uid
+# define CUPSD_UCRED_PID(c) (c).cr_pid
#else
# ifndef __OpenBSD__
typedef struct ucred cupsd_ucred_t;
@@ -50,6 +51,7 @@ typedef struct ucred cupsd_ucred_t;
typedef struct sockpeercred cupsd_ucred_t;
# endif
# define CUPSD_UCRED_UID(c) (c).uid
+# define CUPSD_UCRED_PID(c) (c).pid
#endif /* HAVE_SYS_UCRED_H */
@@ -1520,6 +1522,61 @@ cupsdFreeLocation(cupsd_location_t *loc)/* I - Location to free */
}
+/*
+ * 'cupsdCheckAdminTask()' - Do additional checks on administrative tasks
+ */
+
+int /* O - 1 if admin task authorized */
+cupsdCheckAdminTask(cupsd_client_t *con) /* I - Connection */
+{
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cupsdCheckAdminTask: ADMINISTRATIVE TASK!!");
+
+#if defined(SO_PEERCRED) && defined(AF_LOCAL)
+ /*
+ * Get the client's PID if it accesses locally via domain socket
+ */
+
+ if (httpAddrFamily(con->http->hostaddr) == AF_LOCAL)
+ {
+ cupsd_ucred_t peercred; /* Peer credentials */
+ socklen_t peersize; /* Size of peer credentials */
+ int client_pid; /* PID of client */
+
+ peersize = sizeof(peercred);
+
+# ifdef __APPLE__
+ if (getsockopt(httpGetFd(con->http), 0, LOCAL_PEERCRED, &peercred,
+ &peersize))
+# else
+ if (getsockopt(httpGetFd(con->http), SOL_SOCKET, SO_PEERCRED, &peercred,
+ &peersize))
+# endif /* __APPLE__ */
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "cupsdCheckAdminTask: Unable to get peer credentials - %s",
+ strerror(errno));
+ }
+ else
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cupsdCheckAdminTask: Client UID %d PID %d",
+ CUPSD_UCRED_UID(peercred),
+ CUPSD_UCRED_PID(peercred));
+ client_pid = CUPSD_UCRED_PID(peercred);
+
+ /* Examine client process here */
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cupsdCheckAdminTask: Examining process %d ...",
+ client_pid);
+ }
+ }
+#endif /* SO_PEERCRED && AF_LOCAL */
+
+ return 1;
+}
+
+
/*
* 'cupsdIsAuthorized()' - Check to see if the user is authorized...
*/
@@ -1714,12 +1771,9 @@ cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
/*
* OK, got a username. See if we need normal user access, or group
- * access... (root always matches)
+ * access...
*/
- if (!strcmp(username, "root"))
- return (HTTP_OK);
-
/*
* Strip any @domain or @KDC from the username and owner...
*/
@@ -1749,6 +1803,21 @@ cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
else
pw = NULL;
+ /*
+ * For matching user and group memberships below we will first go
+ * through all names except @SYSTEM to authorize the task as
+ * non-administrative, like printing or deleting one's own job, if this
+ * fails we will check whether we can authorize via the special name
+ * @SYSTEM, as an administrative task, like creating a print queue or
+ * deleting someone else's job.
+ * Note that tasks are considered as administrative by the policies
+ * in cupsd.conf, when they require the user or group @SYSTEM.
+ * We do this separation because if the client is a Snap connecting via
+ * domain socket, we need to additionally check whether it plugs to us
+ * through the "cups-control" interface which allows administration and
+ * not through the "cups" interface which allows only printing.
+ */
+
if (best->level == CUPSD_AUTH_USER)
{
/*
@@ -1779,8 +1848,15 @@ cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
{
if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) && check_authref(con, name + 9))
return (HTTP_OK);
- else if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey &&
- check_authref(con, SystemGroupAuthKey))
+ }
+
+ for (name = (char *)cupsArrayFirst(best->names);
+ name;
+ name = (char *)cupsArrayNext(best->names))
+ {
+ if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey &&
+ check_authref(con, SystemGroupAuthKey) &&
+ cupsdCheckAdminTask(con))
return (HTTP_OK);
}
@@ -1797,9 +1873,8 @@ cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
return (HTTP_OK);
else if (!_cups_strcasecmp(name, "@SYSTEM"))
{
- for (i = 0; i < NumSystemGroups; i ++)
- if (cupsdCheckGroup(username, pw, SystemGroups[i]))
- return (HTTP_OK);
+ /* Do @SYSTEM later, when every other entry fails */
+ continue;
}
else if (name[0] == '@')
{
@@ -1810,6 +1885,19 @@ cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
return (HTTP_OK);
}
+ for (name = (char *)cupsArrayFirst(best->names);
+ name;
+ name = (char *)cupsArrayNext(best->names))
+ {
+ if (!_cups_strcasecmp(name, "@SYSTEM"))
+ {
+ for (i = 0; i < NumSystemGroups; i ++)
+ if (cupsdCheckGroup(username, pw, SystemGroups[i]) &&
+ cupsdCheckAdminTask(con))
+ return (HTTP_OK);
+ }
+ }
+
return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED);
}
@@ -1827,16 +1915,31 @@ cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
name;
name = (char *)cupsArrayNext(best->names))
{
+ if (!_cups_strcasecmp(name, "@SYSTEM"))
+ {
+ /* Do @SYSTEM later, when every other entry fails */
+ continue;
+ }
+
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name);
+ if (cupsdCheckGroup(username, pw, name))
+ return (HTTP_OK);
+ }
+
+ for (name = (char *)cupsArrayFirst(best->names);
+ name;
+ name = (char *)cupsArrayNext(best->names))
+ {
if (!_cups_strcasecmp(name, "@SYSTEM"))
{
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name);
+
for (i = 0; i < NumSystemGroups; i ++)
- if (cupsdCheckGroup(username, pw, SystemGroups[i]))
+ if (cupsdCheckGroup(username, pw, SystemGroups[i]) &&
+ cupsdCheckAdminTask(con))
return (HTTP_OK);
}
- else if (cupsdCheckGroup(username, pw, name))
- return (HTTP_OK);
}
/*