/* * Configuration routines for the CUPS scheduler. * * Copyright 2007-2017 by Apple Inc. * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the * property of Apple Inc. and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "LICENSE.txt" * which should have been included with this file. If this file is * missing or damaged, see the license at "http://www.cups.org/". */ /* * Include necessary headers... */ #include "cupsd.h" #include #include #include #ifdef HAVE_ASL_H # include #elif defined(HAVE_SYSTEMD_SD_JOURNAL_H) # define SD_JOURNAL_SUPPRESS_LOCATION # include #endif /* HAVE_ASL_H */ #include #ifdef HAVE_LIBPAPER # include #endif /* HAVE_LIBPAPER */ /* * Possibly missing network definitions... */ #ifndef INADDR_NONE # define INADDR_NONE 0xffffffff #endif /* !INADDR_NONE */ /* * Configuration variable structure... */ typedef enum { CUPSD_VARTYPE_INTEGER, /* Integer option */ CUPSD_VARTYPE_TIME, /* Time interval option */ CUPSD_VARTYPE_STRING, /* String option */ CUPSD_VARTYPE_BOOLEAN, /* Boolean option */ CUPSD_VARTYPE_PATHNAME, /* File/directory name option */ CUPSD_VARTYPE_PERM /* File/directory permissions */ } cupsd_vartype_t; typedef struct { const char *name; /* Name of variable */ void *ptr; /* Pointer to variable */ cupsd_vartype_t type; /* Type (int, string, address) */ } cupsd_var_t; /* * Local globals... */ static const cupsd_var_t cupsd_vars[] = { { "AutoPurgeJobs", &JobAutoPurge, CUPSD_VARTYPE_BOOLEAN }, #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) { "BrowseDNSSDSubTypes", &DNSSDSubTypes, CUPSD_VARTYPE_STRING }, #endif /* HAVE_DNSSD || HAVE_AVAHI */ { "BrowseWebIF", &BrowseWebIF, CUPSD_VARTYPE_BOOLEAN }, { "Browsing", &Browsing, CUPSD_VARTYPE_BOOLEAN }, { "Classification", &Classification, CUPSD_VARTYPE_STRING }, { "ClassifyOverride", &ClassifyOverride, CUPSD_VARTYPE_BOOLEAN }, { "DefaultLanguage", &DefaultLanguage, CUPSD_VARTYPE_STRING }, { "DefaultLeaseDuration", &DefaultLeaseDuration, CUPSD_VARTYPE_TIME }, { "DefaultPaperSize", &DefaultPaperSize, CUPSD_VARTYPE_STRING }, { "DefaultPolicy", &DefaultPolicy, CUPSD_VARTYPE_STRING }, { "DefaultShared", &DefaultShared, CUPSD_VARTYPE_BOOLEAN }, { "DirtyCleanInterval", &DirtyCleanInterval, CUPSD_VARTYPE_TIME }, { "ErrorPolicy", &ErrorPolicy, CUPSD_VARTYPE_STRING }, { "FilterLimit", &FilterLimit, CUPSD_VARTYPE_INTEGER }, { "FilterNice", &FilterNice, CUPSD_VARTYPE_INTEGER }, #ifdef HAVE_GSSAPI { "GSSServiceName", &GSSServiceName, CUPSD_VARTYPE_STRING }, #endif /* HAVE_GSSAPI */ #ifdef HAVE_ONDEMAND { "IdleExitTimeout", &IdleExitTimeout, CUPSD_VARTYPE_TIME }, #endif /* HAVE_ONDEMAND */ { "JobKillDelay", &JobKillDelay, CUPSD_VARTYPE_TIME }, { "JobRetryLimit", &JobRetryLimit, CUPSD_VARTYPE_INTEGER }, { "JobRetryInterval", &JobRetryInterval, CUPSD_VARTYPE_TIME }, { "KeepAliveTimeout", &KeepAliveTimeout, CUPSD_VARTYPE_TIME }, { "KeepAlive", &KeepAlive, CUPSD_VARTYPE_BOOLEAN }, #ifdef HAVE_LAUNCHD { "LaunchdTimeout", &IdleExitTimeout, CUPSD_VARTYPE_TIME }, #endif /* HAVE_LAUNCHD */ { "LimitRequestBody", &MaxRequestSize, CUPSD_VARTYPE_INTEGER }, { "ListenBackLog", &ListenBackLog, CUPSD_VARTYPE_INTEGER }, { "LogDebugHistory", &LogDebugHistory, CUPSD_VARTYPE_INTEGER }, { "MaxActiveJobs", &MaxActiveJobs, CUPSD_VARTYPE_INTEGER }, { "MaxClients", &MaxClients, CUPSD_VARTYPE_INTEGER }, { "MaxClientsPerHost", &MaxClientsPerHost, CUPSD_VARTYPE_INTEGER }, { "MaxCopies", &MaxCopies, CUPSD_VARTYPE_INTEGER }, { "MaxEvents", &MaxEvents, CUPSD_VARTYPE_INTEGER }, { "MaxHoldTime", &MaxHoldTime, CUPSD_VARTYPE_TIME }, { "MaxJobs", &MaxJobs, CUPSD_VARTYPE_INTEGER }, { "MaxJobsPerPrinter", &MaxJobsPerPrinter, CUPSD_VARTYPE_INTEGER }, { "MaxJobsPerUser", &MaxJobsPerUser, CUPSD_VARTYPE_INTEGER }, { "MaxJobTime", &MaxJobTime, CUPSD_VARTYPE_TIME }, { "MaxLeaseDuration", &MaxLeaseDuration, CUPSD_VARTYPE_TIME }, { "MaxLogSize", &MaxLogSize, CUPSD_VARTYPE_INTEGER }, { "MaxRequestSize", &MaxRequestSize, CUPSD_VARTYPE_INTEGER }, { "MaxSubscriptions", &MaxSubscriptions, CUPSD_VARTYPE_INTEGER }, { "MaxSubscriptionsPerJob", &MaxSubscriptionsPerJob, CUPSD_VARTYPE_INTEGER }, { "MaxSubscriptionsPerPrinter",&MaxSubscriptionsPerPrinter, CUPSD_VARTYPE_INTEGER }, { "MaxSubscriptionsPerUser", &MaxSubscriptionsPerUser, CUPSD_VARTYPE_INTEGER }, { "MultipleOperationTimeout", &MultipleOperationTimeout, CUPSD_VARTYPE_TIME }, { "PageLogFormat", &PageLogFormat, CUPSD_VARTYPE_STRING }, { "PreserveJobFiles", &JobFiles, CUPSD_VARTYPE_TIME }, { "PreserveJobHistory", &JobHistory, CUPSD_VARTYPE_TIME }, { "ReloadTimeout", &ReloadTimeout, CUPSD_VARTYPE_TIME }, { "RIPCache", &RIPCache, CUPSD_VARTYPE_STRING }, { "RootCertDuration", &RootCertDuration, CUPSD_VARTYPE_TIME }, { "ServerAdmin", &ServerAdmin, CUPSD_VARTYPE_STRING }, { "ServerName", &ServerName, CUPSD_VARTYPE_STRING }, { "StrictConformance", &StrictConformance, CUPSD_VARTYPE_BOOLEAN }, { "Timeout", &Timeout, CUPSD_VARTYPE_TIME }, { "WebInterface", &WebInterface, CUPSD_VARTYPE_BOOLEAN } }; static const cupsd_var_t cupsfiles_vars[] = { { "AccessLog", &AccessLog, CUPSD_VARTYPE_STRING }, { "CacheDir", &CacheDir, CUPSD_VARTYPE_STRING }, { "ConfigFilePerm", &ConfigFilePerm, CUPSD_VARTYPE_PERM }, #ifdef HAVE_SSL { "CreateSelfSignedCerts", &CreateSelfSignedCerts, CUPSD_VARTYPE_BOOLEAN }, #endif /* HAVE_SSL */ { "DataDir", &DataDir, CUPSD_VARTYPE_STRING }, { "DocumentRoot", &DocumentRoot, CUPSD_VARTYPE_STRING }, { "ErrorLog", &ErrorLog, CUPSD_VARTYPE_STRING }, { "FileDevice", &FileDevice, CUPSD_VARTYPE_BOOLEAN }, { "FontPath", &FontPath, CUPSD_VARTYPE_STRING }, { "LogFilePerm", &LogFilePerm, CUPSD_VARTYPE_PERM }, { "LPDConfigFile", &LPDConfigFile, CUPSD_VARTYPE_STRING }, { "PageLog", &PageLog, CUPSD_VARTYPE_STRING }, { "Printcap", &Printcap, CUPSD_VARTYPE_STRING }, { "RemoteRoot", &RemoteRoot, CUPSD_VARTYPE_STRING }, { "RequestRoot", &RequestRoot, CUPSD_VARTYPE_STRING }, { "ServerBin", &ServerBin, CUPSD_VARTYPE_PATHNAME }, #ifdef HAVE_SSL { "ServerKeychain", &ServerKeychain, CUPSD_VARTYPE_PATHNAME }, #endif /* HAVE_SSL */ { "ServerRoot", &ServerRoot, CUPSD_VARTYPE_PATHNAME }, { "SMBConfigFile", &SMBConfigFile, CUPSD_VARTYPE_STRING }, { "StateDir", &StateDir, CUPSD_VARTYPE_STRING }, { "SyncOnClose", &SyncOnClose, CUPSD_VARTYPE_BOOLEAN }, #ifdef HAVE_AUTHORIZATION_H { "SystemGroupAuthKey", &SystemGroupAuthKey, CUPSD_VARTYPE_STRING }, #endif /* HAVE_AUTHORIZATION_H */ { "TempDir", &TempDir, CUPSD_VARTYPE_PATHNAME } }; static int default_auth_type = CUPSD_AUTH_AUTO; /* Default AuthType, if not specified */ static const unsigned ones[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; static const unsigned zeros[4] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; /* * Local functions... */ static http_addrlist_t *get_address(const char *value, int defport); static int get_addr_and_mask(const char *value, unsigned *ip, unsigned *mask); static void mime_error_cb(void *ctx, const char *message); static int parse_aaa(cupsd_location_t *loc, char *line, char *value, int linenum); static int parse_fatal_errors(const char *s); static int parse_groups(const char *s, int linenum); static int parse_protocols(const char *s); static int parse_variable(const char *filename, int linenum, const char *line, const char *value, size_t num_vars, const cupsd_var_t *vars); static int read_cupsd_conf(cups_file_t *fp); static int read_cups_files_conf(cups_file_t *fp); static int read_location(cups_file_t *fp, char *name, int linenum); static int read_policy(cups_file_t *fp, char *name, int linenum); static void set_policy_defaults(cupsd_policy_t *pol); /* * 'cupsdAddAlias()' - Add a host alias. */ void cupsdAddAlias(cups_array_t *aliases, /* I - Array of aliases */ const char *name) /* I - Name to add */ { cupsd_alias_t *a; /* New alias */ size_t namelen; /* Length of name */ namelen = strlen(name); if ((a = (cupsd_alias_t *)malloc(sizeof(cupsd_alias_t) + namelen)) == NULL) return; a->namelen = namelen; memcpy(a->name, name, namelen + 1); /* OK since a->name is allocated */ cupsArrayAdd(aliases, a); } /* * 'cupsdCheckPermissions()' - Fix the mode and ownership of a file or directory. */ int /* O - 0 on success, -1 on error, 1 on warning */ cupsdCheckPermissions( const char *filename, /* I - File/directory name */ const char *suffix, /* I - Additional file/directory name */ mode_t mode, /* I - Permissions */ uid_t user, /* I - Owner */ gid_t group, /* I - Group */ int is_dir, /* I - 1 = directory, 0 = file */ int create_dir) /* I - 1 = create directory, -1 = create w/o logging, 0 = not */ { int dir_created = 0; /* Did we create a directory? */ char pathname[1024]; /* File name with prefix */ struct stat fileinfo; /* Stat buffer */ int is_symlink; /* Is "filename" a symlink? */ /* * Prepend the given root to the filename before testing it... */ if (suffix) { snprintf(pathname, sizeof(pathname), "%s/%s", filename, suffix); filename = pathname; } /* * See if we can stat the file/directory... */ if (lstat(filename, &fileinfo)) { if (errno == ENOENT && create_dir) { if (create_dir > 0) cupsdLogMessage(CUPSD_LOG_DEBUG, "Creating missing directory \"%s\"", filename); if (mkdir(filename, mode)) { if (create_dir > 0) cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create directory \"%s\" - %s", filename, strerror(errno)); else #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "Unable to create directory \"%s\" - %s", filename, strerror(errno)); #else syslog(LOG_ERR, "Unable to create directory \"%s\" - %s", filename, strerror(errno)); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ return (-1); } dir_created = 1; fileinfo.st_mode = mode | S_IFDIR; } else return (create_dir ? -1 : 1); } if ((is_symlink = S_ISLNK(fileinfo.st_mode)) != 0) { if (stat(filename, &fileinfo)) { cupsdLogMessage(CUPSD_LOG_ERROR, "\"%s\" is a bad symlink - %s", filename, strerror(errno)); return (-1); } } /* * Make sure it's a regular file or a directory as needed... */ if (!dir_created && !is_dir && !S_ISREG(fileinfo.st_mode)) { cupsdLogMessage(CUPSD_LOG_ERROR, "\"%s\" is not a regular file.", filename); return (-1); } if (!dir_created && is_dir && !S_ISDIR(fileinfo.st_mode)) { if (create_dir >= 0) cupsdLogMessage(CUPSD_LOG_ERROR, "\"%s\" is not a directory.", filename); else #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "\"%s\" is not a directory.", filename); #else syslog(LOG_ERR, "\"%s\" is not a directory.", filename); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ return (-1); } /* * If the filename is a symlink, do not change permissions (STR #2937)... */ if (is_symlink) return (0); /* * Fix owner, group, and mode as needed... */ if (dir_created || fileinfo.st_uid != user || fileinfo.st_gid != group) { if (create_dir >= 0) cupsdLogMessage(CUPSD_LOG_DEBUG, "Repairing ownership of \"%s\"", filename); if (chown(filename, user, group) && !getuid()) { if (create_dir >= 0) cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to change ownership of \"%s\" - %s", filename, strerror(errno)); else #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "Unable to change ownership of \"%s\" - %s", filename, strerror(errno)); #else syslog(LOG_ERR, "Unable to change ownership of \"%s\" - %s", filename, strerror(errno)); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ return (1); } } if (dir_created || (fileinfo.st_mode & 07777) != mode) { if (create_dir >= 0) cupsdLogMessage(CUPSD_LOG_DEBUG, "Repairing access permissions of \"%s\"", filename); if (chmod(filename, mode)) { if (create_dir >= 0) cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to change permissions of \"%s\" - %s", filename, strerror(errno)); else #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "Unable to change permissions of \"%s\" - %s", filename, strerror(errno)); #else syslog(LOG_ERR, "Unable to change permissions of \"%s\" - %s", filename, strerror(errno)); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ return (1); } } /* * Everything is OK... */ return (0); } /* * 'cupsdDefaultAuthType()' - Get the default AuthType. * * When the default_auth_type is "auto", this function tries to get the GSS * credentials for the server. If that succeeds we use Kerberos authentication, * otherwise we do a fallback to Basic authentication against the local user * accounts. */ int /* O - Default AuthType value */ cupsdDefaultAuthType(void) { #ifdef HAVE_GSSAPI OM_uint32 major_status, /* Major status code */ minor_status; /* Minor status code */ gss_name_t server_name; /* Server name */ gss_buffer_desc token = GSS_C_EMPTY_BUFFER; /* Service name token */ char buf[1024]; /* Service name buffer */ #endif /* HAVE_GSSAPI */ /* * If we have already determined the correct default AuthType, use it... */ if (default_auth_type != CUPSD_AUTH_AUTO) return (default_auth_type); #ifdef HAVE_GSSAPI # ifdef __APPLE__ /* * If the weak-linked GSSAPI/Kerberos library is not present, don't try * to use it... */ if (&gss_init_sec_context == NULL) return (default_auth_type = CUPSD_AUTH_BASIC); # endif /* __APPLE__ */ /* * Try to obtain the server's GSS credentials (GSSServiceName@servername). If * that fails we must use Basic... */ snprintf(buf, sizeof(buf), "%s@%s", GSSServiceName, ServerName); token.value = buf; token.length = strlen(buf); server_name = GSS_C_NO_NAME; major_status = gss_import_name(&minor_status, &token, GSS_C_NT_HOSTBASED_SERVICE, &server_name); memset(&token, 0, sizeof(token)); if (GSS_ERROR(major_status)) { cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status, "cupsdDefaultAuthType: gss_import_name(%s) failed", buf); return (default_auth_type = CUPSD_AUTH_BASIC); } major_status = gss_display_name(&minor_status, server_name, &token, NULL); if (GSS_ERROR(major_status)) { cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status, "cupsdDefaultAuthType: gss_display_name(%s) failed", buf); return (default_auth_type = CUPSD_AUTH_BASIC); } cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdDefaultAuthType: Attempting to acquire Kerberos " "credentials for %s...", (char *)token.value); ServerCreds = GSS_C_NO_CREDENTIAL; major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &ServerCreds, NULL, NULL); if (GSS_ERROR(major_status)) { cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status, "cupsdDefaultAuthType: gss_acquire_cred(%s) failed", (char *)token.value); gss_release_name(&minor_status, &server_name); gss_release_buffer(&minor_status, &token); return (default_auth_type = CUPSD_AUTH_BASIC); } cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdDefaultAuthType: Kerberos credentials acquired " "successfully for %s.", (char *)token.value); gss_release_name(&minor_status, &server_name); gss_release_buffer(&minor_status, &token); HaveServerCreds = 1; return (default_auth_type = CUPSD_AUTH_NEGOTIATE); #else /* * No Kerberos support compiled in so just use Basic all the time... */ return (default_auth_type = CUPSD_AUTH_BASIC); #endif /* HAVE_GSSAPI */ } /* * 'cupsdFreeAliases()' - Free all of the alias entries. */ void cupsdFreeAliases(cups_array_t *aliases) /* I - Array of aliases */ { cupsd_alias_t *a; /* Current alias */ for (a = (cupsd_alias_t *)cupsArrayFirst(aliases); a; a = (cupsd_alias_t *)cupsArrayNext(aliases)) free(a); cupsArrayDelete(aliases); } /* * 'cupsdReadConfiguration()' - Read the cupsd.conf file. */ int /* O - 1 on success, 0 otherwise */ cupsdReadConfiguration(void) { int i; /* Looping var */ cups_file_t *fp; /* Configuration file */ int status; /* Return status */ char temp[1024], /* Temporary buffer */ mimedir[1024], /* MIME directory */ *slash; /* Directory separator */ cups_lang_t *language; /* Language */ struct passwd *user; /* Default user */ struct group *group; /* Default group */ char *old_serverroot, /* Old ServerRoot */ *old_requestroot; /* Old RequestRoot */ int old_remote_port; /* Old RemotePort */ const char *tmpdir; /* TMPDIR environment variable */ struct stat tmpinfo; /* Temporary directory info */ cupsd_policy_t *p; /* Policy */ /* * Save the old root paths... */ old_serverroot = NULL; cupsdSetString(&old_serverroot, ServerRoot); old_requestroot = NULL; cupsdSetString(&old_requestroot, RequestRoot); /* * Reset the server configuration data... */ cupsdDeleteAllLocations(); cupsdDeleteAllListeners(); old_remote_port = RemotePort; RemotePort = 0; /* * String options... */ cupsdFreeAliases(ServerAlias); ServerAlias = NULL; cupsdClearString(&ServerName); cupsdClearString(&ServerAdmin); cupsdSetString(&ServerBin, CUPS_SERVERBIN); cupsdSetString(&RequestRoot, CUPS_REQUESTS); cupsdSetString(&CacheDir, CUPS_CACHEDIR); cupsdSetString(&DataDir, CUPS_DATADIR); cupsdSetString(&DocumentRoot, CUPS_DOCROOT); cupsdSetString(&AccessLog, CUPS_LOGDIR "/access_log"); cupsdClearString(&ErrorLog); cupsdSetString(&PageLog, CUPS_LOGDIR "/page_log"); cupsdSetString(&PageLogFormat, "%p %u %j %T %P %C %{job-billing} " "%{job-originating-host-name} %{job-name} %{media} %{sides}"); cupsdSetString(&Printcap, CUPS_DEFAULT_PRINTCAP); cupsdSetString(&FontPath, CUPS_FONTPATH); cupsdSetString(&RemoteRoot, "remroot"); cupsdSetStringf(&ServerHeader, "CUPS/%d.%d IPP/2.1", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR); cupsdSetString(&StateDir, CUPS_STATEDIR); if (!strcmp(CUPS_DEFAULT_PRINTCAP, "/etc/printers.conf")) PrintcapFormat = PRINTCAP_SOLARIS; else if (!strcmp(CUPS_DEFAULT_PRINTCAP, "/Library/Preferences/org.cups.printers.plist")) PrintcapFormat = PRINTCAP_PLIST; else PrintcapFormat = PRINTCAP_BSD; strlcpy(temp, ConfigurationFile, sizeof(temp)); if ((slash = strrchr(temp, '/')) != NULL) *slash = '\0'; cupsdSetString(&ServerRoot, temp); cupsdClearString(&Classification); ClassifyOverride = 0; #ifdef HAVE_SSL # ifdef HAVE_GNUTLS cupsdSetString(&ServerKeychain, "ssl"); # else cupsdSetString(&ServerKeychain, "/Library/Keychains/System.keychain"); # endif /* HAVE_GNUTLS */ _httpTLSSetOptions(_HTTP_TLS_NONE, _HTTP_TLS_1_0, _HTTP_TLS_MAX); #endif /* HAVE_SSL */ language = cupsLangDefault(); if (!strcmp(language->language, "C") || !strcmp(language->language, "POSIX")) cupsdSetString(&DefaultLanguage, "en"); else cupsdSetString(&DefaultLanguage, language->language); cupsdClearString(&DefaultPaperSize); cupsdSetString(&RIPCache, "128m"); cupsdSetString(&TempDir, NULL); #ifdef HAVE_GSSAPI cupsdSetString(&GSSServiceName, CUPS_DEFAULT_GSSSERVICENAME); if (HaveServerCreds) { OM_uint32 minor_status; /* Minor status code */ gss_release_cred(&minor_status, &ServerCreds); HaveServerCreds = 0; } ServerCreds = GSS_C_NO_CREDENTIAL; #endif /* HAVE_GSSAPI */ /* * Find the default user... */ if ((user = getpwnam(CUPS_DEFAULT_USER)) != NULL) User = user->pw_uid; else { /* * Use the (historical) NFS nobody user ID (-2 as a 16-bit twos- * complement number...) */ User = 65534; } endpwent(); /* * Find the default group... */ group = getgrnam(CUPS_DEFAULT_GROUP); endgrent(); if (group) Group = group->gr_gid; else { /* * Fallback to group "nobody"... */ group = getgrnam("nobody"); endgrent(); if (group) Group = group->gr_gid; else { /* * Use the (historical) NFS nobody group ID (-2 as a 16-bit twos- * complement number...) */ Group = 65534; } } /* * Numeric options... */ AccessLogLevel = CUPSD_ACCESSLOG_ACTIONS; ConfigFilePerm = CUPS_DEFAULT_CONFIG_FILE_PERM; FatalErrors = parse_fatal_errors(CUPS_DEFAULT_FATAL_ERRORS); default_auth_type = CUPSD_AUTH_BASIC; #ifdef HAVE_SSL CreateSelfSignedCerts = TRUE; DefaultEncryption = HTTP_ENCRYPT_REQUIRED; #endif /* HAVE_SSL */ DirtyCleanInterval = DEFAULT_KEEPALIVE; JobKillDelay = DEFAULT_TIMEOUT; JobRetryLimit = 5; JobRetryInterval = 300; FileDevice = FALSE; FilterLevel = 0; FilterLimit = 0; FilterNice = 0; HostNameLookups = FALSE; KeepAlive = TRUE; KeepAliveTimeout = DEFAULT_KEEPALIVE; ListenBackLog = SOMAXCONN; LogDebugHistory = 99999; LogFilePerm = CUPS_DEFAULT_LOG_FILE_PERM; LogLevel = CUPSD_LOG_WARN; LogTimeFormat = CUPSD_TIME_STANDARD; MaxClients = 100; MaxClientsPerHost = 0; MaxLogSize = 1024 * 1024; MaxRequestSize = 0; MultipleOperationTimeout = DEFAULT_TIMEOUT; NumSystemGroups = 0; ReloadTimeout = DEFAULT_KEEPALIVE; RootCertDuration = 300; Sandboxing = CUPSD_SANDBOXING_STRICT; StrictConformance = FALSE; SyncOnClose = TRUE; Timeout = DEFAULT_TIMEOUT; WebInterface = CUPS_DEFAULT_WEBIF; BrowseLocalProtocols = parse_protocols(CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS); BrowseWebIF = FALSE; Browsing = CUPS_DEFAULT_BROWSING; DefaultShared = CUPS_DEFAULT_DEFAULT_SHARED; #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) cupsdSetString(&DNSSDSubTypes, "_cups,_print,_universal"); #endif /* HAVE_DNSSD || HAVE_AVAHI */ cupsdSetString(&LPDConfigFile, CUPS_DEFAULT_LPD_CONFIG_FILE); cupsdSetString(&SMBConfigFile, CUPS_DEFAULT_SMB_CONFIG_FILE); cupsdSetString(&ErrorPolicy, "retry-job"); JobHistory = DEFAULT_HISTORY; JobFiles = DEFAULT_FILES; JobAutoPurge = 0; MaxHoldTime = 0; MaxJobs = 500; MaxActiveJobs = 0; MaxJobsPerUser = 0; MaxJobsPerPrinter = 0; MaxJobTime = 3 * 60 * 60; /* 3 hours */ MaxCopies = CUPS_DEFAULT_MAX_COPIES; cupsdDeleteAllPolicies(); cupsdClearString(&DefaultPolicy); #ifdef HAVE_AUTHORIZATION_H cupsdSetString(&SystemGroupAuthKey, CUPS_DEFAULT_SYSTEM_AUTHKEY); #endif /* HAVE_AUTHORIZATION_H */ MaxSubscriptions = 100; MaxSubscriptionsPerJob = 0; MaxSubscriptionsPerPrinter = 0; MaxSubscriptionsPerUser = 0; DefaultLeaseDuration = 86400; MaxLeaseDuration = 0; #ifdef HAVE_ONDEMAND IdleExitTimeout = 60; #endif /* HAVE_ONDEMAND */ /* * Setup environment variables... */ cupsdInitEnv(); /* * Read the cups-files.conf file... */ if ((fp = cupsFileOpen(CupsFilesFile, "r")) != NULL) { status = read_cups_files_conf(fp); cupsFileClose(fp); if (!status) { if (TestConfigFile) printf("\"%s\" contains errors.\n", CupsFilesFile); else #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "Unable to read \"%s\" due to errors.", CupsFilesFile); #else syslog(LOG_LPR, "Unable to read \"%s\" due to errors.", CupsFilesFile); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ return (0); } } else if (errno == ENOENT) cupsdLogMessage(CUPSD_LOG_INFO, "No %s, using defaults.", CupsFilesFile); else { #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "Unable to open \"%s\" - %s", CupsFilesFile, strerror(errno)); #else syslog(LOG_LPR, "Unable to open \"%s\" - %s", CupsFilesFile, strerror(errno)); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ return (0); } if (!ErrorLog) cupsdSetString(&ErrorLog, CUPS_LOGDIR "/error_log"); /* * Read the cupsd.conf file... */ if ((fp = cupsFileOpen(ConfigurationFile, "r")) == NULL) { #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "Unable to open \"%s\" - %s", ConfigurationFile, strerror(errno)); #else syslog(LOG_LPR, "Unable to open \"%s\" - %s", ConfigurationFile, strerror(errno)); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ return (0); } status = read_cupsd_conf(fp); cupsFileClose(fp); if (!status) { if (TestConfigFile) printf("\"%s\" contains errors.\n", ConfigurationFile); else #ifdef HAVE_SYSTEMD_SD_JOURNAL_H sd_journal_print(LOG_ERR, "Unable to read \"%s\" due to errors.", ConfigurationFile); #else syslog(LOG_LPR, "Unable to read \"%s\" due to errors.", ConfigurationFile); #endif /* HAVE_SYSTEMD_SD_JOURNAL_H */ return (0); } RunUser = getuid(); cupsdLogMessage(CUPSD_LOG_INFO, "Remote access is %s.", RemotePort ? "enabled" : "disabled"); if (!RemotePort) BrowseLocalProtocols = 0; /* Disable sharing - no remote access */ /* * See if the ServerName is an IP address... */ if (ServerName) { if (!ServerAlias) ServerAlias = cupsArrayNew(NULL, NULL); cupsdAddAlias(ServerAlias, ServerName); cupsdLogMessage(CUPSD_LOG_DEBUG, "Added auto ServerAlias %s", ServerName); } else { if (gethostname(temp, sizeof(temp))) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get hostname: %s", strerror(errno)); strlcpy(temp, "localhost", sizeof(temp)); } cupsdSetString(&ServerName, temp); if (!ServerAlias) ServerAlias = cupsArrayNew(NULL, NULL); cupsdAddAlias(ServerAlias, temp); cupsdLogMessage(CUPSD_LOG_DEBUG, "Added auto ServerAlias %s", temp); if (HostNameLookups) { struct hostent *host; /* Host entry to get FQDN */ if ((host = gethostbyname(temp)) != NULL) { if (_cups_strcasecmp(temp, host->h_name)) { cupsdSetString(&ServerName, host->h_name); cupsdAddAlias(ServerAlias, host->h_name); cupsdLogMessage(CUPSD_LOG_DEBUG, "Added auto ServerAlias %s", host->h_name); } if (host->h_aliases) { for (i = 0; host->h_aliases[i]; i ++) if (_cups_strcasecmp(temp, host->h_aliases[i])) { cupsdAddAlias(ServerAlias, host->h_aliases[i]); cupsdLogMessage(CUPSD_LOG_DEBUG, "Added auto ServerAlias %s", host->h_aliases[i]); } } } } /* * Make sure we have the base hostname added as an alias, too! */ if ((slash = strchr(temp, '.')) != NULL) { *slash = '\0'; cupsdAddAlias(ServerAlias, temp); cupsdLogMessage(CUPSD_LOG_DEBUG, "Added auto ServerAlias %s", temp); } } for (slash = ServerName; isdigit(*slash & 255) || *slash == '.'; slash ++); ServerNameIsIP = !*slash; /* * Make sure ServerAdmin is initialized... */ if (!ServerAdmin) cupsdSetStringf(&ServerAdmin, "root@%s", ServerName); /* * Use the default system group if none was supplied in cupsd.conf... */ if (NumSystemGroups == 0) { if (!parse_groups(CUPS_DEFAULT_SYSTEM_GROUPS, 0)) { /* * Find the group associated with GID 0... */ group = getgrgid(0); endgrent(); if (group != NULL) cupsdSetString(&SystemGroups[0], group->gr_name); else cupsdSetString(&SystemGroups[0], "unknown"); SystemGroupIDs[0] = 0; NumSystemGroups = 1; } } /* * Make sure ConfigFilePerm and LogFilePerm have sane values... */ ConfigFilePerm &= 0664; LogFilePerm &= 0664; /* * Open the system log for cupsd if necessary... */ if (!LogStderr) { if (!strcmp(AccessLog, "stderr")) cupsdSetString(&AccessLog, "syslog"); if (!strcmp(ErrorLog, "stderr")) cupsdSetString(&ErrorLog, "syslog"); if (!strcmp(PageLog, "stderr")) cupsdSetString(&PageLog, "syslog"); } #if defined(HAVE_VSYSLOG) && !defined(HAVE_ASL_H) && !defined(HAVE_SYSTEMD_SD_JOURNAL_H) if (!strcmp(AccessLog, "syslog") || !strcmp(ErrorLog, "syslog") || !strcmp(PageLog, "syslog")) openlog("cupsd", LOG_PID | LOG_NOWAIT | LOG_NDELAY, LOG_LPR); #endif /* HAVE_VSYSLOG && !HAVE_ASL_H && !HAVE_SYSTEMD_SD_JOURNAL_H */ /* * Log the configuration file that was used... */ cupsdLogMessage(CUPSD_LOG_INFO, "Loaded configuration file \"%s\"", ConfigurationFile); /* * Validate the Group and SystemGroup settings - they cannot be the same, * otherwise the CGI programs will be able to authenticate as root without * a password! */ if (!RunUser) { for (i = 0; i < NumSystemGroups; i ++) if (Group == SystemGroupIDs[i]) break; if (i < NumSystemGroups) { /* * Log the error and reset the group to a safe value... */ cupsdLogMessage(CUPSD_LOG_ERROR, "Group and SystemGroup cannot use the same groups."); if (FatalErrors & (CUPSD_FATAL_CONFIG | CUPSD_FATAL_PERMISSIONS)) return (0); cupsdLogMessage(CUPSD_LOG_INFO, "Resetting Group to \"nobody\"..."); group = getgrnam("nobody"); endgrent(); if (group != NULL) Group = group->gr_gid; else { /* * Use the (historical) NFS nobody group ID (-2 as a 16-bit twos- * complement number...) */ Group = 65534; } } } /* * Check that we have at least one listen/port line; if not, report this * as an error and exit! */ if (cupsArrayCount(Listeners) == 0) { /* * No listeners! */ cupsdLogMessage(CUPSD_LOG_EMERG, "No valid Listen or Port lines were found in the " "configuration file."); /* * Commit suicide... */ cupsdEndProcess(getpid(), 0); } /* * Set the default locale using the language and charset... */ cupsdSetStringf(&DefaultLocale, "%s.UTF-8", DefaultLanguage); /* * Update all relative filenames to include the full path from ServerRoot... */ if (DocumentRoot[0] != '/') cupsdSetStringf(&DocumentRoot, "%s/%s", ServerRoot, DocumentRoot); if (RequestRoot[0] != '/') cupsdSetStringf(&RequestRoot, "%s/%s", ServerRoot, RequestRoot); if (ServerBin[0] != '/') cupsdSetStringf(&ServerBin, "%s/%s", ServerRoot, ServerBin); if (StateDir[0] != '/') cupsdSetStringf(&StateDir, "%s/%s", ServerRoot, StateDir); if (CacheDir[0] != '/') cupsdSetStringf(&CacheDir, "%s/%s", ServerRoot, CacheDir); #ifdef HAVE_SSL if (!_cups_strcasecmp(ServerKeychain, "internal")) cupsdClearString(&ServerKeychain); else if (ServerKeychain[0] != '/') cupsdSetStringf(&ServerKeychain, "%s/%s", ServerRoot, ServerKeychain); cupsdLogMessage(CUPSD_LOG_DEBUG, "Using keychain \"%s\" for server name \"%s\".", ServerKeychain ? ServerKeychain : "internal", ServerName); if (!CreateSelfSignedCerts) cupsdLogMessage(CUPSD_LOG_DEBUG, "Self-signed TLS certificate generation is disabled."); cupsSetServerCredentials(ServerKeychain, ServerName, CreateSelfSignedCerts); #endif /* HAVE_SSL */ /* * Make sure that directories and config files are owned and * writable by the user and group in the cupsd.conf file... */ snprintf(temp, sizeof(temp), "%s/rss", CacheDir); if ((cupsdCheckPermissions(RequestRoot, NULL, 0710, RunUser, Group, 1, 1) < 0 || cupsdCheckPermissions(CacheDir, NULL, 0770, RunUser, Group, 1, 1) < 0 || cupsdCheckPermissions(temp, NULL, 0775, RunUser, Group, 1, 1) < 0 || cupsdCheckPermissions(StateDir, NULL, 0755, RunUser, Group, 1, 1) < 0 || cupsdCheckPermissions(StateDir, "certs", RunUser ? 0711 : 0511, User, SystemGroupIDs[0], 1, 1) < 0 || cupsdCheckPermissions(ServerRoot, NULL, 0755, RunUser, Group, 1, 0) < 0 || cupsdCheckPermissions(ServerRoot, "ppd", 0755, RunUser, Group, 1, 1) < 0 || cupsdCheckPermissions(ServerRoot, "ssl", 0700, RunUser, Group, 1, 0) < 0 || /* Never alter permissions of central conffile cupsdCheckPermissions(ConfigurationFile, NULL, ConfigFilePerm, RunUser, Group, 0, 0) < 0 || cupsdCheckPermissions(CupsFilesFile, NULL, ConfigFilePerm, RunUser, Group, 0, 0) < 0 || */ cupsdCheckPermissions(ServerRoot, "classes.conf", 0600, RunUser, Group, 0, 0) < 0 || cupsdCheckPermissions(ServerRoot, "printers.conf", 0600, RunUser, Group, 0, 0) < 0 || cupsdCheckPermissions(ServerRoot, "passwd.md5", 0600, User, Group, 0, 0) < 0) && (FatalErrors & CUPSD_FATAL_PERMISSIONS)) return (0); /* * Update TempDir to the default if it hasn't been set already... */ #ifdef __APPLE__ if (TempDir && !RunUser && (!strncmp(TempDir, "/private/tmp", 12) || !strncmp(TempDir, "/tmp", 4))) { cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot use %s for TempDir.", TempDir); cupsdClearString(&TempDir); } #endif /* __APPLE__ */ if (!TempDir) { #ifdef __APPLE__ if ((tmpdir = getenv("TMPDIR")) != NULL && strncmp(tmpdir, "/private/tmp", 12) && strncmp(tmpdir, "/tmp", 4)) #else if ((tmpdir = getenv("TMPDIR")) != NULL) #endif /* __APPLE__ */ { /* * TMPDIR is defined, see if it is OK for us to use... */ if (stat(tmpdir, &tmpinfo)) cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to access TMPDIR (%s): %s", tmpdir, strerror(errno)); else if (!S_ISDIR(tmpinfo.st_mode)) cupsdLogMessage(CUPSD_LOG_ERROR, "TMPDIR (%s) is not a directory.", tmpdir); else if ((tmpinfo.st_uid != User || !(tmpinfo.st_mode & S_IWUSR)) && (tmpinfo.st_gid != Group || !(tmpinfo.st_mode & S_IWGRP)) && !(tmpinfo.st_mode & S_IWOTH)) cupsdLogMessage(CUPSD_LOG_ERROR, "TMPDIR (%s) has the wrong permissions.", tmpdir); else cupsdSetString(&TempDir, tmpdir); } } if (!TempDir) { cupsdLogMessage(CUPSD_LOG_INFO, "Using default TempDir of %s/tmp...", RequestRoot); cupsdSetStringf(&TempDir, "%s/tmp", RequestRoot); } setenv("TMPDIR", TempDir, 1); /* * Make sure the temporary directory has the right permissions... */ if (!strncmp(TempDir, RequestRoot, strlen(RequestRoot)) || access(TempDir, 0)) { /* * Update ownership and permissions if the CUPS temp directory * is under the spool directory or does not exist... */ if (cupsdCheckPermissions(TempDir, NULL, 01770, RunUser, Group, 1, 1) < 0 && (FatalErrors & CUPSD_FATAL_PERMISSIONS)) return (0); } /* * Update environment variables... */ cupsdUpdateEnv(); /* * Validate the default error policy... */ if (strcmp(ErrorPolicy, "retry-current-job") && strcmp(ErrorPolicy, "abort-job") && strcmp(ErrorPolicy, "retry-job") && strcmp(ErrorPolicy, "stop-printer")) { cupsdLogMessage(CUPSD_LOG_ALERT, "Invalid ErrorPolicy \"%s\", resetting to \"retry-job\".", ErrorPolicy); cupsdSetString(&ErrorPolicy, "retry-job"); } /* * Update default paper size setting as needed... */ if (!DefaultPaperSize) { #ifdef HAVE_LIBPAPER char *paper_result; /* Paper size name from libpaper */ if ((paper_result = systempapername()) != NULL) cupsdSetString(&DefaultPaperSize, paper_result); else #endif /* HAVE_LIBPAPER */ if (!DefaultLanguage || !_cups_strcasecmp(DefaultLanguage, "C") || !_cups_strcasecmp(DefaultLanguage, "POSIX") || !_cups_strcasecmp(DefaultLanguage, "en") || !_cups_strncasecmp(DefaultLanguage, "en.", 3) || !_cups_strncasecmp(DefaultLanguage, "en_US", 5) || !_cups_strncasecmp(DefaultLanguage, "en_CA", 5) || !_cups_strncasecmp(DefaultLanguage, "fr_CA", 5)) { /* * These are the only locales that will default to "letter" size... */ cupsdSetString(&DefaultPaperSize, "Letter"); } else cupsdSetString(&DefaultPaperSize, "A4"); } /* * Update classification setting as needed... */ if (Classification && !_cups_strcasecmp(Classification, "none")) cupsdClearString(&Classification); if (Classification) cupsdLogMessage(CUPSD_LOG_INFO, "Security set to \"%s\"", Classification); /* * Check the MaxClients setting, and then allocate memory for it... */ if (MaxClients > (MaxFDs / 3) || MaxClients <= 0) { if (MaxClients > 0) cupsdLogMessage(CUPSD_LOG_INFO, "MaxClients limited to 1/3 (%d) of the file descriptor " "limit (%d)...", MaxFDs / 3, MaxFDs); MaxClients = MaxFDs / 3; } cupsdLogMessage(CUPSD_LOG_INFO, "Configured for up to %d clients.", MaxClients); /* * Check the MaxActiveJobs setting; limit to 1/3 the available * file descriptors, since we need a pipe for each job... */ if (MaxActiveJobs > (MaxFDs / 3)) MaxActiveJobs = MaxFDs / 3; /* * Update the MaxClientsPerHost value, as needed... */ if (MaxClientsPerHost <= 0) MaxClientsPerHost = MaxClients; if (MaxClientsPerHost > MaxClients) MaxClientsPerHost = MaxClients; cupsdLogMessage(CUPSD_LOG_INFO, "Allowing up to %d client connections per host.", MaxClientsPerHost); /* * Update the default policy, as needed... */ if (DefaultPolicy) DefaultPolicyPtr = cupsdFindPolicy(DefaultPolicy); else DefaultPolicyPtr = NULL; if (!DefaultPolicyPtr) { cupsd_location_t *po; /* New policy operation */ if (DefaultPolicy) cupsdLogMessage(CUPSD_LOG_ERROR, "Default policy \"%s\" not found.", DefaultPolicy); cupsdSetString(&DefaultPolicy, "default"); if ((DefaultPolicyPtr = cupsdFindPolicy("default")) != NULL) cupsdLogMessage(CUPSD_LOG_INFO, "Using policy \"default\" as the default."); else { cupsdLogMessage(CUPSD_LOG_INFO, "Creating CUPS default administrative policy:"); DefaultPolicyPtr = p = cupsdAddPolicy("default"); cupsdLogMessage(CUPSD_LOG_INFO, ""); cupsdLogMessage(CUPSD_LOG_INFO, "JobPrivateAccess default"); cupsdAddString(&(p->job_access), "@OWNER"); cupsdAddString(&(p->job_access), "@SYSTEM"); cupsdLogMessage(CUPSD_LOG_INFO, "JobPrivateValues default"); cupsdAddString(&(p->job_attrs), "job-name"); cupsdAddString(&(p->job_attrs), "job-originating-host-name"); cupsdAddString(&(p->job_attrs), "job-originating-user-name"); cupsdAddString(&(p->job_attrs), "phone"); cupsdLogMessage(CUPSD_LOG_INFO, "SubscriptionPrivateAccess default"); cupsdAddString(&(p->sub_access), "@OWNER"); cupsdAddString(&(p->sub_access), "@SYSTEM"); cupsdLogMessage(CUPSD_LOG_INFO, "SubscriptionPrivateValues default"); cupsdAddString(&(p->job_attrs), "notify-events"); cupsdAddString(&(p->job_attrs), "notify-pull-method"); cupsdAddString(&(p->job_attrs), "notify-recipient-uri"); cupsdAddString(&(p->job_attrs), "notify-subscriber-user-name"); cupsdAddString(&(p->job_attrs), "notify-user-data"); cupsdLogMessage(CUPSD_LOG_INFO, ""); cupsdLogMessage(CUPSD_LOG_INFO, "Order Deny,Allow"); po = cupsdAddPolicyOp(p, NULL, IPP_CREATE_JOB); po->order_type = CUPSD_AUTH_ALLOW; cupsdAddPolicyOp(p, po, IPP_PRINT_JOB); cupsdAddPolicyOp(p, po, IPP_PRINT_URI); cupsdAddPolicyOp(p, po, IPP_VALIDATE_JOB); cupsdLogMessage(CUPSD_LOG_INFO, ""); cupsdLogMessage(CUPSD_LOG_INFO, ""); cupsdLogMessage(CUPSD_LOG_INFO, "Order Deny,Allow"); po = cupsdAddPolicyOp(p, NULL, IPP_SEND_DOCUMENT); po->order_type = CUPSD_AUTH_ALLOW; po->level = CUPSD_AUTH_USER; cupsdAddName(po, "@OWNER"); cupsdAddName(po, "@SYSTEM"); cupsdLogMessage(CUPSD_LOG_INFO, "Require user @OWNER @SYSTEM"); cupsdAddPolicyOp(p, po, IPP_SEND_URI); cupsdAddPolicyOp(p, po, IPP_CANCEL_JOB); cupsdAddPolicyOp(p, po, IPP_HOLD_JOB); cupsdAddPolicyOp(p, po, IPP_RELEASE_JOB); cupsdAddPolicyOp(p, po, IPP_RESTART_JOB); cupsdAddPolicyOp(p, po, IPP_PURGE_JOBS); cupsdAddPolicyOp(p, po, IPP_SET_JOB_ATTRIBUTES); cupsdAddPolicyOp(p, po, IPP_CREATE_JOB_SUBSCRIPTION); cupsdAddPolicyOp(p, po, IPP_RENEW_SUBSCRIPTION); cupsdAddPolicyOp(p, po, IPP_CANCEL_SUBSCRIPTION); cupsdAddPolicyOp(p, po, IPP_GET_NOTIFICATIONS); cupsdAddPolicyOp(p, po, IPP_REPROCESS_JOB); cupsdAddPolicyOp(p, po, IPP_CANCEL_CURRENT_JOB); cupsdAddPolicyOp(p, po, IPP_SUSPEND_CURRENT_JOB); cupsdAddPolicyOp(p, po, IPP_RESUME_JOB); cupsdAddPolicyOp(p, po, IPP_CANCEL_MY_JOBS); cupsdAddPolicyOp(p, po, IPP_CLOSE_JOB); cupsdAddPolicyOp(p, po, CUPS_MOVE_JOB); cupsdAddPolicyOp(p, po, CUPS_AUTHENTICATE_JOB); cupsdAddPolicyOp(p, po, CUPS_GET_DOCUMENT); cupsdLogMessage(CUPSD_LOG_INFO, ""); cupsdLogMessage(CUPSD_LOG_INFO, ""); cupsdLogMessage(CUPSD_LOG_INFO, "Order Deny,Allow"); cupsdLogMessage(CUPSD_LOG_INFO, "AuthType Default"); po = cupsdAddPolicyOp(p, NULL, IPP_PAUSE_PRINTER); po->order_type = CUPSD_AUTH_ALLOW; po->type = CUPSD_AUTH_DEFAULT; po->level = CUPSD_AUTH_USER; cupsdAddName(po, "@SYSTEM"); cupsdLogMessage(CUPSD_LOG_INFO, "Require user @SYSTEM"); cupsdAddPolicyOp(p, po, IPP_RESUME_PRINTER); cupsdAddPolicyOp(p, po, IPP_SET_PRINTER_ATTRIBUTES); cupsdAddPolicyOp(p, po, IPP_ENABLE_PRINTER); cupsdAddPolicyOp(p, po, IPP_DISABLE_PRINTER); cupsdAddPolicyOp(p, po, IPP_PAUSE_PRINTER_AFTER_CURRENT_JOB); cupsdAddPolicyOp(p, po, IPP_HOLD_NEW_JOBS); cupsdAddPolicyOp(p, po, IPP_RELEASE_HELD_NEW_JOBS); cupsdAddPolicyOp(p, po, IPP_DEACTIVATE_PRINTER); cupsdAddPolicyOp(p, po, IPP_ACTIVATE_PRINTER); cupsdAddPolicyOp(p, po, IPP_RESTART_PRINTER); cupsdAddPolicyOp(p, po, IPP_SHUTDOWN_PRINTER); cupsdAddPolicyOp(p, po, IPP_STARTUP_PRINTER); cupsdAddPolicyOp(p, po, IPP_PROMOTE_JOB); cupsdAddPolicyOp(p, po, IPP_SCHEDULE_JOB_AFTER); cupsdAddPolicyOp(p, po, IPP_CANCEL_JOBS); cupsdAddPolicyOp(p, po, CUPS_ADD_PRINTER); cupsdAddPolicyOp(p, po, CUPS_DELETE_PRINTER); cupsdAddPolicyOp(p, po, CUPS_ADD_CLASS); cupsdAddPolicyOp(p, po, CUPS_DELETE_CLASS); cupsdAddPolicyOp(p, po, CUPS_ACCEPT_JOBS); cupsdAddPolicyOp(p, po, CUPS_REJECT_JOBS); cupsdAddPolicyOp(p, po, CUPS_SET_DEFAULT); cupsdLogMessage(CUPSD_LOG_INFO, ""); cupsdLogMessage(CUPSD_LOG_INFO, ""); cupsdLogMessage(CUPSD_LOG_INFO, "Order Deny,Allow"); po = cupsdAddPolicyOp(p, NULL, IPP_ANY_OPERATION); po->order_type = CUPSD_AUTH_ALLOW; cupsdLogMessage(CUPSD_LOG_INFO, ""); cupsdLogMessage(CUPSD_LOG_INFO, ""); } } cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadConfiguration: NumPolicies=%d", cupsArrayCount(Policies)); for (i = 0, p = (cupsd_policy_t *)cupsArrayFirst(Policies); p; i ++, p = (cupsd_policy_t *)cupsArrayNext(Policies)) cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadConfiguration: Policies[%d]=\"%s\"", i, p->name); /* * If we are doing a full reload or the server root has changed, flush * the jobs, printers, etc. and start from scratch... */ if (NeedReload == RELOAD_ALL || old_remote_port != RemotePort || !old_serverroot || !ServerRoot || strcmp(old_serverroot, ServerRoot) || !old_requestroot || !RequestRoot || strcmp(old_requestroot, RequestRoot)) { mime_type_t *type; /* Current type */ char mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE]; /* MIME type name */ cupsdLogMessage(CUPSD_LOG_INFO, "Full reload is required."); /* * Free all memory... */ cupsdDeleteAllSubscriptions(); cupsdFreeAllJobs(); cupsdDeleteAllPrinters(); DefaultPrinter = NULL; if (MimeDatabase != NULL) mimeDelete(MimeDatabase); if (NumMimeTypes) { for (i = 0; i < NumMimeTypes; i ++) _cupsStrFree(MimeTypes[i]); free(MimeTypes); } /* * Read the MIME type and conversion database... */ snprintf(temp, sizeof(temp), "%s/filter", ServerBin); snprintf(mimedir, sizeof(mimedir), "%s/mime", DataDir); MimeDatabase = mimeNew(); mimeSetErrorCallback(MimeDatabase, mime_error_cb, NULL); MimeDatabase = mimeLoadTypes(MimeDatabase, mimedir); MimeDatabase = mimeLoadTypes(MimeDatabase, ServerRoot); MimeDatabase = mimeLoadFilters(MimeDatabase, mimedir, temp); MimeDatabase = mimeLoadFilters(MimeDatabase, ServerRoot, temp); if (!MimeDatabase) { cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to load MIME database from \"%s\" or \"%s\".", mimedir, ServerRoot); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } cupsdLogMessage(CUPSD_LOG_INFO, "Loaded MIME database from \"%s\" and \"%s\": %d types, " "%d filters...", mimedir, ServerRoot, mimeNumTypes(MimeDatabase), mimeNumFilters(MimeDatabase)); /* * Create a list of MIME types for the document-format-supported * attribute... */ NumMimeTypes = mimeNumTypes(MimeDatabase); if (!mimeType(MimeDatabase, "application", "octet-stream")) NumMimeTypes ++; if ((MimeTypes = calloc((size_t)NumMimeTypes, sizeof(const char *))) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for %d MIME types.", NumMimeTypes); NumMimeTypes = 0; } else { for (i = 0, type = mimeFirstType(MimeDatabase); type; i ++, type = mimeNextType(MimeDatabase)) { snprintf(mimetype, sizeof(mimetype), "%s/%s", type->super, type->type); MimeTypes[i] = _cupsStrAlloc(mimetype); } if (i < NumMimeTypes) MimeTypes[i] = _cupsStrAlloc("application/octet-stream"); } if (LogLevel == CUPSD_LOG_DEBUG2) { mime_filter_t *filter; /* Current filter */ for (type = mimeFirstType(MimeDatabase); type; type = mimeNextType(MimeDatabase)) cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadConfiguration: type %s/%s", type->super, type->type); for (filter = mimeFirstFilter(MimeDatabase); filter; filter = mimeNextFilter(MimeDatabase)) cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadConfiguration: filter %s/%s to %s/%s %d %s", filter->src->super, filter->src->type, filter->dst->super, filter->dst->type, filter->cost, filter->filter); } /* * Load banners... */ snprintf(temp, sizeof(temp), "%s/banners", DataDir); cupsdLoadBanners(temp); /* * Load printers and classes... */ cupsdLoadAllPrinters(); cupsdLoadAllClasses(); cupsdCreateCommonData(); /* * Update the printcap file as needed... */ if (Printcap && *Printcap && access(Printcap, 0)) cupsdWritePrintcap(); /* * Load queued jobs... */ cupsdLoadAllJobs(); /* * Load subscriptions... */ cupsdLoadAllSubscriptions(); cupsdLogMessage(CUPSD_LOG_INFO, "Full reload complete."); } else { /* * Not a full reload, so recreate the common printer attributes... */ cupsdCreateCommonData(); /* * Update all jobs as needed... */ cupsdUpdateJobs(); /* * Update all printers as needed... */ cupsdUpdatePrinters(); cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); cupsdLogMessage(CUPSD_LOG_INFO, "Partial reload complete."); } /* * Reset the reload state... */ NeedReload = RELOAD_NONE; cupsdClearString(&old_serverroot); cupsdClearString(&old_requestroot); return (1); } /* * 'get_address()' - Get an address + port number from a line. */ static http_addrlist_t * /* O - Pointer to list if address good, NULL if bad */ get_address(const char *value, /* I - Value string */ int defport) /* I - Default port */ { char buffer[1024], /* Hostname + port number buffer */ defpname[255], /* Default port name */ *hostname, /* Hostname or IP */ *portname; /* Port number or name */ http_addrlist_t *addrlist; /* Address list */ /* * Check for an empty value... */ if (!*value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Bad (empty) address."); return (NULL); } /* * Grab a hostname and port number; if there is no colon and the port name * is only digits, then we have a port number by itself... */ strlcpy(buffer, value, sizeof(buffer)); if ((portname = strrchr(buffer, ':')) != NULL && !strchr(portname, ']')) { *portname++ = '\0'; hostname = buffer; } else { for (portname = buffer; isdigit(*portname & 255); portname ++); if (*portname) { /* * Use the default port... */ sprintf(defpname, "%d", defport); portname = defpname; hostname = buffer; } else { /* * The buffer contains just a port number... */ portname = buffer; hostname = NULL; } } if (hostname && !strcmp(hostname, "*")) hostname = NULL; /* * Now lookup the address using httpAddrGetList()... */ if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL) cupsdLogMessage(CUPSD_LOG_ERROR, "Hostname lookup for \"%s\" failed.", hostname ? hostname : "(nil)"); return (addrlist); } /* * 'get_addr_and_mask()' - Get an IP address and netmask. */ static int /* O - 1 on success, 0 on failure */ get_addr_and_mask(const char *value, /* I - String from config file */ unsigned *ip, /* O - Address value */ unsigned *mask) /* O - Mask value */ { int i, j, /* Looping vars */ family, /* Address family */ ipcount; /* Count of fields in address */ unsigned ipval; /* Value */ const char *maskval, /* Pointer to start of mask value */ *ptr, /* Pointer into value */ *ptr2; /* ... */ /* * Get the address... */ ip[0] = ip[1] = ip[2] = ip[3] = 0x00000000; mask[0] = mask[1] = mask[2] = mask[3] = 0xffffffff; if ((maskval = strchr(value, '/')) != NULL) maskval ++; else maskval = value + strlen(value); #ifdef AF_INET6 /* * Check for an IPv6 address... */ if (*value == '[') { /* * Parse hexadecimal IPv6/IPv4 address... */ family = AF_INET6; for (i = 0, ptr = value + 1; *ptr && i < 8; i ++) { if (*ptr == ']') break; else if (!strncmp(ptr, "::", 2)) { for (ptr2 = strchr(ptr + 2, ':'), j = 0; ptr2; ptr2 = strchr(ptr2 + 1, ':'), j ++); i = 6 - j; ptr += 2; } else if (isdigit(*ptr & 255) && strchr(ptr + 1, '.') && i >= 6) { /* * Read IPv4 dotted quad... */ unsigned val[4] = { 0, 0, 0, 0 }; /* IPv4 address values */ ipcount = sscanf(ptr, "%u.%u.%u.%u", val + 0, val + 1, val + 2, val + 3); /* * Range check the IP numbers... */ for (i = 0; i < ipcount; i ++) if (val[i] > 255) return (0); /* * Merge everything into a 32-bit IPv4 address in ip[3]... */ ip[3] = ((((((unsigned)val[0] << 8) | (unsigned)val[1]) << 8) | (unsigned)val[2]) << 8) | (unsigned)val[3]; if (ipcount < 4) mask[3] = (0xffffffff << (32 - 8 * ipcount)) & 0xffffffff; /* * If the leading words are all 0's then this is an IPv4 address... */ if (!val[0] && !val[1] && !val[2]) family = AF_INET; while (isdigit(*ptr & 255) || *ptr == '.') ptr ++; break; } else if (isxdigit(*ptr & 255)) { ipval = strtoul(ptr, (char **)&ptr, 16); if (*ptr == ':' && ptr[1] != ':') ptr ++; if (ipval > 0xffff) return (0); if (i & 1) ip[i / 2] |= ipval; else ip[i / 2] |= ipval << 16; } else return (0); } if (*ptr != ']') return (0); ptr ++; if (*ptr && *ptr != '/') return (0); } else #endif /* AF_INET6 */ { /* * Parse dotted-decimal IPv4 address... */ unsigned val[4] = { 0, 0, 0, 0 }; /* IPv4 address values */ family = AF_INET; ipcount = sscanf(value, "%u.%u.%u.%u", val + 0, val + 1, val + 2, val + 3); /* * Range check the IP numbers... */ for (i = 0; i < ipcount; i ++) if (val[i] > 255) return (0); /* * Merge everything into a 32-bit IPv4 address in ip[3]... */ ip[3] = ((((((unsigned)val[0] << 8) | (unsigned)val[1]) << 8) | (unsigned)val[2]) << 8) | (unsigned)val[3]; if (ipcount < 4) mask[3] = (0xffffffff << (32 - 8 * ipcount)) & 0xffffffff; } if (*maskval) { /* * Get the netmask value(s)... */ memset(mask, 0, sizeof(unsigned) * 4); if (strchr(maskval, '.')) { /* * Get dotted-decimal mask... */ if (family != AF_INET) return (0); if (sscanf(maskval, "%u.%u.%u.%u", mask + 0, mask + 1, mask + 2, mask + 3) != 4) return (0); mask[3] |= (((((unsigned)mask[0] << 8) | (unsigned)mask[1]) << 8) | (unsigned)mask[2]) << 8; mask[0] = mask[1] = mask[2] = 0; } else { /* * Get address/bits format... */ i = atoi(maskval); #ifdef AF_INET6 if (family == AF_INET6) { if (i > 128) return (0); i = 128 - i; if (i <= 96) mask[0] = 0xffffffff; else mask[0] = (0xffffffff << (i - 96)) & 0xffffffff; if (i <= 64) mask[1] = 0xffffffff; else if (i >= 96) mask[1] = 0; else mask[1] = (0xffffffff << (i - 64)) & 0xffffffff; if (i <= 32) mask[2] = 0xffffffff; else if (i >= 64) mask[2] = 0; else mask[2] = (0xffffffff << (i - 32)) & 0xffffffff; if (i == 0) mask[3] = 0xffffffff; else if (i >= 32) mask[3] = 0; else mask[3] = (0xffffffff << i) & 0xffffffff; } else #endif /* AF_INET6 */ { if (i > 32) return (0); mask[0] = 0xffffffff; mask[1] = 0xffffffff; mask[2] = 0xffffffff; if (i < 32) mask[3] = (0xffffffff << (32 - i)) & 0xffffffff; else mask[3] = 0xffffffff; } } } cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_addr_and_mask(value=\"%s\", " "ip=[%08x:%08x:%08x:%08x], mask=[%08x:%08x:%08x:%08x])", value, ip[0], ip[1], ip[2], ip[3], mask[0], mask[1], mask[2], mask[3]); /* * Check for a valid netmask; no fallback like in CUPS 1.1.x! */ if ((ip[0] & ~mask[0]) != 0 || (ip[1] & ~mask[1]) != 0 || (ip[2] & ~mask[2]) != 0 || (ip[3] & ~mask[3]) != 0) return (0); return (1); } /* * 'mime_error_cb()' - Log a MIME error. */ static void mime_error_cb(void *ctx, /* I - Context pointer (unused) */ const char *message) /* I - Message */ { (void)ctx; cupsdLogMessage(CUPSD_LOG_ERROR, "%s", message); } /* * 'parse_aaa()' - Parse authentication, authorization, and access control lines. */ static int /* O - 1 on success, 0 on failure */ parse_aaa(cupsd_location_t *loc, /* I - Location */ char *line, /* I - Line from file */ char *value, /* I - Start of value data */ int linenum) /* I - Current line number */ { char *valptr; /* Pointer into value */ unsigned ip[4], /* IP address components */ mask[4]; /* IP netmask components */ if (!_cups_strcasecmp(line, "Encryption")) { /* * "Encryption xxx" - set required encryption level... */ if (!_cups_strcasecmp(value, "never")) loc->encryption = HTTP_ENCRYPT_NEVER; else if (!_cups_strcasecmp(value, "always")) { cupsdLogMessage(CUPSD_LOG_ERROR, "Encryption value \"%s\" on line %d of %s is invalid in this " "context. Using \"required\" instead.", value, linenum, ConfigurationFile); loc->encryption = HTTP_ENCRYPT_REQUIRED; } else if (!_cups_strcasecmp(value, "required")) loc->encryption = HTTP_ENCRYPT_REQUIRED; else if (!_cups_strcasecmp(value, "ifrequested")) loc->encryption = HTTP_ENCRYPT_IF_REQUESTED; else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown Encryption value %s on line %d of %s.", value, linenum, ConfigurationFile); return (0); } } else if (!_cups_strcasecmp(line, "Order")) { /* * "Order Deny,Allow" or "Order Allow,Deny"... */ if (!_cups_strncasecmp(value, "deny", 4)) loc->order_type = CUPSD_AUTH_ALLOW; else if (!_cups_strncasecmp(value, "allow", 5)) loc->order_type = CUPSD_AUTH_DENY; else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown Order value %s on line %d of %s.", value, linenum, ConfigurationFile); return (0); } } else if (!_cups_strcasecmp(line, "Allow") || !_cups_strcasecmp(line, "Deny")) { /* * Allow [From] host/ip... * Deny [From] host/ip... */ while (*value) { if (!_cups_strncasecmp(value, "from", 4)) { /* * Strip leading "from"... */ value += 4; while (_cups_isspace(*value)) value ++; if (!*value) break; } /* * Find the end of the value... */ for (valptr = value; *valptr && !_cups_isspace(*valptr); valptr ++); while (_cups_isspace(*valptr)) *valptr++ = '\0'; /* * Figure out what form the allow/deny address takes: * * All * None * *.domain.com * .domain.com * host.domain.com * nnn.* * nnn.nnn.* * nnn.nnn.nnn.* * nnn.nnn.nnn.nnn * nnn.nnn.nnn.nnn/mm * nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm */ if (!_cups_strcasecmp(value, "all")) { /* * All hosts... */ if (!_cups_strcasecmp(line, "Allow")) cupsdAddIPMask(&(loc->allow), zeros, zeros); else cupsdAddIPMask(&(loc->deny), zeros, zeros); } else if (!_cups_strcasecmp(value, "none")) { /* * No hosts... */ if (!_cups_strcasecmp(line, "Allow")) cupsdAddIPMask(&(loc->allow), ones, zeros); else cupsdAddIPMask(&(loc->deny), ones, zeros); } #ifdef AF_INET6 else if (value[0] == '*' || value[0] == '.' || (!isdigit(value[0] & 255) && value[0] != '[')) #else else if (value[0] == '*' || value[0] == '.' || !isdigit(value[0] & 255)) #endif /* AF_INET6 */ { /* * Host or domain name... */ if (value[0] == '*') value ++; if (!_cups_strcasecmp(line, "Allow")) cupsdAddNameMask(&(loc->allow), value); else cupsdAddNameMask(&(loc->deny), value); } else { /* * One of many IP address forms... */ if (!get_addr_and_mask(value, ip, mask)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Bad netmask value %s on line %d of %s.", value, linenum, ConfigurationFile); return (0); } if (!_cups_strcasecmp(line, "Allow")) cupsdAddIPMask(&(loc->allow), ip, mask); else cupsdAddIPMask(&(loc->deny), ip, mask); } /* * Advance to next value... */ value = valptr; } } else if (!_cups_strcasecmp(line, "AuthType")) { /* * AuthType {none,basic,digest,basicdigest,negotiate,default} */ if (!_cups_strcasecmp(value, "none")) { loc->type = CUPSD_AUTH_NONE; loc->level = CUPSD_AUTH_ANON; } else if (!_cups_strcasecmp(value, "basic")) { loc->type = CUPSD_AUTH_BASIC; if (loc->level == CUPSD_AUTH_ANON) loc->level = CUPSD_AUTH_USER; } else if (!_cups_strcasecmp(value, "default")) { loc->type = CUPSD_AUTH_DEFAULT; if (loc->level == CUPSD_AUTH_ANON) loc->level = CUPSD_AUTH_USER; } else if (!_cups_strcasecmp(value, "negotiate")) { loc->type = CUPSD_AUTH_NEGOTIATE; if (loc->level == CUPSD_AUTH_ANON) loc->level = CUPSD_AUTH_USER; } else { cupsdLogMessage(CUPSD_LOG_WARN, "Unknown authorization type %s on line %d of %s.", value, linenum, ConfigurationFile); return (0); } } else if (!_cups_strcasecmp(line, "AuthClass")) { /* * AuthClass anonymous, user, system, group */ if (!_cups_strcasecmp(value, "anonymous")) { loc->type = CUPSD_AUTH_NONE; loc->level = CUPSD_AUTH_ANON; cupsdLogMessage(CUPSD_LOG_WARN, "\"AuthClass %s\" is deprecated; consider removing " "it from line %d.", value, linenum); } else if (!_cups_strcasecmp(value, "user")) { loc->level = CUPSD_AUTH_USER; cupsdLogMessage(CUPSD_LOG_WARN, "\"AuthClass %s\" is deprecated; consider using " "\"Require valid-user\" on line %d of %s.", value, linenum, ConfigurationFile); } else if (!_cups_strcasecmp(value, "group")) { loc->level = CUPSD_AUTH_GROUP; cupsdLogMessage(CUPSD_LOG_WARN, "\"AuthClass %s\" is deprecated; consider using " "\"Require user @groupname\" on line %d of %s.", value, linenum, ConfigurationFile); } else if (!_cups_strcasecmp(value, "system")) { loc->level = CUPSD_AUTH_GROUP; cupsdAddName(loc, "@SYSTEM"); cupsdLogMessage(CUPSD_LOG_WARN, "\"AuthClass %s\" is deprecated; consider using " "\"Require user @SYSTEM\" on line %d of %s.", value, linenum, ConfigurationFile); } else { cupsdLogMessage(CUPSD_LOG_WARN, "Unknown authorization class %s on line %d of %s.", value, linenum, ConfigurationFile); return (0); } } else if (!_cups_strcasecmp(line, "AuthGroupName")) { cupsdAddName(loc, value); cupsdLogMessage(CUPSD_LOG_WARN, "\"AuthGroupName %s\" directive is deprecated; consider " "using \"Require user @%s\" on line %d of %s.", value, value, linenum, ConfigurationFile); } else if (!_cups_strcasecmp(line, "Require")) { /* * Apache synonym for AuthClass and AuthGroupName... * * Get initial word: * * Require valid-user * Require group names * Require user names */ for (valptr = value; !_cups_isspace(*valptr) && *valptr; valptr ++); if (*valptr) *valptr++ = '\0'; if (!_cups_strcasecmp(value, "valid-user") || !_cups_strcasecmp(value, "user")) loc->level = CUPSD_AUTH_USER; else if (!_cups_strcasecmp(value, "group")) loc->level = CUPSD_AUTH_GROUP; else { cupsdLogMessage(CUPSD_LOG_WARN, "Unknown Require type %s on line %d of %s.", value, linenum, ConfigurationFile); return (0); } /* * Get the list of names from the line... */ for (value = valptr; *value;) { while (_cups_isspace(*value)) 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 == '\'') { /* * Grab quoted name... */ for (valptr = value + 1; *valptr != *value && *valptr; valptr ++); value ++; } else { /* * Grab literal name. */ for (valptr = value; !_cups_isspace(*valptr) && *valptr; valptr ++); } if (*valptr) *valptr++ = '\0'; cupsdAddName(loc, value); for (value = valptr; _cups_isspace(*value); value ++); } } else if (!_cups_strcasecmp(line, "Satisfy")) { if (!_cups_strcasecmp(value, "all")) loc->satisfy = CUPSD_AUTH_SATISFY_ALL; else if (!_cups_strcasecmp(value, "any")) loc->satisfy = CUPSD_AUTH_SATISFY_ANY; else { cupsdLogMessage(CUPSD_LOG_WARN, "Unknown Satisfy value %s on line %d of %s.", value, linenum, ConfigurationFile); return (0); } } else return (0); return (1); } /* * 'parse_fatal_errors()' - Parse FatalErrors values in a string. */ static int /* O - FatalErrors bits */ parse_fatal_errors(const char *s) /* I - FatalErrors string */ { int fatal; /* FatalErrors bits */ char value[1024], /* Value string */ *valstart, /* Pointer into value */ *valend; /* End of value */ /* * Empty FatalErrors line yields NULL pointer... */ if (!s) return (CUPSD_FATAL_NONE); /* * Loop through the value string,... */ strlcpy(value, s, sizeof(value)); fatal = CUPSD_FATAL_NONE; for (valstart = value; *valstart;) { /* * Get the current space/comma-delimited kind name... */ for (valend = valstart; *valend; valend ++) if (_cups_isspace(*valend) || *valend == ',') break; if (*valend) *valend++ = '\0'; /* * Add the error to the bitmask... */ if (!_cups_strcasecmp(valstart, "all")) fatal = CUPSD_FATAL_ALL; else if (!_cups_strcasecmp(valstart, "browse")) fatal |= CUPSD_FATAL_BROWSE; else if (!_cups_strcasecmp(valstart, "-browse")) fatal &= ~CUPSD_FATAL_BROWSE; else if (!_cups_strcasecmp(valstart, "config")) fatal |= CUPSD_FATAL_CONFIG; else if (!_cups_strcasecmp(valstart, "-config")) fatal &= ~CUPSD_FATAL_CONFIG; else if (!_cups_strcasecmp(valstart, "listen")) fatal |= CUPSD_FATAL_LISTEN; else if (!_cups_strcasecmp(valstart, "-listen")) fatal &= ~CUPSD_FATAL_LISTEN; else if (!_cups_strcasecmp(valstart, "log")) fatal |= CUPSD_FATAL_LOG; else if (!_cups_strcasecmp(valstart, "-log")) fatal &= ~CUPSD_FATAL_LOG; else if (!_cups_strcasecmp(valstart, "permissions")) fatal |= CUPSD_FATAL_PERMISSIONS; else if (!_cups_strcasecmp(valstart, "-permissions")) fatal &= ~CUPSD_FATAL_PERMISSIONS; else if (_cups_strcasecmp(valstart, "none")) cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown FatalErrors kind \"%s\" ignored.", valstart); for (valstart = valend; *valstart; valstart ++) if (!_cups_isspace(*valstart) || *valstart != ',') break; } return (fatal); } /* * 'parse_groups()' - Parse system group names in a string. */ static int /* O - 1 on success, 0 on failure */ parse_groups(const char *s, /* I - Space-delimited groups */ int linenum) /* I - Line number in cups-files.conf */ { int status; /* Return status */ char value[1024], /* Value string */ *valstart, /* Pointer into value */ *valend, /* End of value */ quote; /* Quote character */ struct group *group; /* Group */ /* * Make a copy of the string and parse out the groups... */ strlcpy(value, s, sizeof(value)); status = 1; valstart = value; while (*valstart && NumSystemGroups < MAX_SYSTEM_GROUPS) { if (*valstart == '\'' || *valstart == '\"') { /* * Scan quoted name... */ quote = *valstart++; for (valend = valstart; *valend; valend ++) if (*valend == quote) break; } else { /* * Scan space or comma-delimited name... */ for (valend = valstart; *valend; valend ++) if (_cups_isspace(*valend) || *valend == ',') break; } if (*valend) *valend++ = '\0'; group = getgrnam(valstart); if (group) { cupsdSetString(SystemGroups + NumSystemGroups, valstart); SystemGroupIDs[NumSystemGroups] = group->gr_gid; NumSystemGroups ++; } else { if (linenum) cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown SystemGroup \"%s\" on line %d of %s.", valstart, linenum, CupsFilesFile); else cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown default SystemGroup \"%s\".", valstart); status = 0; } endgrent(); valstart = valend; while (*valstart == ',' || _cups_isspace(*valstart)) valstart ++; } return (status); } /* * 'parse_protocols()' - Parse browse protocols in a string. */ static int /* O - Browse protocol bits */ parse_protocols(const char *s) /* I - Space-delimited protocols */ { int protocols; /* Browse protocol bits */ char value[1024], /* Value string */ *valstart, /* Pointer into value */ *valend; /* End of value */ /* * Empty protocol line yields NULL pointer... */ if (!s) return (0); /* * Loop through the value string,... */ strlcpy(value, s, sizeof(value)); protocols = 0; for (valstart = value; *valstart;) { /* * Get the current space/comma-delimited protocol name... */ for (valend = valstart; *valend; valend ++) if (_cups_isspace(*valend) || *valend == ',') break; if (*valend) *valend++ = '\0'; /* * Add the protocol to the bitmask... */ if (!_cups_strcasecmp(valstart, "dnssd") || !_cups_strcasecmp(valstart, "dns-sd") || !_cups_strcasecmp(valstart, "bonjour")) protocols |= BROWSE_DNSSD; else if (!_cups_strcasecmp(valstart, "all")) protocols |= BROWSE_ALL; else if (_cups_strcasecmp(valstart, "none")) cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown browse protocol \"%s\" ignored.", valstart); for (valstart = valend; *valstart; valstart ++) if (!_cups_isspace(*valstart) || *valstart != ',') break; } return (protocols); } /* * 'parse_variable()' - Parse a variable line. */ static int /* O - 1 on success, 0 on failure */ parse_variable( const char *filename, /* I - Name of configuration file */ int linenum, /* I - Line in configuration file */ const char *line, /* I - Line from configuration file */ const char *value, /* I - Value from configuration file */ size_t num_vars, /* I - Number of variables */ const cupsd_var_t *vars) /* I - Variables */ { size_t i; /* Looping var */ const cupsd_var_t *var; /* Variables */ char temp[1024]; /* Temporary string */ for (i = num_vars, var = vars; i > 0; i --, var ++) if (!_cups_strcasecmp(line, var->name)) break; if (i == 0) { /* * Unknown directive! Output an error message and continue... */ if (!value) cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value for %s on line %d of %s.", line, linenum, filename); else cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown directive %s on line %d of %s.", line, linenum, filename); return (0); } switch (var->type) { case CUPSD_VARTYPE_INTEGER : if (!value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Missing integer value for %s on line %d of %s.", line, linenum, filename); return (0); } else if (!isdigit(*value & 255)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Bad integer value for %s on line %d of %s.", line, linenum, filename); return (0); } else { int n; /* Number */ char *units; /* Units */ n = strtol(value, &units, 0); if (units && *units) { if (tolower(units[0] & 255) == 'g') n *= 1024 * 1024 * 1024; else if (tolower(units[0] & 255) == 'm') n *= 1024 * 1024; else if (tolower(units[0] & 255) == 'k') n *= 1024; else if (tolower(units[0] & 255) == 't') n *= 262144; else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown integer value for %s on line %d of %s.", line, linenum, filename); return (0); } } if (n < 0) { cupsdLogMessage(CUPSD_LOG_ERROR, "Bad negative integer value for %s on line %d of " "%s.", line, linenum, filename); return (0); } else { *((int *)var->ptr) = n; } } break; case CUPSD_VARTYPE_PERM : if (!value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Missing permissions value for %s on line %d of %s.", line, linenum, filename); return (0); } else if (!isdigit(*value & 255)) { /* TODO: Add chmod UGO syntax support */ cupsdLogMessage(CUPSD_LOG_ERROR, "Bad permissions value for %s on line %d of %s.", line, linenum, filename); return (0); } else { int n = strtol(value, NULL, 8); /* Permissions value */ if (n < 0) { cupsdLogMessage(CUPSD_LOG_ERROR, "Bad negative permissions value for %s on line %d of " "%s.", line, linenum, filename); return (0); } else { *((mode_t *)var->ptr) = (mode_t)n; } } break; case CUPSD_VARTYPE_TIME : if (!value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Missing time interval value for %s on line %d of " "%s.", line, linenum, filename); return (0); } else if (!_cups_strncasecmp(line, "PreserveJob", 11) && (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "enabled") || !_cups_strcasecmp(value, "yes"))) { *((int *)var->ptr) = INT_MAX; } else if (!_cups_strcasecmp(value, "false") || !_cups_strcasecmp(value, "off") || !_cups_strcasecmp(value, "disabled") || !_cups_strcasecmp(value, "no")) { *((int *)var->ptr) = 0; } else if (!isdigit(*value & 255)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown time interval value for %s on line %d of " "%s.", line, linenum, filename); return (0); } else { double n; /* Number */ char *units; /* Units */ n = strtod(value, &units); if (units && *units) { if (tolower(units[0] & 255) == 'w') n *= 7 * 24 * 60 * 60; else if (tolower(units[0] & 255) == 'd') n *= 24 * 60 * 60; else if (tolower(units[0] & 255) == 'h') n *= 60 * 60; else if (tolower(units[0] & 255) == 'm') n *= 60; else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown time interval value for %s on line " "%d of %s.", line, linenum, filename); return (0); } } if (n < 0.0 || n > INT_MAX) { cupsdLogMessage(CUPSD_LOG_ERROR, "Bad time value for %s on line %d of %s.", line, linenum, filename); return (0); } else { *((int *)var->ptr) = (int)n; } } break; case CUPSD_VARTYPE_BOOLEAN : if (!value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Missing boolean value for %s on line %d of %s.", line, linenum, filename); return (0); } else if (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "enabled") || !_cups_strcasecmp(value, "yes") || atoi(value) != 0) { *((int *)var->ptr) = TRUE; } else if (!_cups_strcasecmp(value, "false") || !_cups_strcasecmp(value, "off") || !_cups_strcasecmp(value, "disabled") || !_cups_strcasecmp(value, "no") || !_cups_strcasecmp(value, "0")) { *((int *)var->ptr) = FALSE; } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown boolean value %s on line %d of %s.", value, linenum, filename); return (0); } break; case CUPSD_VARTYPE_PATHNAME : if (!value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Missing pathname value for %s on line %d of %s.", line, linenum, filename); return (0); } if (value[0] == '/') strlcpy(temp, value, sizeof(temp)); else snprintf(temp, sizeof(temp), "%s/%s", ServerRoot, value); if (access(temp, 0) && _cups_strcasecmp(value, "internal") && _cups_strcasecmp(line, "ServerKeychain")) { cupsdLogMessage(CUPSD_LOG_ERROR, "File or directory for \"%s %s\" on line %d of %s " "does not exist.", line, value, linenum, filename); return (0); } cupsdSetString((char **)var->ptr, temp); break; case CUPSD_VARTYPE_STRING : cupsdSetString((char **)var->ptr, value); break; } return (1); } /* * 'read_cupsd_conf()' - Read the cupsd.conf configuration file. */ static int /* O - 1 on success, 0 on failure */ read_cupsd_conf(cups_file_t *fp) /* I - File to read from */ { int linenum; /* Current line number */ char line[HTTP_MAX_BUFFER], /* Line from file */ temp[HTTP_MAX_BUFFER], /* Temporary buffer for value */ *value, /* Pointer to value */ *valueptr; /* Pointer into value */ int valuelen; /* Length of value */ http_addrlist_t *addrlist, /* Address list */ *addr; /* Current address */ cups_file_t *incfile; /* Include file */ char incname[1024]; /* Include filename */ /* * Loop through each line in the file... */ linenum = 0; while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) { /* * Decode the directive... */ if (!_cups_strcasecmp(line, "Include") && value) { /* * Include filename */ if (value[0] == '/') strlcpy(incname, value, sizeof(incname)); else snprintf(incname, sizeof(incname), "%s/%s", ServerRoot, value); if ((incfile = cupsFileOpen(incname, "rb")) == NULL) cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to include config file \"%s\" - %s", incname, strerror(errno)); else { read_cupsd_conf(incfile); cupsFileClose(incfile); } } else if (!_cups_strcasecmp(line, " */ linenum = read_location(fp, value, linenum); if (linenum == 0) return (0); } else if (!_cups_strcasecmp(line, " */ linenum = read_policy(fp, value, linenum); if (linenum == 0) return (0); } else if (!_cups_strcasecmp(line, "FaxRetryInterval") && value) { JobRetryInterval = atoi(value); cupsdLogMessage(CUPSD_LOG_WARN, "FaxRetryInterval is deprecated; use " "JobRetryInterval on line %d of %s.", linenum, ConfigurationFile); } else if (!_cups_strcasecmp(line, "FaxRetryLimit") && value) { JobRetryLimit = atoi(value); cupsdLogMessage(CUPSD_LOG_WARN, "FaxRetryLimit is deprecated; use " "JobRetryLimit on line %d of %s.", linenum, ConfigurationFile); } #ifdef HAVE_SSL else if (!_cups_strcasecmp(line, "SSLOptions")) { /* * SSLOptions [AllowRC4] [AllowSSL3] [AllowDH] [DenyCBC] [DenyTLS1.0] [None] */ int options = _HTTP_TLS_NONE,/* SSL/TLS options */ min_version = _HTTP_TLS_1_0, max_version = _HTTP_TLS_MAX; if (value) { char *start, /* Start of option */ *end; /* End of option */ for (start = value; *start; start = end) { /* * Find end of keyword... */ end = start; while (*end && !_cups_isspace(*end)) end ++; if (*end) *end++ = '\0'; /* * Compare... */ if (!_cups_strcasecmp(start, "AllowRC4")) options |= _HTTP_TLS_ALLOW_RC4; else if (!_cups_strcasecmp(start, "AllowSSL3")) min_version = _HTTP_TLS_SSL3; else if (!_cups_strcasecmp(start, "AllowDH")) options |= _HTTP_TLS_ALLOW_DH; else if (!_cups_strcasecmp(start, "DenyCBC")) options |= _HTTP_TLS_DENY_CBC; else if (!_cups_strcasecmp(start, "DenyTLS1.0")) min_version = _HTTP_TLS_1_1; else if (!_cups_strcasecmp(start, "MaxTLS1.0")) max_version = _HTTP_TLS_1_0; else if (!_cups_strcasecmp(start, "MaxTLS1.1")) max_version = _HTTP_TLS_1_1; else if (!_cups_strcasecmp(start, "MaxTLS1.2")) max_version = _HTTP_TLS_1_2; else if (!_cups_strcasecmp(start, "MaxTLS1.3")) max_version = _HTTP_TLS_1_3; else if (!_cups_strcasecmp(start, "MinTLS1.0")) min_version = _HTTP_TLS_1_0; else if (!_cups_strcasecmp(start, "MinTLS1.1")) min_version = _HTTP_TLS_1_1; else if (!_cups_strcasecmp(start, "MinTLS1.2")) min_version = _HTTP_TLS_1_2; else if (!_cups_strcasecmp(start, "MinTLS1.3")) min_version = _HTTP_TLS_1_3; else if (!_cups_strcasecmp(start, "None")) options = _HTTP_TLS_NONE; else if (_cups_strcasecmp(start, "NoEmptyFragments")) cupsdLogMessage(CUPSD_LOG_WARN, "Unknown SSL option %s at line %d.", start, linenum); } } _httpTLSSetOptions(options, min_version, max_version); } #endif /* HAVE_SSL */ else if ((!_cups_strcasecmp(line, "Port") || !_cups_strcasecmp(line, "Listen") #ifdef HAVE_SSL || !_cups_strcasecmp(line, "SSLPort") || !_cups_strcasecmp(line, "SSLListen") #endif /* HAVE_SSL */ ) && value) { /* * Add listening address(es) to the list... */ cupsd_listener_t *lis; /* New listeners array */ /* * Get the address list... */ addrlist = get_address(value, IPP_PORT); if (!addrlist) { cupsdLogMessage(CUPSD_LOG_ERROR, "Bad %s address %s at line %d.", line, value, linenum); continue; } /* * Add each address... */ for (addr = addrlist; addr; addr = addr->next) { /* * See if this address is already present... */ for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); lis; lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) if (httpAddrEqual(&(addr->addr), &(lis->address)) && httpAddrPort(&(addr->addr)) == httpAddrPort(&(lis->address))) break; if (lis) { #ifdef HAVE_ONDEMAND if (!lis->on_demand) #endif /* HAVE_ONDEMAND */ { httpAddrString(&lis->address, temp, sizeof(temp)); cupsdLogMessage(CUPSD_LOG_WARN, "Duplicate listen address \"%s\" ignored.", temp); } continue; } /* * Allocate another listener... */ if (!Listeners) Listeners = cupsArrayNew(NULL, NULL); if (!Listeners) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate %s at line %d - %s.", line, linenum, strerror(errno)); break; } if ((lis = calloc(1, sizeof(cupsd_listener_t))) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate %s at line %d - %s.", line, linenum, strerror(errno)); break; } cupsArrayAdd(Listeners, lis); /* * Copy the current address and log it... */ memcpy(&(lis->address), &(addr->addr), sizeof(lis->address)); lis->fd = -1; #ifdef HAVE_SSL if (!_cups_strcasecmp(line, "SSLPort") || !_cups_strcasecmp(line, "SSLListen")) lis->encryption = HTTP_ENCRYPT_ALWAYS; #endif /* HAVE_SSL */ httpAddrString(&lis->address, temp, sizeof(temp)); #ifdef AF_LOCAL if (lis->address.addr.sa_family == AF_LOCAL) cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s (Domain)", temp); else #endif /* AF_LOCAL */ cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d (IPv%d)", temp, httpAddrPort(&(lis->address)), httpAddrFamily(&(lis->address)) == AF_INET ? 4 : 6); if (!httpAddrLocalhost(&(lis->address))) RemotePort = httpAddrPort(&(lis->address)); } /* * Free the list... */ httpAddrFreeList(addrlist); } else if (!_cups_strcasecmp(line, "BrowseProtocols") || !_cups_strcasecmp(line, "BrowseLocalProtocols")) { /* * "BrowseProtocols name [... name]" * "BrowseLocalProtocols name [... name]" */ int protocols = parse_protocols(value); if (protocols < 0) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown browse protocol \"%s\" on line %d of %s.", value, linenum, ConfigurationFile); break; } BrowseLocalProtocols = protocols; } else if (!_cups_strcasecmp(line, "DefaultAuthType") && value) { /* * DefaultAuthType {basic,digest,basicdigest,negotiate} */ if (!_cups_strcasecmp(value, "none")) default_auth_type = CUPSD_AUTH_NONE; else if (!_cups_strcasecmp(value, "basic")) default_auth_type = CUPSD_AUTH_BASIC; else if (!_cups_strcasecmp(value, "negotiate")) default_auth_type = CUPSD_AUTH_NEGOTIATE; else if (!_cups_strcasecmp(value, "auto")) default_auth_type = CUPSD_AUTH_AUTO; else { cupsdLogMessage(CUPSD_LOG_WARN, "Unknown default authorization type %s on line %d of %s.", value, linenum, ConfigurationFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } } #ifdef HAVE_SSL else if (!_cups_strcasecmp(line, "DefaultEncryption")) { /* * DefaultEncryption {Never,IfRequested,Required} */ if (!value || !_cups_strcasecmp(value, "never")) DefaultEncryption = HTTP_ENCRYPT_NEVER; else if (!_cups_strcasecmp(value, "required")) DefaultEncryption = HTTP_ENCRYPT_REQUIRED; else if (!_cups_strcasecmp(value, "ifrequested")) DefaultEncryption = HTTP_ENCRYPT_IF_REQUESTED; else { cupsdLogMessage(CUPSD_LOG_WARN, "Unknown default encryption %s on line %d of %s.", value, linenum, ConfigurationFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } } #endif /* HAVE_SSL */ else if (!_cups_strcasecmp(line, "HostNameLookups") && value) { /* * Do hostname lookups? */ if (!_cups_strcasecmp(value, "off") || !_cups_strcasecmp(value, "no") || !_cups_strcasecmp(value, "false")) HostNameLookups = 0; else if (!_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "yes") || !_cups_strcasecmp(value, "true")) HostNameLookups = 1; else if (!_cups_strcasecmp(value, "double")) HostNameLookups = 2; else cupsdLogMessage(CUPSD_LOG_WARN, "Unknown HostNameLookups %s on line %d of %s.", value, linenum, ConfigurationFile); } else if (!_cups_strcasecmp(line, "AccessLogLevel") && value) { /* * Amount of logging to do to access log... */ if (!_cups_strcasecmp(value, "all")) AccessLogLevel = CUPSD_ACCESSLOG_ALL; else if (!_cups_strcasecmp(value, "actions")) AccessLogLevel = CUPSD_ACCESSLOG_ACTIONS; else if (!_cups_strcasecmp(value, "config")) AccessLogLevel = CUPSD_ACCESSLOG_CONFIG; else if (!_cups_strcasecmp(value, "none")) AccessLogLevel = CUPSD_ACCESSLOG_NONE; else cupsdLogMessage(CUPSD_LOG_WARN, "Unknown AccessLogLevel %s on line %d of %s.", value, linenum, ConfigurationFile); } else if (!_cups_strcasecmp(line, "LogLevel") && value) { /* * Amount of logging to do to error log... */ if (!_cups_strcasecmp(value, "debug2")) LogLevel = CUPSD_LOG_DEBUG2; else if (!_cups_strcasecmp(value, "debug")) LogLevel = CUPSD_LOG_DEBUG; else if (!_cups_strcasecmp(value, "info")) LogLevel = CUPSD_LOG_INFO; else if (!_cups_strcasecmp(value, "notice")) LogLevel = CUPSD_LOG_NOTICE; else if (!_cups_strcasecmp(value, "warn")) LogLevel = CUPSD_LOG_WARN; else if (!_cups_strcasecmp(value, "error")) LogLevel = CUPSD_LOG_ERROR; else if (!_cups_strcasecmp(value, "crit")) LogLevel = CUPSD_LOG_CRIT; else if (!_cups_strcasecmp(value, "alert")) LogLevel = CUPSD_LOG_ALERT; else if (!_cups_strcasecmp(value, "emerg")) LogLevel = CUPSD_LOG_EMERG; else if (!_cups_strcasecmp(value, "none")) LogLevel = CUPSD_LOG_NONE; else cupsdLogMessage(CUPSD_LOG_WARN, "Unknown LogLevel %s on line %d of %s.", value, linenum, ConfigurationFile); } else if (!_cups_strcasecmp(line, "LogTimeFormat") && value) { /* * Amount of logging to do to error log... */ if (!_cups_strcasecmp(value, "standard")) LogTimeFormat = CUPSD_TIME_STANDARD; else if (!_cups_strcasecmp(value, "usecs")) LogTimeFormat = CUPSD_TIME_USECS; else cupsdLogMessage(CUPSD_LOG_WARN, "Unknown LogTimeFormat %s on line %d of %s.", value, linenum, ConfigurationFile); } else if (!_cups_strcasecmp(line, "ServerTokens") && value) { /* * Set the string used for the Server header... */ struct utsname plat; /* Platform info */ uname(&plat); if (!_cups_strcasecmp(value, "ProductOnly")) cupsdSetString(&ServerHeader, "CUPS IPP"); else if (!_cups_strcasecmp(value, "Major")) cupsdSetStringf(&ServerHeader, "CUPS/%d IPP/2", CUPS_VERSION_MAJOR); else if (!_cups_strcasecmp(value, "Minor")) cupsdSetStringf(&ServerHeader, "CUPS/%d.%d IPP/2.1", CUPS_VERSION_MAJOR, CUPS_VERSION_MINOR); else if (!_cups_strcasecmp(value, "Minimal")) cupsdSetString(&ServerHeader, CUPS_MINIMAL " IPP/2.1"); else if (!_cups_strcasecmp(value, "OS")) cupsdSetStringf(&ServerHeader, CUPS_MINIMAL " (%s %s) IPP/2.1", plat.sysname, plat.release); else if (!_cups_strcasecmp(value, "Full")) cupsdSetStringf(&ServerHeader, CUPS_MINIMAL " (%s %s; %s) IPP/2.1", plat.sysname, plat.release, plat.machine); else if (!_cups_strcasecmp(value, "None")) cupsdSetString(&ServerHeader, ""); else cupsdLogMessage(CUPSD_LOG_WARN, "Unknown ServerTokens %s on line %d of %s.", value, linenum, ConfigurationFile); } else if (!_cups_strcasecmp(line, "PassEnv") && value) { /* * PassEnv variable [... variable] */ for (; *value;) { for (valuelen = 0; value[valuelen]; valuelen ++) if (_cups_isspace(value[valuelen]) || value[valuelen] == ',') break; if (value[valuelen]) { value[valuelen] = '\0'; valuelen ++; } cupsdSetEnv(value, NULL); for (value += valuelen; *value; value ++) if (!_cups_isspace(*value) || *value != ',') break; } } else if (!_cups_strcasecmp(line, "ServerAlias") && value) { /* * ServerAlias name [... name] */ if (!ServerAlias) ServerAlias = cupsArrayNew(NULL, NULL); for (; *value;) { for (valuelen = 0; value[valuelen]; valuelen ++) if (_cups_isspace(value[valuelen]) || value[valuelen] == ',') break; if (value[valuelen]) { value[valuelen] = '\0'; valuelen ++; } cupsdAddAlias(ServerAlias, value); for (value += valuelen; *value; value ++) if (!_cups_isspace(*value) || *value != ',') break; } } else if (!_cups_strcasecmp(line, "SetEnv") && value) { /* * SetEnv variable value */ for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++); if (*valueptr) { /* * Found a value... */ while (isspace(*valueptr & 255)) *valueptr++ = '\0'; cupsdSetEnv(value, valueptr); } else cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value for SetEnv directive on line %d of %s.", linenum, ConfigurationFile); } else if (!_cups_strcasecmp(line, "AccessLog") || !_cups_strcasecmp(line, "CacheDir") || !_cups_strcasecmp(line, "ConfigFilePerm") || !_cups_strcasecmp(line, "DataDir") || !_cups_strcasecmp(line, "DocumentRoot") || !_cups_strcasecmp(line, "ErrorLog") || !_cups_strcasecmp(line, "FatalErrors") || !_cups_strcasecmp(line, "FileDevice") || !_cups_strcasecmp(line, "FontPath") || !_cups_strcasecmp(line, "Group") || !_cups_strcasecmp(line, "LogFilePerm") || !_cups_strcasecmp(line, "LPDConfigFile") || !_cups_strcasecmp(line, "PageLog") || !_cups_strcasecmp(line, "Printcap") || !_cups_strcasecmp(line, "PrintcapFormat") || !_cups_strcasecmp(line, "RemoteRoot") || !_cups_strcasecmp(line, "RequestRoot") || !_cups_strcasecmp(line, "ServerBin") || !_cups_strcasecmp(line, "ServerCertificate") || !_cups_strcasecmp(line, "ServerKey") || !_cups_strcasecmp(line, "ServerKeychain") || !_cups_strcasecmp(line, "ServerRoot") || !_cups_strcasecmp(line, "SMBConfigFile") || !_cups_strcasecmp(line, "StateDir") || !_cups_strcasecmp(line, "SystemGroup") || !_cups_strcasecmp(line, "SystemGroupAuthKey") || !_cups_strcasecmp(line, "TempDir") || !_cups_strcasecmp(line, "User")) { cupsdLogMessage(CUPSD_LOG_INFO, "Please move \"%s%s%s\" on line %d of %s to the %s file; " "this will become an error in a future release.", line, value ? " " : "", value ? value : "", linenum, ConfigurationFile, CupsFilesFile); } else parse_variable(ConfigurationFile, linenum, line, value, sizeof(cupsd_vars) / sizeof(cupsd_vars[0]), cupsd_vars); } return (1); } /* * 'read_cups_files_conf()' - Read the cups-files.conf configuration file. */ static int /* O - 1 on success, 0 on failure */ read_cups_files_conf(cups_file_t *fp) /* I - File to read from */ { int linenum; /* Current line number */ char line[HTTP_MAX_BUFFER], /* Line from file */ *value; /* Value from line */ struct group *group; /* Group */ /* * Loop through each line in the file... */ linenum = 0; while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) { if (!_cups_strcasecmp(line, "FatalErrors")) FatalErrors = parse_fatal_errors(value); else if (!_cups_strcasecmp(line, "Group") && value) { /* * Group ID to run as... */ if (isdigit(value[0])) Group = (gid_t)atoi(value); else { endgrent(); group = getgrnam(value); if (group != NULL) Group = group->gr_gid; else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown Group \"%s\" on line %d of %s.", value, linenum, CupsFilesFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } } } else if (!_cups_strcasecmp(line, "PrintcapFormat") && value) { /* * Format of printcap file? */ if (!_cups_strcasecmp(value, "bsd")) PrintcapFormat = PRINTCAP_BSD; else if (!_cups_strcasecmp(value, "plist")) PrintcapFormat = PRINTCAP_PLIST; else if (!_cups_strcasecmp(value, "solaris")) PrintcapFormat = PRINTCAP_SOLARIS; else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown PrintcapFormat \"%s\" on line %d of %s.", value, linenum, CupsFilesFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } } else if (!_cups_strcasecmp(line, "Sandboxing") && value) { /* * Level of sandboxing? */ if (!_cups_strcasecmp(value, "off") && getuid()) { Sandboxing = CUPSD_SANDBOXING_OFF; cupsdLogMessage(CUPSD_LOG_WARN, "Disabling sandboxing is not recommended (line %d of %s)", linenum, CupsFilesFile); } else if (!_cups_strcasecmp(value, "relaxed")) Sandboxing = CUPSD_SANDBOXING_RELAXED; else if (!_cups_strcasecmp(value, "strict")) Sandboxing = CUPSD_SANDBOXING_STRICT; else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown Sandboxing \"%s\" on line %d of %s.", value, linenum, CupsFilesFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } } else if (!_cups_strcasecmp(line, "SystemGroup") && value) { /* * SystemGroup (admin) group(s)... */ if (!parse_groups(value, linenum)) { if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } } else if (!_cups_strcasecmp(line, "User") && value) { /* * User ID to run as... */ if (isdigit(value[0] & 255)) { int uid = atoi(value); if (!uid) { cupsdLogMessage(CUPSD_LOG_ERROR, "Will not use User 0 as specified on line %d of %s " "for security reasons. You must use a non-" "privileged account instead.", linenum, CupsFilesFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } else User = (uid_t)atoi(value); } else { struct passwd *p; /* Password information */ endpwent(); p = getpwnam(value); if (p) { if (!p->pw_uid) { cupsdLogMessage(CUPSD_LOG_ERROR, "Will not use User %s (UID=0) as specified on line " "%d of %s for security reasons. You must use a " "non-privileged account instead.", value, linenum, CupsFilesFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } else User = p->pw_uid; } else { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown User \"%s\" on line %d of %s.", value, linenum, CupsFilesFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } } } else if (!_cups_strcasecmp(line, "ServerCertificate") || !_cups_strcasecmp(line, "ServerKey")) { cupsdLogMessage(CUPSD_LOG_INFO, "The \"%s\" directive on line %d of %s is no longer " "supported; this will become an error in a future " "release.", line, linenum, CupsFilesFile); } else if (!parse_variable(CupsFilesFile, linenum, line, value, sizeof(cupsfiles_vars) / sizeof(cupsfiles_vars[0]), cupsfiles_vars) && (FatalErrors & CUPSD_FATAL_CONFIG)) return (0); } return (1); } /* * 'read_location()' - Read a definition. */ static int /* O - New line number or 0 on error */ read_location(cups_file_t *fp, /* I - Configuration file */ char *location, /* I - Location name/path */ int linenum) /* I - Current line number */ { cupsd_location_t *loc, /* New location */ *parent; /* Parent location */ char line[HTTP_MAX_BUFFER], /* Line buffer */ *value, /* Value for directive */ *valptr; /* Pointer into value */ if ((parent = cupsdFindLocation(location)) != NULL) cupsdLogMessage(CUPSD_LOG_WARN, "Duplicate on line %d of %s.", location, linenum, ConfigurationFile); else if ((parent = cupsdNewLocation(location)) == NULL) return (0); else { cupsdAddLocation(parent); parent->limit = CUPSD_AUTH_LIMIT_ALL; } loc = parent; while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) { /* * Decode the directive... */ if (!_cups_strcasecmp(line, "")) return (linenum); else if (!_cups_strcasecmp(line, "limit = 0; while (*value) { for (valptr = value; !isspace(*valptr & 255) && *valptr; valptr ++); if (*valptr) *valptr++ = '\0'; if (!strcmp(value, "ALL")) loc->limit = CUPSD_AUTH_LIMIT_ALL; else if (!strcmp(value, "GET")) loc->limit |= CUPSD_AUTH_LIMIT_GET; else if (!strcmp(value, "HEAD")) loc->limit |= CUPSD_AUTH_LIMIT_HEAD; else if (!strcmp(value, "OPTIONS")) loc->limit |= CUPSD_AUTH_LIMIT_OPTIONS; else if (!strcmp(value, "POST")) loc->limit |= CUPSD_AUTH_LIMIT_POST; else if (!strcmp(value, "PUT")) loc->limit |= CUPSD_AUTH_LIMIT_PUT; else if (!strcmp(value, "TRACE")) loc->limit |= CUPSD_AUTH_LIMIT_TRACE; else cupsdLogMessage(CUPSD_LOG_WARN, "Unknown request type %s on line %d of %s.", value, linenum, ConfigurationFile); for (value = valptr; isspace(*value & 255); value ++); } if (!_cups_strcasecmp(line, "limit = CUPSD_AUTH_LIMIT_ALL ^ loc->limit; parent->limit &= ~loc->limit; } else if (!_cups_strcasecmp(line, "") || !_cups_strcasecmp(line, "")) loc = parent; else if (!value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d of %s.", linenum, ConfigurationFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } else if (!parse_aaa(loc, line, value, linenum)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown Location directive %s on line %d of %s.", line, linenum, ConfigurationFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } } cupsdLogMessage(CUPSD_LOG_ERROR, "Unexpected end-of-file at line %d while reading location.", linenum); return ((FatalErrors & CUPSD_FATAL_CONFIG) ? 0 : linenum); } /* * 'read_policy()' - Read a definition. */ static int /* O - New line number or 0 on error */ read_policy(cups_file_t *fp, /* I - Configuration file */ char *policy, /* I - Location name/path */ int linenum) /* I - Current line number */ { int i; /* Looping var */ cupsd_policy_t *pol; /* Policy */ cupsd_location_t *op; /* Policy operation */ int num_ops; /* Number of IPP operations */ ipp_op_t ops[100]; /* Operations */ char line[HTTP_MAX_BUFFER], /* Line buffer */ *value, /* Value for directive */ *valptr; /* Pointer into value */ /* * Create the policy... */ if ((pol = cupsdFindPolicy(policy)) != NULL) cupsdLogMessage(CUPSD_LOG_WARN, "Duplicate on line %d of %s.", policy, linenum, ConfigurationFile); else if ((pol = cupsdAddPolicy(policy)) == NULL) return (0); /* * Read from the file... */ op = NULL; num_ops = 0; while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) { /* * Decode the directive... */ if (!_cups_strcasecmp(line, "")) { if (op) cupsdLogMessage(CUPSD_LOG_WARN, "Missing before on line %d of %s.", linenum, ConfigurationFile); set_policy_defaults(pol); return (linenum); } else if (!_cups_strcasecmp(line, "") && op) { /* * Finish the current operation limit... */ if (num_ops > 1) { /* * Copy the policy to the other operations... */ for (i = 1; i < num_ops; i ++) cupsdAddPolicyOp(pol, op, ops[i]); } op = NULL; } else if (!value) { cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d of %s.", linenum, ConfigurationFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } else if (!_cups_strcasecmp(line, "JobPrivateAccess") || !_cups_strcasecmp(line, "JobPrivateValues") || !_cups_strcasecmp(line, "SubscriptionPrivateAccess") || !_cups_strcasecmp(line, "SubscriptionPrivateValues")) { if (op) { cupsdLogMessage(CUPSD_LOG_ERROR, "%s directive must appear outside ... " "on line %d of %s.", line, linenum, ConfigurationFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } else { /* * Pull out whitespace-delimited values... */ while (*value) { /* * Find the end of the current value... */ for (valptr = value; !isspace(*valptr & 255) && *valptr; valptr ++); if (*valptr) *valptr++ = '\0'; /* * Save it appropriately... */ if (!_cups_strcasecmp(line, "JobPrivateAccess")) { /* * JobPrivateAccess {all|default|user/group list|@@ACL} */ if (!_cups_strcasecmp(value, "default")) { cupsdAddString(&(pol->job_access), "@OWNER"); cupsdAddString(&(pol->job_access), "@SYSTEM"); } else cupsdAddString(&(pol->job_access), value); } else if (!_cups_strcasecmp(line, "JobPrivateValues")) { /* * JobPrivateValues {all|none|default|attribute list} */ if (!_cups_strcasecmp(value, "default")) { cupsdAddString(&(pol->job_attrs), "job-name"); cupsdAddString(&(pol->job_attrs), "job-originating-host-name"); cupsdAddString(&(pol->job_attrs), "job-originating-user-name"); cupsdAddString(&(pol->job_attrs), "phone"); } else cupsdAddString(&(pol->job_attrs), value); } else if (!_cups_strcasecmp(line, "SubscriptionPrivateAccess")) { /* * SubscriptionPrivateAccess {all|default|user/group list|@@ACL} */ if (!_cups_strcasecmp(value, "default")) { cupsdAddString(&(pol->sub_access), "@OWNER"); cupsdAddString(&(pol->sub_access), "@SYSTEM"); } else cupsdAddString(&(pol->sub_access), value); } else /* if (!_cups_strcasecmp(line, "SubscriptionPrivateValues")) */ { /* * SubscriptionPrivateValues {all|none|default|attribute list} */ if (!_cups_strcasecmp(value, "default")) { cupsdAddString(&(pol->sub_attrs), "notify-events"); cupsdAddString(&(pol->sub_attrs), "notify-pull-method"); cupsdAddString(&(pol->sub_attrs), "notify-recipient-uri"); cupsdAddString(&(pol->sub_attrs), "notify-subscriber-user-name"); cupsdAddString(&(pol->sub_attrs), "notify-user-data"); } else cupsdAddString(&(pol->sub_attrs), value); } /* * Find the next string on the line... */ for (value = valptr; isspace(*value & 255); value ++); } } } else if (!op) { cupsdLogMessage(CUPSD_LOG_ERROR, "Missing directive before %s on line %d of %s.", line, linenum, ConfigurationFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } else if (!parse_aaa(op, line, value, linenum)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown Policy Limit directive %s on line %d of %s.", line, linenum, ConfigurationFile); if (FatalErrors & CUPSD_FATAL_CONFIG) return (0); } } cupsdLogMessage(CUPSD_LOG_ERROR, "Unexpected end-of-file at line %d while reading policy " "\"%s\".", linenum, policy); return ((FatalErrors & CUPSD_FATAL_CONFIG) ? 0 : linenum); } /* * 'set_policy_defaults()' - Set default policy values as needed. */ static void set_policy_defaults(cupsd_policy_t *pol)/* I - Policy */ { cupsd_location_t *op; /* Policy operation */ /* * Verify that we have an explicit policy for Validate-Job, Cancel-Jobs, * Cancel-My-Jobs, Close-Job, and CUPS-Get-Document, which ensures that * upgrades do not introduce new security issues... * * CUPS STR #4659: Allow a lone policy. */ if (cupsArrayCount(pol->ops) > 1) { if ((op = cupsdFindPolicyOp(pol, IPP_VALIDATE_JOB)) == NULL || op->op == IPP_ANY_OPERATION) { if ((op = cupsdFindPolicyOp(pol, IPP_PRINT_JOB)) != NULL && op->op != IPP_ANY_OPERATION) { /* * Add a new limit for Validate-Job using the Print-Job limit as a * template... */ cupsdLogMessage(CUPSD_LOG_WARN, "No limit for Validate-Job defined in policy %s - using Print-Job's policy.", pol->name); cupsdAddPolicyOp(pol, op, IPP_VALIDATE_JOB); } else cupsdLogMessage(CUPSD_LOG_WARN, "No limit for Validate-Job defined in policy %s and no suitable template found.", pol->name); } if ((op = cupsdFindPolicyOp(pol, IPP_CANCEL_JOBS)) == NULL || op->op == IPP_ANY_OPERATION) { if ((op = cupsdFindPolicyOp(pol, IPP_PAUSE_PRINTER)) != NULL && op->op != IPP_ANY_OPERATION) { /* * Add a new limit for Cancel-Jobs using the Pause-Printer limit as a * template... */ cupsdLogMessage(CUPSD_LOG_WARN, "No limit for Cancel-Jobs defined in policy %s - using Pause-Printer's policy.", pol->name); cupsdAddPolicyOp(pol, op, IPP_CANCEL_JOBS); } else cupsdLogMessage(CUPSD_LOG_WARN, "No limit for Cancel-Jobs defined in policy %s and no suitable template found.", pol->name); } if ((op = cupsdFindPolicyOp(pol, IPP_CANCEL_MY_JOBS)) == NULL || op->op == IPP_ANY_OPERATION) { if ((op = cupsdFindPolicyOp(pol, IPP_SEND_DOCUMENT)) != NULL && op->op != IPP_ANY_OPERATION) { /* * Add a new limit for Cancel-My-Jobs using the Send-Document limit as * a template... */ cupsdLogMessage(CUPSD_LOG_WARN, "No limit for Cancel-My-Jobs defined in policy %s - using Send-Document's policy.", pol->name); cupsdAddPolicyOp(pol, op, IPP_CANCEL_MY_JOBS); } else cupsdLogMessage(CUPSD_LOG_WARN, "No limit for Cancel-My-Jobs defined in policy %s and no suitable template found.", pol->name); } if ((op = cupsdFindPolicyOp(pol, IPP_CLOSE_JOB)) == NULL || op->op == IPP_ANY_OPERATION) { if ((op = cupsdFindPolicyOp(pol, IPP_SEND_DOCUMENT)) != NULL && op->op != IPP_ANY_OPERATION) { /* * Add a new limit for Close-Job using the Send-Document limit as a * template... */ cupsdLogMessage(CUPSD_LOG_WARN, "No limit for Close-Job defined in policy %s - using Send-Document's policy.", pol->name); cupsdAddPolicyOp(pol, op, IPP_CLOSE_JOB); } else cupsdLogMessage(CUPSD_LOG_WARN, "No limit for Close-Job defined in policy %s and no suitable template found.", pol->name); } if ((op = cupsdFindPolicyOp(pol, CUPS_GET_DOCUMENT)) == NULL || op->op == IPP_ANY_OPERATION) { if ((op = cupsdFindPolicyOp(pol, IPP_SEND_DOCUMENT)) != NULL && op->op != IPP_ANY_OPERATION) { /* * Add a new limit for CUPS-Get-Document using the Send-Document * limit as a template... */ cupsdLogMessage(CUPSD_LOG_WARN, "No limit for CUPS-Get-Document defined in policy %s - using Send-Document's policy.", pol->name); cupsdAddPolicyOp(pol, op, CUPS_GET_DOCUMENT); } else cupsdLogMessage(CUPSD_LOG_WARN, "No limit for CUPS-Get-Document defined in policy %s and no suitable template found.", pol->name); } } /* * Verify we have JobPrivateAccess, JobPrivateValues, * SubscriptionPrivateAccess, and SubscriptionPrivateValues in the policy. */ if (!pol->job_access) { cupsdLogMessage(CUPSD_LOG_WARN, "No JobPrivateAccess defined in policy %s - using defaults.", pol->name); cupsdAddString(&(pol->job_access), "@OWNER"); cupsdAddString(&(pol->job_access), "@SYSTEM"); } if (!pol->job_attrs) { cupsdLogMessage(CUPSD_LOG_WARN, "No JobPrivateValues defined in policy %s - using defaults.", pol->name); cupsdAddString(&(pol->job_attrs), "job-name"); cupsdAddString(&(pol->job_attrs), "job-originating-host-name"); cupsdAddString(&(pol->job_attrs), "job-originating-user-name"); cupsdAddString(&(pol->job_attrs), "phone"); } if (!pol->sub_access) { cupsdLogMessage(CUPSD_LOG_WARN, "No SubscriptionPrivateAccess defined in policy %s - using defaults.", pol->name); cupsdAddString(&(pol->sub_access), "@OWNER"); cupsdAddString(&(pol->sub_access), "@SYSTEM"); } if (!pol->sub_attrs) { cupsdLogMessage(CUPSD_LOG_WARN, "No SubscriptionPrivateValues defined in policy %s - using defaults.", pol->name); cupsdAddString(&(pol->sub_attrs), "notify-events"); cupsdAddString(&(pol->sub_attrs), "notify-pull-method"); cupsdAddString(&(pol->sub_attrs), "notify-recipient-uri"); cupsdAddString(&(pol->sub_attrs), "notify-subscriber-user-name"); cupsdAddString(&(pol->sub_attrs), "notify-user-data"); } }