]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Update httpConnectURI to do X.509 pinning, and use it when doing the IPP
authorMichael R Sweet <msweet@msweet.org>
Fri, 20 Sep 2024 15:32:56 +0000 (11:32 -0400)
committerMichael R Sweet <msweet@msweet.org>
Fri, 20 Sep 2024 15:32:56 +0000 (11:32 -0400)
Everywhere printer configuration.

backend/ipp.c
cups/http.c
cups/tls-gnutls.c
cups/tls-openssl.c
scheduler/ipp.c

index 0e658690074e5ed58c282b36d218744a58400220..23255b71d05aa96af882f2691dfe1e20f3437e4f 100644 (file)
@@ -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);
index d76f0e7d992f279e14cb5cdad1b3bb500fd1ee0e..3bed7a79e3b899bf6e0f0973b4c387027be9ead8 100644 (file)
@@ -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);
index ede3e3712103d72fd9e9e449fd274edc1b1b2267..2e85be805f6914ff9b0ee481cfe1f060d7ede5cf 100644 (file)
@@ -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;
     }
   }
index dea21a0d726ade666bd949e820705fe6e071d330..6158dc24b0da7df78ada050c0f2efc22959e693b 100644 (file)
@@ -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;
     }
   }
index caa07394528fb0a98435e0965d9c1b96eda2331a..7946b9f3c2a9ba05288ca3fa8fb4984cdf0bf035 100644 (file)
@@ -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.
  *
  * information.
  */
 
-/*
- * Include necessary headers...
- */
-
 #include "cupsd.h"
 #include <cups/ppd-private.h>
 
@@ -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;
   }