]>
Commit | Line | Data |
---|---|---|
a74b005d | 1 | /* |
6db7190f | 2 | * "$Id: client.c,v 1.91.2.40 2003/01/24 20:45:18 mike Exp $" |
a74b005d | 3 | * |
4 | * Client routines for the Common UNIX Printing System (CUPS) scheduler. | |
5 | * | |
1d9595ab | 6 | * Copyright 1997-2003 by Easy Software Products, all rights reserved. |
a74b005d | 7 | * |
8 | * These coded instructions, statements, and computer programs are the | |
9 | * property of Easy Software Products and are protected by Federal | |
10 | * copyright law. Distribution and use rights are outlined in the file | |
11 | * "LICENSE.txt" which should have been included with this file. If this | |
12 | * file is missing or damaged please contact Easy Software Products | |
13 | * at: | |
14 | * | |
15 | * Attn: CUPS Licensing Information | |
16 | * Easy Software Products | |
8784b6a6 | 17 | * 44141 Airport View Drive, Suite 204 |
a74b005d | 18 | * Hollywood, Maryland 20636-3111 USA |
19 | * | |
20 | * Voice: (301) 373-9603 | |
21 | * EMail: cups-info@cups.org | |
22 | * WWW: http://www.cups.org | |
23 | * | |
24 | * Contents: | |
25 | * | |
93894a43 | 26 | * AcceptClient() - Accept a new client. |
27 | * CloseAllClients() - Close all remote clients immediately. | |
28 | * CloseClient() - Close a remote client. | |
a75c006a | 29 | * EncryptClient() - Enable encryption for the client... |
93894a43 | 30 | * ReadClient() - Read data from a client. |
31 | * SendCommand() - Send output from a command via HTTP. | |
32 | * SendError() - Send an error message via HTTP. | |
33 | * SendFile() - Send a file via HTTP. | |
34 | * SendHeader() - Send an HTTP request. | |
c3c5af5e | 35 | * ShutdownClient() - Shutdown the receiving end of a connection. |
93894a43 | 36 | * WriteClient() - Write data to a client as needed. |
37 | * check_if_modified() - Decode an "If-Modified-Since" line. | |
d2122fde | 38 | * decode_auth() - Decode an authorization string. |
93894a43 | 39 | * get_file() - Get a filename and state info. |
753453e4 | 40 | * install_conf_file() - Install a configuration file. |
93894a43 | 41 | * pipe_command() - Pipe the output of a command to the remote client. |
dcfcaeac | 42 | * CDSAReadFunc() - Read function for CDSA decryption code. |
43 | * CDSAWriteFunc() - Write function for CDSA encryption code. | |
a74b005d | 44 | */ |
45 | ||
46 | /* | |
47 | * Include necessary headers... | |
48 | */ | |
49 | ||
50 | #include "cupsd.h" | |
bd917997 | 51 | #include <grp.h> |
707152b4 | 52 | #include <cups/http-private.h> |
bd917997 | 53 | |
a74b005d | 54 | |
55 | /* | |
56 | * Local functions... | |
57 | */ | |
58 | ||
753453e4 | 59 | static int check_if_modified(client_t *con, |
60 | struct stat *filestats); | |
61 | static void decode_auth(client_t *con); | |
6db7190f | 62 | static char *get_file(client_t *con, struct stat *filestats, |
63 | char *filename, int len); | |
753453e4 | 64 | static http_status_t install_conf_file(client_t *con); |
65 | static int pipe_command(client_t *con, int infile, int *outfile, | |
66 | char *command, char *options); | |
a74b005d | 67 | |
dcfcaeac | 68 | #ifdef HAVE_CDSASSL |
69 | OSStatus CDSAReadFunc(SSLConnectionRef connection, void *data, | |
70 | size_t *dataLength); | |
71 | OSStatus CDSAWriteFunc(SSLConnectionRef connection, | |
72 | const void *data, size_t *dataLength); | |
73 | #endif /* HAVE_CDSASSL */ | |
74 | ||
a74b005d | 75 | |
76 | /* | |
77 | * 'AcceptClient()' - Accept a new client. | |
78 | */ | |
79 | ||
80 | void | |
81 | AcceptClient(listener_t *lis) /* I - Listener socket */ | |
82 | { | |
27eba2dd | 83 | int i; /* Looping var */ |
1060c9b6 | 84 | int count; /* Count of connections on a host */ |
a74b005d | 85 | int val; /* Parameter value */ |
86 | client_t *con; /* New client pointer */ | |
99de6da0 | 87 | const struct hostent *host; /* Host entry for address */ |
18fe941f | 88 | char *hostname;/* Hostname for address */ |
99de6da0 | 89 | http_addr_t temp; /* Temporary address variable */ |
f0ad8cdc | 90 | static time_t last_dos = 0; |
91 | /* Time of last DoS attack */ | |
a74b005d | 92 | |
93 | ||
aa7e125a | 94 | LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d", |
27eba2dd | 95 | lis, lis->fd, NumClients); |
bfa1abf0 | 96 | |
12aa0c1f | 97 | /* |
98 | * Make sure we don't have a full set of clients already... | |
99 | */ | |
100 | ||
101 | if (NumClients == MaxClients) | |
102 | return; | |
103 | ||
a74b005d | 104 | /* |
105 | * Get a pointer to the next available client... | |
106 | */ | |
107 | ||
108 | con = Clients + NumClients; | |
109 | ||
110 | memset(con, 0, sizeof(client_t)); | |
111 | con->http.activity = time(NULL); | |
112 | ||
113 | /* | |
114 | * Accept the client and get the remote address... | |
115 | */ | |
116 | ||
117 | val = sizeof(struct sockaddr_in); | |
118 | ||
2f6d083c | 119 | if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr), |
120 | &val)) < 0) | |
a74b005d | 121 | { |
27eba2dd | 122 | LogMessage(L_ERROR, "Unable to accept client connection - %s.", |
123 | strerror(errno)); | |
a74b005d | 124 | return; |
125 | } | |
126 | ||
99de6da0 | 127 | #ifdef AF_INET6 |
128 | if (lis->address.addr.sa_family == AF_INET6) | |
129 | con->http.hostaddr.ipv6.sin6_port = lis->address.ipv6.sin6_port; | |
130 | else | |
131 | #endif /* AF_INET6 */ | |
132 | con->http.hostaddr.ipv4.sin_port = lis->address.ipv4.sin_port; | |
a74b005d | 133 | |
1060c9b6 | 134 | /* |
135 | * Check the number of clients on the same address... | |
136 | */ | |
137 | ||
138 | for (i = 0, count = 0; i < NumClients; i ++) | |
139 | if (memcmp(&(Clients[i].http.hostaddr), &(con->http.hostaddr), | |
140 | sizeof(con->http.hostaddr)) == 0) | |
141 | { | |
142 | count ++; | |
143 | if (count >= MaxClientsPerHost) | |
144 | break; | |
145 | } | |
146 | ||
147 | if (count >= MaxClientsPerHost) | |
148 | { | |
f0ad8cdc | 149 | if ((time(NULL) - last_dos) >= 60) |
150 | { | |
151 | last_dos = time(NULL); | |
152 | LogMessage(L_WARN, "Possible DoS attack - more than %d clients connecting from %s!", | |
153 | MaxClientsPerHost, Clients[i].http.hostname); | |
154 | } | |
1060c9b6 | 155 | |
156 | #ifdef WIN32 | |
157 | closesocket(con->http.fd); | |
158 | #else | |
159 | close(con->http.fd); | |
160 | #endif /* WIN32 */ | |
161 | ||
162 | return; | |
163 | } | |
164 | ||
165 | /* | |
166 | * Get the hostname or format the IP address as needed... | |
167 | */ | |
168 | ||
a74b005d | 169 | if (HostNameLookups) |
18fe941f | 170 | hostname = httpAddrLookup(&(con->http.hostaddr), con->http.hostname, |
171 | sizeof(con->http.hostname)); | |
a74b005d | 172 | else |
99de6da0 | 173 | { |
18fe941f | 174 | hostname = NULL; |
99de6da0 | 175 | httpAddrString(&(con->http.hostaddr), con->http.hostname, |
176 | sizeof(con->http.hostname)); | |
177 | } | |
a74b005d | 178 | |
06415a96 | 179 | if (httpAddrLocalhost(&(con->http.hostaddr))) |
180 | { | |
181 | /* | |
182 | * Map accesses from the loopback interface to "localhost"... | |
183 | */ | |
184 | ||
185 | strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname)); | |
186 | } | |
187 | else if (httpAddrEqual(&(con->http.hostaddr), &ServerAddr)) | |
2bdd1992 | 188 | { |
189 | /* | |
190 | * Map accesses from the same host to the server name. | |
191 | */ | |
192 | ||
def978d5 | 193 | strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname)); |
2bdd1992 | 194 | } |
27eba2dd | 195 | |
18fe941f | 196 | if (hostname == NULL && HostNameLookups == 2) |
99de6da0 | 197 | { |
198 | /* | |
199 | * Can't have an unresolved IP address with double-lookups enabled... | |
200 | */ | |
27eba2dd | 201 | |
6cd03542 | 202 | LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...", |
203 | con->http.fd); | |
204 | ||
27eba2dd | 205 | #ifdef WIN32 |
99de6da0 | 206 | closesocket(con->http.fd); |
27eba2dd | 207 | #else |
99de6da0 | 208 | close(con->http.fd); |
27eba2dd | 209 | #endif /* WIN32 */ |
210 | ||
99de6da0 | 211 | LogMessage(L_WARN, "Name lookup failed - connection from %s closed!", |
212 | con->http.hostname); | |
213 | return; | |
27eba2dd | 214 | } |
a74b005d | 215 | |
27eba2dd | 216 | if (HostNameLookups == 2) |
217 | { | |
218 | /* | |
219 | * Do double lookups as needed... | |
220 | */ | |
221 | ||
753453e4 | 222 | if ((host = httpGetHostByName(con->http.hostname)) != NULL) |
27eba2dd | 223 | { |
224 | /* | |
99de6da0 | 225 | * See if the hostname maps to the same IP address... |
27eba2dd | 226 | */ |
227 | ||
99de6da0 | 228 | if (host->h_addrtype != con->http.hostaddr.addr.sa_family) |
27eba2dd | 229 | { |
230 | /* | |
99de6da0 | 231 | * Not the right type of address... |
27eba2dd | 232 | */ |
233 | ||
46490d9d | 234 | host = NULL; |
27eba2dd | 235 | } |
236 | else | |
237 | { | |
238 | /* | |
239 | * Compare all of the addresses against this one... | |
240 | */ | |
241 | ||
46490d9d | 242 | for (i = 0; host->h_addr_list[i]; i ++) |
99de6da0 | 243 | { |
244 | httpAddrLoad(host, 0, i, &temp); | |
245 | ||
246 | if (httpAddrEqual(&(con->http.hostaddr), &temp)) | |
27eba2dd | 247 | break; |
99de6da0 | 248 | } |
27eba2dd | 249 | |
46490d9d | 250 | if (!host->h_addr_list[i]) |
251 | host = NULL; | |
27eba2dd | 252 | } |
253 | } | |
254 | ||
46490d9d | 255 | if (host == NULL) |
27eba2dd | 256 | { |
257 | /* | |
258 | * Can't have a hostname that doesn't resolve to the same IP address | |
259 | * with double-lookups enabled... | |
260 | */ | |
261 | ||
6cd03542 | 262 | LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...", |
263 | con->http.fd); | |
264 | ||
27eba2dd | 265 | #ifdef WIN32 |
266 | closesocket(con->http.fd); | |
267 | #else | |
268 | close(con->http.fd); | |
269 | #endif /* WIN32 */ | |
270 | ||
271 | LogMessage(L_WARN, "IP lookup failed - connection from %s closed!", | |
272 | con->http.hostname); | |
273 | return; | |
274 | } | |
275 | } | |
276 | ||
99de6da0 | 277 | #ifdef AF_INET6 |
278 | if (con->http.hostaddr.addr.sa_family == AF_INET6) | |
18fe941f | 279 | LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd, |
99de6da0 | 280 | con->http.hostname, ntohs(con->http.hostaddr.ipv6.sin6_port)); |
281 | else | |
282 | #endif /* AF_INET6 */ | |
18fe941f | 283 | LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd, |
99de6da0 | 284 | con->http.hostname, ntohs(con->http.hostaddr.ipv4.sin_port)); |
a74b005d | 285 | |
9ae34eb7 | 286 | /* |
287 | * Using TCP_NODELAY improves responsiveness, especially on systems | |
288 | * with a slow loopback interface... Since we write large buffers | |
289 | * when sending print files and requests, there shouldn't be any | |
290 | * performance penalty for this... | |
291 | */ | |
292 | ||
293 | val = 1; | |
294 | setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); | |
295 | ||
a74b005d | 296 | /* |
297 | * Add the socket to the select() input mask. | |
298 | */ | |
299 | ||
300 | fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC); | |
301 | ||
18fe941f | 302 | LogMessage(L_DEBUG2, "AcceptClient: Adding fd %d to InputSet...", |
27eba2dd | 303 | con->http.fd); |
a74b005d | 304 | FD_SET(con->http.fd, &InputSet); |
305 | ||
306 | NumClients ++; | |
307 | ||
308 | /* | |
309 | * Temporarily suspend accept()'s until we lose a client... | |
310 | */ | |
311 | ||
997edb40 | 312 | if (NumClients == MaxClients) |
d236cb49 | 313 | PauseListening(); |
2a0ef17a | 314 | |
bcf61448 | 315 | #ifdef HAVE_SSL |
2a0ef17a | 316 | /* |
0b50ddbc | 317 | * See if we are connecting on a secure port... |
2a0ef17a | 318 | */ |
319 | ||
0b50ddbc | 320 | if (lis->encryption == HTTP_ENCRYPT_ALWAYS) |
2a0ef17a | 321 | { |
322 | /* | |
323 | * https connection; go secure... | |
324 | */ | |
325 | ||
b92c5a4f | 326 | con->http.encryption = HTTP_ENCRYPT_ALWAYS; |
327 | ||
2a0ef17a | 328 | EncryptClient(con); |
329 | } | |
bcf61448 | 330 | #endif /* HAVE_SSL */ |
a74b005d | 331 | } |
332 | ||
333 | ||
334 | /* | |
335 | * 'CloseAllClients()' - Close all remote clients immediately. | |
336 | */ | |
337 | ||
338 | void | |
339 | CloseAllClients(void) | |
340 | { | |
341 | while (NumClients > 0) | |
342 | CloseClient(Clients); | |
343 | } | |
344 | ||
345 | ||
346 | /* | |
347 | * 'CloseClient()' - Close a remote client. | |
348 | */ | |
349 | ||
350 | void | |
351 | CloseClient(client_t *con) /* I - Client to close */ | |
352 | { | |
c3c5af5e | 353 | int status; /* Exit status of pipe command */ |
dcfcaeac | 354 | #if defined(HAVE_LIBSSL) |
2a0ef17a | 355 | SSL_CTX *context; /* Context for encryption */ |
356 | SSL *conn; /* Connection for encryption */ | |
c3c5af5e | 357 | unsigned long error; /* Error code */ |
dcfcaeac | 358 | #elif defined(HAVE_GNUTLS) |
359 | http_tls_t *conn; /* TLS connection information */ | |
bcf61448 | 360 | int error; /* Error code */ |
dcfcaeac | 361 | gnutls_certificate_server_credentials *credentials; |
362 | /* TLS credentials */ | |
bcf61448 | 363 | #endif /* HAVE_GNUTLS */ |
a74b005d | 364 | |
365 | ||
5ea8888e | 366 | LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd); |
a74b005d | 367 | |
bcf61448 | 368 | #ifdef HAVE_SSL |
2a0ef17a | 369 | /* |
370 | * Shutdown encryption as needed... | |
371 | */ | |
372 | ||
373 | if (con->http.tls) | |
374 | { | |
dcfcaeac | 375 | # ifdef HAVE_LIBSSL |
2a0ef17a | 376 | conn = (SSL *)(con->http.tls); |
377 | context = SSL_get_SSL_CTX(conn); | |
378 | ||
c3c5af5e | 379 | switch (SSL_shutdown(conn)) |
380 | { | |
381 | case 1 : | |
382 | LogMessage(L_INFO, "CloseClient: SSL shutdown successful!"); | |
383 | break; | |
384 | case -1 : | |
385 | LogMessage(L_ERROR, "CloseClient: Fatal error during SSL shutdown!"); | |
386 | default : | |
387 | while ((error = ERR_get_error()) != 0) | |
388 | LogMessage(L_ERROR, "CloseClient: %s", ERR_error_string(error, NULL)); | |
389 | break; | |
390 | } | |
391 | ||
2a0ef17a | 392 | SSL_CTX_free(context); |
393 | SSL_free(conn); | |
dcfcaeac | 394 | |
395 | # elif defined(HAVE_GNUTLS) | |
bcf61448 | 396 | conn = (http_tls_t *)(con->http.tls); |
397 | credentials = (gnutls_certificate_server_credentials *)(conn->credentials); | |
398 | ||
399 | error = gnutls_bye(conn->session, GNUTLS_SHUT_WR); | |
400 | switch (error) | |
401 | { | |
402 | case GNUTLS_E_SUCCESS: | |
403 | LogMessage(L_INFO, "CloseClient: SSL shutdown successful!"); | |
404 | break; | |
405 | default: | |
406 | LogMessage(L_ERROR, "CloseClient: %s", gnutls_strerror(error)); | |
407 | break; | |
408 | } | |
409 | ||
410 | gnutls_deinit(conn->session); | |
411 | gnutls_certificate_free_credentials(*credentials); | |
412 | free(credentials); | |
413 | free(conn); | |
dcfcaeac | 414 | |
415 | # elif defined(HAVE_CDSASSL) | |
416 | status = SSLClose((SSLContextRef)con->http.tls); | |
417 | SSLDisposeContext((SSLContextRef)con->http.tls); | |
418 | # endif /* HAVE_LIBSSL */ | |
2a0ef17a | 419 | |
420 | con->http.tls = NULL; | |
421 | } | |
dcfcaeac | 422 | #endif /* HAVE_SSL */ |
2a0ef17a | 423 | |
a74b005d | 424 | /* |
425 | * Close the socket and clear the file from the input set for select()... | |
426 | */ | |
427 | ||
6cd03542 | 428 | if (con->http.fd >= 0) |
882031b3 | 429 | { |
e668e627 | 430 | LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...", |
27eba2dd | 431 | con->http.fd); |
882031b3 | 432 | close(con->http.fd); |
433 | FD_CLR(con->http.fd, &InputSet); | |
434 | FD_CLR(con->http.fd, &OutputSet); | |
435 | con->http.fd = 0; | |
436 | } | |
a74b005d | 437 | |
a74b005d | 438 | if (con->pipe_pid != 0) |
bfa1abf0 | 439 | { |
27eba2dd | 440 | LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...", |
441 | con->file); | |
a74b005d | 442 | FD_CLR(con->file, &InputSet); |
bfa1abf0 | 443 | } |
444 | ||
bfa1abf0 | 445 | if (con->file) |
a74b005d | 446 | { |
26bb6ac6 | 447 | /* |
448 | * Close the open data file... | |
449 | */ | |
450 | ||
a74b005d | 451 | if (con->pipe_pid) |
452 | { | |
453 | kill(con->pipe_pid, SIGKILL); | |
454 | waitpid(con->pipe_pid, &status, WNOHANG); | |
455 | } | |
456 | ||
bd917997 | 457 | LogMessage(L_DEBUG2, "CloseClient() %d Closing data file %d.", |
458 | con->http.fd, con->file); | |
e668e627 | 459 | LogMessage(L_DEBUG2, "CloseClient() %d Removing fd %d from InputSet.", |
460 | con->http.fd, con->file); | |
bd917997 | 461 | |
882031b3 | 462 | FD_CLR(con->file, &InputSet); |
a74b005d | 463 | close(con->file); |
bfa1abf0 | 464 | con->file = 0; |
a74b005d | 465 | } |
466 | ||
25135af7 | 467 | if (con->request) |
468 | { | |
469 | ippDelete(con->request); | |
470 | con->request = NULL; | |
471 | } | |
472 | ||
473 | if (con->response) | |
474 | { | |
475 | ippDelete(con->response); | |
476 | con->response = NULL; | |
477 | } | |
478 | ||
479 | if (con->language) | |
480 | { | |
481 | cupsLangFree(con->language); | |
482 | con->language = NULL; | |
483 | } | |
484 | ||
26bb6ac6 | 485 | /* |
486 | * Re-enable new client connections if we are going back under the | |
487 | * limit... | |
488 | */ | |
489 | ||
490 | if (NumClients == MaxClients) | |
d236cb49 | 491 | ResumeListening(); |
26bb6ac6 | 492 | |
a74b005d | 493 | /* |
494 | * Compact the list of clients as necessary... | |
495 | */ | |
496 | ||
497 | NumClients --; | |
498 | ||
499 | if (con < (Clients + NumClients)) | |
500 | memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t)); | |
501 | } | |
502 | ||
503 | ||
a75c006a | 504 | /* |
505 | * 'EncryptClient()' - Enable encryption for the client... | |
506 | */ | |
507 | ||
508 | int /* O - 1 on success, 0 on error */ | |
509 | EncryptClient(client_t *con) /* I - Client to encrypt */ | |
510 | { | |
bcf61448 | 511 | #if defined HAVE_LIBSSL |
a75c006a | 512 | SSL_CTX *context; /* Context for encryption */ |
513 | SSL *conn; /* Connection for encryption */ | |
753453e4 | 514 | unsigned long error; /* Error code */ |
1c6682dd | 515 | |
516 | ||
517 | /* | |
518 | * Create the SSL context and accept the connection... | |
519 | */ | |
520 | ||
bcf61448 | 521 | context = SSL_CTX_new(SSLv23_server_method()); |
522 | ||
523 | SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM); | |
524 | SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM); | |
a75c006a | 525 | |
bcf61448 | 526 | conn = SSL_new(context); |
a75c006a | 527 | |
528 | SSL_set_fd(conn, con->http.fd); | |
529 | if (SSL_accept(conn) != 1) | |
530 | { | |
1c6682dd | 531 | while ((error = ERR_get_error()) != 0) |
532 | LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL)); | |
533 | ||
a75c006a | 534 | SSL_CTX_free(context); |
535 | SSL_free(conn); | |
536 | return (0); | |
537 | } | |
538 | ||
bcf61448 | 539 | con->http.tls = conn; |
540 | return (1); | |
dcfcaeac | 541 | |
542 | #elif defined(HAVE_GNUTLS) | |
bcf61448 | 543 | http_tls_t *conn; /* TLS session object */ |
544 | int error; /* Error code */ | |
dcfcaeac | 545 | gnutls_certificate_server_credentials *credentials; |
546 | /* TLS credentials */ | |
bcf61448 | 547 | |
548 | /* | |
549 | * Create the SSL object and perform the SSL handshake... | |
550 | */ | |
551 | ||
552 | conn = (http_tls_t *)malloc(sizeof(gnutls_session)); | |
dcfcaeac | 553 | |
bcf61448 | 554 | if (conn == NULL) |
555 | return (0); | |
dcfcaeac | 556 | |
bcf61448 | 557 | credentials = (gnutls_certificate_server_credentials *) |
dcfcaeac | 558 | malloc(sizeof(gnutls_certificate_server_credentials)); |
bcf61448 | 559 | if (credentials == NULL) |
560 | { | |
561 | free(conn); | |
562 | return (0); | |
563 | } | |
564 | ||
565 | gnutls_certificate_allocate_credentials(credentials); | |
566 | gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate, | |
567 | ServerKey, GNUTLS_X509_FMT_PEM); | |
568 | ||
569 | gnutls_init(&(conn->session), GNUTLS_SERVER); | |
570 | gnutls_set_default_priority(conn->session); | |
571 | gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials); | |
572 | gnutls_transport_set_ptr(conn->session, con->http.fd); | |
573 | ||
574 | error = gnutls_handshake(conn->session); | |
575 | ||
576 | if (error != GNUTLS_E_SUCCESS) | |
577 | { | |
578 | LogMessage(L_ERROR, "EncryptClient: %s", gnutls_strerror(error)); | |
579 | gnutls_deinit(conn->session); | |
580 | gnutls_certificate_free_credentials(*credentials); | |
581 | free(conn); | |
582 | free(credentials); | |
583 | return (0); | |
584 | } | |
585 | ||
586 | LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.", | |
587 | con->http.fd); | |
588 | ||
589 | conn->credentials = credentials; | |
a75c006a | 590 | con->http.tls = conn; |
591 | return (1); | |
dcfcaeac | 592 | |
593 | #elif defined(HAVE_CDSASSL) | |
594 | OSStatus error; /* Error info */ | |
595 | SSLContextRef conn; /* New connection */ | |
596 | SSLProtocol tryVersion; /* Protocol version */ | |
597 | const char *hostName; /* Local hostname */ | |
598 | int allowExpired; /* Allow expired certificates? */ | |
599 | int allowAnyRoot; /* Allow any root certificate? */ | |
600 | SSLProtocol *negVersion; /* Negotiated protocol version */ | |
601 | SSLCipherSuite *negCipher; /* Negotiated cypher */ | |
602 | CFArrayRef *peerCerts; /* Certificates */ | |
603 | ||
604 | ||
605 | conn = NULL; | |
606 | error = SSLNewContext(true, &conn); | |
607 | allowExpired = 1; | |
608 | allowAnyRoot = 1; | |
609 | ||
610 | if (!error) | |
611 | error = SSLSetIOFuncs(conn, CDSAReadFunc, CDSAWriteFunc); | |
612 | ||
613 | if (!error) | |
614 | error = SSLSetProtocolVersion(conn, kSSLProtocol3); | |
615 | ||
616 | if (!error) | |
617 | error = SSLSetConnection(conn, (SSLConnectionRef)con->http.fd); | |
618 | ||
619 | if (!error) | |
620 | { | |
621 | hostName = ServerName; /* MRS: ??? */ | |
622 | error = SSLSetPeerDomainName(conn, hostName, strlen(hostName) + 1); | |
623 | } | |
624 | ||
625 | /* have to do these options befor setting server certs */ | |
626 | if (!error && allowExpired) | |
627 | error = SSLSetAllowsExpiredCerts(conn, true); | |
628 | ||
629 | if (!error && allowAnyRoot) | |
630 | error = SSLSetAllowsAnyRoot(conn, true); | |
631 | ||
632 | if (!error && ServerCertificatesArray != NULL) | |
633 | error = SSLSetCertificate(conn, ServerCertificatesArray); | |
634 | ||
635 | /* | |
636 | * Perform SSL/TLS handshake | |
637 | */ | |
638 | ||
639 | do | |
640 | { | |
641 | error = SSLHandshake(conn); | |
642 | } | |
643 | while (error == errSSLWouldBlock); | |
644 | ||
645 | if (error) | |
646 | { | |
647 | LogMessage(L_ERROR, "EncryptClient: %d", error); | |
648 | ||
649 | con->http.error = error; | |
650 | con->http.status = HTTP_ERROR; | |
651 | ||
652 | if (conn != NULL) | |
653 | SSLDisposeContext(conn); | |
654 | ||
655 | return (0); | |
656 | } | |
657 | ||
658 | LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.", | |
659 | con->http.fd); | |
660 | con->http.tls = conn; | |
661 | return (1); | |
662 | ||
a75c006a | 663 | #else |
664 | return (0); | |
bcf61448 | 665 | #endif /* HAVE_GNUTLS */ |
a75c006a | 666 | } |
667 | ||
668 | ||
a74b005d | 669 | /* |
670 | * 'ReadClient()' - Read data from a client. | |
671 | */ | |
672 | ||
673 | int /* O - 1 on success, 0 on error */ | |
674 | ReadClient(client_t *con) /* I - Client to read from */ | |
675 | { | |
753453e4 | 676 | char line[32768], /* Line from client... */ |
a74b005d | 677 | operation[64], /* Operation code from socket */ |
678 | version[64]; /* HTTP version number string */ | |
679 | int major, minor; /* HTTP version numbers */ | |
680 | http_status_t status; /* Transfer status */ | |
bd176c9c | 681 | ipp_state_t ipp_state; /* State of IPP transfer */ |
a74b005d | 682 | int bytes; /* Number of bytes to POST */ |
e31bfb6e | 683 | char *filename; /* Name of file for GET/HEAD */ |
6db7190f | 684 | char buf[1024]; /* Buffer for real filename */ |
a74b005d | 685 | struct stat filestats; /* File information */ |
e31bfb6e | 686 | mime_type_t *type; /* MIME type of file */ |
2a8fc30c | 687 | printer_t *p; /* Printer */ |
8f85ff77 | 688 | location_t *best; /* Best match for authentication */ |
5d99df62 | 689 | static unsigned request_id = 0;/* Request ID for temp files */ |
a74b005d | 690 | |
bfa1abf0 | 691 | |
a74b005d | 692 | status = HTTP_CONTINUE; |
693 | ||
2d5e06dd | 694 | LogMessage(L_DEBUG2, "ReadClient() %d, used=%d", con->http.fd, |
695 | con->http.used); | |
55809c6f | 696 | |
901b295d | 697 | if (con->http.error) |
698 | { | |
699 | CloseClient(con); | |
700 | return (0); | |
701 | } | |
702 | ||
a74b005d | 703 | switch (con->http.state) |
704 | { | |
705 | case HTTP_WAITING : | |
706 | /* | |
707 | * See if we've received a request line... | |
708 | */ | |
709 | ||
710 | if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL) | |
93894a43 | 711 | { |
712 | CloseClient(con); | |
713 | return (0); | |
714 | } | |
a74b005d | 715 | |
716 | /* | |
717 | * Ignore blank request lines... | |
718 | */ | |
719 | ||
720 | if (line[0] == '\0') | |
721 | break; | |
722 | ||
723 | /* | |
724 | * Clear other state variables... | |
725 | */ | |
726 | ||
727 | httpClearFields(HTTP(con)); | |
728 | ||
729 | con->http.activity = time(NULL); | |
730 | con->http.version = HTTP_1_0; | |
731 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
732 | con->http.data_encoding = HTTP_ENCODE_LENGTH; | |
733 | con->http.data_remaining = 0; | |
6a0c519d | 734 | con->operation = HTTP_WAITING; |
735 | con->bytes = 0; | |
a74b005d | 736 | con->file = 0; |
737 | con->pipe_pid = 0; | |
f63a2256 | 738 | con->command[0] = '\0'; |
a74b005d | 739 | con->username[0] = '\0'; |
740 | con->password[0] = '\0'; | |
741 | con->uri[0] = '\0'; | |
742 | ||
743 | if (con->language != NULL) | |
744 | { | |
745 | cupsLangFree(con->language); | |
746 | con->language = NULL; | |
747 | } | |
748 | ||
749 | /* | |
750 | * Grab the request line... | |
751 | */ | |
752 | ||
970017a4 | 753 | switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version)) |
a74b005d | 754 | { |
755 | case 1 : | |
434ddc80 | 756 | LogMessage(L_ERROR, "Bad request line \"%s\"!", line); |
a74b005d | 757 | SendError(con, HTTP_BAD_REQUEST); |
c3c5af5e | 758 | ShutdownClient(con); |
2d5e06dd | 759 | return (1); |
a74b005d | 760 | case 2 : |
761 | con->http.version = HTTP_0_9; | |
762 | break; | |
763 | case 3 : | |
764 | if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2) | |
765 | { | |
434ddc80 | 766 | LogMessage(L_ERROR, "Bad request line \"%s\"!", line); |
a74b005d | 767 | SendError(con, HTTP_BAD_REQUEST); |
c3c5af5e | 768 | ShutdownClient(con); |
2d5e06dd | 769 | return (1); |
a74b005d | 770 | } |
771 | ||
772 | if (major < 2) | |
773 | { | |
774 | con->http.version = (http_version_t)(major * 100 + minor); | |
753453e4 | 775 | if (con->http.version == HTTP_1_1 && KeepAlive) |
a74b005d | 776 | con->http.keep_alive = HTTP_KEEPALIVE_ON; |
777 | else | |
778 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
779 | } | |
780 | else | |
781 | { | |
782 | SendError(con, HTTP_NOT_SUPPORTED); | |
c3c5af5e | 783 | ShutdownClient(con); |
2d5e06dd | 784 | return (1); |
a74b005d | 785 | } |
786 | break; | |
787 | } | |
788 | ||
f726e5b9 | 789 | /* |
790 | * Handle full URLs in the request line... | |
791 | */ | |
792 | ||
793 | if (con->uri[0] != '/') | |
794 | { | |
795 | char method[HTTP_MAX_URI], /* Method/scheme */ | |
796 | userpass[HTTP_MAX_URI], /* Username:password */ | |
797 | hostname[HTTP_MAX_URI], /* Hostname */ | |
798 | resource[HTTP_MAX_URI]; /* Resource path */ | |
799 | int port; /* Port number */ | |
800 | ||
801 | ||
802 | /* | |
803 | * Separate the URI into its components... | |
804 | */ | |
805 | ||
806 | httpSeparate(con->uri, method, userpass, hostname, &port, resource); | |
807 | ||
808 | /* | |
809 | * Only allow URIs with the servername, localhost, or an IP | |
810 | * address... | |
811 | */ | |
812 | ||
813 | if (strcasecmp(hostname, ServerName) && | |
814 | strcasecmp(hostname, "localhost") && | |
815 | !isdigit(hostname[0])) | |
816 | { | |
817 | /* | |
818 | * Nope, we don't do proxies... | |
819 | */ | |
820 | ||
821 | LogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri); | |
822 | SendError(con, HTTP_METHOD_NOT_ALLOWED); | |
823 | ShutdownClient(con); | |
824 | return (1); | |
825 | } | |
826 | ||
827 | /* | |
828 | * Copy the resource portion back into the URI; both resource and | |
829 | * con->uri are HTTP_MAX_URI bytes in size... | |
830 | */ | |
831 | ||
832 | strcpy(con->uri, resource); | |
833 | } | |
834 | ||
a74b005d | 835 | /* |
836 | * Process the request... | |
837 | */ | |
838 | ||
839 | if (strcmp(operation, "GET") == 0) | |
840 | con->http.state = HTTP_GET; | |
841 | else if (strcmp(operation, "PUT") == 0) | |
842 | con->http.state = HTTP_PUT; | |
843 | else if (strcmp(operation, "POST") == 0) | |
844 | con->http.state = HTTP_POST; | |
845 | else if (strcmp(operation, "DELETE") == 0) | |
846 | con->http.state = HTTP_DELETE; | |
847 | else if (strcmp(operation, "TRACE") == 0) | |
848 | con->http.state = HTTP_TRACE; | |
a74b005d | 849 | else if (strcmp(operation, "OPTIONS") == 0) |
850 | con->http.state = HTTP_OPTIONS; | |
851 | else if (strcmp(operation, "HEAD") == 0) | |
852 | con->http.state = HTTP_HEAD; | |
853 | else | |
854 | { | |
434ddc80 | 855 | LogMessage(L_ERROR, "Bad operation \"%s\"!", operation); |
a74b005d | 856 | SendError(con, HTTP_BAD_REQUEST); |
c3c5af5e | 857 | ShutdownClient(con); |
2d5e06dd | 858 | return (1); |
a74b005d | 859 | } |
860 | ||
6a0c519d | 861 | con->start = time(NULL); |
862 | con->operation = con->http.state; | |
863 | ||
5ea8888e | 864 | LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd, |
4a64fdb7 | 865 | operation, con->uri, |
866 | con->http.version / 100, con->http.version % 100); | |
a74b005d | 867 | |
868 | con->http.status = HTTP_OK; | |
a74b005d | 869 | |
8f85ff77 | 870 | case HTTP_OPTIONS : |
a74b005d | 871 | case HTTP_DELETE : |
872 | case HTTP_GET : | |
873 | case HTTP_HEAD : | |
874 | case HTTP_POST : | |
875 | case HTTP_PUT : | |
876 | case HTTP_TRACE : | |
877 | /* | |
878 | * Parse incoming parameters until the status changes... | |
879 | */ | |
880 | ||
881 | status = httpUpdate(HTTP(con)); | |
882 | ||
883 | if (status != HTTP_OK && status != HTTP_CONTINUE) | |
884 | { | |
885 | SendError(con, HTTP_BAD_REQUEST); | |
c3c5af5e | 886 | ShutdownClient(con); |
2d5e06dd | 887 | return (1); |
a74b005d | 888 | } |
889 | break; | |
d21a7597 | 890 | |
891 | default : | |
892 | break; /* Anti-compiler-warning-code */ | |
a74b005d | 893 | } |
894 | ||
895 | /* | |
896 | * Handle new transfers... | |
897 | */ | |
898 | ||
899 | if (status == HTTP_OK) | |
900 | { | |
901 | con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]); | |
902 | ||
d2122fde | 903 | decode_auth(con); |
a74b005d | 904 | |
753453e4 | 905 | if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0 && |
906 | KeepAlive) | |
b1ac1113 | 907 | con->http.keep_alive = HTTP_KEEPALIVE_ON; |
908 | ||
a74b005d | 909 | if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' && |
d558743f | 910 | con->http.version >= HTTP_1_1) |
a74b005d | 911 | { |
d558743f | 912 | /* |
913 | * HTTP/1.1 and higher require the "Host:" field... | |
914 | */ | |
915 | ||
a74b005d | 916 | if (!SendError(con, HTTP_BAD_REQUEST)) |
917 | { | |
918 | CloseClient(con); | |
919 | return (0); | |
920 | } | |
921 | } | |
8f85ff77 | 922 | else if (con->operation == HTTP_OPTIONS) |
923 | { | |
924 | /* | |
925 | * Do OPTIONS command... | |
926 | */ | |
927 | ||
753453e4 | 928 | if ((best = FindBest(con->uri, con->http.state)) != NULL && |
8f85ff77 | 929 | best->type != AUTH_NONE) |
930 | { | |
931 | if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL)) | |
932 | { | |
933 | CloseClient(con); | |
934 | return (0); | |
935 | } | |
936 | } | |
a75c006a | 937 | |
b5cb0608 | 938 | if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 && |
a75c006a | 939 | con->http.tls == NULL) |
8f85ff77 | 940 | { |
bcf61448 | 941 | #ifdef HAVE_SSL |
a75c006a | 942 | /* |
943 | * Do encryption stuff... | |
944 | */ | |
945 | ||
b38fb7fc | 946 | if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL)) |
8f85ff77 | 947 | { |
948 | CloseClient(con); | |
949 | return (0); | |
950 | } | |
a75c006a | 951 | |
952 | httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); | |
b5cb0608 | 953 | httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); |
1c6682dd | 954 | httpPrintf(HTTP(con), "Content-Length: 0\r\n"); |
a75c006a | 955 | httpPrintf(HTTP(con), "\r\n"); |
956 | ||
957 | EncryptClient(con); | |
958 | #else | |
959 | if (!SendError(con, HTTP_NOT_IMPLEMENTED)) | |
960 | { | |
961 | CloseClient(con); | |
962 | return (0); | |
963 | } | |
bcf61448 | 964 | #endif /* HAVE_SSL */ |
a75c006a | 965 | } |
966 | ||
65c098da | 967 | if (con->http.expect) |
968 | { | |
969 | /**** TODO: send expected header ****/ | |
970 | } | |
971 | ||
a75c006a | 972 | if (!SendHeader(con, HTTP_OK, NULL)) |
973 | { | |
974 | CloseClient(con); | |
975 | return (0); | |
8f85ff77 | 976 | } |
977 | ||
753453e4 | 978 | httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n"); |
1c6682dd | 979 | httpPrintf(HTTP(con), "Content-Length: 0\r\n"); |
8f85ff77 | 980 | httpPrintf(HTTP(con), "\r\n"); |
981 | } | |
bd84e0d1 | 982 | else if (strstr(con->uri, "..") != NULL) |
a74b005d | 983 | { |
984 | /* | |
985 | * Protect against malicious users! | |
986 | */ | |
987 | ||
988 | if (!SendError(con, HTTP_FORBIDDEN)) | |
989 | { | |
990 | CloseClient(con); | |
991 | return (0); | |
992 | } | |
993 | } | |
b38fb7fc | 994 | else |
a74b005d | 995 | { |
b5cb0608 | 996 | if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 && |
997 | con->http.tls == NULL) | |
b38fb7fc | 998 | { |
bcf61448 | 999 | #ifdef HAVE_SSL |
b38fb7fc | 1000 | /* |
1001 | * Do encryption stuff... | |
1002 | */ | |
f3d580b9 | 1003 | |
b38fb7fc | 1004 | if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL)) |
1005 | { | |
1006 | CloseClient(con); | |
1007 | return (0); | |
1008 | } | |
f969a074 | 1009 | |
b38fb7fc | 1010 | httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); |
b5cb0608 | 1011 | httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); |
1c6682dd | 1012 | httpPrintf(HTTP(con), "Content-Length: 0\r\n"); |
b38fb7fc | 1013 | httpPrintf(HTTP(con), "\r\n"); |
1014 | ||
1015 | EncryptClient(con); | |
77425121 | 1016 | |
1017 | status = IsAuthorized(con); | |
b38fb7fc | 1018 | #else |
1019 | if (!SendError(con, HTTP_NOT_IMPLEMENTED)) | |
1020 | { | |
1021 | CloseClient(con); | |
1022 | return (0); | |
1023 | } | |
bcf61448 | 1024 | #endif /* HAVE_SSL */ |
b38fb7fc | 1025 | } |
b5cb0608 | 1026 | |
77425121 | 1027 | if (status != HTTP_OK) |
b38fb7fc | 1028 | { |
2d5e06dd | 1029 | LogMessage(L_DEBUG2, "ReadClient: Unauthorized request for %s...\n", |
1030 | con->uri); | |
b38fb7fc | 1031 | SendError(con, status); |
c3c5af5e | 1032 | ShutdownClient(con); |
2d5e06dd | 1033 | return (1); |
b38fb7fc | 1034 | } |
1035 | ||
65c098da | 1036 | if (con->http.expect) |
1037 | { | |
1038 | /**** TODO: send expected header ****/ | |
1039 | } | |
1040 | ||
b38fb7fc | 1041 | switch (con->http.state) |
1042 | { | |
1043 | case HTTP_GET_SEND : | |
1044 | if (strncmp(con->uri, "/printers/", 10) == 0 && | |
1045 | strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0) | |
2a8fc30c | 1046 | { |
b38fb7fc | 1047 | /* |
1048 | * Send PPD file - get the real printer name since printer | |
1049 | * names are not case sensitive but filenames can be... | |
1050 | */ | |
2a8fc30c | 1051 | |
b38fb7fc | 1052 | con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ |
f3d580b9 | 1053 | |
b38fb7fc | 1054 | if ((p = FindPrinter(con->uri + 10)) != NULL) |
1055 | snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); | |
1056 | else | |
1057 | { | |
1058 | if (!SendError(con, HTTP_NOT_FOUND)) | |
1059 | { | |
1060 | CloseClient(con); | |
1061 | return (0); | |
1062 | } | |
a74b005d | 1063 | |
b38fb7fc | 1064 | break; |
1065 | } | |
b14d90ba | 1066 | } |
b38fb7fc | 1067 | |
753453e4 | 1068 | if ((strncmp(con->uri, "/admin", 6) == 0 && |
1069 | strncmp(con->uri, "/admin/conf/", 12) != 0) || | |
b38fb7fc | 1070 | strncmp(con->uri, "/printers", 9) == 0 || |
1071 | strncmp(con->uri, "/classes", 8) == 0 || | |
1072 | strncmp(con->uri, "/jobs", 5) == 0) | |
b14d90ba | 1073 | { |
b38fb7fc | 1074 | /* |
1075 | * Send CGI output... | |
1076 | */ | |
1077 | ||
1078 | if (strncmp(con->uri, "/admin", 6) == 0) | |
1079 | { | |
1080 | snprintf(con->command, sizeof(con->command), | |
1081 | "%s/cgi-bin/admin.cgi", ServerBin); | |
1082 | con->options = con->uri + 6; | |
1083 | } | |
1084 | else if (strncmp(con->uri, "/printers", 9) == 0) | |
1085 | { | |
1086 | snprintf(con->command, sizeof(con->command), | |
1087 | "%s/cgi-bin/printers.cgi", ServerBin); | |
1088 | con->options = con->uri + 9; | |
1089 | } | |
1090 | else if (strncmp(con->uri, "/classes", 8) == 0) | |
1091 | { | |
1092 | snprintf(con->command, sizeof(con->command), | |
1093 | "%s/cgi-bin/classes.cgi", ServerBin); | |
1094 | con->options = con->uri + 8; | |
1095 | } | |
1096 | else | |
1097 | { | |
1098 | snprintf(con->command, sizeof(con->command), | |
1099 | "%s/cgi-bin/jobs.cgi", ServerBin); | |
1100 | con->options = con->uri + 5; | |
1101 | } | |
1102 | ||
1103 | if (con->options[0] == '/') | |
1104 | con->options ++; | |
1105 | ||
1106 | if (!SendCommand(con, con->command, con->options)) | |
1107 | { | |
1108 | if (!SendError(con, HTTP_NOT_FOUND)) | |
1109 | { | |
1110 | CloseClient(con); | |
1111 | return (0); | |
1112 | } | |
1113 | } | |
1114 | else | |
1115 | LogRequest(con, HTTP_OK); | |
1116 | ||
1117 | if (con->http.version <= HTTP_1_0) | |
1118 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
b14d90ba | 1119 | } |
753453e4 | 1120 | else if (strncmp(con->uri, "/admin/conf/", 12) == 0 && |
1121 | (strchr(con->uri + 12, '/') != NULL || | |
1122 | strlen(con->uri) == 12)) | |
1123 | { | |
1124 | /* | |
1125 | * GET can only be done to configuration files under | |
1126 | * /admin/conf... | |
1127 | */ | |
1128 | ||
1129 | if (!SendError(con, HTTP_FORBIDDEN)) | |
1130 | { | |
1131 | CloseClient(con); | |
1132 | return (0); | |
1133 | } | |
1134 | ||
1135 | break; | |
1136 | } | |
b14d90ba | 1137 | else |
1138 | { | |
b38fb7fc | 1139 | /* |
1140 | * Serve a file... | |
1141 | */ | |
1142 | ||
6db7190f | 1143 | if ((filename = get_file(con, &filestats, buf, |
1144 | sizeof(buf))) == NULL) | |
b38fb7fc | 1145 | { |
1146 | if (!SendError(con, HTTP_NOT_FOUND)) | |
1147 | { | |
1148 | CloseClient(con); | |
1149 | return (0); | |
1150 | } | |
1151 | } | |
1152 | else if (!check_if_modified(con, &filestats)) | |
1153 | { | |
1154 | if (!SendError(con, HTTP_NOT_MODIFIED)) | |
1155 | { | |
1156 | CloseClient(con); | |
1157 | return (0); | |
1158 | } | |
1159 | } | |
1160 | else | |
1161 | { | |
1162 | type = mimeFileType(MimeDatabase, filename); | |
1163 | if (type == NULL) | |
1164 | strcpy(line, "text/plain"); | |
1165 | else | |
1166 | snprintf(line, sizeof(line), "%s/%s", type->super, type->type); | |
1167 | ||
1168 | if (!SendFile(con, HTTP_OK, filename, line, &filestats)) | |
1169 | { | |
1170 | CloseClient(con); | |
1171 | return (0); | |
1172 | } | |
1173 | } | |
b14d90ba | 1174 | } |
b38fb7fc | 1175 | break; |
1176 | ||
1177 | case HTTP_POST_RECV : | |
1178 | /* | |
1179 | * See if the POST request includes a Content-Length field, and if | |
1180 | * so check the length against any limits that are set... | |
1181 | */ | |
b14d90ba | 1182 | |
b38fb7fc | 1183 | LogMessage(L_DEBUG2, "POST %s", con->uri); |
1184 | LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]); | |
b14d90ba | 1185 | |
b38fb7fc | 1186 | if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && |
1187 | atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize && | |
1188 | MaxRequestSize > 0) | |
a74b005d | 1189 | { |
b38fb7fc | 1190 | /* |
1191 | * Request too large... | |
1192 | */ | |
1193 | ||
1194 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
a74b005d | 1195 | { |
b38fb7fc | 1196 | CloseClient(con); |
a74b005d | 1197 | return (0); |
1198 | } | |
b38fb7fc | 1199 | |
1200 | break; | |
a74b005d | 1201 | } |
901b295d | 1202 | else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0) |
1203 | { | |
1204 | /* | |
1205 | * Negative content lengths are invalid! | |
1206 | */ | |
1207 | ||
1208 | if (!SendError(con, HTTP_BAD_REQUEST)) | |
1209 | { | |
1210 | CloseClient(con); | |
1211 | return (0); | |
1212 | } | |
1213 | ||
1214 | break; | |
1215 | } | |
a74b005d | 1216 | |
b38fb7fc | 1217 | /* |
1218 | * See what kind of POST request this is; for IPP requests the | |
1219 | * content-type field will be "application/ipp"... | |
a74b005d | 1220 | */ |
1221 | ||
b38fb7fc | 1222 | if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0) |
1223 | con->request = ippNew(); | |
753453e4 | 1224 | else if ((strncmp(con->uri, "/admin", 6) == 0 && |
1225 | strncmp(con->uri, "/admin/conf/", 12) != 0) || | |
b38fb7fc | 1226 | strncmp(con->uri, "/printers", 9) == 0 || |
1227 | strncmp(con->uri, "/classes", 8) == 0 || | |
1228 | strncmp(con->uri, "/jobs", 5) == 0) | |
a74b005d | 1229 | { |
b38fb7fc | 1230 | /* |
1231 | * CGI request... | |
1232 | */ | |
1233 | ||
1234 | if (strncmp(con->uri, "/admin", 6) == 0) | |
a74b005d | 1235 | { |
b38fb7fc | 1236 | snprintf(con->command, sizeof(con->command), |
1237 | "%s/cgi-bin/admin.cgi", ServerBin); | |
1238 | con->options = con->uri + 6; | |
a74b005d | 1239 | } |
b38fb7fc | 1240 | else if (strncmp(con->uri, "/printers", 9) == 0) |
a74b005d | 1241 | { |
b38fb7fc | 1242 | snprintf(con->command, sizeof(con->command), |
1243 | "%s/cgi-bin/printers.cgi", ServerBin); | |
1244 | con->options = con->uri + 9; | |
1245 | } | |
1246 | else if (strncmp(con->uri, "/classes", 8) == 0) | |
1247 | { | |
1248 | snprintf(con->command, sizeof(con->command), | |
1249 | "%s/cgi-bin/classes.cgi", ServerBin); | |
1250 | con->options = con->uri + 8; | |
a74b005d | 1251 | } |
e31bfb6e | 1252 | else |
a74b005d | 1253 | { |
b38fb7fc | 1254 | snprintf(con->command, sizeof(con->command), |
1255 | "%s/cgi-bin/jobs.cgi", ServerBin); | |
1256 | con->options = con->uri + 5; | |
a74b005d | 1257 | } |
8b43895a | 1258 | |
b38fb7fc | 1259 | if (con->options[0] == '/') |
1260 | con->options ++; | |
f63a2256 | 1261 | |
b38fb7fc | 1262 | LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"", |
1263 | con->http.fd, con->command, con->options); | |
8b43895a | 1264 | |
b38fb7fc | 1265 | if (con->http.version <= HTTP_1_0) |
1266 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
1267 | } | |
1268 | else if (!SendError(con, HTTP_UNAUTHORIZED)) | |
8b43895a | 1269 | { |
1270 | CloseClient(con); | |
1271 | return (0); | |
1272 | } | |
8b43895a | 1273 | break; |
b14d90ba | 1274 | |
b38fb7fc | 1275 | case HTTP_PUT_RECV : |
753453e4 | 1276 | /* |
1277 | * Validate the resource name... | |
1278 | */ | |
1279 | ||
1280 | if (strncmp(con->uri, "/admin/conf/", 12) != 0 || | |
1281 | strchr(con->uri + 12, '/') != NULL || | |
1282 | strlen(con->uri) == 12) | |
1283 | { | |
1284 | /* | |
1285 | * PUT can only be done to configuration files under | |
1286 | * /admin/conf... | |
1287 | */ | |
1288 | ||
1289 | if (!SendError(con, HTTP_FORBIDDEN)) | |
1290 | { | |
1291 | CloseClient(con); | |
1292 | return (0); | |
1293 | } | |
1294 | ||
1295 | break; | |
1296 | } | |
1297 | ||
1298 | /* | |
1299 | * See if the PUT request includes a Content-Length field, and if | |
1300 | * so check the length against any limits that are set... | |
1301 | */ | |
1302 | ||
1303 | LogMessage(L_DEBUG2, "PUT %s", con->uri); | |
1304 | LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]); | |
1305 | ||
1306 | if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && | |
1307 | atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize && | |
1308 | MaxRequestSize > 0) | |
1309 | { | |
1310 | /* | |
1311 | * Request too large... | |
1312 | */ | |
1313 | ||
1314 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1315 | { | |
1316 | CloseClient(con); | |
1317 | return (0); | |
1318 | } | |
1319 | ||
1320 | break; | |
1321 | } | |
1322 | ||
1323 | /* | |
1324 | * Open a temporary file to hold the request... | |
1325 | */ | |
1326 | ||
1327 | snprintf(con->filename, sizeof(con->filename), "%s/%08x", | |
1328 | RequestRoot, request_id ++); | |
1329 | con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); | |
1330 | fchmod(con->file, 0640); | |
1331 | fchown(con->file, User, Group); | |
1332 | ||
1333 | LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd, | |
1334 | con->filename, con->file); | |
1335 | ||
1336 | if (con->file < 0) | |
1337 | { | |
1338 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1339 | { | |
1340 | CloseClient(con); | |
1341 | return (0); | |
1342 | } | |
1343 | } | |
1344 | break; | |
1345 | ||
b38fb7fc | 1346 | case HTTP_DELETE : |
1347 | case HTTP_TRACE : | |
1348 | SendError(con, HTTP_NOT_IMPLEMENTED); | |
c3c5af5e | 1349 | ShutdownClient(con); |
2d5e06dd | 1350 | return (1); |
b14d90ba | 1351 | |
b38fb7fc | 1352 | case HTTP_HEAD : |
d588a92e | 1353 | if (strncmp(con->uri, "/printers/", 10) == 0 && |
b38fb7fc | 1354 | strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0) |
bd84e0d1 | 1355 | { |
b38fb7fc | 1356 | /* |
d588a92e | 1357 | * Send PPD file - get the real printer name since printer |
1358 | * names are not case sensitive but filenames can be... | |
b38fb7fc | 1359 | */ |
a74b005d | 1360 | |
d588a92e | 1361 | con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ |
1362 | ||
1363 | if ((p = FindPrinter(con->uri + 10)) != NULL) | |
1364 | snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); | |
1365 | else | |
1366 | { | |
1367 | if (!SendError(con, HTTP_NOT_FOUND)) | |
1368 | { | |
1369 | CloseClient(con); | |
1370 | return (0); | |
1371 | } | |
1372 | ||
1373 | break; | |
1374 | } | |
b38fb7fc | 1375 | } |
a74b005d | 1376 | |
753453e4 | 1377 | if ((strncmp(con->uri, "/admin/", 7) == 0 && |
1378 | strncmp(con->uri, "/admin/conf/", 12) != 0) || | |
b38fb7fc | 1379 | strncmp(con->uri, "/printers/", 10) == 0 || |
1380 | strncmp(con->uri, "/classes/", 9) == 0 || | |
1381 | strncmp(con->uri, "/jobs/", 6) == 0) | |
1382 | { | |
1383 | /* | |
1384 | * CGI output... | |
1385 | */ | |
f3d580b9 | 1386 | |
b38fb7fc | 1387 | if (!SendHeader(con, HTTP_OK, "text/html")) |
1388 | { | |
1389 | CloseClient(con); | |
1390 | return (0); | |
1391 | } | |
f3d580b9 | 1392 | |
b38fb7fc | 1393 | if (httpPrintf(HTTP(con), "\r\n") < 0) |
1394 | { | |
1395 | CloseClient(con); | |
1396 | return (0); | |
1397 | } | |
a74b005d | 1398 | |
b38fb7fc | 1399 | LogRequest(con, HTTP_OK); |
a74b005d | 1400 | } |
753453e4 | 1401 | else if (strncmp(con->uri, "/admin/conf/", 12) == 0 && |
1402 | (strchr(con->uri + 12, '/') != NULL || | |
1403 | strlen(con->uri) == 12)) | |
1404 | { | |
1405 | /* | |
1406 | * HEAD can only be done to configuration files under | |
1407 | * /admin/conf... | |
1408 | */ | |
1409 | ||
1410 | if (!SendError(con, HTTP_FORBIDDEN)) | |
1411 | { | |
1412 | CloseClient(con); | |
1413 | return (0); | |
1414 | } | |
1415 | ||
1416 | break; | |
1417 | } | |
6db7190f | 1418 | else if ((filename = get_file(con, &filestats, buf, |
1419 | sizeof(buf))) == NULL) | |
a74b005d | 1420 | { |
b38fb7fc | 1421 | if (!SendHeader(con, HTTP_NOT_FOUND, "text/html")) |
1422 | { | |
1423 | CloseClient(con); | |
1424 | return (0); | |
1425 | } | |
6a0c519d | 1426 | |
b38fb7fc | 1427 | LogRequest(con, HTTP_NOT_FOUND); |
a74b005d | 1428 | } |
b38fb7fc | 1429 | else if (!check_if_modified(con, &filestats)) |
1430 | { | |
1431 | if (!SendError(con, HTTP_NOT_MODIFIED)) | |
1432 | { | |
1433 | CloseClient(con); | |
1434 | return (0); | |
1435 | } | |
6a0c519d | 1436 | |
b38fb7fc | 1437 | LogRequest(con, HTTP_NOT_MODIFIED); |
a74b005d | 1438 | } |
b38fb7fc | 1439 | else |
1440 | { | |
1441 | /* | |
1442 | * Serve a file... | |
1443 | */ | |
6a0c519d | 1444 | |
b38fb7fc | 1445 | type = mimeFileType(MimeDatabase, filename); |
1446 | if (type == NULL) | |
1447 | strcpy(line, "text/plain"); | |
1448 | else | |
1449 | snprintf(line, sizeof(line), "%s/%s", type->super, type->type); | |
a74b005d | 1450 | |
b38fb7fc | 1451 | if (!SendHeader(con, HTTP_OK, line)) |
1452 | { | |
1453 | CloseClient(con); | |
1454 | return (0); | |
1455 | } | |
a74b005d | 1456 | |
b38fb7fc | 1457 | if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", |
1458 | httpGetDateString(filestats.st_mtime)) < 0) | |
1459 | { | |
1460 | CloseClient(con); | |
1461 | return (0); | |
1462 | } | |
a74b005d | 1463 | |
753453e4 | 1464 | if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n", |
1465 | (unsigned long)filestats.st_size) < 0) | |
b38fb7fc | 1466 | { |
1467 | CloseClient(con); | |
1468 | return (0); | |
1469 | } | |
1470 | ||
1471 | LogRequest(con, HTTP_OK); | |
a74b005d | 1472 | } |
1473 | ||
b38fb7fc | 1474 | if (httpPrintf(HTTP(con), "\r\n") < 0) |
a74b005d | 1475 | { |
1476 | CloseClient(con); | |
1477 | return (0); | |
1478 | } | |
6a0c519d | 1479 | |
b38fb7fc | 1480 | con->http.state = HTTP_WAITING; |
1481 | break; | |
a74b005d | 1482 | |
b38fb7fc | 1483 | default : |
1484 | break; /* Anti-compiler-warning-code */ | |
1485 | } | |
a74b005d | 1486 | } |
1487 | } | |
1488 | ||
1489 | /* | |
1490 | * Handle any incoming data... | |
1491 | */ | |
1492 | ||
1493 | switch (con->http.state) | |
1494 | { | |
1495 | case HTTP_PUT_RECV : | |
753453e4 | 1496 | LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d", |
1497 | con->http.fd, | |
1498 | con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length", | |
1499 | con->http.data_remaining, con->file); | |
1500 | ||
1501 | if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0) | |
1502 | { | |
1503 | CloseClient(con); | |
1504 | return (0); | |
1505 | } | |
1506 | else if (bytes > 0) | |
1507 | { | |
1508 | con->bytes += bytes; | |
1509 | ||
1510 | LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d", | |
1511 | con->http.fd, bytes, con->file); | |
1512 | ||
1513 | if (write(con->file, line, bytes) < bytes) | |
1514 | { | |
1515 | LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s", | |
1516 | bytes, con->filename, strerror(errno)); | |
1517 | ||
6cd03542 | 1518 | LogMessage(L_DEBUG2, "ReadClient: Closing data file %d...", |
1519 | con->file); | |
1520 | ||
753453e4 | 1521 | close(con->file); |
1522 | con->file = 0; | |
1523 | unlink(con->filename); | |
1524 | con->filename[0] = '\0'; | |
1525 | ||
1526 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1527 | { | |
1528 | CloseClient(con); | |
1529 | return (0); | |
1530 | } | |
1531 | } | |
1532 | } | |
1533 | ||
1534 | if (con->http.state == HTTP_WAITING) | |
1535 | { | |
1536 | /* | |
1537 | * End of file, see how big it is... | |
1538 | */ | |
1539 | ||
1540 | fstat(con->file, &filestats); | |
1541 | ||
1542 | LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.", | |
aa7e125a | 1543 | con->http.fd, con->file, (int)filestats.st_size); |
753453e4 | 1544 | |
1545 | close(con->file); | |
1546 | con->file = 0; | |
1547 | ||
1548 | if (filestats.st_size > MaxRequestSize && | |
1549 | MaxRequestSize > 0) | |
1550 | { | |
1551 | /* | |
1552 | * Request is too big; remove it and send an error... | |
1553 | */ | |
1554 | ||
1555 | LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s", | |
1556 | con->http.fd, con->filename); | |
1557 | unlink(con->filename); | |
1558 | con->filename[0] = '\0'; | |
1559 | ||
1560 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1561 | { | |
1562 | CloseClient(con); | |
1563 | return (0); | |
1564 | } | |
1565 | } | |
1566 | ||
1567 | /* | |
1568 | * Install the configuration file... | |
1569 | */ | |
1570 | ||
1571 | status = install_conf_file(con); | |
1572 | ||
1573 | /* | |
1574 | * Return the status to the client... | |
1575 | */ | |
1576 | ||
1577 | if (!SendError(con, status)) | |
1578 | { | |
1579 | CloseClient(con); | |
1580 | return (0); | |
1581 | } | |
1582 | } | |
a74b005d | 1583 | break; |
1584 | ||
1585 | case HTTP_POST_RECV : | |
58e8cf34 | 1586 | LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d", |
93894a43 | 1587 | con->http.fd, |
a74b005d | 1588 | con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length", |
f63a2256 | 1589 | con->http.data_remaining, con->file); |
a74b005d | 1590 | |
b14d90ba | 1591 | if (con->request != NULL) |
1592 | { | |
1593 | /* | |
1594 | * Grab any request data from the connection... | |
1595 | */ | |
1596 | ||
bd176c9c | 1597 | if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR) |
1598 | { | |
5ea8888e | 1599 | LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!", |
bd176c9c | 1600 | con->http.fd); |
3955a26e | 1601 | |
1602 | if (!SendError(con, HTTP_BAD_REQUEST)) | |
1603 | { | |
1604 | CloseClient(con); | |
1605 | return (0); | |
1606 | } | |
1607 | ||
1608 | ShutdownClient(con); | |
1609 | return (1); | |
bd176c9c | 1610 | } |
1611 | else if (ipp_state != IPP_DATA) | |
b14d90ba | 1612 | break; |
68ff4f01 | 1613 | else |
1614 | con->bytes += ippLength(con->request); | |
f63a2256 | 1615 | } |
b14d90ba | 1616 | |
f63a2256 | 1617 | if (con->file == 0 && con->http.state != HTTP_POST_SEND) |
1618 | { | |
1619 | /* | |
1620 | * Create a file as needed for the request data... | |
1621 | */ | |
1d2c70a6 | 1622 | |
04de52f8 | 1623 | snprintf(con->filename, sizeof(con->filename), "%s/%08x", |
f63a2256 | 1624 | RequestRoot, request_id ++); |
1625 | con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); | |
1626 | fchmod(con->file, 0640); | |
1627 | fchown(con->file, User, Group); | |
b14d90ba | 1628 | |
a61a2be6 | 1629 | LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd, |
1630 | con->filename, con->file); | |
b14d90ba | 1631 | |
f63a2256 | 1632 | if (con->file < 0) |
1633 | { | |
1634 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
b14d90ba | 1635 | { |
f63a2256 | 1636 | CloseClient(con); |
1637 | return (0); | |
b14d90ba | 1638 | } |
1639 | } | |
f63a2256 | 1640 | } |
b14d90ba | 1641 | |
bfa1abf0 | 1642 | if (con->http.state != HTTP_POST_SEND) |
a74b005d | 1643 | { |
bfa1abf0 | 1644 | if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0) |
a74b005d | 1645 | { |
bfa1abf0 | 1646 | CloseClient(con); |
1647 | return (0); | |
1648 | } | |
1649 | else if (bytes > 0) | |
1650 | { | |
1651 | con->bytes += bytes; | |
a74b005d | 1652 | |
a61a2be6 | 1653 | LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d", |
1654 | con->http.fd, bytes, con->file); | |
bfa1abf0 | 1655 | |
1656 | if (write(con->file, line, bytes) < bytes) | |
a74b005d | 1657 | { |
a61a2be6 | 1658 | LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s", |
1659 | bytes, con->filename, strerror(errno)); | |
1660 | ||
6cd03542 | 1661 | LogMessage(L_DEBUG2, "ReadClient: Closing file %d...", |
1662 | con->file); | |
1663 | ||
bfa1abf0 | 1664 | close(con->file); |
1665 | con->file = 0; | |
1666 | unlink(con->filename); | |
753453e4 | 1667 | con->filename[0] = '\0'; |
bfa1abf0 | 1668 | |
1669 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1670 | { | |
1671 | CloseClient(con); | |
1672 | return (0); | |
1673 | } | |
a74b005d | 1674 | } |
1675 | } | |
bd176c9c | 1676 | else if (con->http.state != HTTP_POST_SEND) |
1677 | { | |
1678 | CloseClient(con); | |
1679 | return (0); | |
1680 | } | |
a74b005d | 1681 | } |
93894a43 | 1682 | |
1683 | if (con->http.state == HTTP_POST_SEND) | |
1684 | { | |
bfa1abf0 | 1685 | if (con->file) |
1686 | { | |
8b43895a | 1687 | fstat(con->file, &filestats); |
bd917997 | 1688 | |
1689 | LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.", | |
aa7e125a | 1690 | con->http.fd, con->file, (int)filestats.st_size); |
bd917997 | 1691 | |
bfa1abf0 | 1692 | close(con->file); |
1693 | con->file = 0; | |
8b43895a | 1694 | |
1695 | if (filestats.st_size > MaxRequestSize && | |
1696 | MaxRequestSize > 0) | |
1697 | { | |
1698 | /* | |
1699 | * Request is too big; remove it and send an error... | |
1700 | */ | |
1701 | ||
a61a2be6 | 1702 | LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s", |
1703 | con->http.fd, con->filename); | |
8b43895a | 1704 | unlink(con->filename); |
753453e4 | 1705 | con->filename[0] = '\0'; |
8b43895a | 1706 | |
1707 | if (con->request) | |
1708 | { | |
1709 | /* | |
1710 | * Delete any IPP request data... | |
1711 | */ | |
1712 | ||
1713 | ippDelete(con->request); | |
1714 | con->request = NULL; | |
1715 | } | |
1716 | ||
1717 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1718 | { | |
1719 | CloseClient(con); | |
1720 | return (0); | |
1721 | } | |
1722 | } | |
f63a2256 | 1723 | |
1724 | if (con->command[0]) | |
1725 | { | |
1726 | if (!SendCommand(con, con->command, con->options)) | |
1727 | { | |
1728 | if (!SendError(con, HTTP_NOT_FOUND)) | |
1729 | { | |
1730 | CloseClient(con); | |
1731 | return (0); | |
1732 | } | |
1733 | } | |
1734 | else | |
1735 | LogRequest(con, HTTP_OK); | |
1736 | } | |
bfa1abf0 | 1737 | } |
93894a43 | 1738 | |
1739 | if (con->request) | |
6e3d4276 | 1740 | return (ProcessIPPRequest(con)); |
93894a43 | 1741 | } |
a74b005d | 1742 | break; |
d21a7597 | 1743 | |
1744 | default : | |
1745 | break; /* Anti-compiler-warning-code */ | |
a74b005d | 1746 | } |
1747 | ||
1748 | if (!con->http.keep_alive && con->http.state == HTTP_WAITING) | |
1749 | { | |
1750 | CloseClient(con); | |
1751 | return (0); | |
1752 | } | |
1753 | else | |
1754 | return (1); | |
1755 | } | |
1756 | ||
1757 | ||
a74b005d | 1758 | /* |
1759 | * 'SendCommand()' - Send output from a command via HTTP. | |
1760 | */ | |
1761 | ||
1762 | int | |
1763 | SendCommand(client_t *con, | |
a74b005d | 1764 | char *command, |
b14d90ba | 1765 | char *options) |
a74b005d | 1766 | { |
f63a2256 | 1767 | int fd; |
1768 | ||
1769 | ||
1770 | if (con->filename[0]) | |
1771 | fd = open(con->filename, O_RDONLY); | |
1772 | else | |
1773 | fd = open("/dev/null", O_RDONLY); | |
1774 | ||
1775 | con->pipe_pid = pipe_command(con, fd, &(con->file), command, options); | |
1776 | ||
1777 | close(fd); | |
a74b005d | 1778 | |
b5cb0608 | 1779 | LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid); |
1780 | ||
1781 | LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file); | |
a74b005d | 1782 | |
1783 | if (con->pipe_pid == 0) | |
1784 | return (0); | |
1785 | ||
1786 | fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); | |
1787 | ||
27eba2dd | 1788 | LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to InputSet...", con->file); |
e668e627 | 1789 | LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to OutputSet...", |
1790 | con->http.fd); | |
1791 | ||
a74b005d | 1792 | FD_SET(con->file, &InputSet); |
1793 | FD_SET(con->http.fd, &OutputSet); | |
1794 | ||
96df88bb | 1795 | if (!SendHeader(con, HTTP_OK, NULL)) |
a74b005d | 1796 | return (0); |
1797 | ||
1798 | if (con->http.version == HTTP_1_1) | |
1799 | { | |
1800 | con->http.data_encoding = HTTP_ENCODE_CHUNKED; | |
1801 | ||
1802 | if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0) | |
1803 | return (0); | |
1804 | } | |
1805 | ||
13486cb7 | 1806 | con->got_fields = 0; |
1807 | con->field_col = 0; | |
1808 | ||
a74b005d | 1809 | return (1); |
1810 | } | |
1811 | ||
1812 | ||
1813 | /* | |
1814 | * 'SendError()' - Send an error message via HTTP. | |
1815 | */ | |
1816 | ||
1817 | int /* O - 1 if successful, 0 otherwise */ | |
1818 | SendError(client_t *con, /* I - Connection */ | |
1819 | http_status_t code) /* I - Error code */ | |
1820 | { | |
83e740a5 | 1821 | char message[1024]; /* Message for user */ |
a74b005d | 1822 | |
1823 | ||
6a0c519d | 1824 | /* |
1825 | * Put the request in the access_log file... | |
1826 | */ | |
1827 | ||
993e15da | 1828 | if (con->operation > HTTP_WAITING) |
1829 | LogRequest(con, code); | |
6a0c519d | 1830 | |
753453e4 | 1831 | LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code); |
a61a2be6 | 1832 | |
a74b005d | 1833 | /* |
1834 | * To work around bugs in some proxies, don't use Keep-Alive for some | |
1835 | * error messages... | |
1836 | */ | |
1837 | ||
1838 | if (code >= HTTP_BAD_REQUEST) | |
1839 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
1840 | ||
1841 | /* | |
1842 | * Send an error message back to the client. If the error code is a | |
1843 | * 400 or 500 series, make sure the message contains some text, too! | |
1844 | */ | |
1845 | ||
1846 | if (!SendHeader(con, code, NULL)) | |
1847 | return (0); | |
1848 | ||
bcf61448 | 1849 | #ifdef HAVE_SSL |
a75c006a | 1850 | if (code == HTTP_UPGRADE_REQUIRED) |
a75c006a | 1851 | if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0) |
1852 | return (0); | |
1853 | ||
b5cb0608 | 1854 | if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0) |
1855 | return (0); | |
bcf61448 | 1856 | #endif /* HAVE_SSL */ |
a75c006a | 1857 | |
55809c6f | 1858 | if ((con->http.version >= HTTP_1_1 && !con->http.keep_alive) || |
1859 | (code >= HTTP_BAD_REQUEST && code != HTTP_UPGRADE_REQUIRED)) | |
a74b005d | 1860 | { |
1861 | if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0) | |
1862 | return (0); | |
1863 | } | |
1864 | ||
1865 | if (code >= HTTP_BAD_REQUEST) | |
1866 | { | |
1867 | /* | |
1868 | * Send a human-readable error message. | |
1869 | */ | |
1870 | ||
04de52f8 | 1871 | snprintf(message, sizeof(message), |
970017a4 | 1872 | "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>" |
1873 | "<BODY><H1>%s</H1>%s</BODY></HTML>\n", | |
1874 | code, httpStatus(code), httpStatus(code), | |
1875 | con->language ? con->language->messages[code] : | |
1876 | httpStatus(code)); | |
a74b005d | 1877 | |
1878 | if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0) | |
1879 | return (0); | |
e0ba31cd | 1880 | if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", |
1881 | (int)strlen(message)) < 0) | |
a74b005d | 1882 | return (0); |
1883 | if (httpPrintf(HTTP(con), "\r\n") < 0) | |
1884 | return (0); | |
97d73ddb | 1885 | if (httpPrintf(HTTP(con), "%s", message) < 0) |
a74b005d | 1886 | return (0); |
1887 | } | |
1888 | else if (httpPrintf(HTTP(con), "\r\n") < 0) | |
1889 | return (0); | |
1890 | ||
1891 | con->http.state = HTTP_WAITING; | |
1892 | ||
1893 | return (1); | |
1894 | } | |
1895 | ||
1896 | ||
1897 | /* | |
1898 | * 'SendFile()' - Send a file via HTTP. | |
1899 | */ | |
1900 | ||
1901 | int | |
1902 | SendFile(client_t *con, | |
1903 | http_status_t code, | |
1904 | char *filename, | |
1905 | char *type, | |
1906 | struct stat *filestats) | |
1907 | { | |
1908 | con->file = open(filename, O_RDONLY); | |
1909 | ||
5ea8888e | 1910 | LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file); |
a74b005d | 1911 | |
1912 | if (con->file < 0) | |
1913 | return (0); | |
1914 | ||
1915 | fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); | |
1916 | ||
1917 | con->pipe_pid = 0; | |
1918 | ||
1919 | if (!SendHeader(con, code, type)) | |
1920 | return (0); | |
1921 | ||
1922 | if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0) | |
1923 | return (0); | |
753453e4 | 1924 | if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n", |
1925 | (unsigned long)filestats->st_size) < 0) | |
a74b005d | 1926 | return (0); |
1927 | if (httpPrintf(HTTP(con), "\r\n") < 0) | |
1928 | return (0); | |
1929 | ||
e668e627 | 1930 | LogMessage(L_DEBUG2, "SendFile: Adding fd %d to OutputSet...", con->http.fd); |
1931 | ||
a74b005d | 1932 | FD_SET(con->http.fd, &OutputSet); |
1933 | ||
1934 | return (1); | |
1935 | } | |
1936 | ||
1937 | ||
1938 | /* | |
93894a43 | 1939 | * 'SendHeader()' - Send an HTTP request. |
a74b005d | 1940 | */ |
1941 | ||
1942 | int /* O - 1 on success, 0 on failure */ | |
1943 | SendHeader(client_t *con, /* I - Client to send to */ | |
1944 | http_status_t code, /* I - HTTP status code */ | |
1945 | char *type) /* I - MIME type of document */ | |
1946 | { | |
83e740a5 | 1947 | location_t *loc; /* Authentication location */ |
1948 | ||
1949 | ||
a74b005d | 1950 | if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100, |
ffe90a99 | 1951 | con->http.version % 100, code, httpStatus(code)) < 0) |
a74b005d | 1952 | return (0); |
1953 | if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0) | |
1954 | return (0); | |
bd84e0d1 | 1955 | if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0) |
a74b005d | 1956 | return (0); |
1957 | if (con->http.keep_alive && con->http.version >= HTTP_1_0) | |
1958 | { | |
1959 | if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0) | |
1960 | return (0); | |
1961 | if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0) | |
1962 | return (0); | |
1963 | } | |
8f85ff77 | 1964 | if (code == HTTP_METHOD_NOT_ALLOWED) |
9bfcbffc | 1965 | if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0) |
8f85ff77 | 1966 | return (0); |
1967 | ||
5ea8888e | 1968 | if (code == HTTP_UNAUTHORIZED) |
83e740a5 | 1969 | { |
753453e4 | 1970 | /* |
1971 | * This already succeeded in IsAuthorized... | |
1972 | */ | |
1973 | ||
1974 | loc = FindBest(con->uri, con->http.state); | |
83e740a5 | 1975 | |
753453e4 | 1976 | if (loc->type != AUTH_DIGEST) |
83e740a5 | 1977 | { |
1978 | if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0) | |
1979 | return (0); | |
1980 | } | |
1981 | else | |
1982 | { | |
1983 | if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" " | |
1984 | "nonce=\"%s\"\r\n", con->http.hostname) < 0) | |
1985 | return (0); | |
1986 | } | |
1987 | } | |
a74b005d | 1988 | if (con->language != NULL) |
1989 | { | |
1990 | if (httpPrintf(HTTP(con), "Content-Language: %s\r\n", | |
1991 | con->language->language) < 0) | |
1992 | return (0); | |
1993 | ||
1994 | if (type != NULL) | |
1995 | if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type, | |
1996 | cupsLangEncoding(con->language)) < 0) | |
1997 | return (0); | |
1998 | } | |
1999 | else if (type != NULL) | |
2000 | if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0) | |
2001 | return (0); | |
2002 | ||
2003 | return (1); | |
2004 | } | |
2005 | ||
2006 | ||
c3c5af5e | 2007 | /* |
2008 | * 'ShutdownClient()' - Shutdown the receiving end of a connection. | |
2009 | */ | |
2010 | ||
2011 | void | |
2012 | ShutdownClient(client_t *con) /* I - Client connection */ | |
2013 | { | |
2014 | /* | |
2015 | * Shutdown the receiving end of the socket, since the client | |
2016 | * still needs to read the error message... | |
2017 | */ | |
2018 | ||
2019 | shutdown(con->http.fd, 0); | |
549d1888 | 2020 | con->http.used = 0; |
c3c5af5e | 2021 | |
de218675 | 2022 | /* |
2023 | * Update the activity time so that we timeout after 30 seconds rather | |
2024 | * then the current Timeout setting (300 by default). This prevents | |
2025 | * some DoS situations... | |
2026 | */ | |
2027 | ||
2028 | con->http.activity = time(NULL) - Timeout + 30; | |
2029 | ||
c3c5af5e | 2030 | LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...", |
2031 | con->http.fd); | |
2032 | ||
2033 | FD_CLR(con->http.fd, &InputSet); | |
2034 | } | |
2035 | ||
2036 | ||
a74b005d | 2037 | /* |
2038 | * 'WriteClient()' - Write data to a client as needed. | |
2039 | */ | |
2040 | ||
ff49100f | 2041 | int /* O - 1 if success, 0 if fail */ |
2042 | WriteClient(client_t *con) /* I - Client connection */ | |
a74b005d | 2043 | { |
ff49100f | 2044 | int bytes; /* Number of bytes written */ |
13486cb7 | 2045 | char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */ |
2046 | char *bufptr; /* Pointer into buffer */ | |
ff49100f | 2047 | ipp_state_t ipp_state; /* IPP state value */ |
a74b005d | 2048 | |
2049 | ||
2050 | if (con->http.state != HTTP_GET_SEND && | |
2051 | con->http.state != HTTP_POST_SEND) | |
2052 | return (1); | |
2053 | ||
ff49100f | 2054 | if (con->response != NULL) |
bfa1abf0 | 2055 | { |
ff49100f | 2056 | ipp_state = ippWrite(&(con->http), con->response); |
2057 | bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA; | |
bfa1abf0 | 2058 | } |
13486cb7 | 2059 | else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0) |
a74b005d | 2060 | { |
13486cb7 | 2061 | if (con->pipe_pid && !con->got_fields) |
2062 | { | |
2063 | /* | |
2064 | * Inspect the data for Content-Type and other fields. | |
2065 | */ | |
2066 | ||
2067 | buf[bytes] = '\0'; | |
2068 | ||
2069 | for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++) | |
2070 | if (*bufptr == '\n') | |
2071 | { | |
2072 | /* | |
2073 | * Send line to client... | |
2074 | */ | |
2075 | ||
2076 | if (bufptr > buf && bufptr[-1] == '\r') | |
2077 | bufptr[-1] = '\0'; | |
2078 | *bufptr++ = '\0'; | |
2079 | ||
2080 | httpPrintf(HTTP(con), "%s\r\n", buf); | |
58e8cf34 | 2081 | LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf); |
13486cb7 | 2082 | |
2083 | /* | |
2084 | * Update buffer... | |
2085 | */ | |
2086 | ||
2087 | bytes -= (bufptr - buf); | |
2088 | memcpy(buf, bufptr, bytes + 1); | |
2089 | bufptr = buf - 1; | |
2090 | ||
2091 | /* | |
2092 | * See if the line was empty... | |
2093 | */ | |
2094 | ||
2095 | if (con->field_col == 0) | |
2096 | con->got_fields = 1; | |
2097 | else | |
2098 | con->field_col = 0; | |
2099 | } | |
2100 | else if (*bufptr != '\r') | |
2101 | con->field_col ++; | |
2102 | ||
2103 | if (bytes > 0 && !con->got_fields) | |
2104 | { | |
2105 | /* | |
2106 | * Remaining text needs to go out... | |
2107 | */ | |
2108 | ||
2109 | httpPrintf(HTTP(con), "%s", buf); | |
2110 | ||
2111 | con->http.activity = time(NULL); | |
2112 | return (1); | |
2113 | } | |
2114 | else if (bytes == 0) | |
2115 | { | |
2116 | con->http.activity = time(NULL); | |
2117 | return (1); | |
2118 | } | |
2119 | } | |
2120 | ||
a74b005d | 2121 | if (httpWrite(HTTP(con), buf, bytes) < 0) |
2122 | { | |
2123 | CloseClient(con); | |
2124 | return (0); | |
2125 | } | |
6a0c519d | 2126 | |
2127 | con->bytes += bytes; | |
a74b005d | 2128 | } |
b14d90ba | 2129 | |
2130 | if (bytes <= 0) | |
a74b005d | 2131 | { |
6a0c519d | 2132 | LogRequest(con, HTTP_OK); |
2133 | ||
a74b005d | 2134 | if (con->http.data_encoding == HTTP_ENCODE_CHUNKED) |
2135 | { | |
2136 | if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0) | |
2137 | { | |
2138 | CloseClient(con); | |
2139 | return (0); | |
2140 | } | |
2141 | } | |
2142 | ||
96df88bb | 2143 | con->http.state = HTTP_WAITING; |
2144 | ||
e668e627 | 2145 | LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from OutputSet...", |
2146 | con->http.fd); | |
2147 | ||
a74b005d | 2148 | FD_CLR(con->http.fd, &OutputSet); |
a74b005d | 2149 | |
bfa1abf0 | 2150 | if (con->file) |
a74b005d | 2151 | { |
27eba2dd | 2152 | LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...", |
2153 | con->file); | |
bfa1abf0 | 2154 | FD_CLR(con->file, &InputSet); |
a74b005d | 2155 | |
bfa1abf0 | 2156 | if (con->pipe_pid) |
96df88bb | 2157 | kill(con->pipe_pid, SIGTERM); |
bfa1abf0 | 2158 | |
bd917997 | 2159 | LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.", |
2160 | con->http.fd, con->file); | |
2161 | ||
bfa1abf0 | 2162 | close(con->file); |
96df88bb | 2163 | con->file = 0; |
2164 | con->pipe_pid = 0; | |
bfa1abf0 | 2165 | } |
a74b005d | 2166 | |
d4c438d4 | 2167 | if (con->filename[0]) |
a61a2be6 | 2168 | { |
2169 | LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s", | |
bd917997 | 2170 | con->http.fd, con->filename); |
d4c438d4 | 2171 | unlink(con->filename); |
753453e4 | 2172 | con->filename[0] = '\0'; |
a61a2be6 | 2173 | } |
d4c438d4 | 2174 | |
b14d90ba | 2175 | if (con->request != NULL) |
2176 | { | |
2177 | ippDelete(con->request); | |
2178 | con->request = NULL; | |
2179 | } | |
2180 | ||
2181 | if (con->response != NULL) | |
2182 | { | |
2183 | ippDelete(con->response); | |
2184 | con->response = NULL; | |
2185 | } | |
96df88bb | 2186 | |
2187 | if (!con->http.keep_alive) | |
2188 | { | |
2189 | CloseClient(con); | |
2190 | return (0); | |
2191 | } | |
a74b005d | 2192 | } |
2193 | ||
997edb40 | 2194 | if (bytes >= 1024) |
58e8cf34 | 2195 | LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes); |
a74b005d | 2196 | |
2197 | con->http.activity = time(NULL); | |
2198 | ||
2199 | return (1); | |
2200 | } | |
2201 | ||
2202 | ||
a74b005d | 2203 | /* |
2204 | * 'check_if_modified()' - Decode an "If-Modified-Since" line. | |
2205 | */ | |
2206 | ||
2207 | static int /* O - 1 if modified since */ | |
2208 | check_if_modified(client_t *con, /* I - Client connection */ | |
2209 | struct stat *filestats) /* I - File information */ | |
2210 | { | |
2211 | char *ptr; /* Pointer into field */ | |
2212 | time_t date; /* Time/date value */ | |
2213 | int size; /* Size/length value */ | |
2214 | ||
2215 | ||
2216 | size = 0; | |
2217 | date = 0; | |
2218 | ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE]; | |
2219 | ||
2220 | if (*ptr == '\0') | |
2221 | return (1); | |
2222 | ||
58e8cf34 | 2223 | LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"", |
4a64fdb7 | 2224 | con->http.fd, ptr); |
2225 | ||
a74b005d | 2226 | while (*ptr != '\0') |
2227 | { | |
4a64fdb7 | 2228 | while (isspace(*ptr) || *ptr == ';') |
a74b005d | 2229 | ptr ++; |
2230 | ||
2231 | if (strncasecmp(ptr, "length=", 7) == 0) | |
2232 | { | |
2233 | ptr += 7; | |
2234 | size = atoi(ptr); | |
2235 | ||
2236 | while (isdigit(*ptr)) | |
2237 | ptr ++; | |
2238 | } | |
4a64fdb7 | 2239 | else if (isalpha(*ptr)) |
a74b005d | 2240 | { |
2241 | date = httpGetDateTime(ptr); | |
4a64fdb7 | 2242 | while (*ptr != '\0' && *ptr != ';') |
a74b005d | 2243 | ptr ++; |
2244 | } | |
2245 | } | |
2246 | ||
58e8cf34 | 2247 | LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d", |
aa7e125a | 2248 | con->http.fd, size, (int)filestats->st_size, (int)date, |
2249 | (int)filestats->st_mtime); | |
4a64fdb7 | 2250 | |
a74b005d | 2251 | return ((size != filestats->st_size && size != 0) || |
4a64fdb7 | 2252 | (date < filestats->st_mtime && date != 0) || |
2253 | (size == 0 && date == 0)); | |
a74b005d | 2254 | } |
2255 | ||
2256 | ||
2257 | /* | |
d2122fde | 2258 | * 'decode_auth()' - Decode an authorization string. |
a74b005d | 2259 | */ |
2260 | ||
93894a43 | 2261 | static void |
d2122fde | 2262 | decode_auth(client_t *con) /* I - Client to decode to */ |
a74b005d | 2263 | { |
d2122fde | 2264 | char *s, /* Authorization string */ |
2265 | value[1024]; /* Value string */ | |
2266 | const char *username; /* Certificate username */ | |
a74b005d | 2267 | |
a74b005d | 2268 | |
2269 | /* | |
b0d58d07 | 2270 | * Decode the string... |
a74b005d | 2271 | */ |
2272 | ||
f3d580b9 | 2273 | s = con->http.fields[HTTP_FIELD_AUTHORIZATION]; |
f3d580b9 | 2274 | |
aa7e125a | 2275 | LogMessage(L_DEBUG2, "decode_auth(%p): Authorization string = \"%s\"", |
27eba2dd | 2276 | con, s); |
69f14ebb | 2277 | |
d2122fde | 2278 | if (strncmp(s, "Basic", 5) == 0) |
2279 | { | |
2280 | s += 5; | |
2281 | while (isspace(*s)) | |
2282 | s ++; | |
f3d580b9 | 2283 | |
d2122fde | 2284 | httpDecode64(value, s); |
a74b005d | 2285 | |
d2122fde | 2286 | /* |
2287 | * Pull the username and password out... | |
2288 | */ | |
b0d58d07 | 2289 | |
d2122fde | 2290 | if ((s = strchr(value, ':')) == NULL) |
2291 | { | |
5ea8888e | 2292 | LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"", |
d2122fde | 2293 | con->http.fd, value); |
2294 | return; | |
2295 | } | |
2296 | ||
2297 | *s++ = '\0'; | |
b0d58d07 | 2298 | |
def978d5 | 2299 | strlcpy(con->username, value, sizeof(con->username)); |
2300 | strlcpy(con->password, s, sizeof(con->password)); | |
d2122fde | 2301 | } |
2302 | else if (strncmp(s, "Local", 5) == 0) | |
2303 | { | |
2304 | s += 5; | |
2305 | while (isspace(*s)) | |
2306 | s ++; | |
b0d58d07 | 2307 | |
d2122fde | 2308 | if ((username = FindCert(s)) != NULL) |
def978d5 | 2309 | strlcpy(con->username, username, sizeof(con->username)); |
d2122fde | 2310 | } |
83e740a5 | 2311 | else if (strncmp(s, "Digest", 5) == 0) |
2312 | { | |
2313 | /* | |
2314 | * Get the username and password from the Digest attributes... | |
2315 | */ | |
2316 | ||
2317 | if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username", | |
2318 | value)) | |
def978d5 | 2319 | strlcpy(con->username, value, sizeof(con->username)); |
83e740a5 | 2320 | |
2321 | if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response", | |
2322 | value)) | |
def978d5 | 2323 | strlcpy(con->password, value, sizeof(con->password) - 1); |
83e740a5 | 2324 | } |
41637133 | 2325 | |
27eba2dd | 2326 | LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"", |
41637133 | 2327 | con->http.fd, con->username); |
a74b005d | 2328 | } |
2329 | ||
2330 | ||
e31bfb6e | 2331 | /* |
2332 | * 'get_file()' - Get a filename and state info. | |
2333 | */ | |
2334 | ||
6db7190f | 2335 | static char * /* O - Real filename */ |
2336 | get_file(client_t *con, /* I - Client connection */ | |
2337 | struct stat *filestats,/* O - File information */ | |
2338 | char *filename, /* IO - Filename buffer */ | |
2339 | int len) /* I - Buffer length */ | |
e31bfb6e | 2340 | { |
2341 | int status; /* Status of filesystem calls */ | |
2342 | char *params; /* Pointer to parameters in URI */ | |
e31bfb6e | 2343 | |
2344 | ||
2345 | /* | |
2346 | * Need to add DocumentRoot global... | |
2347 | */ | |
2348 | ||
f3d580b9 | 2349 | if (strncmp(con->uri, "/ppd/", 5) == 0) |
6db7190f | 2350 | snprintf(filename, len, "%s%s", ServerRoot, con->uri); |
753453e4 | 2351 | else if (strncmp(con->uri, "/admin/conf/", 12) == 0) |
6db7190f | 2352 | snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11); |
f3d580b9 | 2353 | else if (con->language != NULL) |
6db7190f | 2354 | snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language, |
e31bfb6e | 2355 | con->uri); |
2356 | else | |
6db7190f | 2357 | snprintf(filename, len, "%s%s", DocumentRoot, con->uri); |
e31bfb6e | 2358 | |
2359 | if ((params = strchr(filename, '?')) != NULL) | |
2360 | *params = '\0'; | |
2361 | ||
2362 | /* | |
2363 | * Grab the status for this language; if there isn't a language-specific file | |
2364 | * then fallback to the default one... | |
2365 | */ | |
2366 | ||
2367 | if ((status = stat(filename, filestats)) != 0 && con->language != NULL) | |
2368 | { | |
2369 | /* | |
2370 | * Drop the language prefix and try the current directory... | |
2371 | */ | |
2372 | ||
753453e4 | 2373 | if (strncmp(con->uri, "/ppd/", 5) != 0 && |
2374 | strncmp(con->uri, "/admin/conf/", 12) != 0) | |
f3d580b9 | 2375 | { |
6db7190f | 2376 | snprintf(filename, len, "%s%s", DocumentRoot, con->uri); |
e31bfb6e | 2377 | |
f3d580b9 | 2378 | status = stat(filename, filestats); |
2379 | } | |
e31bfb6e | 2380 | } |
2381 | ||
2382 | /* | |
2383 | * If we're found a directory, get the index.html file instead... | |
2384 | */ | |
2385 | ||
2386 | if (!status && S_ISDIR(filestats->st_mode)) | |
2387 | { | |
2388 | if (filename[strlen(filename) - 1] == '/') | |
6db7190f | 2389 | strlcat(filename, "index.html", len); |
e31bfb6e | 2390 | else |
6db7190f | 2391 | strlcat(filename, "/index.html", len); |
e31bfb6e | 2392 | |
2393 | status = stat(filename, filestats); | |
2394 | } | |
2395 | ||
58e8cf34 | 2396 | LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d", |
aa7e125a | 2397 | con->http.fd, filename, status ? -1 : (int)filestats->st_size); |
e31bfb6e | 2398 | |
2399 | if (status) | |
2400 | return (NULL); | |
2401 | else | |
2402 | return (filename); | |
2403 | } | |
2404 | ||
2405 | ||
753453e4 | 2406 | /* |
2407 | * 'install_conf_file()' - Install a configuration file. | |
2408 | */ | |
2409 | ||
2410 | static http_status_t /* O - Status */ | |
2411 | install_conf_file(client_t *con) /* I - Connection */ | |
2412 | { | |
2413 | FILE *in, /* Input file */ | |
2414 | *out; /* Output file */ | |
2415 | char buffer[1024]; /* Copy buffer */ | |
2416 | int bytes; /* Number of bytes */ | |
2417 | char conffile[1024], /* Configuration filename */ | |
2418 | newfile[1024], /* New config filename */ | |
2419 | oldfile[1024]; /* Old config filename */ | |
2420 | struct stat confinfo; /* Config file info */ | |
2421 | ||
2422 | ||
2423 | /* | |
2424 | * First construct the filenames... | |
2425 | */ | |
2426 | ||
2427 | snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11); | |
2428 | snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11); | |
2429 | snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11); | |
2430 | ||
2431 | LogMessage(L_INFO, "Installing config file \"%s\"...", conffile); | |
2432 | ||
2433 | /* | |
2434 | * Get the owner, group, and permissions of the configuration file. | |
2435 | * If it doesn't exist, assign it to the User and Group in the | |
2436 | * cupsd.conf file with mode 0640 permissions. | |
2437 | */ | |
2438 | ||
2439 | if (stat(conffile, &confinfo)) | |
2440 | { | |
2441 | confinfo.st_uid = User; | |
2442 | confinfo.st_gid = Group; | |
2443 | confinfo.st_mode = 0640; | |
2444 | } | |
2445 | ||
2446 | /* | |
2447 | * Open the request file and new config file... | |
2448 | */ | |
2449 | ||
2450 | if ((in = fopen(con->filename, "rb")) == NULL) | |
2451 | { | |
2452 | LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s", | |
2453 | con->filename, strerror(errno)); | |
2454 | return (HTTP_SERVER_ERROR); | |
2455 | } | |
2456 | ||
2457 | if ((out = fopen(newfile, "wb")) == NULL) | |
2458 | { | |
2459 | fclose(in); | |
2460 | LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s", | |
2461 | newfile, strerror(errno)); | |
2462 | return (HTTP_SERVER_ERROR); | |
2463 | } | |
2464 | ||
2465 | fchmod(fileno(out), confinfo.st_mode); | |
2466 | fchown(fileno(out), confinfo.st_uid, confinfo.st_gid); | |
2467 | ||
2468 | /* | |
2469 | * Copy from the request to the new config file... | |
2470 | */ | |
2471 | ||
2472 | while ((bytes = fread(buffer, 1, sizeof(buffer), in)) > 0) | |
2473 | if (fwrite(buffer, 1, bytes, out) < bytes) | |
2474 | { | |
2475 | LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s", | |
2476 | newfile, strerror(errno)); | |
2477 | ||
2478 | fclose(in); | |
2479 | fclose(out); | |
2480 | unlink(newfile); | |
2481 | ||
2482 | return (HTTP_SERVER_ERROR); | |
2483 | } | |
2484 | ||
2485 | /* | |
2486 | * Close the files... | |
2487 | */ | |
2488 | ||
2489 | fclose(in); | |
2490 | if (fclose(out)) | |
2491 | { | |
2492 | LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s", | |
2493 | newfile, strerror(errno)); | |
2494 | ||
2495 | unlink(newfile); | |
2496 | ||
2497 | return (HTTP_SERVER_ERROR); | |
2498 | } | |
2499 | ||
2500 | /* | |
2501 | * Remove the request file... | |
2502 | */ | |
2503 | ||
2504 | unlink(con->filename); | |
2505 | con->filename[0] = '\0'; | |
2506 | ||
2507 | /* | |
2508 | * Unlink the old backup, rename the current config file to the backup | |
2509 | * filename, and rename the new config file to the config file name... | |
2510 | */ | |
2511 | ||
2512 | if (unlink(oldfile)) | |
2513 | if (errno != ENOENT) | |
2514 | { | |
2515 | LogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s", | |
2516 | oldfile, strerror(errno)); | |
2517 | ||
2518 | unlink(newfile); | |
2519 | ||
2520 | return (HTTP_SERVER_ERROR); | |
2521 | } | |
2522 | ||
2523 | if (rename(conffile, oldfile)) | |
2524 | if (errno != ENOENT) | |
2525 | { | |
2526 | LogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s", | |
2527 | conffile, strerror(errno)); | |
2528 | ||
2529 | unlink(newfile); | |
2530 | ||
2531 | return (HTTP_SERVER_ERROR); | |
2532 | } | |
2533 | ||
2534 | if (rename(newfile, conffile)) | |
2535 | { | |
2536 | LogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s", | |
2537 | newfile, strerror(errno)); | |
2538 | ||
2539 | rename(oldfile, conffile); | |
2540 | unlink(newfile); | |
2541 | ||
2542 | return (HTTP_SERVER_ERROR); | |
2543 | } | |
2544 | ||
2545 | /* | |
2546 | * If the cupsd.conf file was updated, set the NeedReload flag... | |
2547 | */ | |
2548 | ||
2549 | if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0) | |
2550 | NeedReload = TRUE; | |
2551 | ||
2552 | /* | |
2553 | * Return that the file was created successfully... | |
2554 | */ | |
2555 | ||
2556 | return (HTTP_CREATED); | |
2557 | } | |
2558 | ||
2559 | ||
a74b005d | 2560 | /* |
2561 | * 'pipe_command()' - Pipe the output of a command to the remote client. | |
2562 | */ | |
2563 | ||
879062a9 | 2564 | static int /* O - Process ID */ |
2565 | pipe_command(client_t *con, /* I - Client connection */ | |
2566 | int infile, /* I - Standard input for command */ | |
2567 | int *outfile, /* O - Standard output for command */ | |
2568 | char *command, /* I - Command to run */ | |
2569 | char *options) /* I - Options for command */ | |
a74b005d | 2570 | { |
879062a9 | 2571 | int pid; /* Process ID */ |
2572 | char *commptr; /* Command string pointer */ | |
2573 | int fd; /* Looping var */ | |
2574 | int fds[2]; /* Pipe FDs */ | |
2575 | int argc; /* Number of arguments */ | |
2576 | int envc; /* Number of environment variables */ | |
2577 | char argbuf[10240], /* Argument buffer */ | |
2578 | *argv[100], /* Argument strings */ | |
2579 | *envp[100]; /* Environment variables */ | |
2580 | char lang[1024], /* LANG env variable */ | |
2581 | content_length[1024], /* CONTENT_LENGTH env variable */ | |
2582 | content_type[1024], /* CONTENT_TYPE env variable */ | |
2583 | ipp_port[1024], /* Default listen port */ | |
2584 | server_port[1024], /* Default server port */ | |
2585 | server_name[1024], /* Default listen hostname */ | |
2586 | remote_host[1024], /* REMOTE_HOST env variable */ | |
2587 | remote_user[1024], /* REMOTE_USER env variable */ | |
2588 | tmpdir[1024], /* TMPDIR environment variable */ | |
2589 | ldpath[1024], /* LD_LIBRARY_PATH environment variable */ | |
2590 | nlspath[1024], /* NLSPATH environment variable */ | |
2591 | datadir[1024], /* CUPS_DATADIR environment variable */ | |
2592 | root[1024], /* CUPS_SERVERROOT environment variable */ | |
2593 | query_string[10240]; /* QUERY_STRING env variable */ | |
2594 | #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) | |
2595 | sigset_t oldmask, /* POSIX signal masks */ | |
2596 | newmask; | |
c7a9d594 | 2597 | struct sigaction action; /* POSIX signal handler */ |
879062a9 | 2598 | #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ |
a74b005d | 2599 | |
05e63c18 | 2600 | |
a74b005d | 2601 | /* |
2602 | * Copy the command string... | |
2603 | */ | |
2604 | ||
def978d5 | 2605 | strlcpy(argbuf, options, sizeof(argbuf)); |
a74b005d | 2606 | |
2607 | /* | |
f63a2256 | 2608 | * Parse the string; arguments can be separated by + and are terminated |
2609 | * by ?... | |
a74b005d | 2610 | */ |
2611 | ||
2612 | argv[0] = argbuf; | |
2613 | ||
bfa1abf0 | 2614 | for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++) |
f63a2256 | 2615 | if (*commptr == ' ' || *commptr == '+') |
a74b005d | 2616 | { |
2617 | *commptr++ = '\0'; | |
2618 | ||
2619 | while (*commptr == ' ') | |
2620 | commptr ++; | |
2621 | ||
2622 | if (*commptr != '\0') | |
2623 | { | |
2624 | argv[argc] = commptr; | |
2625 | argc ++; | |
2626 | } | |
2627 | ||
2628 | commptr --; | |
2629 | } | |
2630 | else if (*commptr == '%') | |
2631 | { | |
2632 | if (commptr[1] >= '0' && commptr[1] <= '9') | |
2633 | *commptr = (commptr[1] - '0') << 4; | |
2634 | else | |
2635 | *commptr = (tolower(commptr[1]) - 'a' + 10) << 4; | |
2636 | ||
2637 | if (commptr[2] >= '0' && commptr[2] <= '9') | |
2638 | *commptr |= commptr[2] - '0'; | |
2639 | else | |
2640 | *commptr |= tolower(commptr[2]) - 'a' + 10; | |
2641 | ||
2642 | strcpy(commptr + 1, commptr + 3); | |
2643 | } | |
f63a2256 | 2644 | else if (*commptr == '?') |
2645 | break; | |
a74b005d | 2646 | |
2647 | argv[argc] = NULL; | |
2648 | ||
bfa1abf0 | 2649 | if (argv[0][0] == '\0') |
2650 | argv[0] = strrchr(command, '/') + 1; | |
b14d90ba | 2651 | |
2652 | /* | |
2653 | * Setup the environment variables as needed... | |
2654 | */ | |
2655 | ||
a6988fb1 | 2656 | snprintf(lang, sizeof(lang), "LANG=%s", |
2657 | con->language ? con->language->language : "C"); | |
99de6da0 | 2658 | #ifdef AF_INET6 |
2659 | if (con->http.hostaddr.addr.sa_family == AF_INET6) | |
2660 | { | |
2661 | sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv6.sin6_port)); | |
2662 | sprintf(server_port, "SERVER_PORT=%d", | |
2663 | ntohs(con->http.hostaddr.ipv6.sin6_port)); | |
2664 | } | |
2665 | else | |
2666 | #endif /* AF_INET6 */ | |
2667 | { | |
2668 | sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv4.sin_port)); | |
2669 | sprintf(server_port, "SERVER_PORT=%d", | |
2670 | ntohs(con->http.hostaddr.ipv4.sin_port)); | |
2671 | } | |
2672 | ||
c5d918d8 | 2673 | if (strcmp(con->http.hostname, "localhost") == 0) |
2674 | strlcpy(server_name, "SERVER_NAME=localhost", sizeof(server_name)); | |
2675 | else | |
2676 | snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName); | |
04de52f8 | 2677 | snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname); |
2678 | snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username); | |
2679 | snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir); | |
2680 | snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir); | |
2681 | snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot); | |
df3f73ff | 2682 | |
2683 | if (getenv("LD_LIBRARY_PATH") != NULL) | |
1cf29e95 | 2684 | snprintf(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s", |
2685 | getenv("LD_LIBRARY_PATH")); | |
2686 | else if (getenv("DYLD_LIBRARY_PATH") != NULL) | |
2687 | snprintf(ldpath, sizeof(ldpath), "DYLD_LIBRARY_PATH=%s", | |
2688 | getenv("DYLD_LIBRARY_PATH")); | |
103abc03 | 2689 | else if (getenv("SHLIB_PATH") != NULL) |
2690 | snprintf(ldpath, sizeof(ldpath), "SHLIB_PATH=%s", | |
2691 | getenv("SHLIB_PATH")); | |
df3f73ff | 2692 | else |
2693 | ldpath[0] = '\0'; | |
7f0679f5 | 2694 | |
1cf29e95 | 2695 | if (getenv("NLSPATH") != NULL) |
2696 | snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH")); | |
2697 | else | |
2698 | nlspath[0] = '\0'; | |
2699 | ||
7f0679f5 | 2700 | envp[0] = "PATH=/bin:/usr/bin"; |
99de6da0 | 2701 | envp[1] = "SERVER_SOFTWARE=CUPS/1.2"; |
7f0679f5 | 2702 | envp[2] = "GATEWAY_INTERFACE=CGI/1.1"; |
2703 | envp[3] = "SERVER_PROTOCOL=HTTP/1.1"; | |
57c77867 | 2704 | envp[4] = ipp_port; |
2705 | envp[5] = server_name; | |
2706 | envp[6] = server_port; | |
2707 | envp[7] = remote_host; | |
2708 | envp[8] = remote_user; | |
2709 | envp[9] = lang; | |
05e63c18 | 2710 | envp[10] = TZ; |
57c77867 | 2711 | envp[11] = tmpdir; |
df3f73ff | 2712 | envp[12] = datadir; |
245bf65d | 2713 | envp[13] = root; |
df3f73ff | 2714 | |
245bf65d | 2715 | envc = 14; |
df3f73ff | 2716 | |
2717 | if (ldpath[0]) | |
2718 | envp[envc ++] = ldpath; | |
b14d90ba | 2719 | |
1cf29e95 | 2720 | if (nlspath[0]) |
2721 | envp[envc ++] = nlspath; | |
2722 | ||
b14d90ba | 2723 | if (con->operation == HTTP_GET) |
2724 | { | |
df3f73ff | 2725 | envp[envc ++] = "REQUEST_METHOD=GET"; |
f63a2256 | 2726 | |
2727 | if (*commptr) | |
2728 | { | |
2729 | /* | |
2730 | * Add GET form variables after ?... | |
2731 | */ | |
2732 | ||
2733 | *commptr++ = '\0'; | |
2734 | ||
04de52f8 | 2735 | snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr); |
df3f73ff | 2736 | envp[envc ++] = query_string; |
f63a2256 | 2737 | } |
b14d90ba | 2738 | } |
2739 | else | |
2740 | { | |
f63a2256 | 2741 | sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes); |
04de52f8 | 2742 | snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s", |
970017a4 | 2743 | con->http.fields[HTTP_FIELD_CONTENT_TYPE]); |
b14d90ba | 2744 | |
df3f73ff | 2745 | envp[envc ++] = "REQUEST_METHOD=POST"; |
2746 | envp[envc ++] = content_length; | |
2747 | envp[envc ++] = content_type; | |
b14d90ba | 2748 | } |
2749 | ||
30d60919 | 2750 | /* |
2751 | * Tell the CGI if we are using encryption... | |
2752 | */ | |
2753 | ||
b92c5a4f | 2754 | if (con->http.encryption == HTTP_ENCRYPT_ALWAYS) |
2755 | { | |
30d60919 | 2756 | envp[envc ++] = "HTTPS=ON"; |
b92c5a4f | 2757 | envp[envc ++] = "CUPS_ENCRYPTION=Always"; |
2758 | } | |
30d60919 | 2759 | |
df3f73ff | 2760 | envp[envc] = NULL; |
2761 | ||
a74b005d | 2762 | /* |
2763 | * Create a pipe for the output... | |
2764 | */ | |
2765 | ||
2766 | if (pipe(fds)) | |
1d6c68fb | 2767 | { |
5ea8888e | 2768 | LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s", |
1d6c68fb | 2769 | argv[0], strerror(errno)); |
a74b005d | 2770 | return (0); |
1d6c68fb | 2771 | } |
a74b005d | 2772 | |
879062a9 | 2773 | /* |
2774 | * Block signals before forking... | |
2775 | */ | |
2776 | ||
5c8eff27 | 2777 | #ifdef HAVE_SIGSET |
2778 | sighold(SIGTERM); | |
2779 | sighold(SIGCHLD); | |
2780 | #elif defined(HAVE_SIGACTION) | |
879062a9 | 2781 | sigemptyset(&newmask); |
2782 | sigaddset(&newmask, SIGTERM); | |
2783 | sigaddset(&newmask, SIGCHLD); | |
2784 | sigprocmask(SIG_BLOCK, &newmask, &oldmask); | |
5c8eff27 | 2785 | #endif /* HAVE_SIGSET */ |
879062a9 | 2786 | |
a74b005d | 2787 | /* |
1d6c68fb | 2788 | * Then execute the command... |
a74b005d | 2789 | */ |
2790 | ||
2791 | if ((pid = fork()) == 0) | |
2792 | { | |
2793 | /* | |
2794 | * Child comes here... Close stdin if necessary and dup the pipe to stdout. | |
2795 | */ | |
2796 | ||
5aeb433c | 2797 | if (getuid() == 0) |
2798 | { | |
2799 | /* | |
753453e4 | 2800 | * Running as root, so change to a non-priviledged user... |
5aeb433c | 2801 | */ |
2802 | ||
2803 | if (setgid(Group)) | |
2804 | exit(errno); | |
2805 | ||
2806 | if (setuid(User)) | |
2807 | exit(errno); | |
2808 | } | |
2809 | ||
2810 | /* | |
2811 | * Reset group membership to just the main one we belong to. | |
2812 | */ | |
2813 | ||
2814 | setgroups(0, NULL); | |
2815 | ||
2816 | /* | |
a9775e85 | 2817 | * Update stdin/stdout/stderr... |
5aeb433c | 2818 | */ |
b14d90ba | 2819 | |
a74b005d | 2820 | if (infile) |
2821 | { | |
2822 | close(0); | |
753453e4 | 2823 | if (dup(infile) < 0) |
2824 | exit(errno); | |
a74b005d | 2825 | } |
2826 | ||
2827 | close(1); | |
753453e4 | 2828 | if (dup(fds[1]) < 0) |
2829 | exit(errno); | |
a74b005d | 2830 | |
a9775e85 | 2831 | close(2); |
2832 | open("/dev/null", O_WRONLY); | |
2833 | ||
082b40d2 | 2834 | /* |
2835 | * Close extra file descriptors... | |
2836 | */ | |
2837 | ||
93336dbc | 2838 | for (fd = 3; fd < MaxFDs; fd ++) |
082b40d2 | 2839 | close(fd); |
2840 | ||
2841 | /* | |
2842 | * Change umask to restrict permissions on created files... | |
2843 | */ | |
2844 | ||
2845 | umask(077); | |
a74b005d | 2846 | |
5f152161 | 2847 | /* |
2848 | * Unblock signals before doing the exec... | |
2849 | */ | |
2850 | ||
2851 | #ifdef HAVE_SIGSET | |
c7a9d594 | 2852 | sigset(SIGTERM, SIG_DFL); |
2853 | sigset(SIGCHLD, SIG_DFL); | |
2854 | ||
5f152161 | 2855 | sigrelse(SIGTERM); |
2856 | sigrelse(SIGCHLD); | |
2857 | #elif defined(HAVE_SIGACTION) | |
c7a9d594 | 2858 | memset(&action, 0, sizeof(action)); |
2859 | ||
2860 | sigemptyset(&action.sa_mask); | |
2861 | action.sa_handler = SIG_DFL; | |
2862 | ||
2863 | sigaction(SIGTERM, &action, NULL); | |
2864 | sigaction(SIGCHLD, &action, NULL); | |
2865 | ||
5f152161 | 2866 | sigprocmask(SIG_SETMASK, &oldmask, NULL); |
c7a9d594 | 2867 | #else |
2868 | signal(SIGTERM, SIG_DFL); | |
2869 | signal(SIGCHLD, SIG_DFL); | |
5f152161 | 2870 | #endif /* HAVE_SIGSET */ |
2871 | ||
c7a9d594 | 2872 | |
a74b005d | 2873 | /* |
2874 | * Execute the pipe program; if an error occurs, exit with status 1... | |
2875 | */ | |
2876 | ||
b14d90ba | 2877 | execve(command, argv, envp); |
96df88bb | 2878 | exit(errno); |
a74b005d | 2879 | return (0); |
2880 | } | |
2881 | else if (pid < 0) | |
2882 | { | |
2883 | /* | |
2884 | * Error - can't fork! | |
2885 | */ | |
2886 | ||
5ea8888e | 2887 | LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0], |
1d6c68fb | 2888 | strerror(errno)); |
2889 | ||
a74b005d | 2890 | close(fds[0]); |
2891 | close(fds[1]); | |
5c8eff27 | 2892 | pid = 0; |
a74b005d | 2893 | } |
2894 | else | |
2895 | { | |
2896 | /* | |
2897 | * Fork successful - return the PID... | |
2898 | */ | |
2899 | ||
f63a2256 | 2900 | AddCert(pid, con->username); |
2901 | ||
753453e4 | 2902 | LogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid); |
1d6c68fb | 2903 | |
a74b005d | 2904 | *outfile = fds[0]; |
2905 | close(fds[1]); | |
5c8eff27 | 2906 | } |
a74b005d | 2907 | |
5c8eff27 | 2908 | #ifdef HAVE_SIGSET |
2909 | sigrelse(SIGTERM); | |
2910 | sigrelse(SIGCHLD); | |
2911 | #elif defined(HAVE_SIGACTION) | |
2912 | sigprocmask(SIG_SETMASK, &oldmask, NULL); | |
2913 | #endif /* HAVE_SIGSET */ | |
879062a9 | 2914 | |
5c8eff27 | 2915 | return (pid); |
a74b005d | 2916 | } |
2917 | ||
2918 | ||
dcfcaeac | 2919 | #if defined(HAVE_CDSASSL) |
2920 | /* | |
2921 | * 'CDSAReadFunc()' - Read function for CDSA decryption code. | |
2922 | */ | |
2923 | ||
2924 | static OSStatus /* O - -1 on error, 0 on success */ | |
2925 | CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */ | |
2926 | void *data, /* I - Data buffer */ | |
2927 | size_t *dataLength) /* IO - Number of bytes */ | |
2928 | { | |
2929 | ssize_t bytes; /* Number of bytes read */ | |
2930 | ||
2931 | ||
2932 | bytes = recv((int)connection, data, *dataLength, 0); | |
2933 | if (bytes >= 0) | |
2934 | { | |
2935 | *dataLength = bytes; | |
2936 | return (0); | |
2937 | } | |
2938 | else | |
2939 | return (-1); | |
2940 | } | |
2941 | ||
2942 | ||
2943 | /* | |
2944 | * 'CDSAWriteFunc()' - Write function for CDSA encryption code. | |
2945 | */ | |
2946 | ||
2947 | static OSStatus /* O - -1 on error, 0 on success */ | |
2948 | CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */ | |
2949 | const void *data, /* I - Data buffer */ | |
2950 | size_t *dataLength) /* IO - Number of bytes */ | |
2951 | { | |
2952 | ssize_t bytes; | |
2953 | ||
2954 | ||
2955 | bytes = write((int)connection, data, *dataLength); | |
2956 | if (bytes >= 0) | |
2957 | { | |
2958 | *dataLength = bytes; | |
2959 | return (0); | |
2960 | } | |
2961 | else | |
2962 | return (-1); | |
2963 | } | |
2964 | #endif /* HAVE_CDSASSL */ | |
2965 | ||
2966 | ||
93894a43 | 2967 | /* |
6db7190f | 2968 | * End of "$Id: client.c,v 1.91.2.40 2003/01/24 20:45:18 mike Exp $". |
a74b005d | 2969 | */ |