]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c 5083 2006-02-06 02:57:43Z mike $"
3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
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
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * cupsdAcceptClient() - Accept a new client.
27 * cupsdCloseAllClients() - Close all remote clients immediately.
28 * cupsdCloseClient() - Close a remote client.
29 * cupsdEncryptClient() - Enable encryption for the client...
30 * cupsdIsCGI() - Is the resource a CGI script/program?
31 * cupsdReadClient() - Read data from a client.
32 * cupsdSendCommand() - Send output from a command via HTTP.
33 * cupsdSendError() - Send an error message via HTTP.
34 * cupsdSendFile() - Send a file via HTTP.
35 * cupsdSendHeader() - Send an HTTP request.
36 * cupsdUpdateCGI() - Read status messages from CGI scripts and programs.
37 * cupsdWriteClient() - Write data to a client as needed.
38 * check_if_modified() - Decode an "If-Modified-Since" line.
39 * get_cdsa_server_certs() - Convert a keychain name into the CFArrayRef
40 * required by SSLSetCertificate.
41 * get_file() - Get a filename and state info.
42 * install_conf_file() - Install a configuration file.
43 * is_path_absolute() - Is a path absolute and free of relative elements.
44 * pipe_command() - Pipe the output of a command to the remote client.
45 */
46
47 /*
48 * Include necessary headers...
49 */
50
51 #include <cups/http-private.h>
52 #include "cupsd.h"
53
54 #ifdef HAVE_CDSASSL
55 # include <Security/Security.h>
56 #endif /* HAVE_CDSASSL */
57
58
59 /*
60 * Local functions...
61 */
62
63 static int check_if_modified(cupsd_client_t *con,
64 struct stat *filestats);
65 #ifdef HAVE_CDSASSL
66 static CFArrayRef get_cdsa_server_certs(void);
67 #endif /* HAVE_CDSASSL */
68 static char *get_file(cupsd_client_t *con, struct stat *filestats,
69 char *filename, int len);
70 static http_status_t install_conf_file(cupsd_client_t *con);
71 static int is_path_absolute(const char *path);
72 static int pipe_command(cupsd_client_t *con, int infile, int *outfile,
73 char *command, char *options, int root);
74
75
76 /*
77 * 'cupsdAcceptClient()' - Accept a new client.
78 */
79
80 void
81 cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
82 {
83 int count; /* Count of connections on a host */
84 int val; /* Parameter value */
85 cupsd_client_t *con, /* New client pointer */
86 *tempcon; /* Temporary client pointer */
87 http_addrlist_t *addrlist, /* List of adddresses for host */
88 *addr; /* Current address */
89 socklen_t addrlen; /* Length of address */
90 char *hostname; /* Hostname for address */
91 http_addr_t temp; /* Temporary address variable */
92 static time_t last_dos = 0; /* Time of last DoS attack */
93
94
95 cupsdLogMessage(CUPSD_LOG_DEBUG2,
96 "cupsdAcceptClient(lis=%p) %d Clients = %d",
97 lis, lis->fd, cupsArrayCount(Clients));
98
99 /*
100 * Make sure we don't have a full set of clients already...
101 */
102
103 if (cupsArrayCount(Clients) == MaxClients)
104 return;
105
106 /*
107 * Get a pointer to the next available client...
108 */
109
110 if (!Clients)
111 Clients = cupsArrayNew(NULL, NULL);
112
113 if (!Clients)
114 return;
115
116 con = calloc(1, sizeof(cupsd_client_t));
117
118 con->http.activity = time(NULL);
119 con->file = -1;
120 con->http.hostaddr = &(con->clientaddr);
121
122 /*
123 * Accept the client and get the remote address...
124 */
125
126 addrlen = sizeof(http_addr_t);
127
128 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)con->http.hostaddr,
129 &addrlen)) < 0)
130 {
131 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.",
132 strerror(errno));
133 free(con);
134 return;
135 }
136
137 #ifdef AF_INET6
138 if (lis->address.addr.sa_family == AF_INET6)
139 {
140 /*
141 * Save the connected port number...
142 */
143
144 con->http.hostaddr->ipv6.sin6_port = lis->address.ipv6.sin6_port;
145
146 /*
147 * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we
148 * can more easily use...
149 */
150
151 if (con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0] == 0 &&
152 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1] == 0 &&
153 ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]) == 0xffff)
154 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2] = 0;
155 }
156 else
157 #endif /* AF_INET6 */
158 if (lis->address.addr.sa_family == AF_INET)
159 con->http.hostaddr->ipv4.sin_port = lis->address.ipv4.sin_port;
160
161 /*
162 * Check the number of clients on the same address...
163 */
164
165 for (count = 0, tempcon = (cupsd_client_t *)cupsArrayFirst(Clients);
166 tempcon;
167 tempcon = (cupsd_client_t *)cupsArrayNext(Clients))
168 if (httpAddrEqual(tempcon->http.hostaddr, con->http.hostaddr))
169 {
170 count ++;
171 if (count >= MaxClientsPerHost)
172 break;
173 }
174
175 if (count >= MaxClientsPerHost)
176 {
177 if ((time(NULL) - last_dos) >= 60)
178 {
179 last_dos = time(NULL);
180 cupsdLogMessage(CUPSD_LOG_WARN,
181 "Possible DoS attack - more than %d clients connecting "
182 "from %s!",
183 MaxClientsPerHost, tempcon->http.hostname);
184 }
185
186 #ifdef WIN32
187 closesocket(con->http.fd);
188 #else
189 close(con->http.fd);
190 #endif /* WIN32 */
191
192 free(con);
193 return;
194 }
195
196 /*
197 * Get the hostname or format the IP address as needed...
198 */
199
200 if (httpAddrLocalhost(con->http.hostaddr))
201 {
202 /*
203 * Map accesses from the loopback interface to "localhost"...
204 */
205
206 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
207 hostname = con->http.hostname;
208 }
209 else
210 {
211 /*
212 * Map accesses from the same host to the server name.
213 */
214
215 for (addr = ServerAddrs; addr; addr = addr->next)
216 if (httpAddrEqual(con->http.hostaddr, &(addr->addr)))
217 break;
218
219 if (addr)
220 {
221 strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname));
222 hostname = con->http.hostname;
223 }
224 else if (HostNameLookups)
225 hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname,
226 sizeof(con->http.hostname));
227 else
228 {
229 hostname = NULL;
230 httpAddrString(con->http.hostaddr, con->http.hostname,
231 sizeof(con->http.hostname));
232 }
233 }
234
235 if (hostname == NULL && HostNameLookups == 2)
236 {
237 /*
238 * Can't have an unresolved IP address with double-lookups enabled...
239 */
240
241 cupsdLogMessage(CUPSD_LOG_DEBUG2,
242 "cupsdAcceptClient: Closing connection %d...",
243 con->http.fd);
244
245 #ifdef WIN32
246 closesocket(con->http.fd);
247 #else
248 close(con->http.fd);
249 #endif /* WIN32 */
250
251 cupsdLogMessage(CUPSD_LOG_WARN,
252 "Name lookup failed - connection from %s closed!",
253 con->http.hostname);
254
255 free(con);
256 return;
257 }
258
259 if (HostNameLookups == 2)
260 {
261 /*
262 * Do double lookups as needed...
263 */
264
265 if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL)) != NULL)
266 {
267 /*
268 * See if the hostname maps to the same IP address...
269 */
270
271 for (addr = addrlist; addr; addr = addr->next)
272 if (httpAddrEqual(con->http.hostaddr, &(addr->addr)))
273 break;
274 }
275 else
276 addr = NULL;
277
278 httpAddrFreeList(addrlist);
279
280 if (!addr)
281 {
282 /*
283 * Can't have a hostname that doesn't resolve to the same IP address
284 * with double-lookups enabled...
285 */
286
287 cupsdLogMessage(CUPSD_LOG_DEBUG2,
288 "cupsdAcceptClient: Closing connection %d...",
289 con->http.fd);
290
291 #ifdef WIN32
292 closesocket(con->http.fd);
293 #else
294 close(con->http.fd);
295 #endif /* WIN32 */
296
297 cupsdLogMessage(CUPSD_LOG_WARN,
298 "IP lookup failed - connection from %s closed!",
299 con->http.hostname);
300 free(con);
301 return;
302 }
303 }
304
305 #ifdef AF_INET6
306 if (con->http.hostaddr->addr.sa_family == AF_INET6)
307 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv6)",
308 con->http.fd, con->http.hostname,
309 ntohs(con->http.hostaddr->ipv6.sin6_port));
310 else
311 #endif /* AF_INET6 */
312 #ifdef AF_LOCAL
313 if (con->http.hostaddr->addr.sa_family == AF_LOCAL)
314 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s (Domain)",
315 con->http.fd, con->http.hostname);
316 else
317 #endif /* AF_LOCAL */
318 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv4)",
319 con->http.fd, con->http.hostname,
320 ntohs(con->http.hostaddr->ipv4.sin_port));
321
322 /*
323 * Get the local address the client connected to...
324 */
325
326 addrlen = sizeof(temp);
327 if (getsockname(con->http.fd, (struct sockaddr *)&temp, &addrlen))
328 {
329 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get local address - %s",
330 strerror(errno));
331
332 strcpy(con->servername, "localhost");
333 con->serverport = LocalPort;
334 }
335 else
336 {
337 #ifdef AF_INET6
338 if (temp.addr.sa_family == AF_INET6)
339 {
340 httpAddrLookup(&temp, con->servername, sizeof(con->servername));
341 con->serverport = ntohs(lis->address.ipv6.sin6_port);
342 }
343 else
344 #endif /* AF_INET6 */
345 if (temp.addr.sa_family == AF_INET)
346 {
347 httpAddrLookup(&temp, con->servername, sizeof(con->servername));
348 con->serverport = ntohs(lis->address.ipv4.sin_port);
349 }
350 else
351 {
352 strcpy(con->servername, "localhost");
353 con->serverport = LocalPort;
354 }
355 }
356
357 cupsArrayAdd(Clients, con);
358
359 cupsdLogMessage(CUPSD_LOG_DEBUG2,
360 "cupsdAcceptClient: %d connected to server on %s:%d",
361 con->http.fd, con->servername, con->serverport);
362
363 /*
364 * Using TCP_NODELAY improves responsiveness, especially on systems
365 * with a slow loopback interface... Since we write large buffers
366 * when sending print files and requests, there shouldn't be any
367 * performance penalty for this...
368 */
369
370 val = 1;
371 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
372
373 /*
374 * Close this file on all execs...
375 */
376
377 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
378
379 /*
380 * Add the socket to the select() input mask.
381 */
382
383 cupsdLogMessage(CUPSD_LOG_DEBUG2,
384 "cupsdAcceptClient: Adding fd %d to InputSet...",
385 con->http.fd);
386 FD_SET(con->http.fd, InputSet);
387
388 /*
389 * Temporarily suspend accept()'s until we lose a client...
390 */
391
392 if (cupsArrayCount(Clients) == MaxClients)
393 cupsdPauseListening();
394
395 #ifdef HAVE_SSL
396 /*
397 * See if we are connecting on a secure port...
398 */
399
400 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
401 {
402 /*
403 * https connection; go secure...
404 */
405
406 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
407
408 cupsdEncryptClient(con);
409 }
410 else
411 con->auto_ssl = 1;
412 #endif /* HAVE_SSL */
413 }
414
415
416 /*
417 * 'cupsdCloseAllClients()' - Close all remote clients immediately.
418 */
419
420 void
421 cupsdCloseAllClients(void)
422 {
423 cupsd_client_t *con; /* Current client */
424
425
426 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
427 con;
428 con = (cupsd_client_t *)cupsArrayNext(Clients))
429 cupsdCloseClient(con);
430 }
431
432
433 /*
434 * 'cupsdCloseClient()' - Close a remote client.
435 */
436
437 int /* O - 1 if partial close, 0 if fully closed */
438 cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */
439 {
440 int partial; /* Do partial close for SSL? */
441 #ifdef HAVE_LIBSSL
442 SSL_CTX *context; /* Context for encryption */
443 SSL *conn; /* Connection for encryption */
444 unsigned long error; /* Error code */
445 #elif defined(HAVE_GNUTLS)
446 http_tls_t *conn; /* TLS connection information */
447 int error; /* Error code */
448 gnutls_certificate_server_credentials *credentials;
449 /* TLS credentials */
450 #endif /* HAVE_LIBSSL */
451
452
453 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCloseClient: %d", con->http.fd);
454
455 /*
456 * Flush pending writes before closing...
457 */
458
459 httpFlushWrite(HTTP(con));
460
461 partial = 0;
462
463 #ifdef HAVE_SSL
464 /*
465 * Shutdown encryption as needed...
466 */
467
468 if (con->http.tls)
469 {
470 partial = 1;
471
472 # ifdef HAVE_LIBSSL
473 conn = (SSL *)(con->http.tls);
474 context = SSL_get_SSL_CTX(conn);
475
476 switch (SSL_shutdown(conn))
477 {
478 case 1 :
479 cupsdLogMessage(CUPSD_LOG_INFO,
480 "cupsdCloseClient: SSL shutdown successful!");
481 break;
482 case -1 :
483 cupsdLogMessage(CUPSD_LOG_ERROR,
484 "cupsdCloseClient: Fatal error during SSL shutdown!");
485 default :
486 while ((error = ERR_get_error()) != 0)
487 cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCloseClient: %s",
488 ERR_error_string(error, NULL));
489 break;
490 }
491
492 SSL_CTX_free(context);
493 SSL_free(conn);
494
495 # elif defined(HAVE_GNUTLS)
496 conn = (http_tls_t *)(con->http.tls);
497 credentials = (gnutls_certificate_server_credentials *)(conn->credentials);
498
499 error = gnutls_bye(conn->session, GNUTLS_SHUT_WR);
500 switch (error)
501 {
502 case GNUTLS_E_SUCCESS:
503 cupsdLogMessage(CUPSD_LOG_INFO,
504 "cupsdCloseClient: SSL shutdown successful!");
505 break;
506 default:
507 cupsdLogMessage(CUPSD_LOG_ERROR,
508 "cupsdCloseClient: %s", gnutls_strerror(error));
509 break;
510 }
511
512 gnutls_deinit(conn->session);
513 gnutls_certificate_free_credentials(*credentials);
514 free(credentials);
515 free(conn);
516
517 # elif defined(HAVE_CDSASSL)
518 SSLClose((SSLContextRef)con->http.tls);
519 SSLDisposeContext((SSLContextRef)con->http.tls);
520 # endif /* HAVE_LIBSSL */
521
522 con->http.tls = NULL;
523 }
524 #endif /* HAVE_SSL */
525
526 if (con->pipe_pid != 0)
527 {
528 /*
529 * Stop any CGI process...
530 */
531
532 cupsdLogMessage(CUPSD_LOG_DEBUG2,
533 "cupsdCloseClient: %d Killing process ID %d...",
534 con->http.fd, con->pipe_pid);
535 cupsdEndProcess(con->pipe_pid, 1);
536 con->pipe_pid = 0;
537 }
538
539 if (con->file >= 0)
540 {
541 if (FD_ISSET(con->file, InputSet))
542 {
543 cupsdLogMessage(CUPSD_LOG_DEBUG2,
544 "cupsdCloseClient: %d Removing fd %d from InputSet...",
545 con->http.fd, con->file);
546 FD_CLR(con->file, InputSet);
547 }
548
549 cupsdLogMessage(CUPSD_LOG_DEBUG2,
550 "cupsdCloseClient: %d Closing data file %d.",
551 con->http.fd, con->file);
552
553 close(con->file);
554 con->file = -1;
555 }
556
557 /*
558 * Close the socket and clear the file from the input set for select()...
559 */
560
561 if (con->http.fd > 0)
562 {
563 if (partial)
564 {
565 /*
566 * Only do a partial close so that the encrypted client gets everything.
567 */
568
569 cupsdLogMessage(CUPSD_LOG_DEBUG2,
570 "cupsdCloseClient: Removing fd %d from OutputSet...",
571 con->http.fd);
572 shutdown(con->http.fd, 0);
573 FD_CLR(con->http.fd, OutputSet);
574 }
575 else
576 {
577 /*
578 * Shut the socket down fully...
579 */
580
581 cupsdLogMessage(CUPSD_LOG_DEBUG2,
582 "cupsdCloseClient: Removing fd %d from InputSet and OutputSet...",
583 con->http.fd);
584 close(con->http.fd);
585 FD_CLR(con->http.fd, InputSet);
586 FD_CLR(con->http.fd, OutputSet);
587 con->http.fd = -1;
588 }
589 }
590
591 if (!partial)
592 {
593 /*
594 * Free memory...
595 */
596
597 if (con->http.input_set)
598 free(con->http.input_set);
599
600 httpClearCookie(HTTP(con));
601
602 cupsdClearString(&con->filename);
603 cupsdClearString(&con->command);
604 cupsdClearString(&con->options);
605
606 if (con->request)
607 {
608 ippDelete(con->request);
609 con->request = NULL;
610 }
611
612 if (con->response)
613 {
614 ippDelete(con->response);
615 con->response = NULL;
616 }
617
618 if (con->language)
619 {
620 cupsLangFree(con->language);
621 con->language = NULL;
622 }
623
624 /*
625 * Re-enable new client connections if we are going back under the
626 * limit...
627 */
628
629 if (cupsArrayCount(Clients) == MaxClients)
630 cupsdResumeListening();
631
632 /*
633 * Compact the list of clients as necessary...
634 */
635
636 cupsArrayRemove(Clients, con);
637
638 free(con);
639 }
640
641 return (partial);
642 }
643
644
645 /*
646 * 'cupsdEncryptClient()' - Enable encryption for the client...
647 */
648
649 int /* O - 1 on success, 0 on error */
650 cupsdEncryptClient(cupsd_client_t *con) /* I - Client to encrypt */
651 {
652 #ifdef HAVE_LIBSSL
653 SSL_CTX *context; /* Context for encryption */
654 SSL *conn; /* Connection for encryption */
655 unsigned long error; /* Error code */
656
657
658 /*
659 * Create the SSL context and accept the connection...
660 */
661
662 context = SSL_CTX_new(SSLv23_server_method());
663
664 SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
665 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
666 SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM);
667
668 conn = SSL_new(context);
669
670 SSL_set_fd(conn, con->http.fd);
671 if (SSL_accept(conn) != 1)
672 {
673 cupsdLogMessage(CUPSD_LOG_ERROR,
674 "cupsdEncryptClient: Unable to encrypt connection from %s!",
675 con->http.hostname);
676
677 while ((error = ERR_get_error()) != 0)
678 cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdEncryptClient: %s",
679 ERR_error_string(error, NULL));
680
681 SSL_CTX_free(context);
682 SSL_free(conn);
683 return (0);
684 }
685
686 cupsdLogMessage(CUPSD_LOG_DEBUG,
687 "cupsdEncryptClient: %d Connection from %s now encrypted.",
688 con->http.fd, con->http.hostname);
689
690 con->http.tls = conn;
691 return (1);
692
693 #elif defined(HAVE_GNUTLS)
694 http_tls_t *conn; /* TLS session object */
695 int error; /* Error code */
696 gnutls_certificate_server_credentials *credentials;
697 /* TLS credentials */
698
699 /*
700 * Create the SSL object and perform the SSL handshake...
701 */
702
703 conn = (http_tls_t *)malloc(sizeof(gnutls_session));
704
705 if (conn == NULL)
706 return (0);
707
708 credentials = (gnutls_certificate_server_credentials *)
709 malloc(sizeof(gnutls_certificate_server_credentials));
710 if (credentials == NULL)
711 {
712 cupsdLogMessage(CUPSD_LOG_ERROR,
713 "cupsdEncryptClient: Unable to encrypt connection from %s!",
714 con->http.hostname);
715 cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdEncryptClient: %s", strerror(errno));
716
717 free(conn);
718 return (0);
719 }
720
721 gnutls_certificate_allocate_credentials(credentials);
722 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
723 ServerKey, GNUTLS_X509_FMT_PEM);
724
725 gnutls_init(&(conn->session), GNUTLS_SERVER);
726 gnutls_set_default_priority(conn->session);
727 gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials);
728 gnutls_transport_set_ptr(conn->session, con->http.fd);
729
730 error = gnutls_handshake(conn->session);
731
732 if (error != GNUTLS_E_SUCCESS)
733 {
734 cupsdLogMessage(CUPSD_LOG_ERROR,
735 "cupsdEncryptClient: Unable to encrypt connection from %s!",
736 con->http.hostname);
737 cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdEncryptClient: %s",
738 gnutls_strerror(error));
739
740 gnutls_deinit(conn->session);
741 gnutls_certificate_free_credentials(*credentials);
742 free(conn);
743 free(credentials);
744 return (0);
745 }
746
747 cupsdLogMessage(CUPSD_LOG_DEBUG,
748 "cupsdEncryptClient: %d Connection from %s now encrypted.",
749 con->http.fd, con->http.hostname);
750
751 conn->credentials = credentials;
752 con->http.tls = conn;
753 return (1);
754
755 #elif defined(HAVE_CDSASSL)
756 OSStatus error; /* Error info */
757 SSLContextRef conn; /* New connection */
758 int allowExpired; /* Allow expired certificates? */
759 int allowAnyRoot; /* Allow any root certificate? */
760
761
762 conn = NULL;
763 error = SSLNewContext(true, &conn);
764 allowExpired = 1;
765 allowAnyRoot = 1;
766
767 if (!ServerCertificatesArray)
768 ServerCertificatesArray = get_cdsa_server_certs();
769
770 if (!ServerCertificatesArray)
771 {
772 cupsdLogMessage(CUPSD_LOG_ERROR,
773 "EncryptClient: Could not find signing key in keychain "
774 "\"%s\"", ServerCertificate);
775 error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */
776 }
777
778 if (!error)
779 error = SSLSetIOFuncs(conn, _httpReadCDSA, _httpWriteCDSA);
780
781 if (!error)
782 error = SSLSetProtocolVersion(conn, kSSLProtocol3);
783
784 if (!error)
785 error = SSLSetConnection(conn, (SSLConnectionRef)con->http.fd);
786
787 if (!error)
788 error = SSLSetPeerDomainName(conn, ServerName, strlen(ServerName) + 1);
789
790 /* have to do these options before setting server certs */
791 if (!error && allowExpired)
792 error = SSLSetAllowsExpiredCerts(conn, true);
793
794 if (!error && allowAnyRoot)
795 error = SSLSetAllowsAnyRoot(conn, true);
796
797 if (!error && ServerCertificatesArray)
798 {
799 error = SSLSetCertificate(conn, ServerCertificatesArray);
800
801 /*
802 * Perform SSL/TLS handshake
803 */
804
805 if (!error)
806 {
807 do
808 {
809 error = SSLHandshake(conn);
810 }
811 while (error == errSSLWouldBlock);
812 }
813 }
814
815 if (error)
816 {
817 cupsdLogMessage(CUPSD_LOG_ERROR,
818 "cupsdEncryptClient: Unable to encrypt connection from %s!",
819 con->http.hostname);
820
821 cupsdLogMessage(CUPSD_LOG_ERROR,
822 "cupsdEncryptClient: CDSA error code is %d", (int)error);
823
824 con->http.error = error;
825 con->http.status = HTTP_ERROR;
826
827 if (conn != NULL)
828 SSLDisposeContext(conn);
829
830 return (0);
831 }
832
833 cupsdLogMessage(CUPSD_LOG_DEBUG,
834 "cupsdEncryptClient: %d Connection from %s now encrypted.",
835 con->http.fd, con->http.hostname);
836
837 con->http.tls = conn;
838 return (1);
839
840 #else
841 return (0);
842 #endif /* HAVE_LIBSSL */
843 }
844
845
846 /*
847 * 'cupsdIsCGI()' - Is the resource a CGI script/program?
848 */
849
850 int /* O - 1 = CGI, 0 = file */
851 cupsdIsCGI(cupsd_client_t *con, /* I - Client connection */
852 const char *filename, /* I - Real filename */
853 struct stat *filestats, /* I - File information */
854 mime_type_t *type) /* I - MIME type */
855 {
856 const char *options; /* Options on URL */
857
858
859 cupsdLogMessage(CUPSD_LOG_DEBUG2,
860 "cupsdIsCGI(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)\n",
861 con, filename, filestats, type ? type->super : "unknown",
862 type ? type->type : "unknown");
863
864 /*
865 * Get the options, if any...
866 */
867
868 if ((options = strchr(con->uri, '?')) != NULL)
869 options ++;
870
871 /*
872 * Check for known types...
873 */
874
875 if (!type || strcasecmp(type->super, "application"))
876 {
877 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsCGI: Returning 0...");
878 return (0);
879 }
880
881 if (!strcasecmp(type->type, "x-httpd-cgi") &&
882 (filestats->st_mode & 0111))
883 {
884 /*
885 * "application/x-httpd-cgi" is a CGI script.
886 */
887
888 cupsdSetString(&con->command, filename);
889
890 filename = strrchr(filename, '/') + 1; /* Filename always absolute */
891
892 if (options)
893 cupsdSetStringf(&con->options, "%s %s", filename, options);
894 else
895 cupsdSetStringf(&con->options, "%s", filename);
896
897 cupsdLogMessage(CUPSD_LOG_DEBUG2,
898 "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
899 con->command, con->options);
900
901 return (1);
902 }
903 #ifdef HAVE_JAVA
904 else if (!strcasecmp(type->type, "x-httpd-java"))
905 {
906 /*
907 * "application/x-httpd-java" is a Java servlet.
908 */
909
910 cupsdSetString(&con->command, CUPS_JAVA);
911
912 if (options)
913 cupsdSetStringf(&con->options, "java %s %s", filename, options);
914 else
915 cupsdSetStringf(&con->options, "java %s", filename);
916
917 cupsdLogMessage(CUPSD_LOG_DEBUG2,
918 "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
919 con->command, con->options);
920
921 return (1);
922 }
923 #endif /* HAVE_JAVA */
924 #ifdef HAVE_PERL
925 else if (!strcasecmp(type->type, "x-httpd-perl"))
926 {
927 /*
928 * "application/x-httpd-perl" is a Perl page.
929 */
930
931 cupsdSetString(&con->command, CUPS_PERL);
932
933 if (options)
934 cupsdSetStringf(&con->options, "perl %s %s", filename, options);
935 else
936 cupsdSetStringf(&con->options, "perl %s", filename);
937
938 cupsdLogMessage(CUPSD_LOG_DEBUG2,
939 "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
940 con->command, con->options);
941
942 return (1);
943 }
944 #endif /* HAVE_PERL */
945 #ifdef HAVE_PHP
946 else if (!strcasecmp(type->type, "x-httpd-php"))
947 {
948 /*
949 * "application/x-httpd-php" is a PHP page.
950 */
951
952 cupsdSetString(&con->command, CUPS_PHP);
953
954 if (options)
955 cupsdSetStringf(&con->options, "php %s %s", filename, options);
956 else
957 cupsdSetStringf(&con->options, "php %s", filename);
958
959 cupsdLogMessage(CUPSD_LOG_DEBUG2,
960 "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
961 con->command, con->options);
962
963 return (1);
964 }
965 #endif /* HAVE_PHP */
966 #ifdef HAVE_PYTHON
967 else if (!strcasecmp(type->type, "x-httpd-python"))
968 {
969 /*
970 * "application/x-httpd-python" is a Python page.
971 */
972
973 cupsdSetString(&con->command, CUPS_PYTHON);
974
975 if (options)
976 cupsdSetStringf(&con->options, "python %s %s", filename, options);
977 else
978 cupsdSetStringf(&con->options, "python %s", filename);
979
980 cupsdLogMessage(CUPSD_LOG_DEBUG2,
981 "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
982 con->command, con->options);
983
984 return (1);
985 }
986 #endif /* HAVE_PYTHON */
987
988 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsCGI: Returning 0...");
989
990 return (0);
991 }
992
993
994 /*
995 * 'cupsdReadClient()' - Read data from a client.
996 */
997
998 int /* O - 1 on success, 0 on error */
999 cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */
1000 {
1001 char line[32768], /* Line from client... */
1002 operation[64], /* Operation code from socket */
1003 version[64], /* HTTP version number string */
1004 locale[64], /* Locale */
1005 *ptr; /* Pointer into strings */
1006 int major, minor; /* HTTP version numbers */
1007 http_status_t status; /* Transfer status */
1008 ipp_state_t ipp_state; /* State of IPP transfer */
1009 int bytes; /* Number of bytes to POST */
1010 char *filename; /* Name of file for GET/HEAD */
1011 char buf[1024]; /* Buffer for real filename */
1012 struct stat filestats; /* File information */
1013 mime_type_t *type; /* MIME type of file */
1014 cupsd_printer_t *p; /* Printer */
1015 static unsigned request_id = 0; /* Request ID for temp files */
1016
1017
1018 status = HTTP_CONTINUE;
1019
1020 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1021 "cupsdReadClient: %d, used=%d, file=%d state=%d",
1022 con->http.fd, con->http.used, con->file, con->http.state);
1023
1024 if (con->http.error)
1025 {
1026 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: http error seen...");
1027 return (cupsdCloseClient(con));
1028 }
1029
1030 #ifdef HAVE_SSL
1031 if (con->auto_ssl)
1032 {
1033 /*
1034 * Automatically check for a SSL/TLS handshake...
1035 */
1036
1037 con->auto_ssl = 0;
1038
1039 if (recv(con->http.fd, buf, 1, MSG_PEEK) == 1 &&
1040 (!buf[0] || !strchr("DGHOPT", buf[0])))
1041 {
1042 /*
1043 * Encrypt this connection...
1044 */
1045
1046 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1047 "cupsdReadClient: Saw first byte %02X, auto-negotiating SSL/TLS session...",
1048 buf[0] & 255);
1049
1050 cupsdEncryptClient(con);
1051 return (1);
1052 }
1053 }
1054 #endif /* HAVE_SSL */
1055
1056 switch (con->http.state)
1057 {
1058 case HTTP_WAITING :
1059 /*
1060 * See if we've received a request line...
1061 */
1062
1063 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
1064 {
1065 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1066 "cupsdReadClient: httpGets returned EOF...");
1067 return (cupsdCloseClient(con));
1068 }
1069
1070 /*
1071 * Ignore blank request lines...
1072 */
1073
1074 if (line[0] == '\0')
1075 break;
1076
1077 /*
1078 * Clear other state variables...
1079 */
1080
1081 httpClearFields(HTTP(con));
1082
1083 con->http.activity = time(NULL);
1084 con->http.version = HTTP_1_0;
1085 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1086 con->http.data_encoding = HTTP_ENCODE_LENGTH;
1087 con->http.data_remaining = 0;
1088 con->http._data_remaining = 0;
1089 con->operation = HTTP_WAITING;
1090 con->bytes = 0;
1091 con->file = -1;
1092 con->file_ready = 0;
1093 con->pipe_pid = 0;
1094 con->username[0] = '\0';
1095 con->password[0] = '\0';
1096 con->uri[0] = '\0';
1097
1098 cupsdClearString(&con->command);
1099 cupsdClearString(&con->options);
1100
1101 if (con->language != NULL)
1102 {
1103 cupsLangFree(con->language);
1104 con->language = NULL;
1105 }
1106
1107 /*
1108 * Grab the request line...
1109 */
1110
1111 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
1112 {
1113 case 1 :
1114 cupsdLogMessage(CUPSD_LOG_ERROR,
1115 "Bad request line \"%s\" from %s!", line,
1116 con->http.hostname);
1117 cupsdSendError(con, HTTP_BAD_REQUEST);
1118 return (cupsdCloseClient(con));
1119 case 2 :
1120 con->http.version = HTTP_0_9;
1121 break;
1122 case 3 :
1123 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
1124 {
1125 cupsdLogMessage(CUPSD_LOG_ERROR,
1126 "Bad request line \"%s\" from %s!", line,
1127 con->http.hostname);
1128 cupsdSendError(con, HTTP_BAD_REQUEST);
1129 return (cupsdCloseClient(con));
1130 }
1131
1132 if (major < 2)
1133 {
1134 con->http.version = (http_version_t)(major * 100 + minor);
1135 if (con->http.version == HTTP_1_1 && KeepAlive)
1136 con->http.keep_alive = HTTP_KEEPALIVE_ON;
1137 else
1138 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1139 }
1140 else
1141 {
1142 cupsdSendError(con, HTTP_NOT_SUPPORTED);
1143 return (cupsdCloseClient(con));
1144 }
1145 break;
1146 }
1147
1148 /*
1149 * Handle full URLs in the request line...
1150 */
1151
1152 if (strcmp(con->uri, "*"))
1153 {
1154 char method[HTTP_MAX_URI], /* Method/scheme */
1155 userpass[HTTP_MAX_URI], /* Username:password */
1156 hostname[HTTP_MAX_URI], /* Hostname */
1157 resource[HTTP_MAX_URI]; /* Resource path */
1158 int port; /* Port number */
1159
1160
1161 /*
1162 * Separate the URI into its components...
1163 */
1164
1165 httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
1166 method, sizeof(method),
1167 userpass, sizeof(userpass),
1168 hostname, sizeof(hostname), &port,
1169 resource, sizeof(resource));
1170
1171 /*
1172 * Only allow URIs with the servername, localhost, or an IP
1173 * address...
1174 */
1175
1176 if (strcmp(method, "file") &&
1177 strcasecmp(hostname, ServerName) &&
1178 strcasecmp(hostname, "localhost") &&
1179 !isdigit(hostname[0]))
1180 {
1181 /*
1182 * Nope, we don't do proxies...
1183 */
1184
1185 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!",
1186 con->uri);
1187 cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED);
1188 return (cupsdCloseClient(con));
1189 }
1190
1191 /*
1192 * Copy the resource portion back into the URI; both resource and
1193 * con->uri are HTTP_MAX_URI bytes in size...
1194 */
1195
1196 strcpy(con->uri, resource);
1197 }
1198
1199 /*
1200 * Process the request...
1201 */
1202
1203 if (!strcmp(operation, "GET"))
1204 con->http.state = HTTP_GET;
1205 else if (!strcmp(operation, "PUT"))
1206 con->http.state = HTTP_PUT;
1207 else if (!strcmp(operation, "POST"))
1208 con->http.state = HTTP_POST;
1209 else if (!strcmp(operation, "DELETE"))
1210 con->http.state = HTTP_DELETE;
1211 else if (!strcmp(operation, "TRACE"))
1212 con->http.state = HTTP_TRACE;
1213 else if (!strcmp(operation, "OPTIONS"))
1214 con->http.state = HTTP_OPTIONS;
1215 else if (!strcmp(operation, "HEAD"))
1216 con->http.state = HTTP_HEAD;
1217 else
1218 {
1219 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation);
1220 cupsdSendError(con, HTTP_BAD_REQUEST);
1221 return (cupsdCloseClient(con));
1222 }
1223
1224 con->start = time(NULL);
1225 con->operation = con->http.state;
1226
1227 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %s %s HTTP/%d.%d", con->http.fd,
1228 operation, con->uri,
1229 con->http.version / 100, con->http.version % 100);
1230
1231 con->http.status = HTTP_OK;
1232
1233 case HTTP_OPTIONS :
1234 case HTTP_DELETE :
1235 case HTTP_GET :
1236 case HTTP_HEAD :
1237 case HTTP_POST :
1238 case HTTP_PUT :
1239 case HTTP_TRACE :
1240 /*
1241 * Parse incoming parameters until the status changes...
1242 */
1243
1244 status = httpUpdate(HTTP(con));
1245
1246 if (status != HTTP_OK && status != HTTP_CONTINUE)
1247 {
1248 cupsdSendError(con, HTTP_BAD_REQUEST);
1249 return (cupsdCloseClient(con));
1250 }
1251 break;
1252
1253 default :
1254 break; /* Anti-compiler-warning-code */
1255 }
1256
1257 /*
1258 * Handle new transfers...
1259 */
1260
1261 if (status == HTTP_OK)
1262 {
1263 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
1264 {
1265 /*
1266 * Figure out the locale from the Accept-Language and Content-Type
1267 * fields...
1268 */
1269
1270 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ',')) != NULL)
1271 *ptr = '\0';
1272
1273 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ';')) != NULL)
1274 *ptr = '\0';
1275
1276 if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "charset=")) != NULL)
1277 {
1278 /*
1279 * Combine language and charset, and trim any extra params in the
1280 * content-type.
1281 */
1282
1283 snprintf(locale, sizeof(locale), "%s.%s",
1284 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
1285
1286 if ((ptr = strchr(locale, ',')) != NULL)
1287 *ptr = '\0';
1288 }
1289 else
1290 snprintf(locale, sizeof(locale), "%s.%s",
1291 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], DefaultCharset);
1292
1293 con->language = cupsLangGet(locale);
1294 }
1295 else
1296 con->language = cupsLangGet(DefaultLocale);
1297
1298 cupsdAuthorize(con);
1299
1300 if (!strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) &&
1301 KeepAlive)
1302 con->http.keep_alive = HTTP_KEEPALIVE_ON;
1303
1304 if (!con->http.fields[HTTP_FIELD_HOST][0] &&
1305 con->http.version >= HTTP_1_1)
1306 {
1307 /*
1308 * HTTP/1.1 and higher require the "Host:" field...
1309 */
1310
1311 if (!cupsdSendError(con, HTTP_BAD_REQUEST))
1312 return (cupsdCloseClient(con));
1313 }
1314 else if (con->operation == HTTP_OPTIONS)
1315 {
1316 /*
1317 * Do OPTIONS command...
1318 */
1319
1320 if (con->best && con->best->type != AUTH_NONE)
1321 {
1322 if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL))
1323 return (cupsdCloseClient(con));
1324 }
1325
1326 if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1327 con->http.tls == NULL)
1328 {
1329 #ifdef HAVE_SSL
1330 /*
1331 * Do encryption stuff...
1332 */
1333
1334 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1335 return (cupsdCloseClient(con));
1336
1337 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1338 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1339 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1340 httpPrintf(HTTP(con), "\r\n");
1341
1342 cupsdEncryptClient(con);
1343 #else
1344 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
1345 return (cupsdCloseClient(con));
1346 #endif /* HAVE_SSL */
1347 }
1348
1349 if (con->http.expect)
1350 {
1351 /**** TODO: send expected header ****/
1352 }
1353
1354 if (!cupsdSendHeader(con, HTTP_OK, NULL))
1355 return (cupsdCloseClient(con));
1356
1357 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1358 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1359 httpPrintf(HTTP(con), "\r\n");
1360 }
1361 else if (!is_path_absolute(con->uri))
1362 {
1363 /*
1364 * Protect against malicious users!
1365 */
1366
1367 if (!cupsdSendError(con, HTTP_FORBIDDEN))
1368 return (cupsdCloseClient(con));
1369 }
1370 else
1371 {
1372 if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1373 con->http.tls == NULL)
1374 {
1375 #ifdef HAVE_SSL
1376 /*
1377 * Do encryption stuff...
1378 */
1379
1380 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1381 return (cupsdCloseClient(con));
1382
1383 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1384 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1385 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1386 httpPrintf(HTTP(con), "\r\n");
1387
1388 cupsdEncryptClient(con);
1389 #else
1390 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
1391 return (cupsdCloseClient(con));
1392 #endif /* HAVE_SSL */
1393 }
1394
1395 if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK)
1396 {
1397 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1398 "cupsdReadClient: Unauthorized request for %s...\n",
1399 con->uri);
1400 cupsdSendError(con, status);
1401 return (cupsdCloseClient(con));
1402 }
1403
1404 if (con->http.expect)
1405 {
1406 /**** TODO: send expected header ****/
1407 }
1408
1409 switch (con->http.state)
1410 {
1411 case HTTP_GET_SEND :
1412 if (!strncmp(con->uri, "/printers/", 10) &&
1413 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1414 {
1415 /*
1416 * Send PPD file - get the real printer name since printer
1417 * names are not case sensitive but filenames can be...
1418 */
1419
1420 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1421
1422 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1423 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1424 else
1425 {
1426 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1427 return (cupsdCloseClient(con));
1428
1429 break;
1430 }
1431 }
1432
1433 if ((!strncmp(con->uri, "/admin", 6) &&
1434 strncmp(con->uri, "/admin/conf/", 12) &&
1435 strncmp(con->uri, "/admin/log/", 11)) ||
1436 !strncmp(con->uri, "/printers", 9) ||
1437 !strncmp(con->uri, "/classes", 8) ||
1438 !strncmp(con->uri, "/help", 5) ||
1439 !strncmp(con->uri, "/jobs", 5))
1440 {
1441 /*
1442 * Send CGI output...
1443 */
1444
1445 if (!strncmp(con->uri, "/admin", 6))
1446 {
1447 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1448 ServerBin);
1449
1450 if ((ptr = strchr(con->uri + 6, '?')) != NULL)
1451 cupsdSetStringf(&con->options, "admin%s", ptr);
1452 else
1453 cupsdSetString(&con->options, "admin");
1454 }
1455 else if (!strncmp(con->uri, "/printers", 9))
1456 {
1457 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1458 ServerBin);
1459 cupsdSetString(&con->options, con->uri + 9);
1460 }
1461 else if (!strncmp(con->uri, "/classes", 8))
1462 {
1463 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1464 ServerBin);
1465 cupsdSetString(&con->options, con->uri + 8);
1466 }
1467 else if (!strncmp(con->uri, "/jobs", 5))
1468 {
1469 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1470 ServerBin);
1471 cupsdSetString(&con->options, con->uri + 5);
1472 }
1473 else
1474 {
1475 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1476 ServerBin);
1477 cupsdSetString(&con->options, con->uri + 5);
1478 }
1479
1480 if (con->options[0] == '/')
1481 _cups_strcpy(con->options, con->options + 1);
1482
1483 if (!cupsdSendCommand(con, con->command, con->options, 0))
1484 {
1485 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1486 return (cupsdCloseClient(con));
1487 }
1488 else
1489 cupsdLogRequest(con, HTTP_OK);
1490
1491 if (con->http.version <= HTTP_1_0)
1492 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1493 }
1494 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1495 (strchr(con->uri + 12, '/') ||
1496 strlen(con->uri) == 12)) ||
1497 (!strncmp(con->uri, "/admin/log/", 11) &&
1498 (strchr(con->uri + 11, '/') ||
1499 strlen(con->uri) == 11)))
1500 {
1501 /*
1502 * GET can only be done to configuration files under
1503 * /admin/conf...
1504 */
1505
1506 if (!cupsdSendError(con, HTTP_FORBIDDEN))
1507 return (cupsdCloseClient(con));
1508
1509 break;
1510 }
1511 else
1512 {
1513 /*
1514 * Serve a file...
1515 */
1516
1517 if ((filename = get_file(con, &filestats, buf,
1518 sizeof(buf))) == NULL)
1519 {
1520 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1521 return (cupsdCloseClient(con));
1522
1523 break;
1524 }
1525
1526 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1527
1528 if (cupsdIsCGI(con, filename, &filestats, type))
1529 {
1530 /*
1531 * Note: con->command and con->options were set by
1532 * cupsdIsCGI()...
1533 */
1534
1535 if (!cupsdSendCommand(con, con->command, con->options, 0))
1536 {
1537 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1538 return (cupsdCloseClient(con));
1539 }
1540 else
1541 cupsdLogRequest(con, HTTP_OK);
1542
1543 if (con->http.version <= HTTP_1_0)
1544 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1545 break;
1546 }
1547
1548 if (!check_if_modified(con, &filestats))
1549 {
1550 if (!cupsdSendError(con, HTTP_NOT_MODIFIED))
1551 return (cupsdCloseClient(con));
1552 }
1553 else
1554 {
1555 if (type == NULL)
1556 strcpy(line, "text/plain");
1557 else
1558 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1559
1560 if (!cupsdSendFile(con, HTTP_OK, filename, line, &filestats))
1561 return (cupsdCloseClient(con));
1562 }
1563 }
1564 break;
1565
1566 case HTTP_POST_RECV :
1567 /*
1568 * See if the POST request includes a Content-Length field, and if
1569 * so check the length against any limits that are set...
1570 */
1571
1572 cupsdLogMessage(CUPSD_LOG_DEBUG2, "POST %s", con->uri);
1573 cupsdLogMessage(CUPSD_LOG_DEBUG2, "CONTENT_TYPE = %s",
1574 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1575
1576 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1577 MaxRequestSize > 0 &&
1578 con->http.data_remaining > MaxRequestSize)
1579 {
1580 /*
1581 * Request too large...
1582 */
1583
1584 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1585 return (cupsdCloseClient(con));
1586
1587 break;
1588 }
1589 else if (con->http.data_remaining < 0)
1590 {
1591 /*
1592 * Negative content lengths are invalid!
1593 */
1594
1595 if (!cupsdSendError(con, HTTP_BAD_REQUEST))
1596 return (cupsdCloseClient(con));
1597
1598 break;
1599 }
1600
1601 /*
1602 * See what kind of POST request this is; for IPP requests the
1603 * content-type field will be "application/ipp"...
1604 */
1605
1606 if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1607 "application/ipp"))
1608 con->request = ippNew();
1609 else if ((!strncmp(con->uri, "/admin", 6) &&
1610 strncmp(con->uri, "/admin/conf/", 12) &&
1611 strncmp(con->uri, "/admin/log/", 11)) ||
1612 !strncmp(con->uri, "/printers", 9) ||
1613 !strncmp(con->uri, "/classes", 8) ||
1614 !strncmp(con->uri, "/help", 5) ||
1615 !strncmp(con->uri, "/jobs", 5))
1616 {
1617 /*
1618 * CGI request...
1619 */
1620
1621 if (!strncmp(con->uri, "/admin", 6))
1622 {
1623 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1624 ServerBin);
1625
1626 if ((ptr = strchr(con->uri + 6, '?')) != NULL)
1627 cupsdSetStringf(&con->options, "admin%s", ptr);
1628 else
1629 cupsdSetString(&con->options, "admin");
1630 }
1631 else if (!strncmp(con->uri, "/printers", 9))
1632 {
1633 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1634 ServerBin);
1635 cupsdSetString(&con->options, con->uri + 9);
1636 }
1637 else if (!strncmp(con->uri, "/classes", 8))
1638 {
1639 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1640 ServerBin);
1641 cupsdSetString(&con->options, con->uri + 8);
1642 }
1643 else if (!strncmp(con->uri, "/jobs", 5))
1644 {
1645 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1646 ServerBin);
1647 cupsdSetString(&con->options, con->uri + 5);
1648 }
1649 else
1650 {
1651 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1652 ServerBin);
1653 cupsdSetString(&con->options, con->uri + 5);
1654 }
1655
1656 if (con->options[0] == '/')
1657 _cups_strcpy(con->options, con->options + 1);
1658
1659 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1660 "cupsdReadClient: %d command=\"%s\", options = \"%s\"",
1661 con->http.fd, con->command, con->options);
1662
1663 if (con->http.version <= HTTP_1_0)
1664 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1665 }
1666 else
1667 {
1668 /*
1669 * POST to a file...
1670 */
1671
1672 if ((filename = get_file(con, &filestats, buf,
1673 sizeof(buf))) == NULL)
1674 {
1675 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1676 return (cupsdCloseClient(con));
1677
1678 break;
1679 }
1680
1681 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1682
1683 if (!cupsdIsCGI(con, filename, &filestats, type))
1684 {
1685 /*
1686 * Only POST to CGI's...
1687 */
1688
1689 if (!cupsdSendError(con, HTTP_UNAUTHORIZED))
1690 return (cupsdCloseClient(con));
1691 }
1692 }
1693 break;
1694
1695 case HTTP_PUT_RECV :
1696 /*
1697 * Validate the resource name...
1698 */
1699
1700 if (strncmp(con->uri, "/admin/conf/", 12) ||
1701 strchr(con->uri + 12, '/') ||
1702 strlen(con->uri) == 12)
1703 {
1704 /*
1705 * PUT can only be done to configuration files under
1706 * /admin/conf...
1707 */
1708
1709 if (!cupsdSendError(con, HTTP_FORBIDDEN))
1710 return (cupsdCloseClient(con));
1711
1712 break;
1713 }
1714
1715 /*
1716 * See if the PUT request includes a Content-Length field, and if
1717 * so check the length against any limits that are set...
1718 */
1719
1720 cupsdLogMessage(CUPSD_LOG_DEBUG2, "PUT %s", con->uri);
1721 cupsdLogMessage(CUPSD_LOG_DEBUG2, "CONTENT_TYPE = %s",
1722 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1723
1724 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1725 MaxRequestSize > 0 &&
1726 con->http.data_remaining > MaxRequestSize)
1727 {
1728 /*
1729 * Request too large...
1730 */
1731
1732 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1733 return (cupsdCloseClient(con));
1734
1735 break;
1736 }
1737 else if (con->http.data_remaining < 0)
1738 {
1739 /*
1740 * Negative content lengths are invalid!
1741 */
1742
1743 if (!cupsdSendError(con, HTTP_BAD_REQUEST))
1744 return (cupsdCloseClient(con));
1745
1746 break;
1747 }
1748
1749 /*
1750 * Open a temporary file to hold the request...
1751 */
1752
1753 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
1754 request_id ++);
1755 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1756
1757 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1758 "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
1759 con->filename, con->file);
1760
1761 if (con->file < 0)
1762 {
1763 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1764 return (cupsdCloseClient(con));
1765 }
1766
1767 fchmod(con->file, 0640);
1768 fchown(con->file, RunUser, Group);
1769 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1770 break;
1771
1772 case HTTP_DELETE :
1773 case HTTP_TRACE :
1774 cupsdSendError(con, HTTP_NOT_IMPLEMENTED);
1775 return (cupsdCloseClient(con));
1776
1777 case HTTP_HEAD :
1778 if (!strncmp(con->uri, "/printers/", 10) &&
1779 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1780 {
1781 /*
1782 * Send PPD file - get the real printer name since printer
1783 * names are not case sensitive but filenames can be...
1784 */
1785
1786 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1787
1788 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1789 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1790 else
1791 {
1792 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1793 return (cupsdCloseClient(con));
1794
1795 break;
1796 }
1797 }
1798
1799 if ((!strncmp(con->uri, "/admin", 6) &&
1800 strncmp(con->uri, "/admin/conf/", 12) &&
1801 strncmp(con->uri, "/admin/log/", 11)) ||
1802 !strncmp(con->uri, "/printers", 9) ||
1803 !strncmp(con->uri, "/classes", 8) ||
1804 !strncmp(con->uri, "/help", 5) ||
1805 !strncmp(con->uri, "/jobs", 5))
1806 {
1807 /*
1808 * CGI output...
1809 */
1810
1811 if (!cupsdSendHeader(con, HTTP_OK, "text/html"))
1812 return (cupsdCloseClient(con));
1813
1814 if (httpPrintf(HTTP(con), "\r\n") < 0)
1815 return (cupsdCloseClient(con));
1816
1817 cupsdLogRequest(con, HTTP_OK);
1818 }
1819 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1820 (strchr(con->uri + 12, '/') ||
1821 strlen(con->uri) == 12)) ||
1822 (!strncmp(con->uri, "/admin/log/", 11) &&
1823 (strchr(con->uri + 11, '/') ||
1824 strlen(con->uri) == 11)))
1825 {
1826 /*
1827 * HEAD can only be done to configuration files under
1828 * /admin/conf...
1829 */
1830
1831 if (!cupsdSendError(con, HTTP_FORBIDDEN))
1832 return (cupsdCloseClient(con));
1833
1834 break;
1835 }
1836 else if ((filename = get_file(con, &filestats, buf,
1837 sizeof(buf))) == NULL)
1838 {
1839 if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html"))
1840 return (cupsdCloseClient(con));
1841
1842 cupsdLogRequest(con, HTTP_NOT_FOUND);
1843 }
1844 else if (!check_if_modified(con, &filestats))
1845 {
1846 if (!cupsdSendError(con, HTTP_NOT_MODIFIED))
1847 return (cupsdCloseClient(con));
1848
1849 cupsdLogRequest(con, HTTP_NOT_MODIFIED);
1850 }
1851 else
1852 {
1853 /*
1854 * Serve a file...
1855 */
1856
1857 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1858 if (type == NULL)
1859 strcpy(line, "text/plain");
1860 else
1861 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1862
1863 if (!cupsdSendHeader(con, HTTP_OK, line))
1864 return (cupsdCloseClient(con));
1865
1866 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1867 httpGetDateString(filestats.st_mtime)) < 0)
1868 return (cupsdCloseClient(con));
1869
1870 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1871 (unsigned long)filestats.st_size) < 0)
1872 return (cupsdCloseClient(con));
1873
1874 cupsdLogRequest(con, HTTP_OK);
1875 }
1876
1877 if (httpPrintf(HTTP(con), "\r\n") < 0)
1878 return (cupsdCloseClient(con));
1879
1880 con->http.state = HTTP_WAITING;
1881 break;
1882
1883 default :
1884 break; /* Anti-compiler-warning-code */
1885 }
1886 }
1887 }
1888
1889 /*
1890 * Handle any incoming data...
1891 */
1892
1893 switch (con->http.state)
1894 {
1895 case HTTP_PUT_RECV :
1896 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1897 "cupsdReadClient: %d con->data_encoding=HTTP_ENCODE_%s, "
1898 "con->data_remaining=" CUPS_LLFMT ", con->file=%d",
1899 con->http.fd,
1900 con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
1901 "CHUNKED" : "LENGTH",
1902 CUPS_LLCAST con->http.data_remaining, con->file);
1903
1904 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
1905 return (cupsdCloseClient(con));
1906 else if (bytes > 0)
1907 {
1908 con->bytes += bytes;
1909
1910 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1911 "cupsdReadClient: %d writing %d bytes to %d",
1912 con->http.fd, bytes, con->file);
1913
1914 if (write(con->file, line, bytes) < bytes)
1915 {
1916 cupsdLogMessage(CUPSD_LOG_ERROR,
1917 "cupsdReadClient: Unable to write %d bytes to %s: %s",
1918 bytes, con->filename, strerror(errno));
1919
1920 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1921 "cupsdReadClient: Closing data file %d...",
1922 con->file);
1923
1924 close(con->file);
1925 con->file = -1;
1926 unlink(con->filename);
1927 cupsdClearString(&con->filename);
1928
1929 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1930 return (cupsdCloseClient(con));
1931 }
1932 }
1933
1934 if (con->http.state == HTTP_WAITING)
1935 {
1936 /*
1937 * End of file, see how big it is...
1938 */
1939
1940 fstat(con->file, &filestats);
1941
1942 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1943 "cupsdReadClient: %d Closing data file %d, size="
1944 CUPS_LLFMT ".",
1945 con->http.fd, con->file,
1946 CUPS_LLCAST filestats.st_size);
1947
1948 close(con->file);
1949 con->file = -1;
1950
1951 if (filestats.st_size > MaxRequestSize &&
1952 MaxRequestSize > 0)
1953 {
1954 /*
1955 * Request is too big; remove it and send an error...
1956 */
1957
1958 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1959 "cupsdReadClient: %d Removing temp file %s",
1960 con->http.fd, con->filename);
1961 unlink(con->filename);
1962 cupsdClearString(&con->filename);
1963
1964 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1965 return (cupsdCloseClient(con));
1966 }
1967
1968 /*
1969 * Install the configuration file...
1970 */
1971
1972 status = install_conf_file(con);
1973
1974 /*
1975 * Return the status to the client...
1976 */
1977
1978 if (!cupsdSendError(con, status))
1979 return (cupsdCloseClient(con));
1980 }
1981 break;
1982
1983 case HTTP_POST_RECV :
1984 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1985 "cupsdReadClient: %d con->data_encoding=HTTP_ENCODE_"
1986 "%s, con->data_remaining=" CUPS_LLFMT ", con->file=%d",
1987 con->http.fd,
1988 con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
1989 "CHUNKED" : "LENGTH",
1990 CUPS_LLCAST con->http.data_remaining, con->file);
1991
1992 if (con->request != NULL)
1993 {
1994 /*
1995 * Grab any request data from the connection...
1996 */
1997
1998 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
1999 {
2000 cupsdLogMessage(CUPSD_LOG_ERROR,
2001 "cupsdReadClient: %d IPP Read Error!",
2002 con->http.fd);
2003
2004 cupsdSendError(con, HTTP_BAD_REQUEST);
2005 return (cupsdCloseClient(con));
2006 }
2007 else if (ipp_state != IPP_DATA)
2008 {
2009 if (con->http.state == HTTP_POST_SEND)
2010 {
2011 cupsdSendError(con, HTTP_BAD_REQUEST);
2012 return (cupsdCloseClient(con));
2013 }
2014
2015 break;
2016 }
2017 else
2018 con->bytes += ippLength(con->request);
2019 }
2020
2021 if (con->file < 0 && con->http.state != HTTP_POST_SEND)
2022 {
2023 /*
2024 * Create a file as needed for the request data...
2025 */
2026
2027 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
2028 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
2029
2030 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
2031 con->filename, con->file);
2032
2033 if (con->file < 0)
2034 {
2035 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
2036 return (cupsdCloseClient(con));
2037 }
2038
2039 fchmod(con->file, 0640);
2040 fchown(con->file, RunUser, Group);
2041 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2042 }
2043
2044 if (con->http.state != HTTP_POST_SEND)
2045 {
2046 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
2047 return (cupsdCloseClient(con));
2048 else if (bytes > 0)
2049 {
2050 con->bytes += bytes;
2051
2052 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2053 "cupsdReadClient: %d writing %d bytes to %d",
2054 con->http.fd, bytes, con->file);
2055
2056 if (write(con->file, line, bytes) < bytes)
2057 {
2058 cupsdLogMessage(CUPSD_LOG_ERROR,
2059 "cupsdReadClient: Unable to write %d bytes to %s: %s",
2060 bytes, con->filename, strerror(errno));
2061
2062 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2063 "cupsdReadClient: Closing file %d...",
2064 con->file);
2065
2066 close(con->file);
2067 con->file = -1;
2068 unlink(con->filename);
2069 cupsdClearString(&con->filename);
2070
2071 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
2072 return (cupsdCloseClient(con));
2073 }
2074 }
2075 else if (con->http.state == HTTP_POST_RECV)
2076 return (1); /* ??? */
2077 else if (con->http.state != HTTP_POST_SEND)
2078 return (cupsdCloseClient(con));
2079 }
2080
2081 if (con->http.state == HTTP_POST_SEND)
2082 {
2083 if (con->file >= 0)
2084 {
2085 fstat(con->file, &filestats);
2086
2087 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2088 "cupsdReadClient: %d Closing data file %d, "
2089 "size=" CUPS_LLFMT ".",
2090 con->http.fd, con->file,
2091 CUPS_LLCAST filestats.st_size);
2092
2093 close(con->file);
2094 con->file = -1;
2095
2096 if (filestats.st_size > MaxRequestSize &&
2097 MaxRequestSize > 0)
2098 {
2099 /*
2100 * Request is too big; remove it and send an error...
2101 */
2102
2103 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2104 "cupsdReadClient: %d Removing temp file %s",
2105 con->http.fd, con->filename);
2106 unlink(con->filename);
2107 cupsdClearString(&con->filename);
2108
2109 if (con->request)
2110 {
2111 /*
2112 * Delete any IPP request data...
2113 */
2114
2115 ippDelete(con->request);
2116 con->request = NULL;
2117 }
2118
2119 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
2120 return (cupsdCloseClient(con));
2121 }
2122
2123 if (con->command)
2124 {
2125 if (!cupsdSendCommand(con, con->command, con->options, 0))
2126 {
2127 if (!cupsdSendError(con, HTTP_NOT_FOUND))
2128 return (cupsdCloseClient(con));
2129 }
2130 else
2131 cupsdLogRequest(con, HTTP_OK);
2132 }
2133 }
2134
2135 if (con->request)
2136 return (cupsdProcessIPPRequest(con));
2137 }
2138 break;
2139
2140 default :
2141 break; /* Anti-compiler-warning-code */
2142 }
2143
2144 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
2145 return (cupsdCloseClient(con));
2146 else
2147 return (1);
2148 }
2149
2150
2151 /*
2152 * 'cupsdSendCommand()' - Send output from a command via HTTP.
2153 */
2154
2155 int /* O - 1 on success, 0 on failure */
2156 cupsdSendCommand(
2157 cupsd_client_t *con, /* I - Client connection */
2158 char *command, /* I - Command to run */
2159 char *options, /* I - Command-line options */
2160 int root) /* I - Run as root? */
2161 {
2162 int fd; /* Standard input file descriptor */
2163
2164
2165 if (con->filename)
2166 fd = open(con->filename, O_RDONLY);
2167 else
2168 fd = open("/dev/null", O_RDONLY);
2169
2170 if (fd < 0)
2171 {
2172 cupsdLogMessage(CUPSD_LOG_ERROR,
2173 "cupsdSendCommand: %d Unable to open \"%s\" for reading: %s",
2174 con->http.fd, con->filename ? con->filename : "/dev/null",
2175 strerror(errno));
2176 return (0);
2177 }
2178
2179 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
2180
2181 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
2182
2183 close(fd);
2184
2185 cupsdLogMessage(CUPSD_LOG_INFO, "Started \"%s\" (pid=%d)", command,
2186 con->pipe_pid);
2187
2188 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendCommand: %d file=%d",
2189 con->http.fd, con->file);
2190
2191 if (con->pipe_pid == 0)
2192 return (0);
2193
2194 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2195
2196 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2197 "cupsdSendCommand: Adding fd %d to InputSet...", con->file);
2198 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2199 "cupsdSendCommand: Adding fd %d to OutputSet...",
2200 con->http.fd);
2201
2202 FD_SET(con->file, InputSet);
2203 FD_SET(con->http.fd, OutputSet);
2204
2205 con->sent_header = 0;
2206 con->file_ready = 0;
2207 con->got_fields = 0;
2208 con->field_col = 0;
2209
2210 return (1);
2211 }
2212
2213
2214 /*
2215 * 'cupsdSendError()' - Send an error message via HTTP.
2216 */
2217
2218 int /* O - 1 if successful, 0 otherwise */
2219 cupsdSendError(cupsd_client_t *con, /* I - Connection */
2220 http_status_t code) /* I - Error code */
2221 {
2222 /*
2223 * Put the request in the access_log file...
2224 */
2225
2226 cupsdLogRequest(con, code);
2227
2228 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendError: %d code=%d (%s)",
2229 con->http.fd, code, httpStatus(code));
2230
2231 /*
2232 * To work around bugs in some proxies, don't use Keep-Alive for some
2233 * error messages...
2234 */
2235
2236 if (code >= HTTP_BAD_REQUEST)
2237 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2238
2239 /*
2240 * Send an error message back to the client. If the error code is a
2241 * 400 or 500 series, make sure the message contains some text, too!
2242 */
2243
2244 if (!cupsdSendHeader(con, code, NULL))
2245 return (0);
2246
2247 #ifdef HAVE_SSL
2248 if (code == HTTP_UPGRADE_REQUIRED)
2249 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2250 return (0);
2251
2252 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2253 return (0);
2254 #endif /* HAVE_SSL */
2255
2256 if ((con->http.version >= HTTP_1_1 && !con->http.keep_alive) ||
2257 (code >= HTTP_BAD_REQUEST && code != HTTP_UPGRADE_REQUIRED))
2258 {
2259 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2260 return (0);
2261 }
2262
2263 if (code >= HTTP_BAD_REQUEST)
2264 {
2265 /*
2266 * Send a human-readable error message.
2267 */
2268
2269 char message[1024]; /* Message for user */
2270 const char *text; /* Status-specific text */
2271
2272 if (code == HTTP_UNAUTHORIZED)
2273 text = _cupsLangString(con->language,
2274 _("Enter your username and password or the "
2275 "root username and password to access this "
2276 "page."));
2277 else if (code == HTTP_UPGRADE_REQUIRED)
2278 text = _cupsLangString(con->language,
2279 _("You must use a https: URL to access this "
2280 "page."));
2281 else
2282 text = "";
2283
2284 snprintf(message, sizeof(message),
2285 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" "
2286 "\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"
2287 "<HTML>\n"
2288 "<HEAD>\n"
2289 "\t<META HTTP-EQUIV=\"Content-Type\" "
2290 "CONTENT=\"text/html; charset=utf-8\">\n"
2291 "\t<TITLE>%d %s</TITLE>\n"
2292 "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" "
2293 "HREF=\"/cups.css\">\n"
2294 "</HEAD>\n"
2295 "<BODY>\n"
2296 "<H1>%d %s</H1>\n"
2297 "<P>%s</P>\n"
2298 "</BODY>\n"
2299 "</HTML>\n",
2300 code, httpStatus(code), code, httpStatus(code), text);
2301
2302 if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0)
2303 return (0);
2304 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2305 (int)strlen(message)) < 0)
2306 return (0);
2307 if (httpPrintf(HTTP(con), "\r\n") < 0)
2308 return (0);
2309 if (httpPrintf(HTTP(con), "%s", message) < 0)
2310 return (0);
2311 }
2312 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2313 return (0);
2314
2315 con->http.state = HTTP_WAITING;
2316
2317 return (1);
2318 }
2319
2320
2321 /*
2322 * 'cupsdSendFile()' - Send a file via HTTP.
2323 */
2324
2325 int /* O - 0 on failure, 1 on success */
2326 cupsdSendFile(cupsd_client_t *con, /* I - Client connection */
2327 http_status_t code, /* I - HTTP status */
2328 char *filename, /* I - Filename */
2329 char *type, /* I - File type */
2330 struct stat *filestats)/* O - File information */
2331 {
2332 con->file = open(filename, O_RDONLY);
2333
2334 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendFile: %d file=%d", con->http.fd,
2335 con->file);
2336
2337 if (con->file < 0)
2338 return (0);
2339
2340 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2341
2342 con->pipe_pid = 0;
2343
2344 if (!cupsdSendHeader(con, code, type))
2345 return (0);
2346
2347 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
2348 httpGetDateString(filestats->st_mtime)) < 0)
2349 return (0);
2350 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n",
2351 CUPS_LLCAST filestats->st_size) < 0)
2352 return (0);
2353 if (httpPrintf(HTTP(con), "\r\n") < 0)
2354 return (0);
2355
2356 con->http.data_encoding = HTTP_ENCODE_LENGTH;
2357 con->http.data_remaining = filestats->st_size;
2358
2359 if (con->http.data_remaining <= INT_MAX)
2360 con->http._data_remaining = con->http.data_remaining;
2361 else
2362 con->http._data_remaining = INT_MAX;
2363
2364 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2365 "cupsdSendFile: Adding fd %d to OutputSet...", con->http.fd);
2366
2367 FD_SET(con->http.fd, OutputSet);
2368
2369 return (1);
2370 }
2371
2372
2373 /*
2374 * 'cupsdSendHeader()' - Send an HTTP request.
2375 */
2376
2377 int /* O - 1 on success, 0 on failure */
2378 cupsdSendHeader(cupsd_client_t *con, /* I - Client to send to */
2379 http_status_t code, /* I - HTTP status code */
2380 char *type) /* I - MIME type of document */
2381 {
2382 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
2383 con->http.version % 100, code, httpStatus(code)) < 0)
2384 return (0);
2385 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2386 return (0);
2387 if (ServerHeader)
2388 if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0)
2389 return (0);
2390 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2391 {
2392 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2393 return (0);
2394 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n",
2395 KeepAliveTimeout) < 0)
2396 return (0);
2397 }
2398 if (code == HTTP_METHOD_NOT_ALLOWED)
2399 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
2400 return (0);
2401
2402 if (code == HTTP_UNAUTHORIZED)
2403 {
2404 int auth_type; /* Authentication type */
2405
2406
2407 if (!con->best || con->best->type == AUTH_NONE)
2408 auth_type = DefaultAuthType;
2409 else
2410 auth_type = con->best->type;
2411
2412 if (auth_type != AUTH_DIGEST)
2413 {
2414 if (httpPrintf(HTTP(con),
2415 "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
2416 return (0);
2417 }
2418 else
2419 {
2420 if (httpPrintf(HTTP(con),
2421 "WWW-Authenticate: Digest realm=\"CUPS\", nonce=\"%s\"\r\n",
2422 con->http.hostname) < 0)
2423 return (0);
2424 }
2425 }
2426
2427 if (con->language != NULL)
2428 {
2429 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2430 con->language->language) < 0)
2431 return (0);
2432 }
2433
2434 if (type != NULL)
2435 {
2436 if (!strcmp(type, "text/html"))
2437 {
2438 if (httpPrintf(HTTP(con),
2439 "Content-Type: text/html; charset=utf-8\r\n") < 0)
2440 return (0);
2441 }
2442 else if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2443 return (0);
2444 }
2445
2446 return (1);
2447 }
2448
2449
2450 /*
2451 * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs.
2452 */
2453
2454 void
2455 cupsdUpdateCGI(void)
2456 {
2457 char *ptr, /* Pointer to end of line in buffer */
2458 message[1024]; /* Pointer to message text */
2459 int loglevel; /* Log level for message */
2460
2461
2462 while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
2463 message, sizeof(message))) != NULL)
2464 if (!strchr(CGIStatusBuffer->buffer, '\n'))
2465 break;
2466
2467 if (ptr == NULL && errno)
2468 {
2469 /*
2470 * Fatal error on pipe - should never happen!
2471 */
2472
2473 cupsdLogMessage(CUPSD_LOG_CRIT,
2474 "cupsdUpdateCGI: error reading from CGI error pipe - %s",
2475 strerror(errno));
2476 }
2477 }
2478
2479
2480 /*
2481 * 'cupsdWriteClient()' - Write data to a client as needed.
2482 */
2483
2484 int /* O - 1 if success, 0 if fail */
2485 cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */
2486 {
2487 int bytes; /* Number of bytes written */
2488 char buf[16385]; /* Data buffer */
2489 char *bufptr; /* Pointer into buffer */
2490 ipp_state_t ipp_state; /* IPP state value */
2491
2492
2493 #ifdef DEBUG
2494 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2495 "cupsdWriteClient(con=%p) %d response=%p, file=%d "
2496 "pipe_pid=%d state=%d",
2497 con, con->http.fd, con->response, con->file, con->pipe_pid,
2498 con->http.state);
2499 #endif /* DEBUG */
2500
2501 if (con->http.state != HTTP_GET_SEND &&
2502 con->http.state != HTTP_POST_SEND)
2503 return (1);
2504
2505 if (con->response != NULL)
2506 {
2507 ipp_state = ippWrite(&(con->http), con->response);
2508 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
2509 }
2510 else if ((bytes = read(con->file, buf, sizeof(buf) - 1)) > 0)
2511 {
2512 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2513 "cupsdWriteClient: Read %d bytes from file %d...",
2514 bytes, con->file);
2515
2516 if (con->pipe_pid && !con->got_fields)
2517 {
2518 /*
2519 * Inspect the data for Content-Type and other fields.
2520 */
2521
2522 buf[bytes] = '\0';
2523
2524 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
2525 if (*bufptr == '\n')
2526 {
2527 /*
2528 * Send line to client...
2529 */
2530
2531 if (bufptr > buf && bufptr[-1] == '\r')
2532 bufptr[-1] = '\0';
2533 *bufptr++ = '\0';
2534
2535 cupsdLogMessage(CUPSD_LOG_DEBUG2, "Script header: %s", buf);
2536
2537 if (!con->sent_header)
2538 {
2539 /*
2540 * Handle redirection and CGI status codes...
2541 */
2542
2543 if (!strncasecmp(buf, "Location:", 9))
2544 cupsdSendHeader(con, HTTP_SEE_OTHER, NULL);
2545 else if (!strncasecmp(buf, "Status:", 7))
2546 cupsdSendHeader(con, atoi(buf + 7), NULL);
2547 else
2548 cupsdSendHeader(con, HTTP_OK, NULL);
2549
2550 if (con->http.version == HTTP_1_1)
2551 {
2552 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2553
2554 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
2555 return (0);
2556 }
2557
2558 con->sent_header = 1;
2559 }
2560
2561 if (strncasecmp(buf, "Status:", 7))
2562 httpPrintf(HTTP(con), "%s\r\n", buf);
2563
2564 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWriteClient: %d %s",
2565 con->http.fd, buf);
2566
2567 /*
2568 * Update buffer...
2569 */
2570
2571 bytes -= (bufptr - buf);
2572 memmove(buf, bufptr, bytes + 1);
2573 bufptr = buf - 1;
2574
2575 /*
2576 * See if the line was empty...
2577 */
2578
2579 if (con->field_col == 0)
2580 con->got_fields = 1;
2581 else
2582 con->field_col = 0;
2583 }
2584 else if (*bufptr != '\r')
2585 con->field_col ++;
2586
2587 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2588 "cupsdWriteClient: %d bytes=%d, got_fields=%d",
2589 con->http.fd, bytes, con->got_fields);
2590
2591 if (bytes > 0 && !con->got_fields)
2592 {
2593 /*
2594 * Remaining text needs to go out...
2595 */
2596
2597 httpPrintf(HTTP(con), "%s", buf);
2598
2599 con->http.activity = time(NULL);
2600 return (1);
2601 }
2602 else if (bytes == 0)
2603 {
2604 con->http.activity = time(NULL);
2605 return (1);
2606 }
2607 }
2608
2609 if (httpWrite2(HTTP(con), buf, bytes) < 0)
2610 {
2611 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2612 "cupsdWriteClient: %d Write of %d bytes failed!",
2613 con->http.fd, bytes);
2614
2615 cupsdCloseClient(con);
2616 return (0);
2617 }
2618
2619 con->bytes += bytes;
2620
2621 if (con->http.state == HTTP_WAITING)
2622 bytes = 0;
2623 }
2624
2625 if (bytes <= 0)
2626 {
2627 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWriteClient: %d bytes < 0",
2628 con->http.fd);
2629
2630 cupsdLogRequest(con, HTTP_OK);
2631
2632 httpFlushWrite(HTTP(con));
2633
2634 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2635 {
2636 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
2637 {
2638 cupsdCloseClient(con);
2639 return (0);
2640 }
2641 }
2642
2643 con->http.state = HTTP_WAITING;
2644
2645 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2646 "cupsdWriteClient: Removing fd %d from OutputSet...",
2647 con->http.fd);
2648
2649 FD_CLR(con->http.fd, OutputSet);
2650
2651 if (con->file >= 0)
2652 {
2653 if (FD_ISSET(con->file, InputSet))
2654 {
2655 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2656 "cupsdWriteClient: Removing fd %d from InputSet...",
2657 con->file);
2658 FD_CLR(con->file, InputSet);
2659 }
2660
2661 if (con->pipe_pid)
2662 cupsdEndProcess(con->pipe_pid, 0);
2663
2664 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2665 "cupsdWriteClient: %d Closing data file %d.",
2666 con->http.fd, con->file);
2667
2668 close(con->file);
2669 con->file = -1;
2670 con->pipe_pid = 0;
2671 }
2672
2673 if (con->filename)
2674 {
2675 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2676 "cupsdWriteClient: %d Removing temp file %s",
2677 con->http.fd, con->filename);
2678 unlink(con->filename);
2679 cupsdClearString(&con->filename);
2680 }
2681
2682 if (con->request != NULL)
2683 {
2684 ippDelete(con->request);
2685 con->request = NULL;
2686 }
2687
2688 if (con->response != NULL)
2689 {
2690 ippDelete(con->response);
2691 con->response = NULL;
2692 }
2693
2694 cupsdClearString(&con->command);
2695 cupsdClearString(&con->options);
2696
2697 if (!con->http.keep_alive)
2698 {
2699 cupsdCloseClient(con);
2700 return (0);
2701 }
2702 }
2703 else
2704 {
2705 con->file_ready = 0;
2706
2707 if (con->pipe_pid && !FD_ISSET(con->file, InputSet))
2708 {
2709 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2710 "cupsdWriteClient: Adding fd %d to InputSet...",
2711 con->file);
2712 FD_SET(con->file, InputSet);
2713 }
2714 }
2715
2716 con->http.activity = time(NULL);
2717
2718 return (1);
2719 }
2720
2721
2722 /*
2723 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
2724 */
2725
2726 static int /* O - 1 if modified since */
2727 check_if_modified(
2728 cupsd_client_t *con, /* I - Client connection */
2729 struct stat *filestats) /* I - File information */
2730 {
2731 char *ptr; /* Pointer into field */
2732 time_t date; /* Time/date value */
2733 off_t size; /* Size/length value */
2734
2735
2736 size = 0;
2737 date = 0;
2738 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
2739
2740 if (*ptr == '\0')
2741 return (1);
2742
2743 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2744 "check_if_modified: %d If-Modified-Since=\"%s\"",
2745 con->http.fd, ptr);
2746
2747 while (*ptr != '\0')
2748 {
2749 while (isspace(*ptr) || *ptr == ';')
2750 ptr ++;
2751
2752 if (strncasecmp(ptr, "length=", 7) == 0)
2753 {
2754 ptr += 7;
2755 size = strtoll(ptr, NULL, 10);
2756
2757 while (isdigit(*ptr))
2758 ptr ++;
2759 }
2760 else if (isalpha(*ptr))
2761 {
2762 date = httpGetDateTime(ptr);
2763 while (*ptr != '\0' && *ptr != ';')
2764 ptr ++;
2765 }
2766 }
2767
2768 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2769 "check_if_modified: %d sizes=" CUPS_LLFMT ","
2770 CUPS_LLFMT " dates=%d,%d",
2771 con->http.fd, CUPS_LLCAST size,
2772 CUPS_LLCAST filestats->st_size, (int)date,
2773 (int)filestats->st_mtime);
2774
2775 return ((size != filestats->st_size && size != 0) ||
2776 (date < filestats->st_mtime && date != 0) ||
2777 (size == 0 && date == 0));
2778 }
2779
2780
2781 #ifdef HAVE_CDSASSL
2782 /*
2783 * 'get_cdsa_server_certs()' - Convert a keychain name into the CFArrayRef
2784 * required by SSLSetCertificate.
2785 *
2786 * For now we assumes that there is exactly one SecIdentity in the
2787 * keychain - i.e. there is exactly one matching cert/private key pair.
2788 * In the future we will search a keychain for a SecIdentity matching a
2789 * specific criteria. We also skip the operation of adding additional
2790 * non-signing certs from the keychain to the CFArrayRef.
2791 *
2792 * To create a self-signed certificate for testing use the certtool.
2793 * Executing the following as root will do it:
2794 *
2795 * certtool c c v k=CUPS
2796 */
2797
2798 static CFArrayRef /* O - Array of certificates */
2799 get_cdsa_server_certs(void)
2800 {
2801 OSStatus err; /* Error info */
2802 SecKeychainRef kcRef; /* Keychain reference */
2803 SecIdentitySearchRef srchRef; /* Search reference */
2804 SecIdentityRef identity; /* Identity */
2805 CFArrayRef ca; /* Certificate array */
2806
2807
2808 kcRef = NULL;
2809 srchRef = NULL;
2810 identity = NULL;
2811 ca = NULL;
2812 err = SecKeychainOpen(ServerCertificate, &kcRef);
2813
2814 if (err)
2815 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\", error %d.",
2816 ServerCertificate, (int)err);
2817 else
2818 {
2819 /*
2820 * Search for "any" identity matching specified key use;
2821 * in this app, we expect there to be exactly one.
2822 */
2823
2824 err = SecIdentitySearchCreate(kcRef, CSSM_KEYUSE_SIGN, &srchRef);
2825
2826 if (err)
2827 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2828 "Cannot find signing key in keychain \"%s\", error %d",
2829 ServerCertificate, (int)err);
2830 else
2831 {
2832 err = SecIdentitySearchCopyNext(srchRef, &identity);
2833
2834 if (err)
2835 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2836 "Cannot find signing key in keychain \"%s\", error %d",
2837 ServerCertificate, (int)err);
2838 else
2839 {
2840 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
2841 cupsdLogMessage(CUPSD_LOG_ERROR,
2842 "SecIdentitySearchCopyNext CFTypeID failure!");
2843 else
2844 {
2845 /*
2846 * Found one. Place it in a CFArray.
2847 * TBD: snag other (non-identity) certs from keychain and add them
2848 * to array as well.
2849 */
2850
2851 ca = CFArrayCreate(NULL, (const void **)&identity, 1, NULL);
2852
2853 if (ca == nil)
2854 cupsdLogMessage(CUPSD_LOG_ERROR, "CFArrayCreate error");
2855 }
2856
2857 /*CFRelease(identity);*/
2858 }
2859
2860 /*CFRelease(srchRef);*/
2861 }
2862
2863 /*CFRelease(kcRef);*/
2864 }
2865
2866 return (ca);
2867 }
2868 #endif /* HAVE_CDSASSL */
2869
2870
2871 /*
2872 * 'get_file()' - Get a filename and state info.
2873 */
2874
2875 static char * /* O - Real filename */
2876 get_file(cupsd_client_t *con, /* I - Client connection */
2877 struct stat *filestats, /* O - File information */
2878 char *filename, /* IO - Filename buffer */
2879 int len) /* I - Buffer length */
2880 {
2881 int status; /* Status of filesystem calls */
2882 char *ptr; /* Pointer info filename */
2883 int plen; /* Remaining length after pointer */
2884
2885
2886 /*
2887 * Need to add DocumentRoot global...
2888 */
2889
2890 if (!strncmp(con->uri, "/ppd/", 5))
2891 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
2892 else if (!strncmp(con->uri, "/admin/conf/", 12))
2893 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
2894 else if (!strncmp(con->uri, "/admin/log/", 11))
2895 {
2896 if (!strcmp(con->uri + 11, "access_log") && AccessLog[0] == '/')
2897 strlcpy(filename, AccessLog, len);
2898 else if (!strcmp(con->uri + 11, "error_log") && ErrorLog[0] == '/')
2899 strlcpy(filename, ErrorLog, len);
2900 else if (!strcmp(con->uri + 11, "page_log") && PageLog[0] == '/')
2901 strlcpy(filename, PageLog, len);
2902 else
2903 return (NULL);
2904 }
2905 else if (con->language != NULL)
2906 snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language,
2907 con->uri);
2908 else
2909 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
2910
2911 if ((ptr = strchr(filename, '?')) != NULL)
2912 *ptr = '\0';
2913
2914 /*
2915 * Grab the status for this language; if there isn't a language-specific file
2916 * then fallback to the default one...
2917 */
2918
2919 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
2920 {
2921 /*
2922 * Drop the language prefix and try the current directory...
2923 */
2924
2925 if (strncmp(con->uri, "/ppd/", 5) &&
2926 strncmp(con->uri, "/admin/conf/", 12) &&
2927 strncmp(con->uri, "/admin/log/", 11))
2928 {
2929 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
2930
2931 if ((ptr = strchr(filename, '?')) != NULL)
2932 *ptr = '\0';
2933
2934 status = stat(filename, filestats);
2935 }
2936 }
2937
2938 /*
2939 * If we're found a directory, get the index.html file instead...
2940 */
2941
2942 if (!status && S_ISDIR(filestats->st_mode))
2943 {
2944 if (filename[strlen(filename) - 1] != '/')
2945 strlcat(filename, "/", len);
2946
2947 ptr = filename + strlen(filename);
2948 plen = len - (ptr - filename);
2949
2950 strlcpy(ptr, "index.html", plen);
2951 status = stat(filename, filestats);
2952
2953 #ifdef HAVE_JAVA
2954 if (status)
2955 {
2956 strlcpy(ptr, "index.class", plen);
2957 status = stat(filename, filestats);
2958 }
2959 #endif /* HAVE_JAVA */
2960
2961 #ifdef HAVE_PERL
2962 if (status)
2963 {
2964 strlcpy(ptr, "index.pl", plen);
2965 status = stat(filename, filestats);
2966 }
2967 #endif /* HAVE_PERL */
2968
2969 #ifdef HAVE_PHP
2970 if (status)
2971 {
2972 strlcpy(ptr, "index.php", plen);
2973 status = stat(filename, filestats);
2974 }
2975 #endif /* HAVE_PHP */
2976
2977 #ifdef HAVE_PYTHON
2978 if (status)
2979 {
2980 strlcpy(ptr, "index.pyc", plen);
2981 status = stat(filename, filestats);
2982 }
2983
2984 if (status)
2985 {
2986 strlcpy(ptr, "index.py", plen);
2987 status = stat(filename, filestats);
2988 }
2989 #endif /* HAVE_PYTHON */
2990 }
2991
2992 cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_file: %d filename=%s size=%d",
2993 con->http.fd, filename,
2994 status ? -1 : (int)filestats->st_size);
2995
2996 if (!status)
2997 con->http.data_remaining = (int)filestats->st_size;
2998
2999 if (status)
3000 return (NULL);
3001 else
3002 return (filename);
3003 }
3004
3005
3006 /*
3007 * 'install_conf_file()' - Install a configuration file.
3008 */
3009
3010 static http_status_t /* O - Status */
3011 install_conf_file(cupsd_client_t *con) /* I - Connection */
3012 {
3013 cups_file_t *in, /* Input file */
3014 *out; /* Output file */
3015 char buffer[1024]; /* Copy buffer */
3016 int bytes; /* Number of bytes */
3017 char conffile[1024], /* Configuration filename */
3018 newfile[1024], /* New config filename */
3019 oldfile[1024]; /* Old config filename */
3020 struct stat confinfo; /* Config file info */
3021
3022
3023 /*
3024 * First construct the filenames...
3025 */
3026
3027 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
3028 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
3029 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
3030
3031 cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", conffile);
3032
3033 /*
3034 * Get the owner, group, and permissions of the configuration file.
3035 * If it doesn't exist, assign it to the User and Group in the
3036 * cupsd.conf file with mode 0640 permissions.
3037 */
3038
3039 if (stat(conffile, &confinfo))
3040 {
3041 confinfo.st_uid = User;
3042 confinfo.st_gid = Group;
3043 confinfo.st_mode = ConfigFilePerm;
3044 }
3045
3046 /*
3047 * Open the request file and new config file...
3048 */
3049
3050 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
3051 {
3052 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\" - %s",
3053 con->filename, strerror(errno));
3054 return (HTTP_SERVER_ERROR);
3055 }
3056
3057 if ((out = cupsFileOpen(newfile, "wb")) == NULL)
3058 {
3059 cupsFileClose(in);
3060 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open config file \"%s\" - %s",
3061 newfile, strerror(errno));
3062 return (HTTP_SERVER_ERROR);
3063 }
3064
3065 fchmod(cupsFileNumber(out), confinfo.st_mode);
3066 fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid);
3067
3068 /*
3069 * Copy from the request to the new config file...
3070 */
3071
3072 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
3073 if (cupsFileWrite(out, buffer, bytes) < bytes)
3074 {
3075 cupsdLogMessage(CUPSD_LOG_ERROR,
3076 "Unable to copy to config file \"%s\" - %s",
3077 newfile, strerror(errno));
3078
3079 cupsFileClose(in);
3080 cupsFileClose(out);
3081 unlink(newfile);
3082
3083 return (HTTP_SERVER_ERROR);
3084 }
3085
3086 /*
3087 * Close the files...
3088 */
3089
3090 cupsFileClose(in);
3091 if (cupsFileClose(out))
3092 {
3093 cupsdLogMessage(CUPSD_LOG_ERROR,
3094 "Error file closing config file \"%s\" - %s",
3095 newfile, strerror(errno));
3096
3097 unlink(newfile);
3098
3099 return (HTTP_SERVER_ERROR);
3100 }
3101
3102 /*
3103 * Remove the request file...
3104 */
3105
3106 unlink(con->filename);
3107 cupsdClearString(&con->filename);
3108
3109 /*
3110 * Unlink the old backup, rename the current config file to the backup
3111 * filename, and rename the new config file to the config file name...
3112 */
3113
3114 if (unlink(oldfile))
3115 if (errno != ENOENT)
3116 {
3117 cupsdLogMessage(CUPSD_LOG_ERROR,
3118 "Unable to remove backup config file \"%s\" - %s",
3119 oldfile, strerror(errno));
3120
3121 unlink(newfile);
3122
3123 return (HTTP_SERVER_ERROR);
3124 }
3125
3126 if (rename(conffile, oldfile))
3127 if (errno != ENOENT)
3128 {
3129 cupsdLogMessage(CUPSD_LOG_ERROR,
3130 "Unable to rename old config file \"%s\" - %s",
3131 conffile, strerror(errno));
3132
3133 unlink(newfile);
3134
3135 return (HTTP_SERVER_ERROR);
3136 }
3137
3138 if (rename(newfile, conffile))
3139 {
3140 cupsdLogMessage(CUPSD_LOG_ERROR,
3141 "Unable to rename new config file \"%s\" - %s",
3142 newfile, strerror(errno));
3143
3144 rename(oldfile, conffile);
3145 unlink(newfile);
3146
3147 return (HTTP_SERVER_ERROR);
3148 }
3149
3150 /*
3151 * If the cupsd.conf file was updated, set the NeedReload flag...
3152 */
3153
3154 if (!strcmp(con->uri, "/admin/conf/cupsd.conf"))
3155 NeedReload = RELOAD_CUPSD;
3156 else
3157 NeedReload = RELOAD_ALL;
3158
3159 ReloadTime = time(NULL);
3160
3161 /*
3162 * Return that the file was created successfully...
3163 */
3164
3165 return (HTTP_CREATED);
3166 }
3167
3168
3169 /*
3170 * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
3171 */
3172
3173 static int /* O - 0 if relative, 1 if absolute */
3174 is_path_absolute(const char *path) /* I - Input path */
3175 {
3176 /*
3177 * Check for a leading slash...
3178 */
3179
3180 if (path[0] != '/')
3181 return (0);
3182
3183 /*
3184 * Check for "/.." in the path...
3185 */
3186
3187 while ((path = strstr(path, "/..")) != NULL)
3188 {
3189 if (!path[3] || path[3] == '/')
3190 return (0);
3191
3192 path ++;
3193 }
3194
3195 /*
3196 * If we haven't found any relative paths, return 1 indicating an
3197 * absolute path...
3198 */
3199
3200 return (1);
3201 }
3202
3203
3204 /*
3205 * 'pipe_command()' - Pipe the output of a command to the remote client.
3206 */
3207
3208 static int /* O - Process ID */
3209 pipe_command(cupsd_client_t *con, /* I - Client connection */
3210 int infile, /* I - Standard input for command */
3211 int *outfile, /* O - Standard output for command */
3212 char *command, /* I - Command to run */
3213 char *options, /* I - Options for command */
3214 int root) /* I - Run as root? */
3215 {
3216 int i; /* Looping var */
3217 int pid; /* Process ID */
3218 char *commptr; /* Command string pointer */
3219 char *uriptr; /* URI string pointer */
3220 int fds[2]; /* Pipe FDs */
3221 int argc; /* Number of arguments */
3222 int envc; /* Number of environment variables */
3223 char argbuf[10240], /* Argument buffer */
3224 *argv[100], /* Argument strings */
3225 *envp[MAX_ENV + 16]; /* Environment variables */
3226 char content_length[1024], /* CONTENT_LENGTH environment variable */
3227 content_type[1024], /* CONTENT_TYPE environment variable */
3228 http_cookie[32768], /* HTTP_COOKIE environment variable */
3229 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
3230 lang[1024], /* LANG environment variable */
3231 *query_string, /* QUERY_STRING env variable */
3232 remote_addr[1024], /* REMOTE_ADDR environment variable */
3233 remote_host[1024], /* REMOTE_HOST environment variable */
3234 remote_user[1024], /* REMOTE_USER environment variable */
3235 script_name[1024], /* SCRIPT_NAME environment variable */
3236 server_name[1024], /* SERVER_NAME environment variable */
3237 server_port[1024]; /* SERVER_PORT environment variable */
3238
3239
3240 /*
3241 * Parse a copy of the options string, which is of the form:
3242 *
3243 * name argument+argument+argument
3244 * name?argument+argument+argument
3245 * name param=value&param=value
3246 * name?param=value&param=value
3247 *
3248 * If the string contains an "=" character after the initial name,
3249 * then we treat it as a HTTP GET form request and make a copy of
3250 * the remaining string for the environment variable.
3251 *
3252 * The string is always parsed out as command-line arguments, to
3253 * be consistent with Apache...
3254 */
3255
3256 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3257 "pipe_command: command=\"%s\", options=\"%s\"",
3258 command, options);
3259
3260 strlcpy(argbuf, options, sizeof(argbuf));
3261
3262 argv[0] = argbuf;
3263 query_string = NULL;
3264
3265 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
3266 {
3267 /*
3268 * Break arguments whenever we see a + or space...
3269 */
3270
3271 if (*commptr == ' ' || *commptr == '+' || (*commptr == '?' && argc == 1))
3272 {
3273 /*
3274 * Terminate the current string and skip trailing whitespace...
3275 */
3276
3277 *commptr++ = '\0';
3278
3279 while (*commptr == ' ')
3280 commptr ++;
3281
3282 /*
3283 * If we don't have a blank string, save it as another argument...
3284 */
3285
3286 if (*commptr)
3287 {
3288 argv[argc] = commptr;
3289 argc ++;
3290 }
3291 else
3292 break;
3293
3294 /*
3295 * If we see an "=" in the remaining string, make a copy of it since
3296 * it will be query data...
3297 */
3298
3299 if (argc == 2 && strchr(commptr, '=') && con->operation == HTTP_GET)
3300 cupsdSetStringf(&query_string, "QUERY_STRING=%s", commptr);
3301
3302 /*
3303 * Don't skip the first non-blank character...
3304 */
3305
3306 commptr --;
3307 }
3308 else if (*commptr == '%' && isxdigit(commptr[1] & 255) &&
3309 isxdigit(commptr[2] & 255))
3310 {
3311 /*
3312 * Convert the %xx notation to the individual character.
3313 */
3314
3315 if (commptr[1] >= '0' && commptr[1] <= '9')
3316 *commptr = (commptr[1] - '0') << 4;
3317 else
3318 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
3319
3320 if (commptr[2] >= '0' && commptr[2] <= '9')
3321 *commptr |= commptr[2] - '0';
3322 else
3323 *commptr |= tolower(commptr[2]) - 'a' + 10;
3324
3325 _cups_strcpy(commptr + 1, commptr + 3);
3326
3327 /*
3328 * Check for a %00 and break if that is the case...
3329 */
3330
3331 if (!*commptr)
3332 break;
3333 }
3334 }
3335
3336 argv[argc] = NULL;
3337
3338 if (argv[0][0] == '\0')
3339 argv[0] = strrchr(command, '/') + 1;
3340
3341 /*
3342 * Setup the environment variables as needed...
3343 */
3344
3345 if (con->language)
3346 snprintf(lang, sizeof(lang), "LANG=%s.UTF-8", con->language->language);
3347 else
3348 strcpy(lang, "LANG=C");
3349
3350 strcpy(remote_addr, "REMOTE_ADDR=");
3351 httpAddrString(con->http.hostaddr, remote_addr + 12,
3352 sizeof(remote_addr) - 12);
3353
3354 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s",
3355 con->http.hostname);
3356
3357 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
3358 if ((uriptr = strchr(script_name, '?')) != NULL)
3359 *uriptr = '\0';
3360
3361 sprintf(server_port, "SERVER_PORT=%d", con->serverport);
3362
3363 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
3364 con->servername);
3365
3366 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
3367
3368 envp[envc ++] = lang;
3369 envp[envc ++] = "REDIRECT_STATUS=1";
3370 envp[envc ++] = server_name;
3371 envp[envc ++] = server_port;
3372 envp[envc ++] = remote_addr;
3373 envp[envc ++] = remote_host;
3374 envp[envc ++] = script_name;
3375
3376 if (con->username[0])
3377 {
3378 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
3379
3380 envp[envc ++] = remote_user;
3381 }
3382
3383 if (con->http.version == HTTP_1_1)
3384 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
3385 else if (con->http.version == HTTP_1_0)
3386 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
3387 else
3388 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
3389
3390 if (con->http.cookie)
3391 {
3392 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
3393 con->http.cookie);
3394 envp[envc ++] = http_cookie;
3395 }
3396
3397 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
3398 {
3399 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
3400 con->http.fields[HTTP_FIELD_USER_AGENT]);
3401 envp[envc ++] = http_user_agent;
3402 }
3403
3404 if (con->operation == HTTP_GET)
3405 {
3406 for (i = 0; i < argc; i ++)
3407 cupsdLogMessage(CUPSD_LOG_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3408
3409 envp[envc ++] = "REQUEST_METHOD=GET";
3410
3411 if (query_string)
3412 {
3413 /*
3414 * Add GET form variables after ?...
3415 */
3416
3417 envp[envc ++] = query_string;
3418 }
3419 }
3420 else
3421 {
3422 sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT, con->bytes);
3423 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
3424 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
3425
3426 envp[envc ++] = "REQUEST_METHOD=POST";
3427 envp[envc ++] = content_length;
3428 envp[envc ++] = content_type;
3429 }
3430
3431 /*
3432 * Tell the CGI if we are using encryption...
3433 */
3434
3435 if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
3436 envp[envc ++] = "HTTPS=ON";
3437
3438 /*
3439 * Terminate the environment array...
3440 */
3441
3442 envp[envc] = NULL;
3443
3444 if (LogLevel == CUPSD_LOG_DEBUG2)
3445 {
3446 for (i = 0; i < argc; i ++)
3447 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3448 "pipe_command: argv[%d] = \"%s\"", i, argv[i]);
3449 for (i = 0; i < envc; i ++)
3450 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3451 "pipe_command: envp[%d] = \"%s\"", i, envp[i]);
3452 }
3453
3454 /*
3455 * Create a pipe for the output...
3456 */
3457
3458 if (cupsdOpenPipe(fds))
3459 {
3460 cupsdClearString(&query_string);
3461
3462 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create pipes for CGI %s - %s",
3463 argv[0], strerror(errno));
3464 return (0);
3465 }
3466
3467 /*
3468 * Then execute the command...
3469 */
3470
3471 if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
3472 -1, root, &pid) < 0)
3473 {
3474 /*
3475 * Error - can't fork!
3476 */
3477
3478 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for CGI %s - %s", argv[0],
3479 strerror(errno));
3480
3481 cupsdClosePipe(fds);
3482 pid = 0;
3483 }
3484 else
3485 {
3486 /*
3487 * Fork successful - return the PID...
3488 */
3489
3490 if (con->username[0])
3491 cupsdAddCert(pid, con->username);
3492
3493 cupsdLogMessage(CUPSD_LOG_DEBUG, "CGI %s started - PID = %d", command, pid);
3494
3495 *outfile = fds[0];
3496 close(fds[1]);
3497 }
3498
3499 cupsdClearString(&query_string);
3500
3501 return (pid);
3502 }
3503
3504
3505 /*
3506 * End of "$Id: client.c 5083 2006-02-06 02:57:43Z mike $".
3507 */