Sync up with userconfig/sysconfig changes in libcups v3.
if ((creds = httpCopyPeerCredentials(http)) != NULL)
{
- trust = cupsGetCredentialsTrust(NULL, hostname, creds);
+ trust = cupsGetCredentialsTrust(NULL, hostname, creds, /*require_ca*/false);
cupsGetCredentialsInfo(creds, credinfo, sizeof(credinfo));
fprintf(stderr, "DEBUG: %s (%s)\n", trust_msgs[trust], cupsGetErrorString());
if (_cups_strcasecmp(cg->cupsd_hostname, host))
invalidate_cupsd_cache(cg);
- snprintf(name, namesize, "%s/cupsd.conf", cg->cups_serverroot);
+ snprintf(name, namesize, "%s/cupsd.conf", cg->sysconfig);
*remote = 0;
#ifndef _WIN32
// Multiple places...
const char *cups_datadir, // CUPS_DATADIR environment var
*cups_serverbin,// CUPS_SERVERBIN environment var
- *cups_serverroot,
- // CUPS_SERVERROOT environment var
+ *sysconfig, // System configuration directory (influenced by CUPS_SERVERROOT environment var)
*cups_statedir, // CUPS_STATEDIR environment var
- *home, // HOME environment var
+ *userconfig, // User configuration directory (influenced by various environment vars)
*localedir; // LOCALDIR environment var
// adminutil.c
char pw_buf[PW_BUF_SIZE];
// Big buffer for struct passwd buffers
# endif // !_WIN32
- const char *userconfig; // User-specific config files
} _cups_globals_t;
typedef struct _cups_media_db_s // Media database
extern int cupsGetClasses(char ***classes) _CUPS_DEPRECATED_MSG("Use cupsEnumDests instead.");
extern time_t cupsGetCredentialsExpiration(const char *credentials) _CUPS_PUBLIC;
extern char *cupsGetCredentialsInfo(const char *credentials, char *buffer, size_t bufsize) _CUPS_PUBLIC;
-extern http_trust_t cupsGetCredentialsTrust(const char *path, const char *common_name, const char *credentials) _CUPS_PUBLIC;
+extern http_trust_t cupsGetCredentialsTrust(const char *path, const char *common_name, const char *credentials, bool require_ca) _CUPS_PUBLIC;
extern const char *cupsGetDefault(void) _CUPS_PUBLIC;
extern const char *cupsGetDefault2(http_t *http) _CUPS_PUBLIC;
extern cups_dest_t *cupsGetDest(const char *name, const char *instance, int num_dests, cups_dest_t *dests) _CUPS_PUBLIC;
else
instance = NULL;
}
- else if (cg->home)
+ else if (cg->userconfig)
{
/*
* No default in the environment, try the user's lpoptions files...
*/
-#if _WIN32
- snprintf(filename, sizeof(filename), "%s/AppData/Local/cups/lpoptions", cg->home);
-#else
- snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", cg->home);
-#endif // _WIN32
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->userconfig);
dest_name = cups_get_default(filename, defname, sizeof(defname), &instance);
* Still not there? Try the system lpoptions file...
*/
- snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->sysconfig);
dest_name = cups_get_default(filename, defname, sizeof(defname), &instance);
if (dest_name)
// Then add local options...
//
- snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->sysconfig);
cups_get_dests(filename, dest_name, instance, 0, 1, 1, &dest);
- if (cg->home)
+ if (cg->userconfig)
{
-#if _WIN32
- snprintf(filename, sizeof(filename), "%s/AppData/Local/cups/lpoptions", cg->home);
-#else
- snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", cg->home);
-#endif // _WIN32
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->userconfig);
cups_get_dests(filename, dest_name, instance, 0, 1, 1, &dest);
}
// Figure out which file to write to...
//
- snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->sysconfig);
- if (cg->home
+ if (cg->userconfig
#ifndef _WIN32
&& getuid() != 0
#endif // !_WIN32
* Create ~/.cups subdirectory...
*/
-#if _WIN32
- snprintf(filename, sizeof(filename), "%s/AppData/Local/cups", cg->home);
-#else
- snprintf(filename, sizeof(filename), "%s/.cups", cg->home);
-#endif // _WIN32
- if (access(filename, 0))
- mkdir(filename, 0700);
+ mkdir(cg->userconfig, 0700);
-#if _WIN32
- snprintf(filename, sizeof(filename), "%s/AppData/Local/cups/lpoptions", cg->home);
-#else
- snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", cg->home);
-#endif // _WIN32
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->userconfig);
}
//
user_default = _cupsGetUserDefault(data.def_name, sizeof(data.def_name));
- snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->sysconfig);
data.num_dests = cups_get_dests(filename, NULL, NULL, 1, user_default != NULL, data.num_dests, &data.dests);
- if (cg->home)
+ if (cg->userconfig)
{
- // TODO: Use cg->userconfig
-#if _WIN32
- snprintf(filename, sizeof(filename), "%s/AppData/Local/cups/lpoptions", cg->home);
-#else
- snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", cg->home);
-#endif // _WIN32
+ snprintf(filename, sizeof(filename), "%s/lpoptions", cg->userconfig);
data.num_dests = cups_get_dests(filename, NULL, NULL, 1, user_default != NULL, data.num_dests, &data.dests);
}
{
_cups_globals_t *cg = calloc(1, sizeof(_cups_globals_t));
/* Pointer to global data */
-#ifdef _WIN32
- HKEY key; /* Registry key */
- DWORD size; /* Size of string */
- static char homedir[1024] = "", /* Home directory */
- installdir[1024] = "", /* Install directory */
- confdir[1024] = "", /* Server root directory */
- localedir[1024] = ""; /* Locale directory */
-#endif /* _WIN32 */
if (!cg)
*/
#ifdef _WIN32
+ HKEY key; /* Registry key */
+ DWORD size; /* Size of string */
+ static char installdir[1024] = "", /* Install directory */
+ localedir[1024] = "", /* Locale directory */
+ sysconfig[1024] = "", /* Server configuration directory */
+ userconfig[1024] = ""; /* User configuration directory */
+
if (!installdir[0])
{
/*
}
}
- snprintf(confdir, sizeof(confdir), "%s/conf", installdir);
+ snprintf(sysconfig, sizeof(sysconfig), "%s/conf", installdir);
snprintf(localedir, sizeof(localedir), "%s/locale", installdir);
}
if ((cg->cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
cg->cups_serverbin = installdir;
- if ((cg->cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
- cg->cups_serverroot = confdir;
+ if ((cg->sysconfig = getenv("CUPS_SERVERROOT")) == NULL)
+ cg->sysconfig = confdir;
if ((cg->cups_statedir = getenv("CUPS_STATEDIR")) == NULL)
cg->cups_statedir = confdir;
if ((cg->localedir = getenv("LOCALEDIR")) == NULL)
cg->localedir = localedir;
- if (!homedir[0])
+ if (!userconfig[0])
{
const char *userprofile = getenv("USERPROFILE");
// User profile (home) directory
- char *homeptr; // Pointer into homedir
+ char *userptr; // Pointer into userconfig
DEBUG_printf("cups_globals_alloc: USERPROFILE=\"%s\"", userprofile);
- if (!strncmp(userprofile, "C:\\", 3))
- userprofile += 2;
-
- cupsCopyString(homedir, userprofile, sizeof(homedir));
- for (homeptr = homedir; *homeptr; homeptr ++)
+ snprintf(userconfig, sizeof(userconfig), "%s/AppData/Local/cups", userprofile);
+ for (userptr = userconfig; *userptr; userptr ++)
{
// Convert back slashes to forward slashes
- if (*homeptr == '\\')
- *homeptr = '/';
+ if (*userptr == '\\')
+ *userptr = '/';
}
- DEBUG_printf("cups_globals_alloc: homedir=\"%s\"", homedir);
+ DEBUG_printf("cups_globals_alloc: userconfig=\"%s\"", userconfig);
}
- cg->home = homedir;
+ cg->userconfig = userconfig;
#else
+ const char *home = getenv("HOME"); // HOME environment variable
+ char homedir[1024], // Home directory from account
+ temp[1024]; // Temporary directory string
+# ifndef __APPLE__
+ const char *snap_common = getenv("SNAP_COMMON"),
+ *xdg_config_home = getenv("XDG_CONFIG_HOME");
+ // Environment variables
+# endif // !__APPLE__
# ifdef HAVE_GETEUID
if ((geteuid() != getuid() && getuid()) || getegid() != getgid())
# else
cg->cups_datadir = CUPS_DATADIR;
cg->cups_serverbin = CUPS_SERVERBIN;
- cg->cups_serverroot = CUPS_SERVERROOT;
+ cg->sysconfig = CUPS_SERVERROOT;
cg->cups_statedir = CUPS_STATEDIR;
cg->localedir = CUPS_LOCALEDIR;
}
if ((cg->cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
cg->cups_serverbin = CUPS_SERVERBIN;
- if ((cg->cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
- cg->cups_serverroot = CUPS_SERVERROOT;
+ if ((cg->sysconfig = getenv("CUPS_SERVERROOT")) == NULL)
+ cg->sysconfig = CUPS_SERVERROOT;
if ((cg->cups_statedir = getenv("CUPS_STATEDIR")) == NULL)
cg->cups_statedir = CUPS_STATEDIR;
if ((cg->localedir = getenv("LOCALEDIR")) == NULL)
cg->localedir = CUPS_LOCALEDIR;
-
- cg->home = getenv("HOME");
-
-# ifdef __APPLE__ /* Sandboxing now exposes the container as the home directory */
- if (cg->home && strstr(cg->home, "/Library/Containers/"))
- cg->home = NULL;
-# endif /* !__APPLE__ */
}
- if (!cg->home)
+# ifdef __APPLE__
+ if (!home)
+#else
+ if (!home && !xdg_config_home)
+# endif // __APPLE__
+ if (!home)
{
struct passwd pw; /* User info */
struct passwd *result; /* Auxiliary pointer */
getpwuid_r(getuid(), &pw, cg->pw_buf, PW_BUF_SIZE, &result);
if (result)
- cg->home = _cupsStrAlloc(pw.pw_dir);
+ {
+ cupsCopyString(homedir, pw.pw_dir, sizeof(homedir));
+ home = homedir;
+ }
+ }
+
+# ifdef __APPLE__
+ if (home)
+ {
+ // macOS uses ~/Library/Application Support/FOO
+ snprintf(temp, sizeof(temp), "%s/Library/Application Support/cups", home);
}
+ else
+ {
+ // Something went wrong, use temporary directory...
+ snprintf(temp, sizeof(temp), "/private/tmp/cups%u", (unsigned)getuid());
+ }
+
+# else
+ if (snap_common)
+ {
+ // Snaps use $SNAP_COMMON/FOO
+ snprintf(temp, sizeof(temp), "%s/cups", snap_common);
+ }
+ else if (xdg_config_home)
+ {
+ // XDG uses $XDG_CONFIG_HOME/FOO
+ snprintf(temp, sizeof(temp), "%s/cups", xdg_config_home);
+ }
+ else if (home)
+ {
+ // Use ~/.cups if it exists, otherwise ~/.config/cups (XDG standard)
+ snprintf(temp, sizeof(temp), "%s/.cups", home);
+ if (access(temp, 0))
+ snprintf(temp, sizeof(temp), "%s/.config/cups", home);
+ }
+ else
+ {
+ // Something went wrong, use temporary directory...
+ snprintf(temp, sizeof(temp), "/tmp/cups%u", (unsigned)getuid());
+ }
+# endif // __APPLE__
+
+ // Can't use _cupsStrAlloc since it causes a loop with debug logging enabled
+ cg->userconfig = strdup(temp);
#endif /* _WIN32 */
return (cg);
cupsFreeOptions(cg->cupsd_num_settings, cg->cupsd_settings);
- if (cg->raster_error.start)
- free(cg->raster_error.start);
+ free(cg->userconfig);
+ free(cg->raster_error.start);
free(cg);
}
struct stat ppdinfo; /* PPD file information */
- snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", cg->cups_serverroot,
+ snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", cg->sysconfig,
name);
if (!stat(ppdname, &ppdinfo) && !access(ppdname, R_OK))
{
{
cupsCopyString(cg->snmp_community, "public", sizeof(cg->snmp_community));
- snprintf(line, sizeof(line), "%s/snmp.conf", cg->cups_serverroot);
+ snprintf(line, sizeof(line), "%s/snmp.conf", cg->sysconfig);
if ((fp = cupsFileOpen(line, "r")) != NULL)
{
linenum = 0;
puts("TLS Server Credentials:");
if ((hcreds = httpCopyPeerCredentials(http)) != NULL)
{
- trust = cupsGetCredentialsTrust(TEST_CERT_PATH, hostname, hcreds);
+ trust = cupsGetCredentialsTrust(TEST_CERT_PATH, hostname, hcreds, /*require_ca*/false);
cupsGetCredentialsInfo(hcreds, hinfo, sizeof(hinfo));
//
// HTTP test program for CUPS.
//
-// Copyright © 2020-2024 by OpenPrinting.
+// Copyright © 2021-2024 by OpenPrinting.
// Copyright © 2007-2018 by Apple Inc.
// Copyright © 1997-2006 by Easy Software Products.
//
// Test HTTP GET requests...
http = NULL;
- out = stdout;
+ out = stdout;
for (i = 1; i < argc; i ++)
{
if ((creds = httpCopyPeerCredentials(http)) != NULL)
{
char *lcreds;
- http_trust_t trust = cupsGetCredentialsTrust(NULL, hostname, creds);
+ http_trust_t trust = cupsGetCredentialsTrust(NULL, hostname, creds, /*require_ca*/true);
cupsGetCredentialsInfo(creds, info, sizeof(info));
if (trust != HTTP_TRUST_OK)
{
printf("SaveCredentials: %s\n", cupsSaveCredentials(NULL, hostname, creds, /*key*/NULL) ? "true" : "false");
- trust = cupsGetCredentialsTrust(NULL, hostname, creds);
+ trust = cupsGetCredentialsTrust(NULL, hostname, creds, /*require_ca*/true);
printf("New Trust: %s\n", trusts[trust]);
+ printf("SaveCredentials (NULL): %s\n", cupsSaveCredentials(NULL, hostname, /*creds*/NULL, /*key*/NULL) ? "true" : "false");
}
free(creds);
do
{
+ puts("Sending HEAD request...");
if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
{
httpClearFields(http);
httpSetField(http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString(http));
httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
- if (httpWriteRequest(http, "HEAD", resource))
+ if (!httpWriteRequest(http, "HEAD", resource))
{
if (!httpReconnect2(http, 30000, NULL))
{
do
{
+ puts("Sending GET request...");
if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
{
httpClearFields(http);
httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
httpSetField(http, HTTP_FIELD_ACCEPT_ENCODING, encoding);
- if (httpWriteRequest(http, "GET", resource))
+ if (!httpWriteRequest(http, "GET", resource))
{
if (!httpReconnect2(http, 30000, NULL))
{
//
// TLS support code for CUPS using GNU TLS.
//
-// Copyright © 2020-2023 by OpenPrinting
+// Note: This file is included from tls.c
+//
+// Copyright © 2020-2024 by OpenPrinting
// Copyright © 2007-2019 by Apple Inc.
// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
//
// information.
//
-//// This file is included from tls.c
-
-
//
// Local functions...
//
//
// 'cupsGetCredentialsTrust()' - Return the trust of credentials.
//
+// This function determines the level of trust for the supplied credentials.
+// The "path" parameter specifies the certificate/key store for known
+// credentials and certificate authorities. The "common_name" parameter
+// specifies the FQDN of the service being accessed such as
+// "printer.example.com". The "credentials" parameter provides the credentials
+// being evaluated, which are usually obtained with the
+// @link httpCopyPeerCredentials@ function. The "require_ca" parameter
+// specifies whether a CA-signed certificate is required for trust.
+//
+// The `AllowAnyRoot`, `AllowExpiredCerts`, `TrustOnFirstUse`, and
+// `ValidateCerts` options in the "client.conf" file (or corresponding
+// preferences file on macOS) control the trust policy, which defaults to
+// AllowAnyRoot=Yes, AllowExpiredCerts=No, TrustOnFirstUse=Yes, and
+// ValidateCerts=No. When the "require_ca" parameter is `true` the AllowAnyRoot
+// and TrustOnFirstUse policies are turned off ("No").
+//
+// The returned trust value can be one of the following:
+//
+// - `HTTP_TRUST_OK`: Credentials are OK/trusted
+// - `HTTP_TRUST_INVALID`: Credentials are invalid
+// - `HTTP_TRUST_EXPIRED`: Credentials are expired
+// - `HTTP_TRUST_RENEWED`: Credentials have been renewed
+// - `HTTP_TRUST_UNKNOWN`: Credentials are unknown/new
+//
http_trust_t // O - Level of trust
cupsGetCredentialsTrust(
const char *path, // I - Directory path for certificate/key store or `NULL` for default
const char *common_name, // I - Common name for trust lookup
- const char *credentials) // I - Credentials
+ const char *credentials, // I - Credentials
+ bool require_ca) // I - Require a CA-signed certificate?
{
http_trust_t trust = HTTP_TRUST_OK;
// Trusted?
{
// Credentials don't match, let's look at the expiration date of the new
// credentials and allow if the new ones have a later expiration...
- if (!cg->trust_first)
+ if (!cg->trust_first || require_ca)
{
// Do not trust certificates on first use...
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
free(tcreds);
}
- else if (cg->validate_certs && !cupsAreCredentialsValidForName(common_name, credentials))
+ else if ((cg->validate_certs || require_ca) && !cupsAreCredentialsValidForName(common_name, credentials))
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No stored credentials, not valid for name."), 1);
trust = HTTP_TRUST_INVALID;
}
- else if (!cg->trust_first)
+ else if (num_certs > 1 && !http_check_roots(credentials))
{
// See if we have a site CA certificate we can compare...
if ((tcreds = cupsCopyCredentials(path, "_site_")) != NULL)
free(tcreds);
}
- else
+ else if (require_ca)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
trust = HTTP_TRUST_INVALID;
}
+ else if (!cg->trust_first)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
+ trust = HTTP_TRUST_INVALID;
+ }
+ }
+ else if ((!cg->any_root || require_ca) && num_certs == 1)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Self-signed credentials are blocked."), 1);
+ trust = HTTP_TRUST_INVALID;
}
if (trust == HTTP_TRUST_OK && !cg->expired_certs)
}
}
- if (trust == HTTP_TRUST_OK && !cg->any_root && num_certs == 1)
- {
- _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Self-signed credentials are blocked."), 1);
- trust = HTTP_TRUST_INVALID;
- }
-
gnutls_free_certs(num_certs, certs);
return (trust);
//
// 'cupsGetCredentialsTrust()' - Return the trust of credentials.
//
+// This function determines the level of trust for the supplied credentials.
+// The "path" parameter specifies the certificate/key store for known
+// credentials and certificate authorities. The "common_name" parameter
+// specifies the FQDN of the service being accessed such as
+// "printer.example.com". The "credentials" parameter provides the credentials
+// being evaluated, which are usually obtained with the
+// @link httpCopyPeerCredentials@ function. The "require_ca" parameter
+// specifies whether a CA-signed certificate is required for trust.
+//
+// The `AllowAnyRoot`, `AllowExpiredCerts`, `TrustOnFirstUse`, and
+// `ValidateCerts` options in the "client.conf" file (or corresponding
+// preferences file on macOS) control the trust policy, which defaults to
+// AllowAnyRoot=Yes, AllowExpiredCerts=No, TrustOnFirstUse=Yes, and
+// ValidateCerts=No. When the "require_ca" parameter is `true` the AllowAnyRoot
+// and TrustOnFirstUse policies are turned off ("No").
+//
+// The returned trust value can be one of the following:
+//
+// - `HTTP_TRUST_OK`: Credentials are OK/trusted
+// - `HTTP_TRUST_INVALID`: Credentials are invalid
+// - `HTTP_TRUST_EXPIRED`: Credentials are expired
+// - `HTTP_TRUST_RENEWED`: Credentials have been renewed
+// - `HTTP_TRUST_UNKNOWN`: Credentials are unknown/new
+//
http_trust_t // O - Level of trust
cupsGetCredentialsTrust(
const char *path, // I - Directory path for certificate/key store or `NULL` for default
const char *common_name, // I - Common name for trust lookup
- const char *credentials) // I - Credentials
+ const char *credentials, // I - Credentials
+ bool require_ca) // I - Require a CA-signed certificate?
{
http_trust_t trust = HTTP_TRUST_OK;
// Trusted?
{
// Credentials don't match, let's look at the expiration date of the new
// credentials and allow if the new ones have a later expiration...
- if (!cg->trust_first)
+ if (!cg->trust_first || require_ca)
{
// Do not trust certificates on first use...
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
free(tcreds);
}
- else if (cg->validate_certs && !cupsAreCredentialsValidForName(common_name, credentials))
+ else if ((cg->validate_certs || require_ca) && !cupsAreCredentialsValidForName(common_name, credentials))
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No stored credentials, not valid for name."), 1);
trust = HTTP_TRUST_INVALID;
}
- else if (!cg->trust_first)
+ else if (sk_X509_num(certs) > 1 && !http_check_roots(credentials))
{
// See if we have a site CA certificate we can compare...
if ((tcreds = cupsCopyCredentials(path, "_site_")) != NULL)
free(tcreds);
}
- else
+ else if (require_ca)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
+ trust = HTTP_TRUST_INVALID;
+ }
+ else if (!cg->trust_first)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
trust = HTTP_TRUST_INVALID;
}
}
+ else if ((!cg->any_root || require_ca) && sk_X509_num(certs) == 1)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Self-signed credentials are blocked."), 1);
+ trust = HTTP_TRUST_INVALID;
+ }
if (trust == HTTP_TRUST_OK && !cg->expired_certs)
{
}
}
- if (trust == HTTP_TRUST_OK && !cg->any_root && sk_X509_num(certs) == 1)
- {
- _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Self-signed credentials are blocked."), 1);
- trust = HTTP_TRUST_INVALID;
- }
-
sk_X509_free(certs);
return (trust);
STACK_OF(X509) *chain; // Certificate chain
- DEBUG_printf("httpCopyCredentials(http=%p)", (void *)http);
+ DEBUG_printf("httpCopyPeerCredentials(http=%p)", (void *)http);
if (http && http->tls)
{
// Get the chain of certificates for the remote end...
chain = SSL_get_peer_cert_chain(http->tls);
- DEBUG_printf("1httpCopyCredentials: chain=%p", (void *)chain);
+ DEBUG_printf("1httpCopyPeerCredentials: chain=%p", (void *)chain);
if (chain)
{
BIO *bio = BIO_new(BIO_s_mem());
// Memory buffer for cert
+ DEBUG_printf("1httpCopyPeerCredentials: chain[%d/%d]=%p", i + 1, count, cert);
+
+#ifdef DEBUG
+ char subjectName[256], issuerName[256];
+ X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, subjectName, sizeof(subjectName));
+ X509_NAME_get_text_by_NID(X509_get_issuer_name(cert), NID_commonName, issuerName, sizeof(issuerName));
+ DEBUG_printf("1httpCopyPeerCredentials: subjectName=\"%s\", issuerName=\"%s\"", subjectName, issuerName);
+
+ STACK_OF(GENERAL_NAME) *names; // subjectAltName values
+ names = X509_get_ext_d2i(cert, NID_subject_alt_name, /*crit*/NULL, /*idx*/NULL);
+ DEBUG_printf("1httpCopyPeerCredentials: subjectAltNames=%p(%d)", names, names ? sk_GENERAL_NAME_num(names) : 0);
+ if (names)
+ GENERAL_NAMES_free(names);
+#endif // DEBUG
+
if (bio)
{
long bytes; // Number of bytes
}
}
+ DEBUG_printf("1httpCopyPeerCredentials: Returning \"%s\".", credentials);
+
return (credentials);
}
X509_free(cert);
break;
}
+
+ cert = NULL;
}
BIO_free(bio);
//
#include "cups-private.h"
+#include "dir.h"
#include <fcntl.h>
#include <math.h>
#include <sys/stat.h>
+#ifdef __APPLE__
+# include <Security/Security.h>
+#endif // __APPLE__
#ifdef _WIN32
# include <tchar.h>
#else
static int tls_options = -1,// Options for TLS connections
tls_min_version = _HTTP_TLS_1_2,
tls_max_version = _HTTP_TLS_MAX;
+#ifndef __APPLE__
+static cups_array_t *tls_root_certs = NULL;
+ // List of known root CAs
+#endif // __APPLE__
//
// Local functions...
//
+static bool http_check_roots(const char *creds);
static char *http_copy_file(const char *path, const char *common_name, const char *ext);
static const char *http_default_path(char *buffer, size_t bufsize);
static bool http_default_san_cb(const char *common_name, const char *subject_alt_name, void *data);
+#ifdef _WIN32
+static char *http_der_to_pem(const unsigned char *der, size_t dersize);
+#endif // _WIN32
static const char *http_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext);
static bool http_save_file(const char *path, const char *common_name, const char *ext, const char *value);
cupsSaveCredentials(
const char *path, // I - Directory path for certificate/key store or `NULL` for default
const char *common_name, // I - Common name for certificate
- const char *credentials, // I - PEM-encoded certificate chain
+ const char *credentials, // I - PEM-encoded certificate chain or `NULL` to remove
const char *key) // I - PEM-encoded private key or `NULL` for none
{
if (http_save_file(path, common_name, "crt", credentials))
}
+//
+// 'http_check_roots()' - Check whether the supplied credentials use a trusted root CA.
+//
+
+static bool // O - `true` if they use a trusted root, `false` otherwise
+http_check_roots(const char *creds) // I - Credentials
+{
+ bool ret = false; // Return value
+
+
+#ifdef __APPLE__
+ // Apple hides all of the keychain stuff (all deprecated) so the best we can
+ // do is use the SecTrust API to evaluate the certificate...
+ CFMutableArrayRef certs = NULL; // Certificates from credentials
+ SecCertificateRef cert;
+ char *tcreds = NULL, // Copy of credentials string
+ *tstart, // Start of certificate data
+ *tend, // End of certificate data
+ *der = NULL; // DER-encoded fragment buffer
+ size_t dersize, // Size of DER buffer
+ derlen; // Length of DER data
+ SecPolicyRef policy; // X.509 policy
+ SecTrustRef trust; // Trust evaluator
+
+
+ // Convert PEM-encoded credentials to an array of DER-encoded certificates...
+ if ((tcreds = strdup(creds)) == NULL)
+ goto done;
+
+ if ((certs = CFArrayCreateMutable(kCFAllocatorDefault, /*capacity*/0, &kCFTypeArrayCallBacks)) == NULL)
+ goto done;
+
+ dersize = 3 * strlen(tcreds) / 4;
+ if ((der = malloc(dersize)) == NULL)
+ goto done;
+
+ for (tstart = strstr(tcreds, "-----BEGIN CERTIFICATE-----\n"); tstart; tstart = strstr(tend, "-----BEGIN CERTIFICATE-----\n"))
+ {
+ // Find the end of the certificate data...
+ tstart += 28; // Skip "-----BEGIN CERTIFICATE-----\n"
+ if ((tend = strstr(tstart, "-----END CERTIFICATE-----\n")) == NULL)
+ break; // Missing end...
+
+ // Nul-terminate the cert data...
+ *tend++ = '\0';
+
+ // Convert to DER format
+ derlen = dersize;
+ if (httpDecode64_3(der, &derlen, tstart, /*end*/NULL))
+ {
+ // Create a CFData object for the data...
+ CFDataRef data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)der, (CFIndex)derlen);
+
+ if (data)
+ {
+ // Create a certificate from the DER data...
+ if ((cert = SecCertificateCreateWithData(kCFAllocatorDefault, data)) != NULL)
+ {
+ // Add certificate to the array...
+ CFArrayAppendValue(certs, cert);
+ CFRelease(cert);
+ }
+
+ CFRelease(data);
+ }
+ }
+ }
+
+ // Test the certificate list against the macOS/iOS trust store...
+ if ((policy = SecPolicyCreateBasicX509()) != NULL)
+ {
+ if (SecTrustCreateWithCertificates(certs, policy, &trust) == noErr)
+ {
+ ret = SecTrustEvaluateWithError(trust, NULL);
+ CFRelease(trust);
+ }
+
+ CFRelease(policy);
+ }
+
+ done:
+
+ free(tcreds);
+ free(der);
+
+ if (certs)
+ CFRelease(certs);
+
+#else
+ size_t credslen; // Length of credentials string
+ const char *rcreds; // Current root credential
+ size_t rcredslen; // Length of current root credential
+
+
+ cupsMutexLock(&tls_mutex);
+
+ // Load root certificates as needed...
+ if (!tls_root_certs)
+ {
+ // Load root certificates...
+ tls_root_certs = cupsArrayNew(/*cb*/NULL, /*cb_data*/NULL, /*hash_cb*/NULL, /*hash_size*/0, /*copy_cb*/NULL, /*free_cb*/NULL);
+
+# ifdef _WIN32
+ int i; // Looping var
+ HCERTSTORE store; // Certificate store
+ CERT_CONTEXT *cert; // Current certificate
+
+ // Add certificates in both the "ROOT" and "CA" stores...
+ for (i = 0; i < 2; i ++)
+ {
+ if ((store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, i ? L"CA" : L"ROOT")) == NULL)
+ continue;
+
+ // Loop through certificates...
+ for (cert = CertEnumCertificatesInStore(store, NULL); cert; cert = CertEnumCertificatesInStore(store, cert))
+ {
+ if (cert->dwCertEncodingType == X509_ASN_ENCODING)
+ {
+ // Convert DER to PEM and add to the list...
+ char * pem = http_der_to_pem(cert->pbCertEncoded, cert->cbCertEncoded);
+
+ if (pem)
+ cupsArrayAdd(tls_root_certs, pem);
+ }
+ }
+
+ CertCloseStore(store, 0);
+ }
+
+# else
+ size_t i; // Looping var
+ cups_dir_t *dir; // Directory
+ cups_dentry_t *dent; // Directory entry
+ const char *ext; // Pointer to filename extension
+ static const char * const root_dirs[] =
+ { // Root certificate stores
+ "/etc/ssl/certs",
+ "/system/etc/security/cacerts/",
+
+ };
+
+ for (i = 0, dir = NULL; i < (sizeof(root_dirs) / sizeof(root_dirs[0])); i ++)
+ {
+ if ((dir = cupsDirOpen(root_dirs[i])) != NULL)
+ break;
+ }
+
+ if (dir)
+ {
+ while ((dent = cupsDirRead(dir)) != NULL)
+ {
+ if ((ext = strrchr(dent->filename, '.')) != NULL && !strcmp(ext, ".pem"))
+ {
+ char filename[1024], // Certificate filename
+ *cert; // Certificate data
+ int fd; // File descriptor
+
+ snprintf(filename, sizeof(filename), "%s/%s", root_dirs[i], dent->filename);
+ if ((fd = open(filename, O_RDONLY)) >= 0)
+ {
+ if ((cert = calloc(1, (size_t)(dent->fileinfo.st_size + 1))) != NULL)
+ {
+ read(fd, cert, (size_t)dent->fileinfo.st_size);
+ cupsArrayAdd(tls_root_certs, cert);
+ }
+
+ close(fd);
+ }
+ }
+ }
+ }
+# endif // _WIN32
+ }
+
+ // Check all roots
+ credslen = strlen(creds);
+
+ for (rcreds = (const char *)cupsArrayGetFirst(tls_root_certs); rcreds && !ret; rcreds = (const char *)cupsArrayGetNext(tls_root_certs))
+ {
+ // Compare the root against the tail of the current credentials...
+ rcredslen = strlen(rcreds);
+
+ if (credslen >= rcredslen && !strcmp(creds + (credslen - rcredslen), rcreds))
+ ret = true;
+ }
+
+ // Unlock access and return...
+ cupsMutexUnlock(&tls_mutex);
+#endif // __APPLE__
+
+ return (ret);
+}
+
+
//
// 'http_copy_file()' - Copy the contents of a file to a string.
//
}
else
{
- if (mkdir(cg->cups_serverroot, 0755) && errno != EEXIST)
+ if (mkdir(cg->sysconfig, 0755) && errno != EEXIST)
{
- DEBUG_printf("1http_default_path: Failed to make directory '%s': %s", cg->cups_serverroot, strerror(errno));
+ DEBUG_printf("1http_default_path: Failed to make directory '%s': %s", cg->sysconfig, strerror(errno));
return (NULL);
}
- snprintf(buffer, bufsize, "%s/ssl", cg->cups_serverroot);
+ snprintf(buffer, bufsize, "%s/ssl", cg->sysconfig);
if (mkdir(buffer, 0700) && errno != EEXIST)
{
}
+#ifdef _WIN32
+//
+// 'http_der_to_pem()' - Convert DER format certificate data to PEM.
+//
+
+static char * // O - PEM string
+http_der_to_pem(
+ const unsigned char *der, // I - DER-encoded data
+ size_t dersize) // I - Size of DER-encoded data
+{
+ char *pem, // PEM-encoded string
+ *pemptr; // Pointer into PEM-encoded string
+ int col; // Current column
+ size_t pemsize; // Size of PEM-encoded string
+ static const char *base64 = // Base64 alphabet
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+
+ // Calculate the size, accounting for Base64 expansion, line wrapping at
+ // column 64, and the BEGIN/END CERTIFICATE text...
+ pemsize = 65 * 4 * dersize / 3 / 64 + /*"-----BEGIN CERTIFICATE-----"*/28 + /*"-----END CERTIFICATE-----"*/26 + 2;
+
+ if ((pem = calloc(1, pemsize)) == NULL)
+ return (NULL);
+
+ cupsCopyString(pem, "-----BEGIN CERTIFICATE-----\n", pemsize);
+ for (pemptr = pem, col = 0; dersize > 0; der += 3)
+ {
+ // Encode the up to 3 characters as 4 Base64 numbers...
+ switch (dersize)
+ {
+ case 1 :
+ *pemptr ++ = base64[(der[0] & 255) >> 2];
+ *pemptr ++ = base64[((der[0] & 255) << 4) & 63];
+ *pemptr ++ = '=';
+ *pemptr ++ = '=';
+ dersize = 0;
+ break;
+ case 2 :
+ *pemptr ++ = base64[(der[0] & 255) >> 2];
+ *pemptr ++ = base64[(((der[0] & 255) << 4) | ((der[1] & 255) >> 4)) & 63];
+ *pemptr ++ = base64[((der[1] & 255) << 2) & 63];
+ *pemptr ++ = '=';
+ dersize = 0;
+ break;
+ default :
+ *pemptr ++ = base64[(der[0] & 255) >> 2];
+ *pemptr ++ = base64[(((der[0] & 255) << 4) | ((der[1] & 255) >> 4)) & 63];
+ *pemptr ++ = base64[(((der[1] & 255) << 2) | ((der[2] & 255) >> 6)) & 63];
+ *pemptr ++ = base64[der[2] & 63];
+ dersize -= 3;
+ break;
+ }
+
+ // Add a newline as needed...
+ col += 4;
+ if (col >= 64)
+ {
+ *pemptr++ = '\n';
+ col = 0;
+ }
+ }
+
+ if (col > 0)
+ *pemptr++ = '\n';
+ *pemptr = '\0';
+
+ cupsConcatString(pem, "-----END CERTIFICATE-----\n", pemsize);
+
+ // Return the encoded string...
+ return (pem);
+}
+#endif // _WIN32
+
+
//
// 'http_make_path()' - Format a filename for a certificate or key file.
//
// Range check input...
- if (!common_name || !value)
+ if (!common_name)
return (false);
// Get default path as needed...
if (!path)
path = http_default_path(defpath, sizeof(defpath));
- if ((fd = open(http_make_path(filename, sizeof(filename), path, common_name, ext), O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0)
+ http_make_path(filename, sizeof(filename), path, common_name, ext);
+
+ if (!value)
+ {
+ unlink(filename);
+ return (true);
+ }
+
+ if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0)
return (false);
if (write(fd, value, strlen(value)) < 0)
* present.
*/
- snprintf(filename, sizeof(filename), "%s/client.conf", cg->cups_serverroot);
+ snprintf(filename, sizeof(filename), "%s/client.conf", cg->sysconfig);
if ((fp = cupsFileOpen(filename, "r")) != NULL)
{
cups_read_client_conf(fp, &cc);
cupsFileClose(fp);
}
- if (cg->home)
+ if (cg->userconfig)
{
/*
* Look for ~/.cups/client.conf...
*/
-#if _WIN32
- snprintf(filename, sizeof(filename), "%s/AppData/Local/cups/client.conf", cg->home);
-#else
- snprintf(filename, sizeof(filename), "%s/.cups/client.conf", cg->home);
-#endif // _WIN32
+ snprintf(filename, sizeof(filename), "%s/client.conf", cg->userconfig);
if ((fp = cupsFileOpen(filename, "r")) != NULL)
{
if (make_model && !strstr(make_model, "Raw Printer"))
_cupsLangPrintf(stdout,
_("\tInterface: %s/ppd/%s.ppd"),
- cg->cups_serverroot, printer);
+ cg->sysconfig, printer);
}
_cupsLangPuts(stdout, _("\tOn fault: no alert"));
_cupsLangPuts(stdout, _("\tAfter fault: continue"));
if (make_model && !strstr(make_model, "Raw Printer"))
_cupsLangPrintf(stdout,
_("\tInterface: %s/ppd/%s.ppd"),
- cg->cups_serverroot, printer);
+ cg->sysconfig, printer);
}
_cupsLangPuts(stdout, _("\tOn fault: no alert"));
_cupsLangPuts(stdout, _("\tAfter fault: continue"));