diff options
| author | Peter Marko <peter.marko@siemens.com> | 2026-01-01 13:14:57 +0100 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2026-01-26 09:49:25 +0000 |
| commit | 3f703ca1c41cb488cc31282c3c135e2cb1e4a029 (patch) | |
| tree | 1f04d23b1743407a1ecf0e4f347d8a5d15b141a3 | |
| parent | 15ea9a3dcbcbce4dc1c0e61f299ebff01f8b6e5d (diff) | |
| download | poky-3f703ca1c41cb488cc31282c3c135e2cb1e4a029.tar.gz | |
cups: patch CVE-2025-58436
Pick patch from branch 2.4.x corresponding to patch mentioned in [1].
[1] https://nvd.nist.gov/vuln/detail/CVE-2025-58436
(From OE-Core rev: f1014ff6d886312afd55473497934590bc9c78ac)
Signed-off-by: Peter Marko <peter.marko@siemens.com>
Signed-off-by: Yoann Congal <yoann.congal@smile.fr>
Signed-off-by: Paul Barker <paul@pbarker.dev>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
| -rw-r--r-- | meta/recipes-extended/cups/cups.inc | 1 | ||||
| -rw-r--r-- | meta/recipes-extended/cups/cups/CVE-2025-58436.patch | 630 |
2 files changed, 631 insertions, 0 deletions
diff --git a/meta/recipes-extended/cups/cups.inc b/meta/recipes-extended/cups/cups.inc index cba4406720..c808eef9a7 100644 --- a/meta/recipes-extended/cups/cups.inc +++ b/meta/recipes-extended/cups/cups.inc | |||
| @@ -27,6 +27,7 @@ SRC_URI = "https://github.com/OpenPrinting/cups/releases/download/v${PV}/cups-${ | |||
| 27 | file://CVE-2024-47175-5.patch \ | 27 | file://CVE-2024-47175-5.patch \ |
| 28 | file://CVE-2025-58060.patch \ | 28 | file://CVE-2025-58060.patch \ |
| 29 | file://CVE-2025-58364.patch \ | 29 | file://CVE-2025-58364.patch \ |
| 30 | file://CVE-2025-58436.patch \ | ||
| 30 | " | 31 | " |
| 31 | 32 | ||
| 32 | UPSTREAM_CHECK_URI = "https://github.com/OpenPrinting/cups/releases" | 33 | UPSTREAM_CHECK_URI = "https://github.com/OpenPrinting/cups/releases" |
diff --git a/meta/recipes-extended/cups/cups/CVE-2025-58436.patch b/meta/recipes-extended/cups/cups/CVE-2025-58436.patch new file mode 100644 index 0000000000..388c5e57b5 --- /dev/null +++ b/meta/recipes-extended/cups/cups/CVE-2025-58436.patch | |||
| @@ -0,0 +1,630 @@ | |||
| 1 | From 5d414f1f91bdca118413301b148f0b188eb1cdc6 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Zdenek Dohnal <zdohnal@redhat.com> | ||
| 3 | Date: Mon, 13 Oct 2025 10:16:48 +0200 | ||
| 4 | Subject: [PATCH] Fix unresponsive cupsd process caused by a slow client | ||
| 5 | |||
| 6 | If client is very slow, it will slow cupsd process for other clients. | ||
| 7 | The fix is the best effort without turning scheduler cupsd into | ||
| 8 | multithreaded process which would be too complex and error-prone when | ||
| 9 | backporting to 2.4.x series. | ||
| 10 | |||
| 11 | The fix for unencrypted communication is to follow up on communication | ||
| 12 | only if there is the whole line on input, and the waiting time is | ||
| 13 | guarded by timeout. | ||
| 14 | |||
| 15 | Encrypted communication now starts after we have the whole client hello | ||
| 16 | packet, which conflicts with optional upgrade support to HTTPS via | ||
| 17 | methods other than method OPTIONS, so this optional support defined in | ||
| 18 | RFC 2817, section 3.1 is removed. Too slow or incomplete requests are | ||
| 19 | handled by connection timeout. | ||
| 20 | |||
| 21 | Fixes CVE-2025-58436 | ||
| 22 | |||
| 23 | CVE: CVE-2025-58436 | ||
| 24 | Upstream-Status: Backport [https://github.com/OpenPrinting/cups/commit/5d414f1f91bdca118413301b148f0b188eb1cdc6] | ||
| 25 | Signed-off-by: Peter Marko <peter.marko@siemens.com> | ||
| 26 | --- | ||
| 27 | cups/http-private.h | 7 +- | ||
| 28 | cups/http.c | 80 +++++++++++++------- | ||
| 29 | cups/tls-openssl.c | 15 +++- | ||
| 30 | scheduler/client.c | 178 ++++++++++++++++++++++++++++---------------- | ||
| 31 | scheduler/client.h | 3 + | ||
| 32 | scheduler/select.c | 12 +++ | ||
| 33 | 6 files changed, 198 insertions(+), 97 deletions(-) | ||
| 34 | |||
| 35 | diff --git a/cups/http-private.h b/cups/http-private.h | ||
| 36 | index d9854faed..2d9035032 100644 | ||
| 37 | --- a/cups/http-private.h | ||
| 38 | +++ b/cups/http-private.h | ||
| 39 | @@ -120,6 +120,7 @@ extern "C" { | ||
| 40 | * Constants... | ||
| 41 | */ | ||
| 42 | |||
| 43 | +# define _HTTP_MAX_BUFFER 32768 /* Size of read buffer */ | ||
| 44 | # define _HTTP_MAX_SBUFFER 65536 /* Size of (de)compression buffer */ | ||
| 45 | # define _HTTP_RESOLVE_DEFAULT 0 /* Just resolve with default options */ | ||
| 46 | # define _HTTP_RESOLVE_STDERR 1 /* Log resolve progress to stderr */ | ||
| 47 | @@ -231,8 +232,8 @@ struct _http_s /**** HTTP connection structure ****/ | ||
| 48 | http_encoding_t data_encoding; /* Chunked or not */ | ||
| 49 | int _data_remaining;/* Number of bytes left (deprecated) */ | ||
| 50 | int used; /* Number of bytes used in buffer */ | ||
| 51 | - char buffer[HTTP_MAX_BUFFER]; | ||
| 52 | - /* Buffer for incoming data */ | ||
| 53 | + char _buffer[HTTP_MAX_BUFFER]; | ||
| 54 | + /* Old read buffer (deprecated) */ | ||
| 55 | int _auth_type; /* Authentication in use (deprecated) */ | ||
| 56 | unsigned char _md5_state[88]; /* MD5 state (deprecated) */ | ||
| 57 | char nonce[HTTP_MAX_VALUE]; | ||
| 58 | @@ -306,6 +307,8 @@ struct _http_s /**** HTTP connection structure ****/ | ||
| 59 | /* Allocated field values */ | ||
| 60 | *default_fields[HTTP_FIELD_MAX]; | ||
| 61 | /* Default field values, if any */ | ||
| 62 | + char buffer[_HTTP_MAX_BUFFER]; | ||
| 63 | + /* Read buffer */ | ||
| 64 | }; | ||
| 65 | # endif /* !_HTTP_NO_PRIVATE */ | ||
| 66 | |||
| 67 | diff --git a/cups/http.c b/cups/http.c | ||
| 68 | index 7a42cb3d6..214e45158 100644 | ||
| 69 | --- a/cups/http.c | ||
| 70 | +++ b/cups/http.c | ||
| 71 | @@ -53,7 +53,7 @@ static http_t *http_create(const char *host, int port, | ||
| 72 | static void http_debug_hex(const char *prefix, const char *buffer, | ||
| 73 | int bytes); | ||
| 74 | #endif /* DEBUG */ | ||
| 75 | -static ssize_t http_read(http_t *http, char *buffer, size_t length); | ||
| 76 | +static ssize_t http_read(http_t *http, char *buffer, size_t length, int timeout); | ||
| 77 | static ssize_t http_read_buffered(http_t *http, char *buffer, size_t length); | ||
| 78 | static ssize_t http_read_chunk(http_t *http, char *buffer, size_t length); | ||
| 79 | static int http_send(http_t *http, http_state_t request, | ||
| 80 | @@ -1188,7 +1188,7 @@ httpGets(char *line, /* I - Line to read into */ | ||
| 81 | return (NULL); | ||
| 82 | } | ||
| 83 | |||
| 84 | - bytes = http_read(http, http->buffer + http->used, (size_t)(HTTP_MAX_BUFFER - http->used)); | ||
| 85 | + bytes = http_read(http, http->buffer + http->used, (size_t)(_HTTP_MAX_BUFFER - http->used), http->wait_value); | ||
| 86 | |||
| 87 | DEBUG_printf(("4httpGets: read " CUPS_LLFMT " bytes.", CUPS_LLCAST bytes)); | ||
| 88 | |||
| 89 | @@ -1706,24 +1706,13 @@ httpPeek(http_t *http, /* I - HTTP connection */ | ||
| 90 | |||
| 91 | ssize_t buflen; /* Length of read for buffer */ | ||
| 92 | |||
| 93 | - if (!http->blocking) | ||
| 94 | - { | ||
| 95 | - while (!httpWait(http, http->wait_value)) | ||
| 96 | - { | ||
| 97 | - if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data)) | ||
| 98 | - continue; | ||
| 99 | - | ||
| 100 | - return (0); | ||
| 101 | - } | ||
| 102 | - } | ||
| 103 | - | ||
| 104 | if ((size_t)http->data_remaining > sizeof(http->buffer)) | ||
| 105 | buflen = sizeof(http->buffer); | ||
| 106 | else | ||
| 107 | buflen = (ssize_t)http->data_remaining; | ||
| 108 | |||
| 109 | DEBUG_printf(("2httpPeek: Reading %d bytes into buffer.", (int)buflen)); | ||
| 110 | - bytes = http_read(http, http->buffer, (size_t)buflen); | ||
| 111 | + bytes = http_read(http, http->buffer, (size_t)buflen, http->wait_value); | ||
| 112 | |||
| 113 | DEBUG_printf(("2httpPeek: Read " CUPS_LLFMT " bytes into buffer.", | ||
| 114 | CUPS_LLCAST bytes)); | ||
| 115 | @@ -1744,9 +1733,9 @@ httpPeek(http_t *http, /* I - HTTP connection */ | ||
| 116 | int zerr; /* Decompressor error */ | ||
| 117 | z_stream stream; /* Copy of decompressor stream */ | ||
| 118 | |||
| 119 | - if (http->used > 0 && ((z_stream *)http->stream)->avail_in < HTTP_MAX_BUFFER) | ||
| 120 | + if (http->used > 0 && ((z_stream *)http->stream)->avail_in < _HTTP_MAX_BUFFER) | ||
| 121 | { | ||
| 122 | - size_t buflen = HTTP_MAX_BUFFER - ((z_stream *)http->stream)->avail_in; | ||
| 123 | + size_t buflen = _HTTP_MAX_BUFFER - ((z_stream *)http->stream)->avail_in; | ||
| 124 | /* Number of bytes to copy */ | ||
| 125 | |||
| 126 | if (((z_stream *)http->stream)->avail_in > 0 && | ||
| 127 | @@ -2004,7 +1993,7 @@ httpRead2(http_t *http, /* I - HTTP connection */ | ||
| 128 | |||
| 129 | if (bytes == 0) | ||
| 130 | { | ||
| 131 | - ssize_t buflen = HTTP_MAX_BUFFER - (ssize_t)((z_stream *)http->stream)->avail_in; | ||
| 132 | + ssize_t buflen = _HTTP_MAX_BUFFER - (ssize_t)((z_stream *)http->stream)->avail_in; | ||
| 133 | /* Additional bytes for buffer */ | ||
| 134 | |||
| 135 | if (buflen > 0) | ||
| 136 | @@ -2754,7 +2743,7 @@ int /* O - 1 to continue, 0 to stop */ | ||
| 137 | _httpUpdate(http_t *http, /* I - HTTP connection */ | ||
| 138 | http_status_t *status) /* O - Current HTTP status */ | ||
| 139 | { | ||
| 140 | - char line[32768], /* Line from connection... */ | ||
| 141 | + char line[_HTTP_MAX_BUFFER], /* Line from connection... */ | ||
| 142 | *value; /* Pointer to value on line */ | ||
| 143 | http_field_t field; /* Field index */ | ||
| 144 | int major, minor; /* HTTP version numbers */ | ||
| 145 | @@ -2762,12 +2751,46 @@ _httpUpdate(http_t *http, /* I - HTTP connection */ | ||
| 146 | |||
| 147 | DEBUG_printf(("_httpUpdate(http=%p, status=%p), state=%s", (void *)http, (void *)status, httpStateString(http->state))); | ||
| 148 | |||
| 149 | + /* When doing non-blocking I/O, make sure we have a whole line... */ | ||
| 150 | + if (!http->blocking) | ||
| 151 | + { | ||
| 152 | + ssize_t bytes; /* Bytes "peeked" from connection */ | ||
| 153 | + | ||
| 154 | + /* See whether our read buffer is full... */ | ||
| 155 | + DEBUG_printf(("2_httpUpdate: used=%d", http->used)); | ||
| 156 | + | ||
| 157 | + if (http->used > 0 && !memchr(http->buffer, '\n', (size_t)http->used) && (size_t)http->used < sizeof(http->buffer)) | ||
| 158 | + { | ||
| 159 | + /* No, try filling in more data... */ | ||
| 160 | + if ((bytes = http_read(http, http->buffer + http->used, sizeof(http->buffer) - (size_t)http->used, /*timeout*/0)) > 0) | ||
| 161 | + { | ||
| 162 | + DEBUG_printf(("2_httpUpdate: Read %d bytes.", (int)bytes)); | ||
| 163 | + http->used += (int)bytes; | ||
| 164 | + } | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + /* Peek at the incoming data... */ | ||
| 168 | + if (!http->used || !memchr(http->buffer, '\n', (size_t)http->used)) | ||
| 169 | + { | ||
| 170 | + /* Don't have a full line, tell the reader to try again when there is more data... */ | ||
| 171 | + DEBUG_puts("1_htttpUpdate: No newline in buffer yet."); | ||
| 172 | + if ((size_t)http->used == sizeof(http->buffer)) | ||
| 173 | + *status = HTTP_STATUS_ERROR; | ||
| 174 | + else | ||
| 175 | + *status = HTTP_STATUS_CONTINUE; | ||
| 176 | + return (0); | ||
| 177 | + } | ||
| 178 | + | ||
| 179 | + DEBUG_puts("2_httpUpdate: Found newline in buffer."); | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | /* | ||
| 183 | * Grab a single line from the connection... | ||
| 184 | */ | ||
| 185 | |||
| 186 | if (!httpGets(line, sizeof(line), http)) | ||
| 187 | { | ||
| 188 | + DEBUG_puts("1_httpUpdate: Error reading request line."); | ||
| 189 | *status = HTTP_STATUS_ERROR; | ||
| 190 | return (0); | ||
| 191 | } | ||
| 192 | @@ -4089,7 +4112,8 @@ http_debug_hex(const char *prefix, /* I - Prefix for line */ | ||
| 193 | static ssize_t /* O - Number of bytes read or -1 on error */ | ||
| 194 | http_read(http_t *http, /* I - HTTP connection */ | ||
| 195 | char *buffer, /* I - Buffer */ | ||
| 196 | - size_t length) /* I - Maximum bytes to read */ | ||
| 197 | + size_t length, /* I - Maximum bytes to read */ | ||
| 198 | + int timeout) /* I - Wait timeout */ | ||
| 199 | { | ||
| 200 | ssize_t bytes; /* Bytes read */ | ||
| 201 | |||
| 202 | @@ -4098,7 +4122,7 @@ http_read(http_t *http, /* I - HTTP connection */ | ||
| 203 | |||
| 204 | if (!http->blocking || http->timeout_value > 0.0) | ||
| 205 | { | ||
| 206 | - while (!httpWait(http, http->wait_value)) | ||
| 207 | + while (!_httpWait(http, timeout, 1)) | ||
| 208 | { | ||
| 209 | if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data)) | ||
| 210 | continue; | ||
| 211 | @@ -4201,7 +4225,7 @@ http_read_buffered(http_t *http, /* I - HTTP connection */ | ||
| 212 | else | ||
| 213 | bytes = (ssize_t)length; | ||
| 214 | |||
| 215 | - DEBUG_printf(("8http_read: Grabbing %d bytes from input buffer.", | ||
| 216 | + DEBUG_printf(("8http_read_buffered: Grabbing %d bytes from input buffer.", | ||
| 217 | (int)bytes)); | ||
| 218 | |||
| 219 | memcpy(buffer, http->buffer, (size_t)bytes); | ||
| 220 | @@ -4211,7 +4235,7 @@ http_read_buffered(http_t *http, /* I - HTTP connection */ | ||
| 221 | memmove(http->buffer, http->buffer + bytes, (size_t)http->used); | ||
| 222 | } | ||
| 223 | else | ||
| 224 | - bytes = http_read(http, buffer, length); | ||
| 225 | + bytes = http_read(http, buffer, length, http->wait_value); | ||
| 226 | |||
| 227 | return (bytes); | ||
| 228 | } | ||
| 229 | @@ -4557,15 +4581,15 @@ http_set_timeout(int fd, /* I - File descriptor */ | ||
| 230 | static void | ||
| 231 | http_set_wait(http_t *http) /* I - HTTP connection */ | ||
| 232 | { | ||
| 233 | - if (http->blocking) | ||
| 234 | - { | ||
| 235 | - http->wait_value = (int)(http->timeout_value * 1000); | ||
| 236 | + http->wait_value = (int)(http->timeout_value * 1000); | ||
| 237 | |||
| 238 | - if (http->wait_value <= 0) | ||
| 239 | + if (http->wait_value <= 0) | ||
| 240 | + { | ||
| 241 | + if (http->blocking) | ||
| 242 | http->wait_value = 60000; | ||
| 243 | + else | ||
| 244 | + http->wait_value = 1000; | ||
| 245 | } | ||
| 246 | - else | ||
| 247 | - http->wait_value = 10000; | ||
| 248 | } | ||
| 249 | |||
| 250 | |||
| 251 | diff --git a/cups/tls-openssl.c b/cups/tls-openssl.c | ||
| 252 | index 9fcbe0af3..f746f4cba 100644 | ||
| 253 | --- a/cups/tls-openssl.c | ||
| 254 | +++ b/cups/tls-openssl.c | ||
| 255 | @@ -180,12 +180,14 @@ cupsMakeServerCredentials( | ||
| 256 | // Save them... | ||
| 257 | if ((bio = BIO_new_file(keyfile, "wb")) == NULL) | ||
| 258 | { | ||
| 259 | + DEBUG_printf(("1cupsMakeServerCredentials: Unable to create private key file '%s': %s", keyfile, strerror(errno))); | ||
| 260 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); | ||
| 261 | goto done; | ||
| 262 | } | ||
| 263 | |||
| 264 | if (!PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL)) | ||
| 265 | { | ||
| 266 | + DEBUG_puts("1cupsMakeServerCredentials: PEM_write_bio_PrivateKey failed."); | ||
| 267 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write private key."), 1); | ||
| 268 | BIO_free(bio); | ||
| 269 | goto done; | ||
| 270 | @@ -195,12 +197,14 @@ cupsMakeServerCredentials( | ||
| 271 | |||
| 272 | if ((bio = BIO_new_file(crtfile, "wb")) == NULL) | ||
| 273 | { | ||
| 274 | + DEBUG_printf(("1cupsMakeServerCredentials: Unable to create certificate file '%s': %s", crtfile, strerror(errno))); | ||
| 275 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); | ||
| 276 | goto done; | ||
| 277 | } | ||
| 278 | |||
| 279 | if (!PEM_write_bio_X509(bio, cert)) | ||
| 280 | { | ||
| 281 | + DEBUG_puts("1cupsMakeServerCredentials: PEM_write_bio_X509 failed."); | ||
| 282 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write X.509 certificate."), 1); | ||
| 283 | BIO_free(bio); | ||
| 284 | goto done; | ||
| 285 | @@ -1044,10 +1048,10 @@ _httpTLSStart(http_t *http) // I - Connection to server | ||
| 286 | |||
| 287 | if (!cupsMakeServerCredentials(tls_keypath, cn, 0, NULL, time(NULL) + 365 * 86400)) | ||
| 288 | { | ||
| 289 | - DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed."); | ||
| 290 | + DEBUG_printf(("4_httpTLSStart: cupsMakeServerCredentials failed: %s", cupsLastErrorString())); | ||
| 291 | http->error = errno = EINVAL; | ||
| 292 | http->status = HTTP_STATUS_ERROR; | ||
| 293 | - _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1); | ||
| 294 | +// _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1); | ||
| 295 | SSL_CTX_free(context); | ||
| 296 | |||
| 297 | return (-1); | ||
| 298 | @@ -1272,14 +1276,17 @@ http_bio_read(BIO *h, // I - BIO data | ||
| 299 | |||
| 300 | http = (http_t *)BIO_get_data(h); | ||
| 301 | |||
| 302 | - if (!http->blocking) | ||
| 303 | + if (!http->blocking || http->timeout_value > 0.0) | ||
| 304 | { | ||
| 305 | /* | ||
| 306 | * Make sure we have data before we read... | ||
| 307 | */ | ||
| 308 | |||
| 309 | - if (!_httpWait(http, 10000, 0)) | ||
| 310 | + while (!_httpWait(http, http->wait_value, 0)) | ||
| 311 | { | ||
| 312 | + if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data)) | ||
| 313 | + continue; | ||
| 314 | + | ||
| 315 | #ifdef WIN32 | ||
| 316 | http->error = WSAETIMEDOUT; | ||
| 317 | #else | ||
| 318 | diff --git a/scheduler/client.c b/scheduler/client.c | ||
| 319 | index f0349a6c9..9593c9138 100644 | ||
| 320 | --- a/scheduler/client.c | ||
| 321 | +++ b/scheduler/client.c | ||
| 322 | @@ -34,11 +34,11 @@ | ||
| 323 | |||
| 324 | static int check_if_modified(cupsd_client_t *con, | ||
| 325 | struct stat *filestats); | ||
| 326 | +#ifdef HAVE_TLS | ||
| 327 | +static int check_start_tls(cupsd_client_t *con); | ||
| 328 | +#endif /* HAVE_TLS */ | ||
| 329 | static int compare_clients(cupsd_client_t *a, cupsd_client_t *b, | ||
| 330 | void *data); | ||
| 331 | -#ifdef HAVE_TLS | ||
| 332 | -static int cupsd_start_tls(cupsd_client_t *con, http_encryption_t e); | ||
| 333 | -#endif /* HAVE_TLS */ | ||
| 334 | static char *get_file(cupsd_client_t *con, struct stat *filestats, | ||
| 335 | char *filename, size_t len); | ||
| 336 | static http_status_t install_cupsd_conf(cupsd_client_t *con); | ||
| 337 | @@ -360,14 +360,20 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ | ||
| 338 | if (lis->encryption == HTTP_ENCRYPTION_ALWAYS) | ||
| 339 | { | ||
| 340 | /* | ||
| 341 | - * https connection; go secure... | ||
| 342 | + * HTTPS connection, force TLS negotiation... | ||
| 343 | */ | ||
| 344 | |||
| 345 | - if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS)) | ||
| 346 | - cupsdCloseClient(con); | ||
| 347 | + con->tls_start = time(NULL); | ||
| 348 | + con->encryption = HTTP_ENCRYPTION_ALWAYS; | ||
| 349 | } | ||
| 350 | else | ||
| 351 | + { | ||
| 352 | + /* | ||
| 353 | + * HTTP connection, but check for HTTPS negotiation on first data... | ||
| 354 | + */ | ||
| 355 | + | ||
| 356 | con->auto_ssl = 1; | ||
| 357 | + } | ||
| 358 | #endif /* HAVE_TLS */ | ||
| 359 | } | ||
| 360 | |||
| 361 | @@ -597,17 +603,46 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ | ||
| 362 | |||
| 363 | con->auto_ssl = 0; | ||
| 364 | |||
| 365 | - if (recv(httpGetFd(con->http), buf, 1, MSG_PEEK) == 1 && | ||
| 366 | - (!buf[0] || !strchr("DGHOPT", buf[0]))) | ||
| 367 | + if (recv(httpGetFd(con->http), buf, 5, MSG_PEEK) == 5 && buf[0] == 0x16 && buf[1] == 3 && buf[2]) | ||
| 368 | { | ||
| 369 | /* | ||
| 370 | - * Encrypt this connection... | ||
| 371 | + * Client hello record, encrypt this connection... | ||
| 372 | */ | ||
| 373 | |||
| 374 | - cupsdLogClient(con, CUPSD_LOG_DEBUG2, "Saw first byte %02X, auto-negotiating SSL/TLS session.", buf[0] & 255); | ||
| 375 | + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "Saw client hello record, auto-negotiating TLS session."); | ||
| 376 | + con->tls_start = time(NULL); | ||
| 377 | + con->encryption = HTTP_ENCRYPTION_ALWAYS; | ||
| 378 | + } | ||
| 379 | + } | ||
| 380 | |||
| 381 | - if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS)) | ||
| 382 | - cupsdCloseClient(con); | ||
| 383 | + if (con->tls_start) | ||
| 384 | + { | ||
| 385 | + /* | ||
| 386 | + * Try negotiating TLS... | ||
| 387 | + */ | ||
| 388 | + | ||
| 389 | + int tls_status = check_start_tls(con); | ||
| 390 | + | ||
| 391 | + if (tls_status < 0) | ||
| 392 | + { | ||
| 393 | + /* | ||
| 394 | + * TLS negotiation failed, close the connection. | ||
| 395 | + */ | ||
| 396 | + | ||
| 397 | + cupsdCloseClient(con); | ||
| 398 | + return; | ||
| 399 | + } | ||
| 400 | + else if (tls_status == 0) | ||
| 401 | + { | ||
| 402 | + /* | ||
| 403 | + * Nothing to do yet... | ||
| 404 | + */ | ||
| 405 | + | ||
| 406 | + if ((time(NULL) - con->tls_start) > 5) | ||
| 407 | + { | ||
| 408 | + // Timeout, close the connection... | ||
| 409 | + cupsdCloseClient(con); | ||
| 410 | + } | ||
| 411 | |||
| 412 | return; | ||
| 413 | } | ||
| 414 | @@ -771,9 +806,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ | ||
| 415 | * Parse incoming parameters until the status changes... | ||
| 416 | */ | ||
| 417 | |||
| 418 | - while ((status = httpUpdate(con->http)) == HTTP_STATUS_CONTINUE) | ||
| 419 | - if (!httpGetReady(con->http)) | ||
| 420 | - break; | ||
| 421 | + status = httpUpdate(con->http); | ||
| 422 | |||
| 423 | if (status != HTTP_STATUS_OK && status != HTTP_STATUS_CONTINUE) | ||
| 424 | { | ||
| 425 | @@ -935,11 +968,10 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ | ||
| 426 | return; | ||
| 427 | } | ||
| 428 | |||
| 429 | - if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED)) | ||
| 430 | - { | ||
| 431 | - cupsdCloseClient(con); | ||
| 432 | - return; | ||
| 433 | - } | ||
| 434 | + con->tls_start = time(NULL); | ||
| 435 | + con->tls_upgrade = 1; | ||
| 436 | + con->encryption = HTTP_ENCRYPTION_REQUIRED; | ||
| 437 | + return; | ||
| 438 | #else | ||
| 439 | if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) | ||
| 440 | { | ||
| 441 | @@ -978,32 +1010,11 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ | ||
| 442 | if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), | ||
| 443 | "Upgrade") && !httpIsEncrypted(con->http)) | ||
| 444 | { | ||
| 445 | -#ifdef HAVE_TLS | ||
| 446 | - /* | ||
| 447 | - * Do encryption stuff... | ||
| 448 | - */ | ||
| 449 | - | ||
| 450 | - httpClearFields(con->http); | ||
| 451 | - | ||
| 452 | - if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, | ||
| 453 | - CUPSD_AUTH_NONE)) | ||
| 454 | - { | ||
| 455 | - cupsdCloseClient(con); | ||
| 456 | - return; | ||
| 457 | - } | ||
| 458 | - | ||
| 459 | - if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED)) | ||
| 460 | - { | ||
| 461 | - cupsdCloseClient(con); | ||
| 462 | - return; | ||
| 463 | - } | ||
| 464 | -#else | ||
| 465 | if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) | ||
| 466 | { | ||
| 467 | cupsdCloseClient(con); | ||
| 468 | return; | ||
| 469 | } | ||
| 470 | -#endif /* HAVE_TLS */ | ||
| 471 | } | ||
| 472 | |||
| 473 | if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_STATUS_OK) | ||
| 474 | @@ -2631,6 +2642,69 @@ check_if_modified( | ||
| 475 | } | ||
| 476 | |||
| 477 | |||
| 478 | +#ifdef HAVE_TLS | ||
| 479 | +/* | ||
| 480 | + * 'check_start_tls()' - Start encryption on a connection. | ||
| 481 | + */ | ||
| 482 | + | ||
| 483 | +static int /* O - 0 to continue, 1 on success, -1 on error */ | ||
| 484 | +check_start_tls(cupsd_client_t *con) /* I - Client connection */ | ||
| 485 | +{ | ||
| 486 | + unsigned char chello[4096]; /* Client hello record */ | ||
| 487 | + ssize_t chello_bytes; /* Bytes read/peeked */ | ||
| 488 | + int chello_len; /* Length of record */ | ||
| 489 | + | ||
| 490 | + | ||
| 491 | + /* | ||
| 492 | + * See if we have a good and complete client hello record... | ||
| 493 | + */ | ||
| 494 | + | ||
| 495 | + if ((chello_bytes = recv(httpGetFd(con->http), (char *)chello, sizeof(chello), MSG_PEEK)) < 5) | ||
| 496 | + return (0); /* Not enough bytes (yet) */ | ||
| 497 | + | ||
| 498 | + if (chello[0] != 0x016 || chello[1] != 3 || chello[2] == 0) | ||
| 499 | + return (-1); /* Not a TLS Client Hello record */ | ||
| 500 | + | ||
| 501 | + chello_len = (chello[3] << 8) | chello[4]; | ||
| 502 | + | ||
| 503 | + if ((chello_len + 5) > chello_bytes) | ||
| 504 | + return (0); /* Not enough bytes yet */ | ||
| 505 | + | ||
| 506 | + /* | ||
| 507 | + * OK, we do, try negotiating... | ||
| 508 | + */ | ||
| 509 | + | ||
| 510 | + con->tls_start = 0; | ||
| 511 | + | ||
| 512 | + if (httpEncryption(con->http, con->encryption)) | ||
| 513 | + { | ||
| 514 | + cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to encrypt connection: %s", cupsLastErrorString()); | ||
| 515 | + return (-1); | ||
| 516 | + } | ||
| 517 | + | ||
| 518 | + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Connection now encrypted."); | ||
| 519 | + | ||
| 520 | + if (con->tls_upgrade) | ||
| 521 | + { | ||
| 522 | + // Respond to the original OPTIONS command... | ||
| 523 | + con->tls_upgrade = 0; | ||
| 524 | + | ||
| 525 | + httpClearFields(con->http); | ||
| 526 | + httpClearCookie(con->http); | ||
| 527 | + httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); | ||
| 528 | + | ||
| 529 | + if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE)) | ||
| 530 | + { | ||
| 531 | + cupsdCloseClient(con); | ||
| 532 | + return (-1); | ||
| 533 | + } | ||
| 534 | + } | ||
| 535 | + | ||
| 536 | + return (1); | ||
| 537 | +} | ||
| 538 | +#endif /* HAVE_TLS */ | ||
| 539 | + | ||
| 540 | + | ||
| 541 | /* | ||
| 542 | * 'compare_clients()' - Compare two client connections. | ||
| 543 | */ | ||
| 544 | @@ -2651,28 +2725,6 @@ compare_clients(cupsd_client_t *a, /* I - First client */ | ||
| 545 | } | ||
| 546 | |||
| 547 | |||
| 548 | -#ifdef HAVE_TLS | ||
| 549 | -/* | ||
| 550 | - * 'cupsd_start_tls()' - Start encryption on a connection. | ||
| 551 | - */ | ||
| 552 | - | ||
| 553 | -static int /* O - 0 on success, -1 on error */ | ||
| 554 | -cupsd_start_tls(cupsd_client_t *con, /* I - Client connection */ | ||
| 555 | - http_encryption_t e) /* I - Encryption mode */ | ||
| 556 | -{ | ||
| 557 | - if (httpEncryption(con->http, e)) | ||
| 558 | - { | ||
| 559 | - cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to encrypt connection: %s", | ||
| 560 | - cupsLastErrorString()); | ||
| 561 | - return (-1); | ||
| 562 | - } | ||
| 563 | - | ||
| 564 | - cupsdLogClient(con, CUPSD_LOG_DEBUG, "Connection now encrypted."); | ||
| 565 | - return (0); | ||
| 566 | -} | ||
| 567 | -#endif /* HAVE_TLS */ | ||
| 568 | - | ||
| 569 | - | ||
| 570 | /* | ||
| 571 | * 'get_file()' - Get a filename and state info. | ||
| 572 | */ | ||
| 573 | diff --git a/scheduler/client.h b/scheduler/client.h | ||
| 574 | index 9fe4e2ea6..2939ce997 100644 | ||
| 575 | --- a/scheduler/client.h | ||
| 576 | +++ b/scheduler/client.h | ||
| 577 | @@ -51,6 +51,9 @@ struct cupsd_client_s | ||
| 578 | cups_lang_t *language; /* Language to use */ | ||
| 579 | #ifdef HAVE_TLS | ||
| 580 | int auto_ssl; /* Automatic test for SSL/TLS */ | ||
| 581 | + time_t tls_start; /* Do TLS negotiation? */ | ||
| 582 | + int tls_upgrade; /* Doing TLS upgrade via OPTIONS? */ | ||
| 583 | + http_encryption_t encryption; /* Type of TLS negotiation */ | ||
| 584 | #endif /* HAVE_TLS */ | ||
| 585 | http_addr_t clientaddr; /* Client's server address */ | ||
| 586 | char clientname[256];/* Client's server name for connection */ | ||
| 587 | diff --git a/scheduler/select.c b/scheduler/select.c | ||
| 588 | index 2e64f2a7e..ac6205c51 100644 | ||
| 589 | --- a/scheduler/select.c | ||
| 590 | +++ b/scheduler/select.c | ||
| 591 | @@ -408,6 +408,9 @@ cupsdDoSelect(long timeout) /* I - Timeout in seconds */ | ||
| 592 | |||
| 593 | cupsd_in_select = 1; | ||
| 594 | |||
| 595 | + // Prevent 100% CPU by releasing control before the kevent call... | ||
| 596 | + usleep(1); | ||
| 597 | + | ||
| 598 | if (timeout >= 0 && timeout < 86400) | ||
| 599 | { | ||
| 600 | ktimeout.tv_sec = timeout; | ||
| 601 | @@ -454,6 +457,9 @@ cupsdDoSelect(long timeout) /* I - Timeout in seconds */ | ||
| 602 | struct epoll_event *event; /* Current event */ | ||
| 603 | |||
| 604 | |||
| 605 | + // Prevent 100% CPU by releasing control before the epoll_wait call... | ||
| 606 | + usleep(1); | ||
| 607 | + | ||
| 608 | if (timeout >= 0 && timeout < 86400) | ||
| 609 | nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs, | ||
| 610 | timeout * 1000); | ||
| 611 | @@ -546,6 +552,9 @@ cupsdDoSelect(long timeout) /* I - Timeout in seconds */ | ||
| 612 | } | ||
| 613 | } | ||
| 614 | |||
| 615 | + // Prevent 100% CPU by releasing control before the poll call... | ||
| 616 | + usleep(1); | ||
| 617 | + | ||
| 618 | if (timeout >= 0 && timeout < 86400) | ||
| 619 | nfds = poll(cupsd_pollfds, (nfds_t)count, timeout * 1000); | ||
| 620 | else | ||
| 621 | @@ -599,6 +608,9 @@ cupsdDoSelect(long timeout) /* I - Timeout in seconds */ | ||
| 622 | cupsd_current_input = cupsd_global_input; | ||
| 623 | cupsd_current_output = cupsd_global_output; | ||
| 624 | |||
| 625 | + // Prevent 100% CPU by releasing control before the select call... | ||
| 626 | + usleep(1); | ||
| 627 | + | ||
| 628 | if (timeout >= 0 && timeout < 86400) | ||
| 629 | { | ||
| 630 | stimeout.tv_sec = timeout; | ||
