From: Michael R Sweet Date: Wed, 16 Apr 2025 16:22:08 +0000 (-0400) Subject: Add OAuth support to cupsdIsAuthorized (Issue #246) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5578f804d14fd830054a799471ac941104700ccc;p=thirdparty%2Fcups.git Add OAuth support to cupsdIsAuthorized (Issue #246) --- diff --git a/scheduler/auth.c b/scheduler/auth.c index 1ac00f5679..d6063bfc3a 100644 --- a/scheduler/auth.c +++ b/scheduler/auth.c @@ -1620,6 +1620,7 @@ cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */ { "None", "Basic", + "Bearer", "Negotiate" }; @@ -1786,169 +1787,253 @@ cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */ * Strip any @domain or @KDC from the username and owner... */ - if (StripUserDomain && (ptr = strchr(username, '@')) != NULL) + if (type != CUPSD_AUTH_BEARER && StripUserDomain && (ptr = strchr(username, '@')) != NULL) *ptr = '\0'; if (owner) { cupsCopyString(ownername, owner, sizeof(ownername)); - if (StripUserDomain && (ptr = strchr(ownername, '@')) != NULL) + if (type != CUPSD_AUTH_BEARER && StripUserDomain && (ptr = strchr(ownername, '@')) != NULL) *ptr = '\0'; } else ownername[0] = '\0'; - /* - * Get the user info... - */ - - if (username[0]) - { - pw = getpwnam(username); - endpwent(); - } - 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) + if (type == CUPSD_AUTH_BEARER) { /* - * If there are no names associated with this location, then - * any valid user is OK... + * Lookup access via OAuth groups... */ - if (cupsArrayCount(best->names) == 0) - return (HTTP_STATUS_OK); + cupsd_ogroup_t *og; // Current OAuth group - /* - * Otherwise check the user list and return OK if this user is - * allowed... - */ + if (best->level == CUPSD_AUTH_USER) + { + /* + * If there are no names associated with this location, then any valid user + * is OK... + */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking user membership..."); + if (cupsArrayCount(best->names) == 0) + return (HTTP_STATUS_OK); -#ifdef HAVE_AUTHORIZATION_H - /* - * If an authorization reference was supplied it must match a right name... - */ + /* + * Otherwise check the user list and return OK if this user is allowed... + */ - if (con->authref) - { - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking user membership..."); + + for (name = (char *)cupsArrayFirst(best->names); name; name = (char *)cupsArrayNext(best->names)) { - if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) && check_authref(con, name + 9)) + if (!_cups_strcasecmp(name, "@OWNER") && owner && !_cups_strcasecmp(username, ownername)) + { + // User is owner... return (HTTP_STATUS_OK); + } + else if (name[0] == '@') + { + // Check OAuth group membership... + if ((og = cupsdFindOAuthGroup(name + 1)) == NULL) + { + // Group not defined... + cupsdLogMessage(CUPSD_LOG_ERROR, "Authorization policy requires undefined OAuth group \"%s\", ignoring.", name + 1); + } + else if (cupsArrayFind(og->members, username) || (con->email[0] && cupsArrayFind(og->members, con->email))) + { + // User is in group... + return (HTTP_STATUS_OK); + } + } + else if (!_cups_strcasecmp(username, name) || (con->email[0] && !_cups_strcasecmp(con->email, name))) + { + return (HTTP_STATUS_OK); + } } + } + else + { + /* + * Check to see if this user is in any of the named groups... + */ - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group membership..."); + + /* + * Check to see if this user is in any of the named groups... + */ + + for (name = (char *)cupsArrayFirst(best->names); name; name = (char *)cupsArrayNext(best->names)) { - if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey && - check_authref(con, SystemGroupAuthKey)) + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership.", name); + + if ((og = cupsdFindOAuthGroup(name)) == NULL) + { + // Group not defined... + cupsdLogMessage(CUPSD_LOG_ERROR, "Authorization policy requires undefined OAuth group \"%s\", ignoring.", name + 1); + } + else if (cupsArrayFind(og->members, username) || (con->email[0] && cupsArrayFind(og->members, con->email))) + { + // User is in group... return (HTTP_STATUS_OK); + } } + } + } + else + { + /* + * Get the (local) user info... + */ - return (HTTP_STATUS_FORBIDDEN); + if (username[0]) + { + pw = getpwnam(username); + endpwent(); } -#endif /* HAVE_AUTHORIZATION_H */ + else + pw = NULL; - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) + /* + * 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) { - if (!_cups_strcasecmp(name, "@OWNER") && owner && - !_cups_strcasecmp(username, ownername)) + /* + * If there are no names associated with this location, then + * any valid user is OK... + */ + + if (cupsArrayCount(best->names) == 0) return (HTTP_STATUS_OK); - else if (!_cups_strcasecmp(name, "@SYSTEM")) + + /* + * Otherwise check the user list and return OK if this user is + * allowed... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking user membership..."); + +#ifdef HAVE_AUTHORIZATION_H + /* + * If an authorization reference was supplied it must match a right name... + */ + + if (con->authref) { - /* Do @SYSTEM later, when every other entry fails */ - continue; + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) + { + if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) && check_authref(con, name + 9)) + return (HTTP_STATUS_OK); + } + + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) + { + if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey && + check_authref(con, SystemGroupAuthKey)) + return (HTTP_STATUS_OK); + } + + return (HTTP_STATUS_FORBIDDEN); } - else if (name[0] == '@') +#endif /* HAVE_AUTHORIZATION_H */ + + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) { - if (cupsdCheckGroup(username, pw, name + 1)) - return (HTTP_STATUS_OK); + if (!_cups_strcasecmp(name, "@OWNER") && owner && + !_cups_strcasecmp(username, ownername)) + return (HTTP_STATUS_OK); + else if (!_cups_strcasecmp(name, "@SYSTEM")) + { + /* Do @SYSTEM later, when every other entry fails */ + continue; + } + else if (name[0] == '@') + { + if (cupsdCheckGroup(username, pw, name + 1)) + return (HTTP_STATUS_OK); + } + else if (!_cups_strcasecmp(username, name)) + return (HTTP_STATUS_OK); } - else if (!_cups_strcasecmp(username, name)) - return (HTTP_STATUS_OK); - } - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) - { - if (!_cups_strcasecmp(name, "@SYSTEM")) + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) { - for (i = 0; i < NumSystemGroups; i ++) - if (cupsdCheckGroup(username, pw, SystemGroups[i]) && check_admin_access(con)) - return (HTTP_STATUS_OK); + if (!_cups_strcasecmp(name, "@SYSTEM")) + { + for (i = 0; i < NumSystemGroups; i ++) + if (cupsdCheckGroup(username, pw, SystemGroups[i]) && check_admin_access(con)) + return (HTTP_STATUS_OK); + } } } - - return (con->username[0] ? HTTP_STATUS_FORBIDDEN : HTTP_STATUS_UNAUTHORIZED); - } - - /* - * Check to see if this user is in any of the named groups... - */ - - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group membership..."); - - /* - * Check to see if this user is in any of the named groups... - */ - - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) - { - if (!_cups_strcasecmp(name, "@SYSTEM")) + else { - /* Do @SYSTEM later, when every other entry fails */ - continue; - } + /* + * Check to see if this user is in any of the named groups... + */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group membership..."); - if (cupsdCheckGroup(username, pw, name)) - return (HTTP_STATUS_OK); - } + /* + * Check to see if this user is in any of the named groups... + */ - 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 (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) + { + if (!_cups_strcasecmp(name, "@SYSTEM")) + { + /* Do @SYSTEM later, when every other entry fails */ + continue; + } - for (i = 0; i < NumSystemGroups; i ++) - if (cupsdCheckGroup(username, pw, SystemGroups[i]) && check_admin_access(con)) + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name); + + if (cupsdCheckGroup(username, pw, name)) return (HTTP_STATUS_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]) && check_admin_access(con)) + return (HTTP_STATUS_OK); + } + } } } /* - * The user isn't part of the specified group, so deny access... + * The user isn't part of the specified users or groups, so deny access... */ cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: User not in group(s).");