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