From: Michael R Sweet Date: Fri, 20 Sep 2024 15:32:56 +0000 (-0400) Subject: Update httpConnectURI to do X.509 pinning, and use it when doing the IPP X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e3467edf3be2d20a022495d9726a741e36768caf;p=thirdparty%2Fcups.git Update httpConnectURI to do X.509 pinning, and use it when doing the IPP Everywhere printer configuration. --- diff --git a/backend/ipp.c b/backend/ipp.c index 0e65869007..23255b71d0 100644 --- a/backend/ipp.c +++ b/backend/ipp.c @@ -835,15 +835,11 @@ main(int argc, /* I - Number of command-line args */ return (CUPS_BACKEND_STOP); } - if (!lcreds) - { - /* - * Could not load the credentials, let's save the ones we have so we - * can detect changes... - */ + /* + * Save the credentials we have so we can detect changes... + */ - cupsSaveCredentials(NULL, hostname, creds, /*key*/NULL); - } + cupsSaveCredentials(NULL, hostname, creds, /*key*/NULL); free(lcreds); free(creds); diff --git a/cups/http.c b/cups/http.c index d76f0e7d99..3bed7a79e3 100644 --- a/cups/http.c +++ b/cups/http.c @@ -593,6 +593,7 @@ httpConnectURI(const char *uri, // I - Service to connect to lresource[256]; // URI resource (local copy) int lport; // URI port (local copy) http_encryption_t encryption; // Type of encryption to use + http_uri_status_t uri_status; // URI separation status DEBUG_printf("httpConnectURI(uri=\"%s\", host=%p, hsize=%u, port=%p, resource=%p, rsize=%u, blocking=%d, msec=%d, cancel=%p, require_ca=%s)", uri, (void *)host, (unsigned)hsize, (void *)port, (void *)resource, (unsigned)rsize, blocking, msec, (void *)cancel, require_ca ? "true" : "false"); @@ -628,8 +629,11 @@ httpConnectURI(const char *uri, // I - Service to connect to } // Get the URI components... - if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, hsize, port, resource, rsize) < HTTP_URI_STATUS_OK) + if ((uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, hsize, port, resource, rsize)) < HTTP_URI_STATUS_OK) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, httpURIStatusString(uri_status), 0); return (NULL); + } DEBUG_printf("1httpConnectURI: scheme=\"%s\", host=\"%s\", port=%d, resource=\"%s\"", scheme, host, *port, resource); @@ -640,7 +644,7 @@ httpConnectURI(const char *uri, // I - Service to connect to http = httpConnect2(host, *port, /*addrlist*/NULL, AF_UNSPEC, encryption, blocking, msec, cancel); - if (httpIsEncrypted(http) && require_ca) + if (httpIsEncrypted(http)) { // Validate trust with service... char *creds; // Peer credentials... @@ -649,13 +653,19 @@ httpConnectURI(const char *uri, // I - Service to connect to creds = httpCopyPeerCredentials(http); trust = cupsGetCredentialsTrust(/*path*/NULL, host, creds, require_ca); - free(creds); - - if (trust != HTTP_TRUST_OK) + if (trust == HTTP_TRUST_OK) { + // Pin the trusted credentials (for TOFU)... + cupsSaveCredentials(/*path*/NULL, host, creds, /*key*/NULL); + } + else if (trust != HTTP_TRUST_RENEWED) + { + // Don't allow the connection... httpClose(http); http = NULL; } + + free(creds); } return (http); diff --git a/cups/tls-gnutls.c b/cups/tls-gnutls.c index ede3e37121..2e85be805f 100644 --- a/cups/tls-gnutls.c +++ b/cups/tls-gnutls.c @@ -818,7 +818,7 @@ cupsGetCredentialsTrust( // Load the credentials... if (!gnutls_import_certs(credentials, &num_certs, certs)) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to import credentials."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Unable to import credentials."), 1); return (HTTP_TRUST_UNKNOWN); } @@ -844,21 +844,21 @@ cupsGetCredentialsTrust( 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); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Trust on first use is disabled."), 1); trust = HTTP_TRUST_INVALID; } else if (cupsGetCredentialsExpiration(credentials) <= cupsGetCredentialsExpiration(tcreds)) { // The new credentials are not newly issued... - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are older than stored credentials."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("New credentials are older than stored credentials."), 1); trust = HTTP_TRUST_INVALID; } else if (!cupsAreCredentialsValidForName(common_name, credentials)) { // The common name does not match the issued certificate... - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are not valid for name."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("New credentials are not valid for name."), 1); trust = HTTP_TRUST_INVALID; } @@ -875,7 +875,7 @@ cupsGetCredentialsTrust( } else if ((cg->validate_certs || require_ca) && !cupsAreCredentialsValidForName(common_name, credentials)) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No stored credentials, not valid for name."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("No stored credentials, not valid for name."), 1); trust = HTTP_TRUST_INVALID; } else if (num_certs > 1) @@ -899,7 +899,7 @@ cupsGetCredentialsTrust( } if (trust != HTTP_TRUST_OK) - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials do not validate against site CA certificate."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Credentials do not validate against site CA certificate."), 1); free(tcreds); } @@ -907,17 +907,17 @@ cupsGetCredentialsTrust( } else if (require_ca) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials are not CA-signed."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Credentials are not CA-signed."), 1); trust = HTTP_TRUST_INVALID; } else if (!cg->trust_first) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Trust on first use is disabled."), 1); trust = HTTP_TRUST_INVALID; } else if (!cg->any_root || require_ca) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Self-signed credentials are blocked."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Self-signed credentials are blocked."), 1); trust = HTTP_TRUST_INVALID; } @@ -928,7 +928,7 @@ cupsGetCredentialsTrust( time(&curtime); if (curtime < gnutls_x509_crt_get_activation_time(certs[0]) || curtime > gnutls_x509_crt_get_expiration_time(certs[0])) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials have expired."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Credentials have expired."), 1); trust = HTTP_TRUST_EXPIRED; } } diff --git a/cups/tls-openssl.c b/cups/tls-openssl.c index dea21a0d72..6158dc24b0 100644 --- a/cups/tls-openssl.c +++ b/cups/tls-openssl.c @@ -790,7 +790,7 @@ cupsGetCredentialsTrust( // Load the credentials... if ((certs = openssl_load_x509(credentials)) == NULL) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to import credentials."), true); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Unable to import credentials."), true); return (HTTP_TRUST_UNKNOWN); } @@ -818,21 +818,21 @@ cupsGetCredentialsTrust( 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); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Trust on first use is disabled."), 1); trust = HTTP_TRUST_INVALID; } else if (cupsGetCredentialsExpiration(credentials) <= cupsGetCredentialsExpiration(tcreds)) { // The new credentials are not newly issued... - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are older than stored credentials."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("New credentials are older than stored credentials."), 1); trust = HTTP_TRUST_INVALID; } else if (!cupsAreCredentialsValidForName(common_name, credentials)) { // The common name does not match the issued certificate... - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are not valid for name."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("New credentials are not valid for name."), 1); trust = HTTP_TRUST_INVALID; } @@ -849,7 +849,7 @@ cupsGetCredentialsTrust( } else if ((cg->validate_certs || require_ca) && !cupsAreCredentialsValidForName(common_name, credentials)) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No stored credentials, not valid for name."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("No stored credentials, not valid for name."), 1); trust = HTTP_TRUST_INVALID; } else if (sk_X509_num(certs) > 1) @@ -873,7 +873,7 @@ cupsGetCredentialsTrust( } if (trust != HTTP_TRUST_OK) - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials do not validate against site CA certificate."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Credentials do not validate against site CA certificate."), 1); free(tcreds); } @@ -881,17 +881,17 @@ cupsGetCredentialsTrust( } else if (require_ca) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials are not CA-signed."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Credentials are not CA-signed."), 1); trust = HTTP_TRUST_INVALID; } else if (!cg->trust_first) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Trust on first use is disabled."), 1); trust = HTTP_TRUST_INVALID; } else if (!cg->any_root || require_ca) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Self-signed credentials are blocked."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Self-signed credentials are blocked."), 1); trust = HTTP_TRUST_INVALID; } @@ -902,7 +902,7 @@ cupsGetCredentialsTrust( time(&curtime); if (curtime < openssl_get_date(cert, 0) || curtime > openssl_get_date(cert, 1)) { - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials have expired."), 1); + _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, _("Credentials have expired."), 1); trust = HTTP_TRUST_EXPIRED; } } diff --git a/scheduler/ipp.c b/scheduler/ipp.c index caa0739452..7946b9f3c2 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -1,7 +1,7 @@ /* * IPP routines for the CUPS scheduler. * - * Copyright © 2020-2023 by OpenPrinting + * Copyright © 2020-2024 by OpenPrinting * Copyright © 2007-2021 by Apple Inc. * Copyright © 1997-2007 by Easy Software Products, all rights reserved. * @@ -12,10 +12,6 @@ * information. */ -/* - * Include necessary headers... - */ - #include "cupsd.h" #include @@ -5192,14 +5188,11 @@ create_local_bg_thread( char device_uri[1024], /* Device URI */ fromppd[1024], /* Source PPD */ toppd[1024], /* Destination PPD */ - scheme[32], /* URI scheme */ - userpass[256], /* User:pass */ host[256], /* Hostname */ resource[1024], /* Resource path */ uri[1024], /* Resolved URI, if needed */ line[1024]; /* Line from PPD */ int port; /* Port number */ - http_encryption_t encryption; /* Type of encryption to use */ http_t *http; /* Connection to printer */ ipp_t *request, /* Request to printer */ *response = NULL; /* Response from printer */ @@ -5246,27 +5239,9 @@ create_local_bg_thread( cupsCopyString(device_uri, uri, sizeof(device_uri)); } - if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK) + if ((http = httpConnectURI(device_uri, host, sizeof(host), &port, resource, sizeof(resource), /*blocking*/true, /*msec*/30000, /*cancel*/NULL, /*require_ca*/false)) == NULL) { - cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Bad device URI \"%s\".", printer->name, device_uri); - - /* Force printer to timeout and be deleted */ - cupsRWLockWrite(&printer->lock); - printer->state_time = 0; - cupsRWUnlock(&printer->lock); - - send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Bad device URI \"%s\"."), device_uri); - goto finish_response; - } - - if (!strcmp(scheme, "ipps") || port == 443) - encryption = HTTP_ENCRYPTION_ALWAYS; - else - encryption = HTTP_ENCRYPTION_IF_REQUESTED; - - if ((http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to connect to %s:%d: %s", printer->name, host, port, cupsGetErrorString()); + cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to connect to '%s': %s", printer->name, device_uri, cupsGetErrorString()); /* Force printer to timeout and be deleted */ cupsRWLockWrite(&printer->lock); @@ -5281,7 +5256,7 @@ create_local_bg_thread( * Query the printer for its capabilities... */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Connected to %s:%d, sending Get-Printer-Attributes request...", printer->name, host, port); + cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Connected to '%s', sending Get-Printer-Attributes request...", printer->name, device_uri); request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); ippSetVersion(request, 2, 0); @@ -5319,55 +5294,45 @@ create_local_bg_thread( * try to get it separately */ - if (ippFindAttribute(response, "media-col-database", IPP_TAG_BEGIN_COLLECTION) == - NULL) + if (ippFindAttribute(response, "media-col-database", IPP_TAG_BEGIN_COLLECTION) == NULL) { - ipp_t *response2; + ipp_t *response2; /* Second response */ - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Polling \"media-col-database\" attribute separately."); + cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Querying \"media-col-database\" attribute separately.", printer->name); request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); ippSetVersion(request, 2, 0); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, device_uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", NULL, "media-col-database"); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "media-col-database"); response2 = cupsDoRequest(http, request, resource); //ipp_status = cupsGetError(); if (response2) { - if ((attr = ippFindAttribute(response2, "media-col-database", - IPP_TAG_ZERO)) != NULL) + if ((attr = ippFindAttribute(response2, "media-col-database", IPP_TAG_BEGIN_COLLECTION)) != NULL) { - cupsdLogMessage(CUPSD_LOG_WARN, "The printer %s does not support requests" - " with attribute set \"all,media-col-database\", which breaks IPP" - " conformance (RFC 8011, 4.2.5.1 \"requested-attributes\")" - " - report the issue to your printer manufacturer", printer->name); /* * Copy "media-col-database" attribute into the original * IPP response */ - cupsdLogMessage(CUPSD_LOG_DEBUG, - "\"media-col-database\" attribute found."); + cupsdLogMessage(CUPSD_LOG_WARN, "%s: The printer does not support requests with attribute set \"all,media-col-database\", which breaks IPP conformance (RFC 8011, 4.2.5.1 \"requested-attributes\") - report the issue to your printer manufacturer", printer->name); + + cupsdLogMessage(CUPSD_LOG_DEBUG, "\"media-col-database\" attribute found."); ippCopyAttribute(response, attr, 0); } ippDelete(response2); } } - if (ippFindAttribute(response, "media-col-database", IPP_TAG_BEGIN_COLLECTION) == NULL - && ippFindAttribute(response, "media-supported", IPP_TAG_ZERO) == NULL - && ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION) == NULL) + if (ippFindAttribute(response, "media-col-database", IPP_TAG_BEGIN_COLLECTION) == NULL && ippFindAttribute(response, "media-supported", IPP_TAG_ZERO) == NULL && ippFindAttribute(response, "media-size-supported", IPP_TAG_BEGIN_COLLECTION) == NULL) { - cupsdLogMessage(CUPSD_LOG_ERROR, "The printer %s doesn't provide attributes \"media-col-database\", \"media-size-supported\" or \"media-supported\" required for generating printer capabilities.", printer->name); + cupsdLogMessage(CUPSD_LOG_ERROR, "%s: The printer doesn't provide attributes \"media-col-database\", \"media-size-supported\", or \"media-supported\" required for generating the PPD file.", printer->name); /* Force printer to timeout and be deleted */ cupsRWLockWrite(&printer->lock); printer->state_time = 0; cupsRWUnlock(&printer->lock); - send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("The printer %s does not provide attributes required for IPP Everywhere."), printer->name); + send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("The printer does not provide attributes required for IPP Everywhere.")); goto finish_response; }