Index: conf/cupsd.conf.in =================================================================== --- conf/cupsd.conf.in (revision 6197) +++ conf/cupsd.conf.in (working copy) @@ -25,6 +25,9 @@ # Default authentication type, when authentication is required... DefaultAuthType Basic +@CUPS_SYSTEM_AUTHKEY_COMMENT@ +@CUPS_SYSTEM_AUTHKEY@ + # Restrict access to the server... Order allow,deny Index: config-scripts/cups-common.m4 =================================================================== --- config-scripts/cups-common.m4 (revision 6197) +++ config-scripts/cups-common.m4 (working copy) @@ -222,6 +222,13 @@ dnl Check for notify_post support AC_CHECK_HEADER(notify.h,AC_DEFINE(HAVE_NOTIFY_H)) AC_CHECK_FUNCS(notify_post) + + dnl Check for Authorization Services support + AC_CHECK_HEADER(Security/Authorization.h, + [ AC_DEFINE(HAVE_AUTHORIZATION_H) + CUPS_SYSTEM_AUTHKEY_COMMENT="# The Authorization Services key equivalent of the @SYSTEM group..." + CUPS_SYSTEM_AUTHKEY="SystemGroupAuthKey system.preferences"]) + AC_CHECK_HEADER(Security/SecBasePriv.h,AC_DEFINE(HAVE_SECBASEPRIV_H)) ;; Linux*) @@ -247,6 +254,9 @@ ;; esac +AC_SUBST(CUPS_SYSTEM_AUTHKEY_COMMENT) +AC_SUBST(CUPS_SYSTEM_AUTHKEY) + dnl See if we have POSIX ACL support... SAVELIBS="$LIBS" LIBS="" Index: config-scripts/cups-ssl.m4 =================================================================== --- config-scripts/cups-ssl.m4 (revision 6197) +++ config-scripts/cups-ssl.m4 (working copy) @@ -48,7 +48,6 @@ # certificates for CUPS, so don't enable encryption on # /admin just yet... #ENCRYPTION_REQUIRED=" Encryption Required" - AC_CHECK_HEADER(Security/SecBasePriv.h,AC_DEFINE(HAVE_SECBASEPRIV_H)) AC_DEFINE(HAVE_SSL) AC_DEFINE(HAVE_CDSASSL)]) fi Index: cups/auth.c =================================================================== --- cups/auth.c (revision 6197) +++ cups/auth.c (working copy) @@ -52,7 +52,16 @@ # include #endif /* WIN32 || __EMX__ */ +#if HAVE_AUTHORIZATION_H +# include +# ifdef HAVE_SECBASEPRIV_H +# include +# else + extern const char *cssmErrorString(int error); +# endif /* HAVE_SECBASEPRIV_H */ +#endif /* HAVE_AUTHORIZATION_H */ + /* * Local functions... */ @@ -87,6 +96,7 @@ realm[HTTP_MAX_VALUE], /* realm="xyz" string */ nonce[HTTP_MAX_VALUE], /* nonce="xyz" string */ encode[2048]; /* Encoded username:password */ + int localauth; /* Local authentication result */ _cups_globals_t *cg; /* Global data */ @@ -112,14 +122,19 @@ * See if we can do local authentication... */ - if (http->digest_tries < 3 && !cups_local_auth(http)) + if (http->digest_tries < 3) { - DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n", http->authstring)); - - if (http->status == HTTP_UNAUTHORIZED) - http->digest_tries ++; - - return (0); + if ((localauth = cups_local_auth(http)) == 0) + { + DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n", http->authstring)); + + if (http->status == HTTP_UNAUTHORIZED) + http->digest_tries ++; + + return (0); + } + else if (localauth == -1) + return (-1); /* Error or canceled */ } /* @@ -391,7 +406,9 @@ * available/applicable... */ -static int /* O - 0 if available, -1 if not */ +static int /* O - 0 if available */ + /* 1 if not available */ + /* -1 error */ cups_local_auth(http_t *http) /* I - HTTP connection to server */ { #if defined(WIN32) || defined(__EMX__) @@ -422,7 +439,90 @@ return (-1); } +#if defined(HAVE_AUTHORIZATION_H) + OSStatus status; /* Status */ + AuthorizationItem auth_right; /* Authorization right */ + AuthorizationRights auth_rights; /* Authorization rights */ + AuthorizationFlags auth_flags; /* Authorization flags */ + AuthorizationExternalForm auth_extrn; /* Authorization ref external */ + char auth_key[1024]; /* Buffer */ + char buffer[1024]; /* Buffer */ + + /* + * Delete any previous authorization reference... + */ + + if (cg->auth_ref) + { + AuthorizationFree(cg->auth_ref, kAuthorizationFlagDefaults); + cg->auth_ref = NULL; + } + + if (httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey", + auth_key, sizeof(auth_key))) + { + status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, &cg->auth_ref); + if (status != errAuthorizationSuccess) + { + DEBUG_printf(("cups_local_auth: AuthorizationCreate() returned %d (%s)\n", + (int)status, cssmErrorString(status))); + return (-1); + } + + auth_right.name = auth_key; + auth_right.valueLength = 0; + auth_right.value = NULL; + auth_right.flags = 0; + + auth_rights.count = 1; + auth_rights.items = &auth_right; + + auth_flags = kAuthorizationFlagDefaults | + kAuthorizationFlagPreAuthorize | + kAuthorizationFlagInteractionAllowed | + kAuthorizationFlagExtendRights; + + status = AuthorizationCopyRights(cg->auth_ref, &auth_rights, + kAuthorizationEmptyEnvironment, + auth_flags, NULL); + if (status == errAuthorizationSuccess) + status = AuthorizationMakeExternalForm(cg->auth_ref, &auth_extrn); + + + if (status == errAuthorizationSuccess) + { + /* + * Set the authorization string and return... + */ + + httpEncode64_2(buffer, sizeof(buffer), (void *)&auth_extrn, + sizeof(auth_extrn)); + + http->authstring = malloc(strlen(buffer) + 9); + sprintf(http->authstring, "AuthRef %s", buffer); + + /* Copy back to _authstring for backwards compatibility */ + strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring)); + + DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n", + http->authstring)); + return (0); + } + else if (status == errAuthorizationCanceled) + return (-1); + + DEBUG_printf(("cups_local_auth: AuthorizationCopyRights() returned %d (%s)\n", + (int)status, cssmErrorString(status))); + + /* + * Fall through to try certificates... + */ + } +#endif /* HAVE_AUTHORIZATION_H */ + + /* * Try opening a certificate file for this PID. If that fails, * try the root certificate... */ @@ -442,30 +542,33 @@ { DEBUG_printf(("cups_local_auth: Unable to open file %s: %s\n", filename, strerror(errno))); - return (-1); } + else + { + /* + * Read the certificate from the file... + */ + + fgets(certificate, sizeof(certificate), fp); + fclose(fp); + + /* + * Set the authorization string and return... + */ + + http->authstring = malloc(strlen(certificate) + 10); + sprintf(http->authstring, "Local %s", certificate); + + /* Copy back to _authstring for backwards compatibility */ + strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring)); - /* - * Read the certificate from the file... - */ + DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n", + http->authstring)); + + return (0); + } - fgets(certificate, sizeof(certificate), fp); - fclose(fp); - - /* - * Set the authorization string and return... - */ - - http->authstring = malloc(strlen(certificate) + 10); - sprintf(http->authstring, "Local %s", certificate); - - /* Copy back to _authstring for backwards compatibility */ - strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring)); - - DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n", - http->authstring)); - - return (0); + return (1); #endif /* WIN32 || __EMX__ */ } Index: cups/request.c =================================================================== --- cups/request.c (revision 6197) +++ cups/request.c (working copy) @@ -71,6 +71,9 @@ int bytes; /* Number of bytes read/written */ char buffer[32768]; /* Output buffer */ http_status_t expect; /* Expect: header to use */ +#ifdef HAVE_AUTHORIZATION_H + _cups_globals_t *cg = _cupsGlobals(); /* Global data */ +#endif /* HAVE_AUTHORIZATION_H */ DEBUG_printf(("cupsDoFileRequest(%p, %p, \'%s\', \'%s\')\n", @@ -431,6 +434,18 @@ } } +#ifdef HAVE_AUTHORIZATION_H + /* + * Delete any authorization reference created for this request... + */ + + if (cg->auth_ref) + { + AuthorizationFree(cg->auth_ref, kAuthorizationFlagDefaults); + cg->auth_ref = NULL; + } +#endif /* HAVE_AUTHORIZATION_H */ + return (response); } Index: cups/globals.c =================================================================== --- cups/globals.c (revision 6197) +++ cups/globals.c (working copy) @@ -180,6 +180,11 @@ cupsFreeOptions(cg->cupsd_num_settings, cg->cupsd_settings); +#ifdef HAVE_AUTHORIZATION_H + if (cg->auth_ref) + AuthorizationFree(cg->auth_ref, kAuthorizationFlagDefaults); +#endif /* HAVE_AUTHORIZATION_H */ + free(value); } Index: cups/globals.h =================================================================== --- cups/globals.h (revision 6197) +++ cups/globals.h (working copy) @@ -40,6 +40,9 @@ # include # endif /* HAVE_PTHREAD_H */ +# ifdef HAVE_AUTHORIZATION_H +# include +# endif /* HAVE_AUTHORIZATION_H */ /* * C++ magic... @@ -126,6 +129,11 @@ /* Default printer */ char ppd_filename[HTTP_MAX_URI]; /* PPD filename */ + +#ifdef HAVE_AUTHORIZATION_H + /* auth.c */ + AuthorizationRef auth_ref; /* Authorization ref */ +#endif /* HAVE_AUTHORIZATION_H */ } _cups_globals_t; Index: config.h.in =================================================================== --- config.h.in (revision 6197) +++ config.h.in (working copy) @@ -275,12 +275,15 @@ /* - * Do we have ? + * Do we have MacOSX's Security framework headers? */ +#undef HAVE_AUTHORIZATION_H #undef HAVE_SECBASEPRIV_H + + /* * Do we have the SLP library? */ Index: scheduler/client.c =================================================================== --- scheduler/client.c (revision 6197) +++ scheduler/client.c (working copy) @@ -642,6 +642,14 @@ con->language = NULL; } +#ifdef HAVE_AUTHORIZATION_H + if (con->authref) + { + AuthorizationFree(con->authref, kAuthorizationFlagDefaults); + con->authref = NULL; + } +#endif /* HAVE_AUTHORIZATION_H */ + /* * Re-enable new client connections if we are going back under the * limit... @@ -2311,6 +2319,8 @@ http_status_t code, /* I - HTTP status code */ char *type) /* I - MIME type of document */ { + char auth_str[1024]; + /* * Send the HTTP status header... */ @@ -2359,27 +2369,40 @@ else auth_type = con->best->type; + auth_str[0] = '\0'; + if (auth_type == AUTH_BASIC || auth_type == AUTH_BASICDIGEST) - { - if (httpPrintf(HTTP(con), - "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0) - return (0); - } + strncpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str)); else if (auth_type == AUTH_DIGEST) - { - if (httpPrintf(HTTP(con), - "WWW-Authenticate: Digest realm=\"CUPS\", nonce=\"%s\"\r\n", - con->http.hostname) < 0) - return (0); - } + snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"", + con->http.hostname); #ifdef HAVE_GSSAPI else if (auth_type == AUTH_KERBEROS && !con->no_negotiate && con->gss_output_token.length == 0) + strncpy(auth_str, "Negotiate", sizeof(auth_str)); +#endif /* HAVE_GSSAPI */ + +#ifdef HAVE_AUTHORIZATION_H + if (con->best) { - if (httpPrintf(HTTP(con), "WWW-Authenticate: Negotiate\r\n") < 0) - return (0); + int i; /* Looping var */ + char auth_key[1024]; /* Auth key buffer */ + + for (i = 0; i < con->best->num_names; i ++) + { + auth_key[0] = '\0'; + if (!strncasecmp(con->best->names[i], "@AUTHKEY(", 9)) + snprintf(auth_key, sizeof(auth_key), ", authkey=\"%s\"", &con->best->names[i][9]); + else if(!strcasecmp(con->best->names[i], "@SYSTEM") && SystemGroupAuthKey) + snprintf(auth_key, sizeof(auth_key), ", authkey=\"%s\"", SystemGroupAuthKey); + strlcat(auth_str, auth_key, sizeof(auth_str)); + } } -#endif /* HAVE_GSSAPI */ +#endif /* HAVE_AUTHORIZATION_H */ + + if (auth_str[0] && + httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0) + return (0); } #ifdef HAVE_GSSAPI Index: scheduler/client.h =================================================================== --- scheduler/client.h (revision 6197) +++ scheduler/client.h (working copy) @@ -22,6 +22,10 @@ * WWW: http://www.cups.org */ +#ifdef HAVE_AUTHORIZATION_H +# include +#endif /* HAVE_AUTHORIZATION_H */ + /* * HTTP client structure... */ @@ -62,6 +66,9 @@ gss_cred_id_t gss_delegated_cred; /* Credentials from client header */ #endif /* HAVE_GSSAPI */ +#ifdef HAVE_AUTHORIZATION_H + AuthorizationRef authref; /* Authorization ref */ +#endif /* HAVE_AUTHORIZATION_H */ }; #define HTTP(con) &((con)->http) Index: scheduler/conf.c =================================================================== --- scheduler/conf.c (revision 6197) +++ scheduler/conf.c (working copy) @@ -126,6 +126,10 @@ { "JobRetryInterval", &JobRetryInterval, CUPSD_VARTYPE_INTEGER }, { "KeepAliveTimeout", &KeepAliveTimeout, CUPSD_VARTYPE_INTEGER }, { "KeepAlive", &KeepAlive, CUPSD_VARTYPE_BOOLEAN }, +#ifdef HAVE_LAUNCHD + { "LaunchdTimeout", &LaunchdTimeout, CUPSD_VARTYPE_INTEGER }, + { "LaunchdConf", &LaunchdConf, CUPSD_VARTYPE_STRING }, +#endif /* HAVE_LAUNCHD */ { "LimitRequestBody", &MaxRequestSize, CUPSD_VARTYPE_INTEGER }, { "ListenBackLog", &ListenBackLog, CUPSD_VARTYPE_INTEGER }, { "LogFilePerm", &LogFilePerm, CUPSD_VARTYPE_INTEGER }, @@ -163,13 +167,12 @@ { "ServerKey", &ServerKey, CUPSD_VARTYPE_STRING }, # endif /* HAVE_LIBSSL || HAVE_GNUTLS */ #endif /* HAVE_SSL */ -#ifdef HAVE_LAUNCHD - { "LaunchdTimeout", &LaunchdTimeout, CUPSD_VARTYPE_INTEGER }, - { "LaunchdConf", &LaunchdConf, CUPSD_VARTYPE_STRING }, -#endif /* HAVE_LAUNCHD */ { "ServerName", &ServerName, CUPSD_VARTYPE_STRING }, { "ServerRoot", &ServerRoot, CUPSD_VARTYPE_STRING }, { "StateDir", &StateDir, CUPSD_VARTYPE_STRING }, +#ifdef HAVE_AUTHORIZATION_H + { "SystemGroupAuthKey", &SystemGroupAuthKey, CUPSD_VARTYPE_STRING }, +#endif /* HAVE_AUTHORIZATION_H */ { "TempDir", &TempDir, CUPSD_VARTYPE_STRING }, { "Timeout", &Timeout, CUPSD_VARTYPE_INTEGER }, { "UseNetworkDefault", &UseNetworkDefault, CUPSD_VARTYPE_BOOLEAN } @@ -444,6 +447,10 @@ cupsdDeleteAllPolicies(); cupsdClearString(&DefaultPolicy); +#ifdef HAVE_AUTHORIZATION_H + cupsdClearString(&SystemGroupAuthKey); +#endif /* HAVE_AUTHORIZATION_H */ + MaxSubscriptions = 100; MaxSubscriptionsPerJob = 0; MaxSubscriptionsPerPrinter = 0; @@ -1828,6 +1835,20 @@ while (isspace(*value & 255)) value ++; +#ifdef HAVE_AUTHORIZATION_H + if (!strncmp(value, "@AUTHKEY(", 9)) + { + /* + * Grab "@AUTHKEY(name)" value... + */ + + for (valptr = value + 9; *valptr != ')' && *valptr; valptr ++); + + if (*valptr) + *valptr++ = '\0'; + } + else +#endif /* HAVE_AUTHORIZATION_H */ if (*value == '\"' || *value == '\'') { /* Index: scheduler/conf.h =================================================================== --- scheduler/conf.h (revision 6197) +++ scheduler/conf.h (working copy) @@ -201,6 +201,12 @@ /* launchd(8) configuration file */ #endif /* HAVE_LAUNCHD */ +#ifdef HAVE_AUTHORIZATION_H +VAR char *SystemGroupAuthKey VALUE(NULL); + /* System group auth key */ +#endif /* HAVE_AUTHORIZATION_H */ + + /* * Prototypes... */ Index: scheduler/auth.c =================================================================== --- scheduler/auth.c (revision 6197) +++ scheduler/auth.c (working copy) @@ -56,6 +56,7 @@ * get_md5_password() - Get an MD5 password. * pam_func() - PAM conversation function. * to64() - Base64-encode an integer value... + * check_authref() - Check an authorization services reference. */ /* @@ -83,6 +84,14 @@ #ifdef HAVE_MEMBERSHIP_H # include #endif /* HAVE_MEMBERSHIP_H */ +#ifdef HAVE_AUTHORIZATION_H +# include +# ifdef HAVE_SECBASEPRIV_H +# include +# else + extern const char *cssmErrorString(int error); +# endif /* HAVE_SECBASEPRIV_H */ +#endif /* HAVE_AUTHORIZATION_H */ /* @@ -107,6 +116,9 @@ #elif !defined(HAVE_USERSEC_H) static void to64(char *s, unsigned long v, int n); #endif /* HAVE_LIBPAM */ +#ifdef HAVE_AUTHORIZATION_H +static int check_authref(cupsd_client_t *con, const char *right); +#endif /* HAVE_AUTHORIZATION_H */ /* @@ -371,6 +383,14 @@ username[0] = '\0'; password[0] = '\0'; +#ifdef HAVE_AUTHORIZATION_H + if (con->authref) + { + AuthorizationFree(con->authref, kAuthorizationFlagDefaults); + con->authref = NULL; + } +#endif /* HAVE_AUTHORIZATION_H */ + if (type == AUTH_NONE) { /* @@ -391,6 +411,59 @@ "cupsdAuthorize: No authentication data provided."); return; } +#ifdef HAVE_AUTHORIZATION_H + else if (!strncmp(authorization, "AuthRef", 6) && + !strcasecmp(con->http.hostname, "localhost")) + { + OSStatus status; /* Status */ + int authlen; /* Auth string length */ + AuthorizationItemSet *authinfo; /* Authorization item set */ + + /* + * Get the Authorization Services data... + */ + + authorization += 7; + while (isspace(*authorization & 255)) + authorization ++; + + authlen = sizeof(nonce); + httpDecode64_2(nonce, &authlen, authorization); + + if (authlen != kAuthorizationExternalFormLength) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "cupsdAuthorize: External Authorization reference size" + "is incorrect!"); + return; + } + + if ((status = AuthorizationCreateFromExternalForm( + (AuthorizationExternalForm *)nonce, &con->authref))) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "cupsdAuthorize: AuthorizationCreateFromExternalForm " + "returned %d (%s)", + (int)status, cssmErrorString(status)); + return; + } + + if ((status = AuthorizationCopyInfo(con->authref, + kAuthorizationEnvironmentUsername, + &authinfo))) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "cupsdAuthorize: AuthorizationCopyInfo returned %d (%s)", + (int)status, cssmErrorString(status)); + return; + } + + if (authinfo->count == 1) + strlcpy(username, authinfo->items[0].value, sizeof(username)); + + AuthorizationFreeItemSet(authinfo); + } +#endif /* HAVE_AUTHORIZATION_H */ else if (!strncmp(authorization, "Local", 5) && !strcasecmp(con->http.hostname, "localhost")) { @@ -1835,6 +1908,25 @@ 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) + { + for (i = 0; i < best->num_names; i ++) + if (!strncasecmp(best->names[i], "@AUTHKEY(", 9) && + check_authref(con, best->names[i] + 9)) + return (HTTP_OK); + else if (!strcasecmp(best->names[i], "@SYSTEM") && SystemGroupAuthKey && + check_authref(con, SystemGroupAuthKey)) + return (HTTP_OK); + + return (HTTP_UNAUTHORIZED); + } +#endif /* HAVE_AUTHORIZATION_H */ + for (i = 0; i < best->num_names; i ++) { if (!strcasecmp(best->names[i], "@OWNER") && owner && @@ -2363,6 +2455,57 @@ #endif /* HAVE_LIBPAM */ +#ifdef HAVE_AUTHORIZATION_H /* + * 'check_authref()' - Check if an authorization services reference has the + * supplied right. + */ + +static int /* O - 1 if right is valid, 0 otherwise */ +check_authref(cupsd_client_t *con, /* I - Connection */ + const char *right) /* I - Right name */ +{ + OSStatus status; /* OS Status */ + AuthorizationItem authright; /* Authorization right */ + AuthorizationRights authrights; /* Authorization rights */ + AuthorizationFlags authflags; /* Authorization flags */ + + /* + * Check to see if the user is allowed to perform the task... + */ + + if (!con->authref) + return (0); + + authright.name = right; + authright.valueLength = 0; + authright.value = NULL; + authright.flags = 0; + + authrights.count = 1; + authrights.items = &authright; + + authflags = kAuthorizationFlagDefaults | + kAuthorizationFlagExtendRights; + + if ((status = AuthorizationCopyRights(con->authref, &authrights, + kAuthorizationEmptyEnvironment, + authflags, NULL))) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "check_authref: AuthorizationCopyRights(\"%s\") returned %d (%s)", + authright.name, (int)status, cssmErrorString(status)); + return (0); + } + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "AuthorizationCopyRights(\"%s\") succeeded!", + authright.name); + + return (1); +} +#endif /* HAVE_AUTHORIZATION_H */ + + +/* * End of "$Id$". */