]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Merge changes from CUPS 1.5svn-r9567
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c 7950 2008-09-17 00:21:59Z mike $"
3 *
4 * Client routines for the CUPS scheduler.
5 *
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * This file contains Kerberos support code, copyright 2006 by
10 * Jelmer Vernooij.
11 *
12 * These coded instructions, statements, and computer programs are the
13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
15 * which should have been included with this file. If this file is
16 * file is missing or damaged, see the license at "http://www.cups.org/".
17 *
18 * Contents:
19 *
20 * cupsdAcceptClient() - Accept a new client.
21 * cupsdCloseAllClients() - Close all remote clients immediately.
22 * cupsdCloseClient() - Close a remote client.
23 * cupsdFlushHeader() - Flush the header fields to the client.
24 * cupsdReadClient() - Read data from a client.
25 * cupsdSendCommand() - Send output from a command via HTTP.
26 * cupsdSendError() - Send an error message via HTTP.
27 * cupsdSendHeader() - Send an HTTP request.
28 * cupsdUpdateCGI() - Read status messages from CGI scripts and
29 * programs.
30 * cupsdWriteClient() - Write data to a client as needed.
31 * check_if_modified() - Decode an "If-Modified-Since" line.
32 * compare_clients() - Compare two client connections.
33 * data_ready() - Check whether data is available from a client.
34 * encrypt_client() - Enable encryption for the client...
35 * get_cdsa_certificate() - Get a SSL/TLS certificate from the System
36 * keychain.
37 * get_file() - Get a filename and state info.
38 * install_conf_file() - Install a configuration file.
39 * is_cgi() - Is the resource a CGI script/program?
40 * is_path_absolute() - Is a path absolute and free of relative elements
41 * (i.e. "..").
42 * make_certificate() - Make a self-signed SSL/TLS certificate.
43 * pipe_command() - Pipe the output of a command to the remote client.
44 * valid_host() - Is the Host: field valid?
45 * write_file() - Send a file via HTTP.
46 * write_pipe() - Flag that data is available on the CGI pipe.
47 */
48
49 /*
50 * Include necessary headers...
51 */
52
53 #include "cupsd.h"
54
55 #ifdef HAVE_TCPD_H
56 # include <tcpd.h>
57 #endif /* HAVE_TCPD_H */
58
59
60 /*
61 * Local functions...
62 */
63
64 static int check_if_modified(cupsd_client_t *con,
65 struct stat *filestats);
66 static int compare_clients(cupsd_client_t *a, cupsd_client_t *b,
67 void *data);
68 static int data_ready(cupsd_client_t *con);
69 #ifdef HAVE_SSL
70 static int encrypt_client(cupsd_client_t *con);
71 #endif /* HAVE_SSL */
72 #ifdef HAVE_CDSASSL
73 static CFArrayRef get_cdsa_certificate(cupsd_client_t *con);
74 #endif /* HAVE_CDSASSL */
75 static char *get_file(cupsd_client_t *con, struct stat *filestats,
76 char *filename, int len);
77 static http_status_t install_conf_file(cupsd_client_t *con);
78 static int is_cgi(cupsd_client_t *con, const char *filename,
79 struct stat *filestats, mime_type_t *type);
80 static int is_path_absolute(const char *path);
81 #ifdef HAVE_SSL
82 static int make_certificate(cupsd_client_t *con);
83 #endif /* HAVE_SSL */
84 static int pipe_command(cupsd_client_t *con, int infile, int *outfile,
85 char *command, char *options, int root);
86 static int valid_host(cupsd_client_t *con);
87 static int write_file(cupsd_client_t *con, http_status_t code,
88 char *filename, char *type,
89 struct stat *filestats);
90 static void write_pipe(cupsd_client_t *con);
91
92
93 /*
94 * 'cupsdAcceptClient()' - Accept a new client.
95 */
96
97 void
98 cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
99 {
100 int count; /* Count of connections on a host */
101 int val; /* Parameter value */
102 cupsd_client_t *con, /* New client pointer */
103 *tempcon; /* Temporary client pointer */
104 http_addrlist_t *addrlist, /* List of adddresses for host */
105 *addr; /* Current address */
106 socklen_t addrlen; /* Length of address */
107 char *hostname; /* Hostname for address */
108 http_addr_t temp; /* Temporary address variable */
109 static time_t last_dos = 0; /* Time of last DoS attack */
110 #ifdef HAVE_TCPD_H
111 struct request_info wrap_req; /* TCP wrappers request information */
112 #endif /* HAVE_TCPD_H */
113
114
115 cupsdLogMessage(CUPSD_LOG_DEBUG2,
116 "cupsdAcceptClient(lis=%p(%d)) Clients=%d",
117 lis, lis->fd, cupsArrayCount(Clients));
118
119 /*
120 * Make sure we don't have a full set of clients already...
121 */
122
123 if (cupsArrayCount(Clients) == MaxClients)
124 return;
125
126 /*
127 * Get a pointer to the next available client...
128 */
129
130 if (!Clients)
131 Clients = cupsArrayNew(NULL, NULL);
132
133 if (!Clients)
134 {
135 cupsdLogMessage(CUPSD_LOG_ERROR,
136 "Unable to allocate memory for clients array!");
137 cupsdPauseListening();
138 return;
139 }
140
141 if (!ActiveClients)
142 ActiveClients = cupsArrayNew((cups_array_func_t)compare_clients, NULL);
143
144 if (!ActiveClients)
145 {
146 cupsdLogMessage(CUPSD_LOG_ERROR,
147 "Unable to allocate memory for active clients array!");
148 cupsdPauseListening();
149 return;
150 }
151
152 if ((con = calloc(1, sizeof(cupsd_client_t))) == NULL)
153 {
154 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for client!");
155 cupsdPauseListening();
156 return;
157 }
158
159 con->http.activity = time(NULL);
160 con->file = -1;
161 con->http.hostaddr = &(con->clientaddr);
162
163 /*
164 * Accept the client and get the remote address...
165 */
166
167 addrlen = sizeof(http_addr_t);
168
169 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)con->http.hostaddr,
170 &addrlen)) < 0)
171 {
172 if (errno == ENFILE || errno == EMFILE)
173 cupsdPauseListening();
174
175 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.",
176 strerror(errno));
177 free(con);
178
179 return;
180 }
181
182 #ifdef AF_INET6
183 if (lis->address.addr.sa_family == AF_INET6)
184 {
185 /*
186 * Save the connected port number...
187 */
188
189 con->http.hostaddr->ipv6.sin6_port = lis->address.ipv6.sin6_port;
190
191 /*
192 * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we
193 * can more easily use...
194 */
195
196 if (con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0] == 0 &&
197 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1] == 0 &&
198 ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]) == 0xffff)
199 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2] = 0;
200 }
201 else
202 #endif /* AF_INET6 */
203 if (lis->address.addr.sa_family == AF_INET)
204 con->http.hostaddr->ipv4.sin_port = lis->address.ipv4.sin_port;
205
206 /*
207 * Check the number of clients on the same address...
208 */
209
210 for (count = 0, tempcon = (cupsd_client_t *)cupsArrayFirst(Clients);
211 tempcon;
212 tempcon = (cupsd_client_t *)cupsArrayNext(Clients))
213 if (httpAddrEqual(tempcon->http.hostaddr, con->http.hostaddr))
214 {
215 count ++;
216 if (count >= MaxClientsPerHost)
217 break;
218 }
219
220 if (count >= MaxClientsPerHost)
221 {
222 if ((time(NULL) - last_dos) >= 60)
223 {
224 last_dos = time(NULL);
225 cupsdLogMessage(CUPSD_LOG_WARN,
226 "Possible DoS attack - more than %d clients connecting "
227 "from %s!",
228 MaxClientsPerHost,
229 httpAddrString(con->http.hostaddr, con->http.hostname,
230 sizeof(con->http.hostname)));
231 }
232
233 #ifdef WIN32
234 closesocket(con->http.fd);
235 #else
236 close(con->http.fd);
237 #endif /* WIN32 */
238
239 free(con);
240 return;
241 }
242
243 /*
244 * Get the hostname or format the IP address as needed...
245 */
246
247 if (httpAddrLocalhost(con->http.hostaddr))
248 {
249 /*
250 * Map accesses from the loopback interface to "localhost"...
251 */
252
253 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
254 hostname = con->http.hostname;
255 }
256 else
257 {
258 /*
259 * Map accesses from the same host to the server name.
260 */
261
262 if (HostNameLookups)
263 hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname,
264 sizeof(con->http.hostname));
265 else
266 {
267 hostname = NULL;
268 httpAddrString(con->http.hostaddr, con->http.hostname,
269 sizeof(con->http.hostname));
270 }
271 }
272
273 if (hostname == NULL && HostNameLookups == 2)
274 {
275 /*
276 * Can't have an unresolved IP address with double-lookups enabled...
277 */
278
279 #ifdef WIN32
280 closesocket(con->http.fd);
281 #else
282 close(con->http.fd);
283 #endif /* WIN32 */
284
285 cupsdLogMessage(CUPSD_LOG_WARN,
286 "Name lookup failed - connection from %s closed!",
287 con->http.hostname);
288
289 free(con);
290 return;
291 }
292
293 if (HostNameLookups == 2)
294 {
295 /*
296 * Do double lookups as needed...
297 */
298
299 if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL))
300 != NULL)
301 {
302 /*
303 * See if the hostname maps to the same IP address...
304 */
305
306 for (addr = addrlist; addr; addr = addr->next)
307 if (httpAddrEqual(con->http.hostaddr, &(addr->addr)))
308 break;
309 }
310 else
311 addr = NULL;
312
313 httpAddrFreeList(addrlist);
314
315 if (!addr)
316 {
317 /*
318 * Can't have a hostname that doesn't resolve to the same IP address
319 * with double-lookups enabled...
320 */
321
322 #ifdef WIN32
323 closesocket(con->http.fd);
324 #else
325 close(con->http.fd);
326 #endif /* WIN32 */
327
328 cupsdLogMessage(CUPSD_LOG_WARN,
329 "IP lookup failed - connection from %s closed!",
330 con->http.hostname);
331 free(con);
332 return;
333 }
334 }
335
336 #ifdef HAVE_TCPD_H
337 /*
338 * See if the connection is denied by TCP wrappers...
339 */
340
341 request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, con->http.fd, NULL);
342 fromhost(&wrap_req);
343
344 if (!hosts_access(&wrap_req))
345 {
346 #ifdef WIN32
347 closesocket(con->http.fd);
348 #else
349 close(con->http.fd);
350 #endif /* WIN32 */
351
352 cupsdLogMessage(CUPSD_LOG_WARN,
353 "Connection from %s refused by /etc/hosts.allow and "
354 "/etc/hosts.deny rules.", con->http.hostname);
355 free(con);
356 return;
357 }
358 #endif /* HAVE_TCPD_H */
359
360 #ifdef AF_INET6
361 if (con->http.hostaddr->addr.sa_family == AF_INET6)
362 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv6)",
363 con->http.fd, con->http.hostname,
364 ntohs(con->http.hostaddr->ipv6.sin6_port));
365 else
366 #endif /* AF_INET6 */
367 #ifdef AF_LOCAL
368 if (con->http.hostaddr->addr.sa_family == AF_LOCAL)
369 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s (Domain)",
370 con->http.fd, con->http.hostname);
371 else
372 #endif /* AF_LOCAL */
373 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv4)",
374 con->http.fd, con->http.hostname,
375 ntohs(con->http.hostaddr->ipv4.sin_port));
376
377 /*
378 * Get the local address the client connected to...
379 */
380
381 addrlen = sizeof(temp);
382 if (getsockname(con->http.fd, (struct sockaddr *)&temp, &addrlen))
383 {
384 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get local address - %s",
385 strerror(errno));
386
387 strcpy(con->servername, "localhost");
388 con->serverport = LocalPort;
389 }
390 else
391 {
392 #ifdef AF_INET6
393 if (temp.addr.sa_family == AF_INET6)
394 {
395 if (httpAddrLocalhost(&temp))
396 strlcpy(con->servername, "localhost", sizeof(con->servername));
397 else if (HostNameLookups || RemotePort)
398 httpAddrLookup(&temp, con->servername, sizeof(con->servername));
399 else
400 httpAddrString(&temp, con->servername, sizeof(con->servername));
401
402 con->serverport = ntohs(lis->address.ipv6.sin6_port);
403 }
404 else
405 #endif /* AF_INET6 */
406 if (temp.addr.sa_family == AF_INET)
407 {
408 if (httpAddrLocalhost(&temp))
409 strlcpy(con->servername, "localhost", sizeof(con->servername));
410 else if (HostNameLookups || RemotePort)
411 httpAddrLookup(&temp, con->servername, sizeof(con->servername));
412 else
413 httpAddrString(&temp, con->servername, sizeof(con->servername));
414
415 con->serverport = ntohs(lis->address.ipv4.sin_port);
416 }
417 else
418 {
419 strcpy(con->servername, "localhost");
420 con->serverport = LocalPort;
421 }
422 }
423
424 cupsArrayAdd(Clients, con);
425
426 /*
427 * Using TCP_NODELAY improves responsiveness, especially on systems with a slow
428 * loopback interface. Since we write large buffers when sending print files
429 * and requests there shouldn't be any performance penalty for this...
430 */
431
432 val = 1;
433 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
434
435 /*
436 * Close this file on all execs...
437 */
438
439 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
440
441 /*
442 * Add the socket to the server select.
443 */
444
445 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
446
447 /*
448 * Temporarily suspend accept()'s until we lose a client...
449 */
450
451 if (cupsArrayCount(Clients) == MaxClients)
452 cupsdPauseListening();
453
454 #ifdef HAVE_SSL
455 /*
456 * See if we are connecting on a secure port...
457 */
458
459 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
460 {
461 /*
462 * https connection; go secure...
463 */
464
465 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
466
467 if (!encrypt_client(con))
468 cupsdCloseClient(con);
469 }
470 else
471 con->auto_ssl = 1;
472 #endif /* HAVE_SSL */
473 }
474
475
476 /*
477 * 'cupsdCloseAllClients()' - Close all remote clients immediately.
478 */
479
480 void
481 cupsdCloseAllClients(void)
482 {
483 cupsd_client_t *con; /* Current client */
484
485
486 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d",
487 cupsArrayCount(Clients));
488
489 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
490 con;
491 con = (cupsd_client_t *)cupsArrayNext(Clients))
492 if (cupsdCloseClient(con))
493 cupsdCloseClient(con);
494 }
495
496
497 /*
498 * 'cupsdCloseClient()' - Close a remote client.
499 */
500
501 int /* O - 1 if partial close, 0 if fully closed */
502 cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */
503 {
504 int partial; /* Do partial close for SSL? */
505 #ifdef HAVE_LIBSSL
506 SSL_CTX *context; /* Context for encryption */
507 unsigned long error; /* Error code */
508 #elif defined(HAVE_GNUTLS)
509 int error; /* Error code */
510 gnutls_certificate_server_credentials *credentials;
511 /* TLS credentials */
512 # elif defined(HAVE_CDSASSL)
513 #endif /* HAVE_LIBSSL */
514
515
516 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCloseClient: %d", con->http.fd);
517
518 /*
519 * Flush pending writes before closing...
520 */
521
522 httpFlushWrite(HTTP(con));
523
524 partial = 0;
525
526 #ifdef HAVE_SSL
527 /*
528 * Shutdown encryption as needed...
529 */
530
531 if (con->http.tls)
532 {
533 partial = 1;
534
535 # ifdef HAVE_LIBSSL
536 context = SSL_get_SSL_CTX(con->http.tls);
537
538 switch (SSL_shutdown(con->http.tls))
539 {
540 case 1 :
541 cupsdLogMessage(CUPSD_LOG_DEBUG,
542 "SSL shutdown successful!");
543 break;
544 case -1 :
545 cupsdLogMessage(CUPSD_LOG_ERROR,
546 "Fatal error during SSL shutdown!");
547 default :
548 while ((error = ERR_get_error()) != 0)
549 cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s",
550 ERR_error_string(error, NULL));
551 break;
552 }
553
554 SSL_CTX_free(context);
555 SSL_free(con->http.tls);
556
557 # elif defined(HAVE_GNUTLS)
558 credentials = (gnutls_certificate_server_credentials *)(con->http.tls_credentials);
559
560 error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
561 switch (error)
562 {
563 case GNUTLS_E_SUCCESS:
564 cupsdLogMessage(CUPSD_LOG_DEBUG,
565 "SSL shutdown successful!");
566 break;
567 default:
568 cupsdLogMessage(CUPSD_LOG_ERROR,
569 "SSL shutdown failed: %s", gnutls_strerror(error));
570 break;
571 }
572
573 gnutls_deinit(con->http.tls);
574 gnutls_certificate_free_credentials(*credentials);
575 free(credentials);
576
577 # elif defined(HAVE_CDSASSL)
578 while (SSLClose(con->http.tls) == errSSLWouldBlock)
579 usleep(1000);
580
581 SSLDisposeContext(con->http.tls);
582
583 if (con->http.tls_credentials)
584 CFRelease(con->http.tls_credentials);
585
586 # endif /* HAVE_LIBSSL */
587
588 con->http.tls = NULL;
589 }
590 #endif /* HAVE_SSL */
591
592 if (con->pipe_pid != 0)
593 {
594 /*
595 * Stop any CGI process...
596 */
597
598 cupsdEndProcess(con->pipe_pid, 1);
599 con->pipe_pid = 0;
600 }
601
602 if (con->file >= 0)
603 {
604 cupsdRemoveSelect(con->file);
605
606 close(con->file);
607 con->file = -1;
608 }
609
610 /*
611 * Close the socket and clear the file from the input set for select()...
612 */
613
614 if (con->http.fd >= 0)
615 {
616 cupsArrayRemove(ActiveClients, con);
617 cupsdSetBusyState();
618
619 if (partial)
620 {
621 /*
622 * Only do a partial close so that the encrypted client gets everything.
623 */
624
625 shutdown(con->http.fd, 0);
626 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
627 }
628 else
629 {
630 /*
631 * Shut the socket down fully...
632 */
633
634 cupsdRemoveSelect(con->http.fd);
635 close(con->http.fd);
636 con->http.fd = -1;
637 }
638 }
639
640 if (!partial)
641 {
642 /*
643 * Free memory...
644 */
645
646 if (con->http.input_set)
647 free(con->http.input_set);
648
649 httpClearCookie(HTTP(con));
650 httpClearFields(HTTP(con));
651
652 cupsdClearString(&con->filename);
653 cupsdClearString(&con->command);
654 cupsdClearString(&con->options);
655 cupsdClearString(&con->query_string);
656
657 if (con->request)
658 {
659 ippDelete(con->request);
660 con->request = NULL;
661 }
662
663 if (con->response)
664 {
665 ippDelete(con->response);
666 con->response = NULL;
667 }
668
669 if (con->language)
670 {
671 cupsLangFree(con->language);
672 con->language = NULL;
673 }
674
675 #ifdef HAVE_AUTHORIZATION_H
676 if (con->authref)
677 {
678 AuthorizationFree(con->authref, kAuthorizationFlagDefaults);
679 con->authref = NULL;
680 }
681 #endif /* HAVE_AUTHORIZATION_H */
682
683 /*
684 * Re-enable new client connections if we are going back under the
685 * limit...
686 */
687
688 if (cupsArrayCount(Clients) == MaxClients)
689 cupsdResumeListening();
690
691 /*
692 * Compact the list of clients as necessary...
693 */
694
695 cupsArrayRemove(Clients, con);
696
697 free(con);
698 }
699
700 return (partial);
701 }
702
703
704 /*
705 * 'cupsdFlushHeader()' - Flush the header fields to the client.
706 */
707
708 int /* I - Bytes written or -1 on error */
709 cupsdFlushHeader(cupsd_client_t *con) /* I - Client to flush to */
710 {
711 int bytes = httpFlushWrite(HTTP(con));
712
713 con->http.data_encoding = HTTP_ENCODE_LENGTH;
714
715 return (bytes);
716 }
717
718
719 /*
720 * 'cupsdReadClient()' - Read data from a client.
721 */
722
723 void
724 cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */
725 {
726 char line[32768], /* Line from client... */
727 operation[64], /* Operation code from socket */
728 version[64], /* HTTP version number string */
729 locale[64], /* Locale */
730 *ptr; /* Pointer into strings */
731 int major, minor; /* HTTP version numbers */
732 http_status_t status; /* Transfer status */
733 ipp_state_t ipp_state; /* State of IPP transfer */
734 int bytes; /* Number of bytes to POST */
735 char *filename; /* Name of file for GET/HEAD */
736 char buf[1024]; /* Buffer for real filename */
737 struct stat filestats; /* File information */
738 mime_type_t *type; /* MIME type of file */
739 cupsd_printer_t *p; /* Printer */
740 static unsigned request_id = 0; /* Request ID for temp files */
741
742
743 status = HTTP_CONTINUE;
744
745 cupsdLogMessage(CUPSD_LOG_DEBUG2,
746 "cupsdReadClient(con=%p(%d)) "
747 "con->http.error=%d "
748 "con->http.used=%d, "
749 "con->http.state=%d "
750 "con->data_encoding=HTTP_ENCODE_%s, "
751 "con->data_remaining=" CUPS_LLFMT ", "
752 "con->file=%d",
753 con, con->http.fd, con->http.error, con->http.used,
754 con->http.state,
755 con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
756 "CHUNKED" : "LENGTH",
757 CUPS_LLCAST con->http.data_remaining, con->file);
758
759 #ifdef HAVE_SSL
760 if (con->auto_ssl)
761 {
762 /*
763 * Automatically check for a SSL/TLS handshake...
764 */
765
766 con->auto_ssl = 0;
767
768 if (recv(con->http.fd, buf, 1, MSG_PEEK) == 1 &&
769 (!buf[0] || !strchr("DGHOPT", buf[0])))
770 {
771 /*
772 * Encrypt this connection...
773 */
774
775 cupsdLogMessage(CUPSD_LOG_DEBUG2,
776 "cupsdReadClient: Saw first byte %02X, auto-negotiating "
777 "SSL/TLS session...", buf[0] & 255);
778
779 if (!encrypt_client(con))
780 cupsdCloseClient(con);
781
782 return;
783 }
784 }
785 #endif /* HAVE_SSL */
786
787 switch (con->http.state)
788 {
789 case HTTP_WAITING :
790 /*
791 * See if we've received a request line...
792 */
793
794 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
795 {
796 if (con->http.error && con->http.error != EPIPE)
797 cupsdLogMessage(CUPSD_LOG_DEBUG,
798 "cupsdReadClient: %d WAITING Closing for error %d "
799 "(%s)", con->http.fd, con->http.error,
800 strerror(con->http.error));
801 else
802 cupsdLogMessage(CUPSD_LOG_DEBUG,
803 "cupsdReadClient: %d WAITING Closing on EOF",
804 con->http.fd);
805
806 cupsdCloseClient(con);
807 return;
808 }
809
810 /*
811 * Ignore blank request lines...
812 */
813
814 if (line[0] == '\0')
815 break;
816
817 /*
818 * Clear other state variables...
819 */
820
821 httpClearFields(HTTP(con));
822
823 con->http.activity = time(NULL);
824 con->http.version = HTTP_1_0;
825 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
826 con->http.data_encoding = HTTP_ENCODE_LENGTH;
827 con->http.data_remaining = 0;
828 con->http._data_remaining = 0;
829 con->operation = HTTP_WAITING;
830 con->bytes = 0;
831 con->file = -1;
832 con->file_ready = 0;
833 con->pipe_pid = 0;
834 con->username[0] = '\0';
835 con->password[0] = '\0';
836 con->uri[0] = '\0';
837
838 cupsdClearString(&con->command);
839 cupsdClearString(&con->options);
840 cupsdClearString(&con->query_string);
841
842 if (con->request)
843 {
844 ippDelete(con->request);
845 con->request = NULL;
846 }
847
848 if (con->response)
849 {
850 ippDelete(con->response);
851 con->response = NULL;
852 }
853
854 if (con->language)
855 {
856 cupsLangFree(con->language);
857 con->language = NULL;
858 }
859
860 #ifdef HAVE_GSSAPI
861 con->have_gss = 0;
862
863 if (con->gss_creds)
864 {
865 OM_uint32 minor_status;
866 gss_release_cred(&minor_status, &con->gss_creds);
867 }
868 #endif /* HAVE_GSSAPI */
869
870 /*
871 * Grab the request line...
872 */
873
874 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
875 {
876 case 1 :
877 if (line[0])
878 {
879 cupsdLogMessage(CUPSD_LOG_ERROR,
880 "Bad request line \"%s\" from %s!",
881 _httpEncodeURI(buf, line, sizeof(buf)),
882 con->http.hostname);
883 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
884 cupsdCloseClient(con);
885 }
886 return;
887 case 2 :
888 con->http.version = HTTP_0_9;
889 break;
890 case 3 :
891 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
892 {
893 cupsdLogMessage(CUPSD_LOG_ERROR,
894 "Bad request line \"%s\" from %s!",
895 _httpEncodeURI(buf, line, sizeof(buf)),
896 con->http.hostname);
897 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
898 cupsdCloseClient(con);
899 return;
900 }
901
902 if (major < 2)
903 {
904 con->http.version = (http_version_t)(major * 100 + minor);
905 if (con->http.version == HTTP_1_1 && KeepAlive)
906 con->http.keep_alive = HTTP_KEEPALIVE_ON;
907 else
908 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
909 }
910 else
911 {
912 cupsdLogMessage(CUPSD_LOG_ERROR,
913 "Unsupported request line \"%s\" from %s!",
914 _httpEncodeURI(buf, line, sizeof(buf)),
915 con->http.hostname);
916 cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE);
917 cupsdCloseClient(con);
918 return;
919 }
920 break;
921 }
922
923 /*
924 * Handle full URLs in the request line...
925 */
926
927 if (strcmp(con->uri, "*"))
928 {
929 char scheme[HTTP_MAX_URI], /* Method/scheme */
930 userpass[HTTP_MAX_URI], /* Username:password */
931 hostname[HTTP_MAX_URI], /* Hostname */
932 resource[HTTP_MAX_URI]; /* Resource path */
933 int port; /* Port number */
934
935
936 /*
937 * Separate the URI into its components...
938 */
939
940 httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
941 scheme, sizeof(scheme),
942 userpass, sizeof(userpass),
943 hostname, sizeof(hostname), &port,
944 resource, sizeof(resource));
945
946 /*
947 * Only allow URIs with the servername, localhost, or an IP
948 * address...
949 */
950
951 if (strcmp(scheme, "file") &&
952 strcasecmp(hostname, ServerName) &&
953 strcasecmp(hostname, "localhost") &&
954 !isdigit(hostname[0]) && hostname[0] != '[')
955 {
956 /*
957 * Nope, we don't do proxies...
958 */
959
960 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!",
961 con->uri);
962 cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE);
963 cupsdCloseClient(con);
964 return;
965 }
966
967 /*
968 * Copy the resource portion back into the URI; both resource and
969 * con->uri are HTTP_MAX_URI bytes in size...
970 */
971
972 strcpy(con->uri, resource);
973 }
974
975 /*
976 * Process the request...
977 */
978
979 if (!strcmp(operation, "GET"))
980 con->http.state = HTTP_GET;
981 else if (!strcmp(operation, "PUT"))
982 con->http.state = HTTP_PUT;
983 else if (!strcmp(operation, "POST"))
984 con->http.state = HTTP_POST;
985 else if (!strcmp(operation, "DELETE"))
986 con->http.state = HTTP_DELETE;
987 else if (!strcmp(operation, "TRACE"))
988 con->http.state = HTTP_TRACE;
989 else if (!strcmp(operation, "OPTIONS"))
990 con->http.state = HTTP_OPTIONS;
991 else if (!strcmp(operation, "HEAD"))
992 con->http.state = HTTP_HEAD;
993 else
994 {
995 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation);
996 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
997 cupsdCloseClient(con);
998 return;
999 }
1000
1001 gettimeofday(&(con->start), NULL);
1002 con->operation = con->http.state;
1003
1004 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %s %s HTTP/%d.%d",
1005 con->http.fd, operation, con->uri,
1006 con->http.version / 100, con->http.version % 100);
1007
1008 con->http.status = HTTP_OK;
1009
1010 if (!cupsArrayFind(ActiveClients, con))
1011 {
1012 cupsArrayAdd(ActiveClients, con);
1013 cupsdSetBusyState();
1014 }
1015
1016 case HTTP_OPTIONS :
1017 case HTTP_DELETE :
1018 case HTTP_GET :
1019 case HTTP_HEAD :
1020 case HTTP_POST :
1021 case HTTP_PUT :
1022 case HTTP_TRACE :
1023 /*
1024 * Parse incoming parameters until the status changes...
1025 */
1026
1027 while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
1028 if (!data_ready(con))
1029 break;
1030
1031 if (status != HTTP_OK && status != HTTP_CONTINUE)
1032 {
1033 if (con->http.error && con->http.error != EPIPE)
1034 cupsdLogMessage(CUPSD_LOG_DEBUG,
1035 "cupsdReadClient: %d FIELDS Closing for error %d "
1036 "(%s)", con->http.fd, con->http.error,
1037 strerror(con->http.error));
1038 else
1039 cupsdLogMessage(CUPSD_LOG_DEBUG,
1040 "cupsdReadClient: %d FIELDS Closing on EOF",
1041 con->http.fd);
1042
1043 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
1044 cupsdCloseClient(con);
1045 return;
1046 }
1047 break;
1048
1049 default :
1050 if (!data_ready(con) && recv(con->http.fd, buf, 1, MSG_PEEK) < 1)
1051 {
1052 /*
1053 * Connection closed...
1054 */
1055
1056 cupsdLogMessage(CUPSD_LOG_DEBUG,
1057 "cupsdReadClient: %d Closing on EOF", con->http.fd);
1058 cupsdCloseClient(con);
1059 return;
1060 }
1061 break; /* Anti-compiler-warning-code */
1062 }
1063
1064 /*
1065 * Handle new transfers...
1066 */
1067
1068 if (status == HTTP_OK)
1069 {
1070 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
1071 {
1072 /*
1073 * Figure out the locale from the Accept-Language and Content-Type
1074 * fields...
1075 */
1076
1077 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1078 ',')) != NULL)
1079 *ptr = '\0';
1080
1081 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1082 ';')) != NULL)
1083 *ptr = '\0';
1084
1085 if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1086 "charset=")) != NULL)
1087 {
1088 /*
1089 * Combine language and charset, and trim any extra params in the
1090 * content-type.
1091 */
1092
1093 snprintf(locale, sizeof(locale), "%s.%s",
1094 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
1095
1096 if ((ptr = strchr(locale, ',')) != NULL)
1097 *ptr = '\0';
1098 }
1099 else
1100 snprintf(locale, sizeof(locale), "%s.UTF-8",
1101 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
1102
1103 con->language = cupsLangGet(locale);
1104 }
1105 else
1106 con->language = cupsLangGet(DefaultLocale);
1107
1108 cupsdAuthorize(con);
1109
1110 if (!strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive",
1111 10) && KeepAlive)
1112 con->http.keep_alive = HTTP_KEEPALIVE_ON;
1113 else if (!strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "close", 5))
1114 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1115
1116 if (!con->http.fields[HTTP_FIELD_HOST][0] &&
1117 con->http.version >= HTTP_1_1)
1118 {
1119 /*
1120 * HTTP/1.1 and higher require the "Host:" field...
1121 */
1122
1123 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1124 {
1125 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing Host: field in request!");
1126 cupsdCloseClient(con);
1127 return;
1128 }
1129 }
1130 else if (!valid_host(con))
1131 {
1132 /*
1133 * Access to localhost must use "localhost" or the corresponding IPv4
1134 * or IPv6 values in the Host: field.
1135 */
1136
1137 cupsdLogMessage(CUPSD_LOG_ERROR,
1138 "Request from \"%s\" using invalid Host: field \"%s\"",
1139 con->http.hostname, con->http.fields[HTTP_FIELD_HOST]);
1140
1141 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1142 {
1143 cupsdCloseClient(con);
1144 return;
1145 }
1146 }
1147 else if (con->operation == HTTP_OPTIONS)
1148 {
1149 /*
1150 * Do OPTIONS command...
1151 */
1152
1153 if (con->best && con->best->type != CUPSD_AUTH_NONE)
1154 {
1155 if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
1156 {
1157 cupsdCloseClient(con);
1158 return;
1159 }
1160 }
1161
1162 if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1163 con->http.tls == NULL)
1164 {
1165 #ifdef HAVE_SSL
1166 /*
1167 * Do encryption stuff...
1168 */
1169
1170 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
1171 {
1172 cupsdCloseClient(con);
1173 return;
1174 }
1175
1176 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1177 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1178 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1179 httpPrintf(HTTP(con), "\r\n");
1180
1181 if (cupsdFlushHeader(con) < 0)
1182 {
1183 cupsdCloseClient(con);
1184 return;
1185 }
1186
1187 if (!encrypt_client(con))
1188 {
1189 cupsdCloseClient(con);
1190 return;
1191 }
1192 #else
1193 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
1194 {
1195 cupsdCloseClient(con);
1196 return;
1197 }
1198 #endif /* HAVE_SSL */
1199 }
1200
1201 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
1202 {
1203 cupsdCloseClient(con);
1204 return;
1205 }
1206
1207 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1208 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1209 httpPrintf(HTTP(con), "\r\n");
1210
1211 if (cupsdFlushHeader(con) < 0)
1212 {
1213 cupsdCloseClient(con);
1214 return;
1215 }
1216 }
1217 else if (!is_path_absolute(con->uri))
1218 {
1219 /*
1220 * Protect against malicious users!
1221 */
1222
1223 cupsdLogMessage(CUPSD_LOG_ERROR,
1224 "Request for non-absolute resource \"%s\"!", con->uri);
1225
1226 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1227 {
1228 cupsdCloseClient(con);
1229 return;
1230 }
1231 }
1232 else
1233 {
1234 if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1235 con->http.tls == NULL)
1236 {
1237 #ifdef HAVE_SSL
1238 /*
1239 * Do encryption stuff...
1240 */
1241
1242 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
1243 {
1244 cupsdCloseClient(con);
1245 return;
1246 }
1247
1248 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1249 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1250 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1251 httpPrintf(HTTP(con), "\r\n");
1252
1253 if (cupsdFlushHeader(con) < 0)
1254 {
1255 cupsdCloseClient(con);
1256 return;
1257 }
1258
1259 if (!encrypt_client(con))
1260 {
1261 cupsdCloseClient(con);
1262 return;
1263 }
1264 #else
1265 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
1266 {
1267 cupsdCloseClient(con);
1268 return;
1269 }
1270 #endif /* HAVE_SSL */
1271 }
1272
1273 if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK)
1274 {
1275 cupsdSendError(con, status, CUPSD_AUTH_NONE);
1276 cupsdCloseClient(con);
1277 return;
1278 }
1279
1280 if (con->http.expect &&
1281 (con->operation == HTTP_POST || con->operation == HTTP_PUT))
1282 {
1283 if (con->http.expect == HTTP_CONTINUE)
1284 {
1285 /*
1286 * Send 100-continue header...
1287 */
1288
1289 if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE))
1290 {
1291 cupsdCloseClient(con);
1292 return;
1293 }
1294 }
1295 else
1296 {
1297 /*
1298 * Send 417-expectation-failed header...
1299 */
1300
1301 if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL,
1302 CUPSD_AUTH_NONE))
1303 {
1304 cupsdCloseClient(con);
1305 return;
1306 }
1307
1308 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1309 httpPrintf(HTTP(con), "\r\n");
1310
1311 if (cupsdFlushHeader(con) < 0)
1312 {
1313 cupsdCloseClient(con);
1314 return;
1315 }
1316 }
1317 }
1318
1319 switch (con->http.state)
1320 {
1321 case HTTP_GET_SEND :
1322 if (!strncmp(con->uri, "/printers/", 10) &&
1323 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1324 {
1325 /*
1326 * Send PPD file - get the real printer name since printer
1327 * names are not case sensitive but filenames can be...
1328 */
1329
1330 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1331
1332 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1333 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1334 else
1335 {
1336 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1337 {
1338 cupsdCloseClient(con);
1339 return;
1340 }
1341
1342 break;
1343 }
1344 }
1345 else if ((!strncmp(con->uri, "/printers/", 10) ||
1346 !strncmp(con->uri, "/classes/", 9)) &&
1347 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1348 {
1349 /*
1350 * Send icon file - get the real queue name since queue names are
1351 * not case sensitive but filenames can be...
1352 */
1353
1354 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".png" */
1355
1356 if (!strncmp(con->uri, "/printers/", 10))
1357 p = cupsdFindPrinter(con->uri + 10);
1358 else
1359 p = cupsdFindClass(con->uri + 9);
1360
1361 if (p)
1362 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1363 else
1364 {
1365 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1366 {
1367 cupsdCloseClient(con);
1368 return;
1369 }
1370
1371 break;
1372 }
1373 }
1374 else if (!WebInterface)
1375 {
1376 /*
1377 * Web interface is disabled. Show an appropriate message...
1378 */
1379
1380 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1381 {
1382 cupsdCloseClient(con);
1383 return;
1384 }
1385
1386 break;
1387 }
1388
1389 if ((!strncmp(con->uri, "/admin", 6) &&
1390 strncmp(con->uri, "/admin/conf/", 12) &&
1391 strncmp(con->uri, "/admin/log/", 11)) ||
1392 !strncmp(con->uri, "/printers", 9) ||
1393 !strncmp(con->uri, "/classes", 8) ||
1394 !strncmp(con->uri, "/help", 5) ||
1395 !strncmp(con->uri, "/jobs", 5))
1396 {
1397 /*
1398 * Send CGI output...
1399 */
1400
1401 if (!strncmp(con->uri, "/admin", 6))
1402 {
1403 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1404 ServerBin);
1405
1406 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
1407 }
1408 else if (!strncmp(con->uri, "/printers", 9))
1409 {
1410 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1411 ServerBin);
1412
1413 if (con->uri[9] && con->uri[10])
1414 cupsdSetString(&con->options, con->uri + 9);
1415 else
1416 cupsdSetString(&con->options, NULL);
1417 }
1418 else if (!strncmp(con->uri, "/classes", 8))
1419 {
1420 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1421 ServerBin);
1422
1423 if (con->uri[8] && con->uri[9])
1424 cupsdSetString(&con->options, con->uri + 8);
1425 else
1426 cupsdSetString(&con->options, NULL);
1427 }
1428 else if (!strncmp(con->uri, "/jobs", 5))
1429 {
1430 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1431 ServerBin);
1432
1433 if (con->uri[5] && con->uri[6])
1434 cupsdSetString(&con->options, con->uri + 5);
1435 else
1436 cupsdSetString(&con->options, NULL);
1437 }
1438 else
1439 {
1440 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1441 ServerBin);
1442
1443 if (con->uri[5] && con->uri[6])
1444 cupsdSetString(&con->options, con->uri + 5);
1445 else
1446 cupsdSetString(&con->options, NULL);
1447 }
1448
1449 if (!cupsdSendCommand(con, con->command, con->options, 0))
1450 {
1451 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1452 {
1453 cupsdCloseClient(con);
1454 return;
1455 }
1456 }
1457 else
1458 cupsdLogRequest(con, HTTP_OK);
1459
1460 if (con->http.version <= HTTP_1_0)
1461 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1462 }
1463 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1464 (strchr(con->uri + 12, '/') ||
1465 strlen(con->uri) == 12)) ||
1466 (!strncmp(con->uri, "/admin/log/", 11) &&
1467 (strchr(con->uri + 11, '/') ||
1468 strlen(con->uri) == 11)))
1469 {
1470 /*
1471 * GET can only be done to configuration files directly under
1472 * /admin/conf...
1473 */
1474
1475 cupsdLogMessage(CUPSD_LOG_ERROR,
1476 "Request for subdirectory \"%s\"!", con->uri);
1477
1478 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1479 {
1480 cupsdCloseClient(con);
1481 return;
1482 }
1483
1484 break;
1485 }
1486 else
1487 {
1488 /*
1489 * Serve a file...
1490 */
1491
1492 if ((filename = get_file(con, &filestats, buf,
1493 sizeof(buf))) == NULL)
1494 {
1495 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1496 {
1497 cupsdCloseClient(con);
1498 return;
1499 }
1500
1501 break;
1502 }
1503
1504 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1505
1506 if (is_cgi(con, filename, &filestats, type))
1507 {
1508 /*
1509 * Note: con->command and con->options were set by
1510 * is_cgi()...
1511 */
1512
1513 if (!cupsdSendCommand(con, con->command, con->options, 0))
1514 {
1515 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1516 {
1517 cupsdCloseClient(con);
1518 return;
1519 }
1520 }
1521 else
1522 cupsdLogRequest(con, HTTP_OK);
1523
1524 if (con->http.version <= HTTP_1_0)
1525 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1526 break;
1527 }
1528
1529 if (!check_if_modified(con, &filestats))
1530 {
1531 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
1532 {
1533 cupsdCloseClient(con);
1534 return;
1535 }
1536 }
1537 else
1538 {
1539 if (type == NULL)
1540 strcpy(line, "text/plain");
1541 else
1542 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1543
1544 if (!write_file(con, HTTP_OK, filename, line, &filestats))
1545 {
1546 cupsdCloseClient(con);
1547 return;
1548 }
1549 }
1550 }
1551 break;
1552
1553 case HTTP_POST_RECV :
1554 /*
1555 * See if the POST request includes a Content-Length field, and if
1556 * so check the length against any limits that are set...
1557 */
1558
1559 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1560 MaxRequestSize > 0 &&
1561 con->http.data_remaining > MaxRequestSize)
1562 {
1563 /*
1564 * Request too large...
1565 */
1566
1567 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
1568 {
1569 cupsdCloseClient(con);
1570 return;
1571 }
1572
1573 break;
1574 }
1575 else if (con->http.data_remaining < 0 ||
1576 (!con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1577 con->http.data_encoding == HTTP_ENCODE_LENGTH))
1578 {
1579 /*
1580 * Negative content lengths are invalid!
1581 */
1582
1583 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1584 {
1585 cupsdCloseClient(con);
1586 return;
1587 }
1588
1589 break;
1590 }
1591
1592 /*
1593 * See what kind of POST request this is; for IPP requests the
1594 * content-type field will be "application/ipp"...
1595 */
1596
1597 if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1598 "application/ipp"))
1599 con->request = ippNew();
1600 else if (!WebInterface)
1601 {
1602 /*
1603 * Web interface is disabled. Show an appropriate message...
1604 */
1605
1606 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1607 {
1608 cupsdCloseClient(con);
1609 return;
1610 }
1611
1612 break;
1613 }
1614 else if ((!strncmp(con->uri, "/admin", 6) &&
1615 strncmp(con->uri, "/admin/conf/", 12) &&
1616 strncmp(con->uri, "/admin/log/", 11)) ||
1617 !strncmp(con->uri, "/printers", 9) ||
1618 !strncmp(con->uri, "/classes", 8) ||
1619 !strncmp(con->uri, "/help", 5) ||
1620 !strncmp(con->uri, "/jobs", 5))
1621 {
1622 /*
1623 * CGI request...
1624 */
1625
1626 if (!strncmp(con->uri, "/admin", 6))
1627 {
1628 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1629 ServerBin);
1630
1631 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
1632 }
1633 else if (!strncmp(con->uri, "/printers", 9))
1634 {
1635 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1636 ServerBin);
1637
1638 if (con->uri[9] && con->uri[10])
1639 cupsdSetString(&con->options, con->uri + 9);
1640 else
1641 cupsdSetString(&con->options, NULL);
1642 }
1643 else if (!strncmp(con->uri, "/classes", 8))
1644 {
1645 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1646 ServerBin);
1647
1648 if (con->uri[8] && con->uri[9])
1649 cupsdSetString(&con->options, con->uri + 8);
1650 else
1651 cupsdSetString(&con->options, NULL);
1652 }
1653 else if (!strncmp(con->uri, "/jobs", 5))
1654 {
1655 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1656 ServerBin);
1657
1658 if (con->uri[5] && con->uri[6])
1659 cupsdSetString(&con->options, con->uri + 5);
1660 else
1661 cupsdSetString(&con->options, NULL);
1662 }
1663 else
1664 {
1665 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1666 ServerBin);
1667
1668 if (con->uri[5] && con->uri[6])
1669 cupsdSetString(&con->options, con->uri + 5);
1670 else
1671 cupsdSetString(&con->options, NULL);
1672 }
1673
1674 if (con->http.version <= HTTP_1_0)
1675 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1676 }
1677 else
1678 {
1679 /*
1680 * POST to a file...
1681 */
1682
1683 if ((filename = get_file(con, &filestats, buf,
1684 sizeof(buf))) == NULL)
1685 {
1686 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1687 {
1688 cupsdCloseClient(con);
1689 return;
1690 }
1691
1692 break;
1693 }
1694
1695 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1696
1697 if (!is_cgi(con, filename, &filestats, type))
1698 {
1699 /*
1700 * Only POST to CGI's...
1701 */
1702
1703 if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE))
1704 {
1705 cupsdCloseClient(con);
1706 return;
1707 }
1708 }
1709 }
1710 break;
1711
1712 case HTTP_PUT_RECV :
1713 /*
1714 * Validate the resource name...
1715 */
1716
1717 if (strncmp(con->uri, "/admin/conf/", 12) ||
1718 strchr(con->uri + 12, '/') ||
1719 strlen(con->uri) == 12)
1720 {
1721 /*
1722 * PUT can only be done to configuration files under
1723 * /admin/conf...
1724 */
1725
1726 cupsdLogMessage(CUPSD_LOG_ERROR,
1727 "Request for subdirectory \"%s\"!", con->uri);
1728
1729 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1730 {
1731 cupsdCloseClient(con);
1732 return;
1733 }
1734
1735 break;
1736 }
1737
1738 /*
1739 * See if the PUT request includes a Content-Length field, and if
1740 * so check the length against any limits that are set...
1741 */
1742
1743 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1744 MaxRequestSize > 0 &&
1745 con->http.data_remaining > MaxRequestSize)
1746 {
1747 /*
1748 * Request too large...
1749 */
1750
1751 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
1752 {
1753 cupsdCloseClient(con);
1754 return;
1755 }
1756
1757 break;
1758 }
1759 else if (con->http.data_remaining < 0)
1760 {
1761 /*
1762 * Negative content lengths are invalid!
1763 */
1764
1765 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1766 {
1767 cupsdCloseClient(con);
1768 return;
1769 }
1770
1771 break;
1772 }
1773
1774 /*
1775 * Open a temporary file to hold the request...
1776 */
1777
1778 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
1779 request_id ++);
1780 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1781
1782 if (con->file < 0)
1783 {
1784 cupsdLogMessage(CUPSD_LOG_ERROR,
1785 "Unable to create request file %s: %s",
1786 con->filename, strerror(errno));
1787
1788 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
1789 {
1790 cupsdCloseClient(con);
1791 return;
1792 }
1793 }
1794
1795 fchmod(con->file, 0640);
1796 fchown(con->file, RunUser, Group);
1797 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1798 break;
1799
1800 case HTTP_DELETE :
1801 case HTTP_TRACE :
1802 cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE);
1803 cupsdCloseClient(con);
1804 return;
1805
1806 case HTTP_HEAD :
1807 if (!strncmp(con->uri, "/printers/", 10) &&
1808 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1809 {
1810 /*
1811 * Send PPD file - get the real printer name since printer
1812 * names are not case sensitive but filenames can be...
1813 */
1814
1815 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1816
1817 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1818 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1819 else
1820 {
1821 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1822 {
1823 cupsdCloseClient(con);
1824 return;
1825 }
1826
1827 break;
1828 }
1829 }
1830 else if (!strncmp(con->uri, "/printers/", 10) &&
1831 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1832 {
1833 /*
1834 * Send PNG file - get the real printer name since printer
1835 * names are not case sensitive but filenames can be...
1836 */
1837
1838 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1839
1840 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1841 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1842 else
1843 {
1844 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1845 {
1846 cupsdCloseClient(con);
1847 return;
1848 }
1849
1850 break;
1851 }
1852 }
1853 else if (!WebInterface)
1854 {
1855 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
1856 {
1857 cupsdCloseClient(con);
1858 return;
1859 }
1860
1861 if (httpPrintf(HTTP(con), "\r\n") < 0)
1862 {
1863 cupsdCloseClient(con);
1864 return;
1865 }
1866
1867 if (cupsdFlushHeader(con) < 0)
1868 {
1869 cupsdCloseClient(con);
1870 return;
1871 }
1872
1873 con->http.state = HTTP_WAITING;
1874 break;
1875 }
1876
1877 if ((!strncmp(con->uri, "/admin", 6) &&
1878 strncmp(con->uri, "/admin/conf/", 12) &&
1879 strncmp(con->uri, "/admin/log/", 11)) ||
1880 !strncmp(con->uri, "/printers", 9) ||
1881 !strncmp(con->uri, "/classes", 8) ||
1882 !strncmp(con->uri, "/help", 5) ||
1883 !strncmp(con->uri, "/jobs", 5))
1884 {
1885 /*
1886 * CGI output...
1887 */
1888
1889 if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE))
1890 {
1891 cupsdCloseClient(con);
1892 return;
1893 }
1894
1895 if (httpPrintf(HTTP(con), "\r\n") < 0)
1896 {
1897 cupsdCloseClient(con);
1898 return;
1899 }
1900
1901 if (cupsdFlushHeader(con) < 0)
1902 {
1903 cupsdCloseClient(con);
1904 return;
1905 }
1906
1907 cupsdLogRequest(con, HTTP_OK);
1908 }
1909 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1910 (strchr(con->uri + 12, '/') ||
1911 strlen(con->uri) == 12)) ||
1912 (!strncmp(con->uri, "/admin/log/", 11) &&
1913 (strchr(con->uri + 11, '/') ||
1914 strlen(con->uri) == 11)))
1915 {
1916 /*
1917 * HEAD can only be done to configuration files under
1918 * /admin/conf...
1919 */
1920
1921 cupsdLogMessage(CUPSD_LOG_ERROR,
1922 "Request for subdirectory \"%s\"!", con->uri);
1923
1924 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1925 {
1926 cupsdCloseClient(con);
1927 return;
1928 }
1929
1930 break;
1931 }
1932 else if ((filename = get_file(con, &filestats, buf,
1933 sizeof(buf))) == NULL)
1934 {
1935 if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html",
1936 CUPSD_AUTH_NONE))
1937 {
1938 cupsdCloseClient(con);
1939 return;
1940 }
1941
1942 cupsdLogRequest(con, HTTP_NOT_FOUND);
1943 }
1944 else if (!check_if_modified(con, &filestats))
1945 {
1946 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
1947 {
1948 cupsdCloseClient(con);
1949 return;
1950 }
1951
1952 cupsdLogRequest(con, HTTP_NOT_MODIFIED);
1953 }
1954 else
1955 {
1956 /*
1957 * Serve a file...
1958 */
1959
1960 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1961 if (type == NULL)
1962 strcpy(line, "text/plain");
1963 else
1964 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1965
1966 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
1967 {
1968 cupsdCloseClient(con);
1969 return;
1970 }
1971
1972 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1973 httpGetDateString(filestats.st_mtime)) < 0)
1974 {
1975 cupsdCloseClient(con);
1976 return;
1977 }
1978
1979 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1980 (unsigned long)filestats.st_size) < 0)
1981 {
1982 cupsdCloseClient(con);
1983 return;
1984 }
1985
1986 cupsdLogRequest(con, HTTP_OK);
1987 }
1988
1989 if (httpPrintf(HTTP(con), "\r\n") < 0)
1990 {
1991 cupsdCloseClient(con);
1992 return;
1993 }
1994
1995 if (cupsdFlushHeader(con) < 0)
1996 {
1997 cupsdCloseClient(con);
1998 return;
1999 }
2000
2001 con->http.state = HTTP_WAITING;
2002 break;
2003
2004 default :
2005 break; /* Anti-compiler-warning-code */
2006 }
2007 }
2008 }
2009
2010 /*
2011 * Handle any incoming data...
2012 */
2013
2014 switch (con->http.state)
2015 {
2016 case HTTP_PUT_RECV :
2017 do
2018 {
2019 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
2020 {
2021 if (con->http.error && con->http.error != EPIPE)
2022 cupsdLogMessage(CUPSD_LOG_DEBUG,
2023 "cupsdReadClient: %d PUT_RECV Closing for error "
2024 "%d (%s)", con->http.fd, con->http.error,
2025 strerror(con->http.error));
2026 else
2027 cupsdLogMessage(CUPSD_LOG_DEBUG,
2028 "cupsdReadClient: %d PUT_RECV Closing on EOF",
2029 con->http.fd);
2030
2031 cupsdCloseClient(con);
2032 return;
2033 }
2034 else if (bytes > 0)
2035 {
2036 con->bytes += bytes;
2037
2038 if (write(con->file, line, bytes) < bytes)
2039 {
2040 cupsdLogMessage(CUPSD_LOG_ERROR,
2041 "cupsdReadClient: Unable to write %d bytes to %s: %s",
2042 bytes, con->filename, strerror(errno));
2043
2044 close(con->file);
2045 con->file = -1;
2046 unlink(con->filename);
2047 cupsdClearString(&con->filename);
2048
2049 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2050 {
2051 cupsdCloseClient(con);
2052 return;
2053 }
2054 }
2055 }
2056 }
2057 while (con->http.state == HTTP_PUT_RECV && data_ready(con));
2058
2059 if (con->http.state == HTTP_WAITING)
2060 {
2061 /*
2062 * End of file, see how big it is...
2063 */
2064
2065 fstat(con->file, &filestats);
2066
2067 close(con->file);
2068 con->file = -1;
2069
2070 if (filestats.st_size > MaxRequestSize &&
2071 MaxRequestSize > 0)
2072 {
2073 /*
2074 * Request is too big; remove it and send an error...
2075 */
2076
2077 unlink(con->filename);
2078 cupsdClearString(&con->filename);
2079
2080 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2081 {
2082 cupsdCloseClient(con);
2083 return;
2084 }
2085 }
2086
2087 /*
2088 * Install the configuration file...
2089 */
2090
2091 status = install_conf_file(con);
2092
2093 /*
2094 * Return the status to the client...
2095 */
2096
2097 if (!cupsdSendError(con, status, CUPSD_AUTH_NONE))
2098 {
2099 cupsdCloseClient(con);
2100 return;
2101 }
2102 }
2103 break;
2104
2105 case HTTP_POST_RECV :
2106 do
2107 {
2108 if (con->request && con->file < 0)
2109 {
2110 /*
2111 * Grab any request data from the connection...
2112 */
2113
2114 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
2115 {
2116 cupsdLogMessage(CUPSD_LOG_ERROR,
2117 "cupsdReadClient: %d IPP Read Error!",
2118 con->http.fd);
2119
2120 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
2121 cupsdCloseClient(con);
2122 return;
2123 }
2124 else if (ipp_state != IPP_DATA)
2125 {
2126 if (con->http.state == HTTP_POST_SEND)
2127 {
2128 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
2129 cupsdCloseClient(con);
2130 return;
2131 }
2132
2133 break;
2134 }
2135 else
2136 {
2137 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %d.%d %s %d",
2138 con->http.fd, con->request->request.op.version[0],
2139 con->request->request.op.version[1],
2140 ippOpString(con->request->request.op.operation_id),
2141 con->request->request.op.request_id);
2142 con->bytes += ippLength(con->request);
2143 }
2144 }
2145
2146 if (con->file < 0 && con->http.state != HTTP_POST_SEND)
2147 {
2148 /*
2149 * Create a file as needed for the request data...
2150 */
2151
2152 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
2153 request_id ++);
2154 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
2155
2156 if (con->file < 0)
2157 {
2158 cupsdLogMessage(CUPSD_LOG_ERROR,
2159 "Unable to create request file %s: %s",
2160 con->filename, strerror(errno));
2161
2162 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2163 {
2164 cupsdCloseClient(con);
2165 return;
2166 }
2167 }
2168
2169 fchmod(con->file, 0640);
2170 fchown(con->file, RunUser, Group);
2171 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2172 }
2173
2174 if (con->http.state != HTTP_POST_SEND)
2175 {
2176 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
2177 {
2178 if (con->http.error && con->http.error != EPIPE)
2179 cupsdLogMessage(CUPSD_LOG_DEBUG,
2180 "cupsdReadClient: %d POST_SEND Closing for "
2181 "error %d (%s)", con->http.fd, con->http.error,
2182 strerror(con->http.error));
2183 else
2184 cupsdLogMessage(CUPSD_LOG_DEBUG,
2185 "cupsdReadClient: %d POST_SEND Closing on EOF",
2186 con->http.fd);
2187
2188 cupsdCloseClient(con);
2189 return;
2190 }
2191 else if (bytes > 0)
2192 {
2193 con->bytes += bytes;
2194
2195 if (write(con->file, line, bytes) < bytes)
2196 {
2197 cupsdLogMessage(CUPSD_LOG_ERROR,
2198 "cupsdReadClient: Unable to write %d bytes to "
2199 "%s: %s", bytes, con->filename,
2200 strerror(errno));
2201
2202 close(con->file);
2203 con->file = -1;
2204 unlink(con->filename);
2205 cupsdClearString(&con->filename);
2206
2207 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE,
2208 CUPSD_AUTH_NONE))
2209 {
2210 cupsdCloseClient(con);
2211 return;
2212 }
2213 }
2214 }
2215 else if (con->http.state == HTTP_POST_RECV)
2216 return;
2217 else if (con->http.state != HTTP_POST_SEND)
2218 {
2219 cupsdLogMessage(CUPSD_LOG_DEBUG,
2220 "cupsdReadClient: %d Closing on unknown HTTP "
2221 "state %d", con->http.fd, con->http.state);
2222 cupsdCloseClient(con);
2223 return;
2224 }
2225 }
2226 }
2227 while (con->http.state == HTTP_POST_RECV && data_ready(con));
2228
2229 if (con->http.state == HTTP_POST_SEND)
2230 {
2231 if (con->file >= 0)
2232 {
2233 fstat(con->file, &filestats);
2234
2235 close(con->file);
2236 con->file = -1;
2237
2238 if (filestats.st_size > MaxRequestSize &&
2239 MaxRequestSize > 0)
2240 {
2241 /*
2242 * Request is too big; remove it and send an error...
2243 */
2244
2245 unlink(con->filename);
2246 cupsdClearString(&con->filename);
2247
2248 if (con->request)
2249 {
2250 /*
2251 * Delete any IPP request data...
2252 */
2253
2254 ippDelete(con->request);
2255 con->request = NULL;
2256 }
2257
2258 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2259 {
2260 cupsdCloseClient(con);
2261 return;
2262 }
2263 }
2264 else if (filestats.st_size == 0)
2265 {
2266 /*
2267 * Don't allow empty file...
2268 */
2269
2270 unlink(con->filename);
2271 cupsdClearString(&con->filename);
2272 }
2273
2274 if (con->command)
2275 {
2276 if (!cupsdSendCommand(con, con->command, con->options, 0))
2277 {
2278 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
2279 {
2280 cupsdCloseClient(con);
2281 return;
2282 }
2283 }
2284 else
2285 cupsdLogRequest(con, HTTP_OK);
2286 }
2287 }
2288
2289 if (con->request)
2290 {
2291 cupsdProcessIPPRequest(con);
2292
2293 if (con->filename)
2294 {
2295 unlink(con->filename);
2296 cupsdClearString(&con->filename);
2297 }
2298
2299 return;
2300 }
2301 }
2302 break;
2303
2304 default :
2305 break; /* Anti-compiler-warning-code */
2306 }
2307
2308 if (con->http.state == HTTP_WAITING)
2309 {
2310 if (!con->http.keep_alive)
2311 {
2312 cupsdLogMessage(CUPSD_LOG_DEBUG,
2313 "cupsdReadClient: %d Closing because Keep-Alive disabled",
2314 con->http.fd);
2315 cupsdCloseClient(con);
2316 }
2317 else
2318 {
2319 cupsArrayRemove(ActiveClients, con);
2320 cupsdSetBusyState();
2321 }
2322 }
2323 }
2324
2325
2326 /*
2327 * 'cupsdSendCommand()' - Send output from a command via HTTP.
2328 */
2329
2330 int /* O - 1 on success, 0 on failure */
2331 cupsdSendCommand(
2332 cupsd_client_t *con, /* I - Client connection */
2333 char *command, /* I - Command to run */
2334 char *options, /* I - Command-line options */
2335 int root) /* I - Run as root? */
2336 {
2337 int fd; /* Standard input file descriptor */
2338
2339
2340 if (con->filename)
2341 {
2342 fd = open(con->filename, O_RDONLY);
2343
2344 if (fd < 0)
2345 {
2346 cupsdLogMessage(CUPSD_LOG_ERROR,
2347 "cupsdSendCommand: %d Unable to open \"%s\" for reading: %s",
2348 con->http.fd, con->filename ? con->filename : "/dev/null",
2349 strerror(errno));
2350 return (0);
2351 }
2352
2353 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
2354 }
2355 else
2356 fd = -1;
2357
2358 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
2359
2360 if (fd >= 0)
2361 close(fd);
2362
2363 cupsdLogMessage(CUPSD_LOG_INFO, "Started \"%s\" (pid=%d)", command,
2364 con->pipe_pid);
2365
2366 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendCommand: %d file=%d",
2367 con->http.fd, con->file);
2368
2369 if (con->pipe_pid == 0)
2370 return (0);
2371
2372 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2373
2374 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
2375
2376 con->sent_header = 0;
2377 con->file_ready = 0;
2378 con->got_fields = 0;
2379 con->header_used = 0;
2380
2381 return (1);
2382 }
2383
2384
2385 /*
2386 * 'cupsdSendError()' - Send an error message via HTTP.
2387 */
2388
2389 int /* O - 1 if successful, 0 otherwise */
2390 cupsdSendError(cupsd_client_t *con, /* I - Connection */
2391 http_status_t code, /* I - Error code */
2392 int auth_type)/* I - Authentication type */
2393 {
2394 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2395 "cupsdSendError(con=%p(%d), code=%d, auth_type=%d", con,
2396 con->http.fd, code, auth_type);
2397
2398 #ifdef HAVE_SSL
2399 /*
2400 * Force client to upgrade for authentication if that is how the
2401 * server is configured...
2402 */
2403
2404 if (code == HTTP_UNAUTHORIZED &&
2405 DefaultEncryption == HTTP_ENCRYPT_REQUIRED &&
2406 strcasecmp(con->http.hostname, "localhost") &&
2407 !con->http.tls)
2408 {
2409 code = HTTP_UPGRADE_REQUIRED;
2410 }
2411 #endif /* HAVE_SSL */
2412
2413 /*
2414 * Put the request in the access_log file...
2415 */
2416
2417 cupsdLogRequest(con, code);
2418
2419 /*
2420 * To work around bugs in some proxies, don't use Keep-Alive for some
2421 * error messages...
2422 *
2423 * Kerberos authentication doesn't work without Keep-Alive, so
2424 * never disable it in that case.
2425 */
2426
2427 if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE)
2428 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2429
2430 /*
2431 * Send an error message back to the client. If the error code is a
2432 * 400 or 500 series, make sure the message contains some text, too!
2433 */
2434
2435 if (!cupsdSendHeader(con, code, NULL, auth_type))
2436 return (0);
2437
2438 #ifdef HAVE_SSL
2439 if (code == HTTP_UPGRADE_REQUIRED)
2440 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2441 return (0);
2442
2443 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2444 return (0);
2445 #endif /* HAVE_SSL */
2446
2447 if (con->http.version >= HTTP_1_1 &&
2448 con->http.keep_alive == HTTP_KEEPALIVE_OFF)
2449 {
2450 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2451 return (0);
2452 }
2453
2454 if (code >= HTTP_BAD_REQUEST)
2455 {
2456 /*
2457 * Send a human-readable error message.
2458 */
2459
2460 char message[4096], /* Message for user */
2461 urltext[1024], /* URL redirection text */
2462 redirect[1024]; /* Redirection link */
2463 const char *text; /* Status-specific text */
2464
2465
2466 redirect[0] = '\0';
2467
2468 if (code == HTTP_UNAUTHORIZED)
2469 text = _cupsLangString(con->language,
2470 _("Enter your username and password or the "
2471 "root username and password to access this "
2472 "page. If you are using Kerberos authentication, "
2473 "make sure you have a valid Kerberos ticket."));
2474 else if (code == HTTP_UPGRADE_REQUIRED)
2475 {
2476 text = urltext;
2477
2478 snprintf(urltext, sizeof(urltext),
2479 _cupsLangString(con->language,
2480 _("You must access this page using the URL "
2481 "<A HREF=\"https://%s:%d%s\">"
2482 "https://%s:%d%s</A>.")),
2483 con->servername, con->serverport, con->uri,
2484 con->servername, con->serverport, con->uri);
2485
2486 snprintf(redirect, sizeof(redirect),
2487 "<META HTTP-EQUIV=\"Refresh\" "
2488 "CONTENT=\"3;URL=https://%s:%d%s\">\n",
2489 con->servername, con->serverport, con->uri);
2490 }
2491 else if (code == HTTP_WEBIF_DISABLED)
2492 text = _cupsLangString(con->language,
2493 _("The web interface is currently disabled. Run "
2494 "\"cupsctl WebInterface=yes\" to enable it."));
2495 else
2496 text = "";
2497
2498 snprintf(message, sizeof(message),
2499 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2500 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2501 "<HTML>\n"
2502 "<HEAD>\n"
2503 "\t<META HTTP-EQUIV=\"Content-Type\" "
2504 "CONTENT=\"text/html; charset=utf-8\">\n"
2505 "\t<TITLE>%s - " CUPS_SVERSION "</TITLE>\n"
2506 "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" "
2507 "HREF=\"/cups.css\">\n"
2508 "%s"
2509 "</HEAD>\n"
2510 "<BODY>\n"
2511 "<H1>%s</H1>\n"
2512 "<P>%s</P>\n"
2513 "</BODY>\n"
2514 "</HTML>\n",
2515 httpStatus(code), redirect, httpStatus(code), text);
2516
2517 if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0)
2518 return (0);
2519 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2520 (int)strlen(message)) < 0)
2521 return (0);
2522 if (httpPrintf(HTTP(con), "\r\n") < 0)
2523 return (0);
2524 if (httpPrintf(HTTP(con), "%s", message) < 0)
2525 return (0);
2526 }
2527 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2528 return (0);
2529
2530 if (cupsdFlushHeader(con) < 0)
2531 return (0);
2532
2533 con->http.state = HTTP_WAITING;
2534
2535 return (1);
2536 }
2537
2538
2539 /*
2540 * 'cupsdSendHeader()' - Send an HTTP request.
2541 */
2542
2543 int /* O - 1 on success, 0 on failure */
2544 cupsdSendHeader(
2545 cupsd_client_t *con, /* I - Client to send to */
2546 http_status_t code, /* I - HTTP status code */
2547 char *type, /* I - MIME type of document */
2548 int auth_type) /* I - Type of authentication */
2549 {
2550 char auth_str[1024]; /* Authorization string */
2551 #ifdef HAVE_GSSAPI
2552 static char *gss_buf = NULL; /* Kerberos auth data buffer */
2553 static int gss_bufsize = 0; /* Size of Kerberos auth data buffer */
2554 #endif /* HAVE_GSSAPI */
2555
2556
2557 /*
2558 * Send the HTTP status header...
2559 */
2560
2561 if (code == HTTP_CONTINUE)
2562 {
2563 /*
2564 * 100-continue doesn't send any headers...
2565 */
2566
2567 return (httpPrintf(HTTP(con), "HTTP/%d.%d 100 Continue\r\n\r\n",
2568 con->http.version / 100, con->http.version % 100) > 0);
2569 }
2570 else if (code == HTTP_WEBIF_DISABLED)
2571 {
2572 /*
2573 * Treat our special "web interface is disabled" status as "200 OK" for web
2574 * browsers.
2575 */
2576
2577 code = HTTP_OK;
2578 }
2579
2580 httpFlushWrite(HTTP(con));
2581
2582 con->http.data_encoding = HTTP_ENCODE_FIELDS;
2583
2584 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
2585 con->http.version % 100, code, httpStatus(code)) < 0)
2586 return (0);
2587 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2588 return (0);
2589 if (ServerHeader)
2590 if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0)
2591 return (0);
2592 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2593 {
2594 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2595 return (0);
2596 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n",
2597 KeepAliveTimeout) < 0)
2598 return (0);
2599 }
2600 if (code == HTTP_METHOD_NOT_ALLOWED)
2601 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0)
2602 return (0);
2603
2604 if (code == HTTP_UNAUTHORIZED)
2605 {
2606 if (auth_type == CUPSD_AUTH_NONE)
2607 {
2608 if (!con->best || con->best->type <= CUPSD_AUTH_NONE)
2609 auth_type = DefaultAuthType;
2610 else
2611 auth_type = con->best->type;
2612 }
2613
2614 auth_str[0] = '\0';
2615
2616 if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
2617 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
2618 else if (auth_type == CUPSD_AUTH_DIGEST)
2619 snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
2620 con->http.hostname);
2621 #ifdef HAVE_GSSAPI
2622 else if (auth_type == CUPSD_AUTH_NEGOTIATE && con->gss_output_token.length == 0)
2623 strlcpy(auth_str, "Negotiate", sizeof(auth_str));
2624 #endif /* HAVE_GSSAPI */
2625
2626 if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE &&
2627 !strcasecmp(con->http.hostname, "localhost"))
2628 {
2629 /*
2630 * Add a "trc" (try root certification) parameter for local non-Kerberos
2631 * requests when the request requires system group membership - then the
2632 * client knows the root certificate can/should be used.
2633 *
2634 * Also, for Mac OS X we also look for @AUTHKEY and add an "authkey"
2635 * parameter as needed...
2636 */
2637
2638 char *name, /* Current user name */
2639 *auth_key; /* Auth key buffer */
2640 size_t auth_size; /* Size of remaining buffer */
2641
2642 auth_key = auth_str + strlen(auth_str);
2643 auth_size = sizeof(auth_str) - (auth_key - auth_str);
2644
2645 for (name = (char *)cupsArrayFirst(con->best->names);
2646 name;
2647 name = (char *)cupsArrayNext(con->best->names))
2648 {
2649 #ifdef HAVE_AUTHORIZATION_H
2650 if (!strncasecmp(name, "@AUTHKEY(", 9))
2651 {
2652 snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9);
2653 /* end parenthesis is stripped in conf.c */
2654 break;
2655 }
2656 else
2657 #endif /* HAVE_AUTHORIZATION_H */
2658 if (!strcasecmp(name, "@SYSTEM"))
2659 {
2660 #ifdef HAVE_AUTHORIZATION_H
2661 if (SystemGroupAuthKey)
2662 snprintf(auth_key, auth_size,
2663 ", authkey=\"%s\", trc=\"y\"",
2664 SystemGroupAuthKey);
2665 else
2666 #else
2667 strlcpy(auth_key, ", trc=\"y\"", auth_size);
2668 #endif /* HAVE_AUTHORIZATION_H */
2669 break;
2670 }
2671 }
2672 }
2673
2674 if (auth_str[0])
2675 {
2676 cupsdLogMessage(CUPSD_LOG_DEBUG,
2677 "cupsdSendHeader: %d WWW-Authenticate: %s", con->http.fd,
2678 auth_str);
2679
2680 if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
2681 return (0);
2682 }
2683 }
2684
2685 #ifdef HAVE_GSSAPI
2686 /*
2687 * WWW-Authenticate: Negotiate can be included even for
2688 * non-401 replies...
2689 */
2690
2691 if (con->gss_output_token.length > 0 && con->gss_output_token.length <= 65536)
2692 {
2693 OM_uint32 minor_status; /* Minor status code */
2694 int bufsize; /* Size of output token buffer */
2695
2696
2697 bufsize = con->gss_output_token.length * 4 / 3 + 2;
2698
2699 if (bufsize > gss_bufsize)
2700 {
2701 char *buf; /* New buffer */
2702
2703
2704 bufsize = (bufsize + 1023) & 1023;/* Round up */
2705
2706 if (gss_buf)
2707 buf = realloc(gss_buf, bufsize);
2708 else
2709 buf = malloc(bufsize);
2710
2711 if (!buf)
2712 {
2713 cupsdLogMessage(CUPSD_LOG_ERROR,
2714 "Unable to allocate %d bytes for Kerberos credentials!",
2715 bufsize);
2716 return (0);
2717 }
2718
2719 gss_buf = buf;
2720 gss_bufsize = bufsize;
2721 }
2722
2723 httpEncode64_2(gss_buf, gss_bufsize,
2724 con->gss_output_token.value,
2725 con->gss_output_token.length);
2726 gss_release_buffer(&minor_status, &con->gss_output_token);
2727
2728 cupsdLogMessage(CUPSD_LOG_DEBUG,
2729 "cupsdSendHeader: WWW-Authenticate: Negotiate %s", gss_buf);
2730
2731 if (httpPrintf(HTTP(con), "WWW-Authenticate: Negotiate %s\r\n",
2732 gss_buf) < 0)
2733 return (0);
2734 }
2735 else if (con->gss_output_token.length > 65536)
2736 {
2737 cupsdLogMessage(CUPSD_LOG_ERROR,
2738 "Kerberos credentials larger than 64k (%d)!",
2739 (int)con->gss_output_token.length);
2740 return (0);
2741 }
2742 #endif /* HAVE_GSSAPI */
2743
2744 if (con->language && strcmp(con->language->language, "C"))
2745 {
2746 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2747 con->language->language) < 0)
2748 return (0);
2749 }
2750
2751 if (type)
2752 {
2753 if (!strcmp(type, "text/html"))
2754 {
2755 if (httpPrintf(HTTP(con),
2756 "Content-Type: text/html; charset=utf-8\r\n") < 0)
2757 return (0);
2758 }
2759 else if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2760 return (0);
2761 }
2762
2763 return (1);
2764 }
2765
2766
2767 /*
2768 * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs.
2769 */
2770
2771 void
2772 cupsdUpdateCGI(void)
2773 {
2774 char *ptr, /* Pointer to end of line in buffer */
2775 message[1024]; /* Pointer to message text */
2776 int loglevel; /* Log level for message */
2777
2778
2779 while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
2780 message, sizeof(message))) != NULL)
2781 {
2782 if (loglevel == CUPSD_LOG_INFO)
2783 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
2784
2785 if (!strchr(CGIStatusBuffer->buffer, '\n'))
2786 break;
2787 }
2788
2789 if (ptr == NULL && !CGIStatusBuffer->bufused)
2790 {
2791 /*
2792 * Fatal error on pipe - should never happen!
2793 */
2794
2795 cupsdLogMessage(CUPSD_LOG_CRIT,
2796 "cupsdUpdateCGI: error reading from CGI error pipe - %s",
2797 strerror(errno));
2798 }
2799 }
2800
2801
2802 /*
2803 * 'cupsdWriteClient()' - Write data to a client as needed.
2804 */
2805
2806 void
2807 cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */
2808 {
2809 int bytes, /* Number of bytes written */
2810 field_col; /* Current column */
2811 char *bufptr, /* Pointer into buffer */
2812 *bufend; /* Pointer to end of buffer */
2813 ipp_state_t ipp_state; /* IPP state value */
2814
2815
2816 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2817 "cupsdWriteClient(con=%p(%d)) response=%p(%d), file=%d "
2818 "pipe_pid=%d state=%d",
2819 con, con->http.fd, con->response,
2820 con->response ? con->response->state : -1,
2821 con->file, con->pipe_pid, con->http.state);
2822
2823 if (con->http.state != HTTP_GET_SEND &&
2824 con->http.state != HTTP_POST_SEND)
2825 {
2826 /*
2827 * If we get called in the wrong state, then something went wrong with the
2828 * connection and we need to shut it down...
2829 */
2830
2831 cupsdLogMessage(CUPSD_LOG_DEBUG,
2832 "cupsdWriteClient: %d Closing on unknown HTTP state %d",
2833 con->http.fd, con->http.state);
2834 cupsdCloseClient(con);
2835 return;
2836 }
2837
2838 if (con->pipe_pid)
2839 {
2840 /*
2841 * Make sure we select on the CGI output...
2842 */
2843
2844 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
2845
2846 if (!con->file_ready)
2847 {
2848 /*
2849 * Try again later when there is CGI output available...
2850 */
2851
2852 cupsdRemoveSelect(con->http.fd);
2853 return;
2854 }
2855
2856 con->file_ready = 0;
2857 }
2858
2859 if (con->response && con->response->state != IPP_DATA)
2860 {
2861 ipp_state = ippWrite(HTTP(con), con->response);
2862 bytes = ipp_state != IPP_ERROR &&
2863 (con->file >= 0 || ipp_state != IPP_DATA);
2864 }
2865 else if ((bytes = read(con->file, con->header,
2866 sizeof(con->header) - con->header_used)) > 0)
2867 {
2868 con->header_used += bytes;
2869
2870 if (con->pipe_pid && !con->got_fields)
2871 {
2872 /*
2873 * Inspect the data for Content-Type and other fields.
2874 */
2875
2876 for (bufptr = con->header, bufend = con->header + con->header_used,
2877 field_col = 0;
2878 !con->got_fields && bufptr < bufend;
2879 bufptr ++)
2880 {
2881 if (*bufptr == '\n')
2882 {
2883 /*
2884 * Send line to client...
2885 */
2886
2887 if (bufptr > con->header && bufptr[-1] == '\r')
2888 bufptr[-1] = '\0';
2889 *bufptr++ = '\0';
2890
2891 cupsdLogMessage(CUPSD_LOG_DEBUG, "Script header: %s", con->header);
2892
2893 if (!con->sent_header)
2894 {
2895 /*
2896 * Handle redirection and CGI status codes...
2897 */
2898
2899 if (!strncasecmp(con->header, "Location:", 9))
2900 {
2901 cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE);
2902 con->sent_header = 2;
2903
2904 if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
2905 return;
2906 }
2907 else if (!strncasecmp(con->header, "Status:", 7))
2908 {
2909 cupsdSendError(con, (http_status_t)atoi(con->header + 7),
2910 CUPSD_AUTH_NONE);
2911 con->sent_header = 2;
2912 }
2913 else
2914 {
2915 cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE);
2916 con->sent_header = 1;
2917
2918 if (con->http.version == HTTP_1_1)
2919 {
2920 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
2921 return;
2922 }
2923 }
2924 }
2925
2926 if (strncasecmp(con->header, "Status:", 7))
2927 httpPrintf(HTTP(con), "%s\r\n", con->header);
2928
2929 /*
2930 * Update buffer...
2931 */
2932
2933 con->header_used -= bufptr - con->header;
2934
2935 if (con->header_used > 0)
2936 memmove(con->header, bufptr, con->header_used);
2937
2938 bufptr = con->header - 1;
2939
2940 /*
2941 * See if the line was empty...
2942 */
2943
2944 if (field_col == 0)
2945 {
2946 con->got_fields = 1;
2947
2948 if (cupsdFlushHeader(con) < 0)
2949 {
2950 cupsdCloseClient(con);
2951 return;
2952 }
2953
2954 if (con->http.version == HTTP_1_1)
2955 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2956 }
2957 else
2958 field_col = 0;
2959 }
2960 else if (*bufptr != '\r')
2961 field_col ++;
2962 }
2963
2964 if (!con->got_fields)
2965 {
2966 con->http.activity = time(NULL);
2967 return;
2968 }
2969 }
2970
2971 if (con->header_used > 0)
2972 {
2973 if (httpWrite2(HTTP(con), con->header, con->header_used) < 0)
2974 {
2975 cupsdLogMessage(CUPSD_LOG_DEBUG,
2976 "cupsdWriteClient: %d Closing for error %d (%s)",
2977 con->http.fd, con->http.error,
2978 strerror(con->http.error));
2979 cupsdCloseClient(con);
2980 return;
2981 }
2982
2983 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2984 httpFlushWrite(HTTP(con));
2985
2986 con->bytes += con->header_used;
2987
2988 if (con->http.state == HTTP_WAITING)
2989 bytes = 0;
2990 else
2991 bytes = con->header_used;
2992
2993 con->header_used = 0;
2994 }
2995 }
2996
2997 if (bytes <= 0 ||
2998 (con->http.state != HTTP_GET_SEND && con->http.state != HTTP_POST_SEND))
2999 {
3000 if (!con->sent_header && con->pipe_pid)
3001 cupsdSendError(con, HTTP_SERVER_ERROR, CUPSD_AUTH_NONE);
3002 else
3003 {
3004 cupsdLogRequest(con, HTTP_OK);
3005
3006 httpFlushWrite(HTTP(con));
3007
3008 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED && con->sent_header == 1)
3009 {
3010 if (httpWrite2(HTTP(con), "", 0) < 0)
3011 {
3012 cupsdLogMessage(CUPSD_LOG_DEBUG,
3013 "cupsdWriteClient: %d Closing for error %d (%s)",
3014 con->http.fd, con->http.error,
3015 strerror(con->http.error));
3016 cupsdCloseClient(con);
3017 return;
3018 }
3019 }
3020 }
3021
3022 con->http.state = HTTP_WAITING;
3023
3024 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
3025
3026 if (con->file >= 0)
3027 {
3028 cupsdRemoveSelect(con->file);
3029
3030 if (con->pipe_pid)
3031 cupsdEndProcess(con->pipe_pid, 0);
3032
3033 close(con->file);
3034 con->file = -1;
3035 con->pipe_pid = 0;
3036 }
3037
3038 if (con->filename)
3039 {
3040 unlink(con->filename);
3041 cupsdClearString(&con->filename);
3042 }
3043
3044 if (con->request)
3045 {
3046 ippDelete(con->request);
3047 con->request = NULL;
3048 }
3049
3050 if (con->response)
3051 {
3052 ippDelete(con->response);
3053 con->response = NULL;
3054 }
3055
3056 cupsdClearString(&con->command);
3057 cupsdClearString(&con->options);
3058 cupsdClearString(&con->query_string);
3059
3060 if (!con->http.keep_alive)
3061 {
3062 cupsdLogMessage(CUPSD_LOG_DEBUG,
3063 "cupsdWriteClient: %d Closing because Keep-Alive disabled",
3064 con->http.fd);
3065 cupsdCloseClient(con);
3066 return;
3067 }
3068 else
3069 {
3070 cupsArrayRemove(ActiveClients, con);
3071 cupsdSetBusyState();
3072 }
3073 }
3074
3075 con->http.activity = time(NULL);
3076 }
3077
3078
3079 /*
3080 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
3081 */
3082
3083 static int /* O - 1 if modified since */
3084 check_if_modified(
3085 cupsd_client_t *con, /* I - Client connection */
3086 struct stat *filestats) /* I - File information */
3087 {
3088 char *ptr; /* Pointer into field */
3089 time_t date; /* Time/date value */
3090 off_t size; /* Size/length value */
3091
3092
3093 size = 0;
3094 date = 0;
3095 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
3096
3097 if (*ptr == '\0')
3098 return (1);
3099
3100 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3101 "check_if_modified(con=%p(%d), "
3102 "filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"",
3103 con, con->http.fd, filestats, CUPS_LLCAST filestats->st_size,
3104 (int)filestats->st_mtime, ptr);
3105
3106 while (*ptr != '\0')
3107 {
3108 while (isspace(*ptr) || *ptr == ';')
3109 ptr ++;
3110
3111 if (strncasecmp(ptr, "length=", 7) == 0)
3112 {
3113 ptr += 7;
3114 size = strtoll(ptr, NULL, 10);
3115
3116 while (isdigit(*ptr))
3117 ptr ++;
3118 }
3119 else if (isalpha(*ptr))
3120 {
3121 date = httpGetDateTime(ptr);
3122 while (*ptr != '\0' && *ptr != ';')
3123 ptr ++;
3124 }
3125 else
3126 ptr ++;
3127 }
3128
3129 return ((size != filestats->st_size && size != 0) ||
3130 (date < filestats->st_mtime && date != 0) ||
3131 (size == 0 && date == 0));
3132 }
3133
3134
3135 /*
3136 * 'compare_clients()' - Compare two client connections.
3137 */
3138
3139 static int /* O - Result of comparison */
3140 compare_clients(cupsd_client_t *a, /* I - First client */
3141 cupsd_client_t *b, /* I - Second client */
3142 void *data) /* I - User data (not used) */
3143 {
3144 (void)data;
3145
3146 if (a == b)
3147 return (0);
3148 else if (a < b)
3149 return (-1);
3150 else
3151 return (1);
3152 }
3153
3154
3155 /*
3156 * 'data_ready()' - Check whether data is available from a client.
3157 */
3158
3159 static int /* O - 1 if data is ready, 0 otherwise */
3160 data_ready(cupsd_client_t *con) /* I - Client */
3161 {
3162 if (con->http.used > 0)
3163 return (1);
3164 #ifdef HAVE_SSL
3165 else if (con->http.tls)
3166 {
3167 # ifdef HAVE_LIBSSL
3168 if (SSL_pending((SSL *)(con->http.tls)))
3169 return (1);
3170 # elif defined(HAVE_GNUTLS)
3171 if (gnutls_record_check_pending(con->http.tls))
3172 return (1);
3173 # elif defined(HAVE_CDSASSL)
3174 size_t bytes; /* Bytes that are available */
3175
3176 if (!SSLGetBufferedReadSize(con->http.tls, &bytes) && bytes > 0)
3177 return (1);
3178 # endif /* HAVE_LIBSSL */
3179 }
3180 #endif /* HAVE_SSL */
3181
3182 return (0);
3183 }
3184
3185
3186 #ifdef HAVE_SSL
3187 /*
3188 * 'encrypt_client()' - Enable encryption for the client...
3189 */
3190
3191 static int /* O - 1 on success, 0 on error */
3192 encrypt_client(cupsd_client_t *con) /* I - Client to encrypt */
3193 {
3194 # ifdef HAVE_LIBSSL
3195 SSL_CTX *context; /* Context for encryption */
3196 BIO *bio; /* BIO data */
3197 unsigned long error; /* Error code */
3198
3199
3200 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3201 con->http.fd);
3202
3203 /*
3204 * Verify that we have a certificate...
3205 */
3206
3207 if (access(ServerKey, 0) || access(ServerCertificate, 0))
3208 {
3209 /*
3210 * Nope, make a self-signed certificate...
3211 */
3212
3213 if (!make_certificate(con))
3214 return (0);
3215 }
3216
3217 /*
3218 * Create the SSL context and accept the connection...
3219 */
3220
3221 context = SSL_CTX_new(SSLv23_server_method());
3222
3223 SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
3224 if (SSLOptions & CUPSD_SSL_NOEMPTY)
3225 SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
3226 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
3227 SSL_CTX_use_certificate_chain_file(context, ServerCertificate);
3228
3229 bio = BIO_new(_httpBIOMethods());
3230 BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
3231
3232 con->http.tls = SSL_new(context);
3233 SSL_set_bio(con->http.tls, bio, bio);
3234
3235 if (SSL_accept(con->http.tls) != 1)
3236 {
3237 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to encrypt connection from %s!",
3238 con->http.hostname);
3239
3240 while ((error = ERR_get_error()) != 0)
3241 cupsdLogMessage(CUPSD_LOG_ERROR, "%s", ERR_error_string(error, NULL));
3242
3243 SSL_CTX_free(context);
3244 SSL_free(con->http.tls);
3245 con->http.tls = NULL;
3246 return (0);
3247 }
3248
3249 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3250 con->http.hostname);
3251
3252 return (1);
3253
3254 # elif defined(HAVE_GNUTLS)
3255 int error; /* Error code */
3256 gnutls_certificate_server_credentials *credentials;
3257 /* TLS credentials */
3258
3259
3260 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3261 con->http.fd);
3262
3263 /*
3264 * Verify that we have a certificate...
3265 */
3266
3267 if (access(ServerKey, 0) || access(ServerCertificate, 0))
3268 {
3269 /*
3270 * Nope, make a self-signed certificate...
3271 */
3272
3273 if (!make_certificate(con))
3274 return (0);
3275 }
3276
3277 /*
3278 * Create the SSL object and perform the SSL handshake...
3279 */
3280
3281 credentials = (gnutls_certificate_server_credentials *)
3282 malloc(sizeof(gnutls_certificate_server_credentials));
3283 if (credentials == NULL)
3284 {
3285 cupsdLogMessage(CUPSD_LOG_ERROR,
3286 "Unable to encrypt connection from %s - %s",
3287 con->http.hostname, strerror(errno));
3288
3289 return (0);
3290 }
3291
3292 gnutls_certificate_allocate_credentials(credentials);
3293 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
3294 ServerKey, GNUTLS_X509_FMT_PEM);
3295
3296 gnutls_init(&con->http.tls), GNUTLS_SERVER);
3297 gnutls_set_default_priority(con->http.tls);
3298 gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
3299 gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr)HTTP(con));
3300 gnutls_transport_set_pull_function(con->http.tls, _httpReadGNUTLS);
3301 gnutls_transport_set_push_function(con->http.tls, _httpWriteGNUTLS);
3302
3303 error = gnutls_handshake(con->http.tls);
3304
3305 if (error != GNUTLS_E_SUCCESS)
3306 {
3307 cupsdLogMessage(CUPSD_LOG_ERROR,
3308 "Unable to encrypt connection from %s - %s",
3309 con->http.hostname, gnutls_strerror(error));
3310
3311 gnutls_deinit(con->http.tls);
3312 gnutls_certificate_free_credentials(*credentials);
3313 con->http.tls = NULL;
3314 free(credentials);
3315 return (0);
3316 }
3317
3318 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3319 con->http.hostname);
3320
3321 con->http.tls_credentials = credentials;
3322 return (1);
3323
3324 # elif defined(HAVE_CDSASSL)
3325 OSStatus error = 0; /* Error code */
3326 CFArrayRef peerCerts; /* Peer certificates */
3327
3328
3329 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3330 con->http.fd);
3331
3332 con->http.tls_credentials = get_cdsa_certificate(con);
3333
3334 if (!con->http.tls_credentials)
3335 {
3336 /*
3337 * No keychain (yet), make a self-signed certificate...
3338 */
3339
3340 if (make_certificate(con))
3341 con->http.tls_credentials = get_cdsa_certificate(con);
3342 }
3343
3344 if (!con->http.tls_credentials)
3345 {
3346 cupsdLogMessage(CUPSD_LOG_ERROR,
3347 "Could not find signing key in keychain \"%s\"",
3348 ServerCertificate);
3349 error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */
3350 }
3351
3352 if (!error)
3353 error = SSLNewContext(true, &con->http.tls);
3354
3355 if (!error)
3356 error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA);
3357
3358 if (!error)
3359 error = SSLSetProtocolVersionEnabled(con->http.tls, kSSLProtocol2, false);
3360
3361 if (!error)
3362 error = SSLSetConnection(con->http.tls, HTTP(con));
3363
3364 if (!error)
3365 error = SSLSetAllowsExpiredCerts(con->http.tls, true);
3366
3367 if (!error)
3368 error = SSLSetAllowsAnyRoot(con->http.tls, true);
3369
3370 if (!error)
3371 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
3372
3373 if (!error)
3374 {
3375 /*
3376 * Perform SSL/TLS handshake
3377 */
3378
3379 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
3380 usleep(1000);
3381 }
3382
3383 if (error)
3384 {
3385 cupsdLogMessage(CUPSD_LOG_ERROR,
3386 "Unable to encrypt connection from %s - %s (%d)",
3387 con->http.hostname, cssmErrorString(error), (int)error);
3388
3389 con->http.error = error;
3390 con->http.status = HTTP_ERROR;
3391
3392 if (con->http.tls)
3393 {
3394 SSLDisposeContext(con->http.tls);
3395 con->http.tls = NULL;
3396 }
3397
3398 if (con->http.tls_credentials)
3399 {
3400 CFRelease(con->http.tls_credentials);
3401 con->http.tls_credentials = NULL;
3402 }
3403
3404 return (0);
3405 }
3406
3407 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3408 con->http.hostname);
3409
3410 if (!SSLCopyPeerCertificates(con->http.tls, &peerCerts) && peerCerts)
3411 {
3412 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!",
3413 (int)CFArrayGetCount(peerCerts));
3414 }
3415 else
3416 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!");
3417
3418 return (1);
3419
3420 # endif /* HAVE_LIBSSL */
3421 }
3422 #endif /* HAVE_SSL */
3423
3424
3425 #ifdef HAVE_CDSASSL
3426 /*
3427 * 'get_cdsa_certificate()' - Get a SSL/TLS certificate from the System keychain.
3428 */
3429
3430 static CFArrayRef /* O - Array of certificates */
3431 get_cdsa_certificate(
3432 cupsd_client_t *con) /* I - Client connection */
3433 {
3434 OSStatus err; /* Error info */
3435 SecKeychainRef keychain = NULL;/* Keychain reference */
3436 SecIdentitySearchRef search = NULL; /* Search reference */
3437 SecIdentityRef identity = NULL;/* Identity */
3438 CFArrayRef certificates = NULL;
3439 /* Certificate array */
3440 # if HAVE_SECPOLICYCREATESSL
3441 SecPolicyRef policy = NULL; /* Policy ref */
3442 CFStringRef servername = NULL;
3443 /* Server name */
3444 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
3445 char localname[1024];/* Local hostname */
3446 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3447 SecPolicyRef policy = NULL; /* Policy ref */
3448 SecPolicySearchRef policy_search = NULL;
3449 /* Policy search ref */
3450 CSSM_DATA options; /* Policy options */
3451 CSSM_APPLE_TP_SSL_OPTIONS
3452 ssl_options; /* SSL Option for hostname */
3453 char localname[1024];/* Local hostname */
3454 # endif /* HAVE_SECPOLICYCREATESSL */
3455
3456
3457 cupsdLogMessage(CUPSD_LOG_DEBUG,
3458 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3459 con->servername);
3460
3461 if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
3462 {
3463 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
3464 ServerCertificate, cssmErrorString(err), (int)err);
3465 goto cleanup;
3466 }
3467
3468 # if HAVE_SECPOLICYCREATESSL
3469 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
3470 kCFStringEncodingUTF8);
3471
3472 policy = SecPolicyCreateSSL(1, servername);
3473
3474 if (servername)
3475 CFRelease(servername);
3476
3477 if (!policy)
3478 {
3479 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3480 goto cleanup;
3481 }
3482
3483 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3484 &kCFTypeDictionaryKeyCallBacks,
3485 &kCFTypeDictionaryValueCallBacks)))
3486 {
3487 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
3488 goto cleanup;
3489 }
3490
3491 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
3492 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
3493 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
3494 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
3495
3496 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
3497
3498 if (err && DNSSDHostName)
3499 {
3500 /*
3501 * Search for the connection server name failed; try the DNS-SD .local
3502 * hostname instead...
3503 */
3504
3505 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
3506
3507 cupsdLogMessage(CUPSD_LOG_DEBUG,
3508 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3509 localname);
3510
3511 servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
3512 kCFStringEncodingUTF8);
3513
3514 CFRelease(policy);
3515
3516 policy = SecPolicyCreateSSL(1, servername);
3517
3518 if (servername)
3519 CFRelease(servername);
3520
3521 if (!policy)
3522 {
3523 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3524 goto cleanup;
3525 }
3526
3527 CFDictionarySetValue(query, kSecMatchPolicy, policy);
3528
3529 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
3530 }
3531
3532 if (err)
3533 {
3534 cupsdLogMessage(CUPSD_LOG_DEBUG,
3535 "Cannot find signing key in keychain \"%s\": %s (%d)",
3536 ServerCertificate, cssmErrorString(err), (int)err);
3537 goto cleanup;
3538 }
3539
3540 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3541 /*
3542 * Use a policy to search for valid certificates whose common name matches the
3543 * servername...
3544 */
3545
3546 if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL,
3547 NULL, &policy_search))
3548 {
3549 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference");
3550 goto cleanup;
3551 }
3552
3553 if (SecPolicySearchCopyNext(policy_search, &policy))
3554 {
3555 cupsdLogMessage(CUPSD_LOG_ERROR,
3556 "Cannot find a policy to use for searching");
3557 goto cleanup;
3558 }
3559
3560 memset(&ssl_options, 0, sizeof(ssl_options));
3561 ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
3562 ssl_options.ServerName = con->servername;
3563 ssl_options.ServerNameLen = strlen(con->servername);
3564
3565 options.Data = (uint8 *)&ssl_options;
3566 options.Length = sizeof(ssl_options);
3567
3568 if (SecPolicySetValue(policy, &options))
3569 {
3570 cupsdLogMessage(CUPSD_LOG_ERROR,
3571 "Cannot set policy value to use for searching");
3572 goto cleanup;
3573 }
3574
3575 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
3576 keychain, FALSE, &search)))
3577 {
3578 cupsdLogMessage(CUPSD_LOG_ERROR,
3579 "Cannot create identity search reference: %s (%d)",
3580 cssmErrorString(err), (int)err);
3581 goto cleanup;
3582 }
3583
3584 err = SecIdentitySearchCopyNext(search, &identity);
3585
3586 if (err && DNSSDHostName)
3587 {
3588 /*
3589 * Search for the connection server name failed; try the DNS-SD .local
3590 * hostname instead...
3591 */
3592
3593 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
3594
3595 ssl_options.ServerName = localname;
3596 ssl_options.ServerNameLen = strlen(localname);
3597
3598 cupsdLogMessage(CUPSD_LOG_DEBUG,
3599 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3600 localname);
3601
3602 if (SecPolicySetValue(policy, &options))
3603 {
3604 cupsdLogMessage(CUPSD_LOG_ERROR,
3605 "Cannot set policy value to use for searching");
3606 goto cleanup;
3607 }
3608
3609 CFRelease(search);
3610 search = NULL;
3611 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
3612 keychain, FALSE, &search)))
3613 {
3614 cupsdLogMessage(CUPSD_LOG_ERROR,
3615 "Cannot create identity search reference: %s (%d)",
3616 cssmErrorString(err), (int)err);
3617 goto cleanup;
3618 }
3619
3620 err = SecIdentitySearchCopyNext(search, &identity);
3621
3622 }
3623
3624 if (err)
3625 {
3626 cupsdLogMessage(CUPSD_LOG_DEBUG,
3627 "Cannot find signing key in keychain \"%s\": %s (%d)",
3628 ServerCertificate, cssmErrorString(err), (int)err);
3629 goto cleanup;
3630 }
3631
3632 # else
3633 /*
3634 * Assume there is exactly one SecIdentity in the keychain...
3635 */
3636
3637 if ((err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search)))
3638 {
3639 cupsdLogMessage(CUPSD_LOG_DEBUG,
3640 "Cannot create identity search reference (%d)", (int)err);
3641 goto cleanup;
3642 }
3643
3644 if ((err = SecIdentitySearchCopyNext(search, &identity)))
3645 {
3646 cupsdLogMessage(CUPSD_LOG_DEBUG,
3647 "Cannot find signing key in keychain \"%s\": %s (%d)",
3648 ServerCertificate, cssmErrorString(err), (int)err);
3649 goto cleanup;
3650 }
3651 # endif /* HAVE_SECPOLICYCREATESSL */
3652
3653 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
3654 {
3655 cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure!");
3656 goto cleanup;
3657 }
3658
3659 if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
3660 1, &kCFTypeArrayCallBacks)) == NULL)
3661 {
3662 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
3663 goto cleanup;
3664 }
3665
3666 cleanup :
3667
3668 if (keychain)
3669 CFRelease(keychain);
3670 if (search)
3671 CFRelease(search);
3672 if (identity)
3673 CFRelease(identity);
3674
3675 # if HAVE_SECPOLICYCREATESSL
3676 if (policy)
3677 CFRelease(policy);
3678 if (query)
3679 CFRelease(query);
3680 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3681 if (policy)
3682 CFRelease(policy);
3683 if (policy_search)
3684 CFRelease(policy_search);
3685 # endif /* HAVE_SECPOLICYCREATESSL */
3686
3687 return (certificates);
3688 }
3689 #endif /* HAVE_CDSASSL */
3690
3691
3692 /*
3693 * 'get_file()' - Get a filename and state info.
3694 */
3695
3696 static char * /* O - Real filename */
3697 get_file(cupsd_client_t *con, /* I - Client connection */
3698 struct stat *filestats, /* O - File information */
3699 char *filename, /* IO - Filename buffer */
3700 int len) /* I - Buffer length */
3701 {
3702 int status; /* Status of filesystem calls */
3703 char *ptr; /* Pointer info filename */
3704 int plen; /* Remaining length after pointer */
3705 char language[7]; /* Language subdirectory, if any */
3706
3707
3708 /*
3709 * Figure out the real filename...
3710 */
3711
3712 language[0] = '\0';
3713
3714 if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/'))
3715 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
3716 else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/'))
3717 {
3718 snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7);
3719 if (access(filename, F_OK) < 0)
3720 snprintf(filename, len, "%s/images/generic.png", DocumentRoot);
3721 }
3722 else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
3723 snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5);
3724 else if (!strncmp(con->uri, "/admin/conf/", 12))
3725 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
3726 else if (!strncmp(con->uri, "/admin/log/", 11))
3727 {
3728 if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/')
3729 strlcpy(filename, AccessLog, len);
3730 else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/')
3731 strlcpy(filename, ErrorLog, len);
3732 else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/')
3733 strlcpy(filename, PageLog, len);
3734 else
3735 return (NULL);
3736 }
3737 else if (con->language)
3738 {
3739 snprintf(language, sizeof(language), "/%s", con->language->language);
3740 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3741 }
3742 else
3743 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3744
3745 if ((ptr = strchr(filename, '?')) != NULL)
3746 *ptr = '\0';
3747
3748 /*
3749 * Grab the status for this language; if there isn't a language-specific file
3750 * then fallback to the default one...
3751 */
3752
3753 if ((status = stat(filename, filestats)) != 0 && language[0] &&
3754 strncmp(con->uri, "/icons/", 7) &&
3755 strncmp(con->uri, "/ppd/", 5) &&
3756 strncmp(con->uri, "/rss/", 5) &&
3757 strncmp(con->uri, "/admin/conf/", 12) &&
3758 strncmp(con->uri, "/admin/log/", 11))
3759 {
3760 /*
3761 * Drop the country code...
3762 */
3763
3764 language[3] = '\0';
3765 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3766
3767 if ((ptr = strchr(filename, '?')) != NULL)
3768 *ptr = '\0';
3769
3770 if ((status = stat(filename, filestats)) != 0)
3771 {
3772 /*
3773 * Drop the language prefix and try the root directory...
3774 */
3775
3776 language[0] = '\0';
3777 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3778
3779 if ((ptr = strchr(filename, '?')) != NULL)
3780 *ptr = '\0';
3781
3782 status = stat(filename, filestats);
3783 }
3784 }
3785
3786 /*
3787 * If we're found a directory, get the index.html file instead...
3788 */
3789
3790 if (!status && S_ISDIR(filestats->st_mode))
3791 {
3792 /*
3793 * Make sure the URI ends with a slash...
3794 */
3795
3796 if (con->uri[strlen(con->uri) - 1] != '/')
3797 strlcat(con->uri, "/", sizeof(con->uri));
3798
3799 /*
3800 * Find the directory index file, trying every language...
3801 */
3802
3803 do
3804 {
3805 if (status && language[0])
3806 {
3807 /*
3808 * Try a different language subset...
3809 */
3810
3811 if (language[3])
3812 language[0] = '\0'; /* Strip country code */
3813 else
3814 language[0] = '\0'; /* Strip language */
3815 }
3816
3817 /*
3818 * Look for the index file...
3819 */
3820
3821 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3822
3823 if ((ptr = strchr(filename, '?')) != NULL)
3824 *ptr = '\0';
3825
3826 ptr = filename + strlen(filename);
3827 plen = len - (ptr - filename);
3828
3829 strlcpy(ptr, "index.html", plen);
3830 status = stat(filename, filestats);
3831
3832 #ifdef HAVE_JAVA
3833 if (status)
3834 {
3835 strlcpy(ptr, "index.class", plen);
3836 status = stat(filename, filestats);
3837 }
3838 #endif /* HAVE_JAVA */
3839
3840 #ifdef HAVE_PERL
3841 if (status)
3842 {
3843 strlcpy(ptr, "index.pl", plen);
3844 status = stat(filename, filestats);
3845 }
3846 #endif /* HAVE_PERL */
3847
3848 #ifdef HAVE_PHP
3849 if (status)
3850 {
3851 strlcpy(ptr, "index.php", plen);
3852 status = stat(filename, filestats);
3853 }
3854 #endif /* HAVE_PHP */
3855
3856 #ifdef HAVE_PYTHON
3857 if (status)
3858 {
3859 strlcpy(ptr, "index.pyc", plen);
3860 status = stat(filename, filestats);
3861 }
3862
3863 if (status)
3864 {
3865 strlcpy(ptr, "index.py", plen);
3866 status = stat(filename, filestats);
3867 }
3868 #endif /* HAVE_PYTHON */
3869
3870 }
3871 while (status && language[0]);
3872 }
3873
3874 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3875 "get_file(con=%p(%d), filestats=%p, filename=%p, len=%d) = "
3876 "%s", con, con->http.fd, filestats, filename, len,
3877 status ? "(null)" : filename);
3878
3879 if (status)
3880 return (NULL);
3881 else
3882 return (filename);
3883 }
3884
3885
3886 /*
3887 * 'install_conf_file()' - Install a configuration file.
3888 */
3889
3890 static http_status_t /* O - Status */
3891 install_conf_file(cupsd_client_t *con) /* I - Connection */
3892 {
3893 cups_file_t *in, /* Input file */
3894 *out; /* Output file */
3895 char buffer[1024]; /* Copy buffer */
3896 int bytes; /* Number of bytes */
3897 char conffile[1024], /* Configuration filename */
3898 newfile[1024], /* New config filename */
3899 oldfile[1024]; /* Old config filename */
3900 struct stat confinfo; /* Config file info */
3901
3902
3903 /*
3904 * First construct the filenames...
3905 */
3906
3907 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
3908 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
3909 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
3910
3911 cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", conffile);
3912
3913 /*
3914 * Get the owner, group, and permissions of the configuration file.
3915 * If it doesn't exist, assign it to the User and Group in the
3916 * cupsd.conf file with mode 0640 permissions.
3917 */
3918
3919 if (stat(conffile, &confinfo))
3920 {
3921 confinfo.st_uid = User;
3922 confinfo.st_gid = Group;
3923 confinfo.st_mode = ConfigFilePerm;
3924 }
3925
3926 /*
3927 * Open the request file and new config file...
3928 */
3929
3930 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
3931 {
3932 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\" - %s",
3933 con->filename, strerror(errno));
3934 return (HTTP_SERVER_ERROR);
3935 }
3936
3937 if ((out = cupsFileOpen(newfile, "wb")) == NULL)
3938 {
3939 cupsFileClose(in);
3940 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open config file \"%s\" - %s",
3941 newfile, strerror(errno));
3942 return (HTTP_SERVER_ERROR);
3943 }
3944
3945 fchmod(cupsFileNumber(out), confinfo.st_mode);
3946 fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid);
3947
3948 /*
3949 * Copy from the request to the new config file...
3950 */
3951
3952 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
3953 if (cupsFileWrite(out, buffer, bytes) < bytes)
3954 {
3955 cupsdLogMessage(CUPSD_LOG_ERROR,
3956 "Unable to copy to config file \"%s\" - %s",
3957 newfile, strerror(errno));
3958
3959 cupsFileClose(in);
3960 cupsFileClose(out);
3961 unlink(newfile);
3962
3963 return (HTTP_SERVER_ERROR);
3964 }
3965
3966 /*
3967 * Close the files...
3968 */
3969
3970 cupsFileClose(in);
3971 if (cupsFileClose(out))
3972 {
3973 cupsdLogMessage(CUPSD_LOG_ERROR,
3974 "Error file closing config file \"%s\" - %s",
3975 newfile, strerror(errno));
3976
3977 unlink(newfile);
3978
3979 return (HTTP_SERVER_ERROR);
3980 }
3981
3982 /*
3983 * Remove the request file...
3984 */
3985
3986 unlink(con->filename);
3987 cupsdClearString(&con->filename);
3988
3989 /*
3990 * Unlink the old backup, rename the current config file to the backup
3991 * filename, and rename the new config file to the config file name...
3992 */
3993
3994 if (unlink(oldfile))
3995 if (errno != ENOENT)
3996 {
3997 cupsdLogMessage(CUPSD_LOG_ERROR,
3998 "Unable to remove backup config file \"%s\" - %s",
3999 oldfile, strerror(errno));
4000
4001 unlink(newfile);
4002
4003 return (HTTP_SERVER_ERROR);
4004 }
4005
4006 if (rename(conffile, oldfile))
4007 if (errno != ENOENT)
4008 {
4009 cupsdLogMessage(CUPSD_LOG_ERROR,
4010 "Unable to rename old config file \"%s\" - %s",
4011 conffile, strerror(errno));
4012
4013 unlink(newfile);
4014
4015 return (HTTP_SERVER_ERROR);
4016 }
4017
4018 if (rename(newfile, conffile))
4019 {
4020 cupsdLogMessage(CUPSD_LOG_ERROR,
4021 "Unable to rename new config file \"%s\" - %s",
4022 newfile, strerror(errno));
4023
4024 rename(oldfile, conffile);
4025 unlink(newfile);
4026
4027 return (HTTP_SERVER_ERROR);
4028 }
4029
4030 /*
4031 * If the cupsd.conf file was updated, set the NeedReload flag...
4032 */
4033
4034 if (!strcmp(con->uri, "/admin/conf/cupsd.conf"))
4035 NeedReload = RELOAD_CUPSD;
4036 else
4037 NeedReload = RELOAD_ALL;
4038
4039 ReloadTime = time(NULL);
4040
4041 /*
4042 * Return that the file was created successfully...
4043 */
4044
4045 return (HTTP_CREATED);
4046 }
4047
4048
4049 /*
4050 * 'is_cgi()' - Is the resource a CGI script/program?
4051 */
4052
4053 static int /* O - 1 = CGI, 0 = file */
4054 is_cgi(cupsd_client_t *con, /* I - Client connection */
4055 const char *filename, /* I - Real filename */
4056 struct stat *filestats, /* I - File information */
4057 mime_type_t *type) /* I - MIME type */
4058 {
4059 const char *options; /* Options on URL */
4060
4061
4062 /*
4063 * Get the options, if any...
4064 */
4065
4066 if ((options = strchr(con->uri, '?')) != NULL)
4067 {
4068 options ++;
4069 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
4070 }
4071
4072 /*
4073 * Check for known types...
4074 */
4075
4076 if (!type || strcasecmp(type->super, "application"))
4077 {
4078 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4079 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4080 "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
4081 type ? type->super : "unknown",
4082 type ? type->type : "unknown");
4083 return (0);
4084 }
4085
4086 if (!strcasecmp(type->type, "x-httpd-cgi") &&
4087 (filestats->st_mode & 0111))
4088 {
4089 /*
4090 * "application/x-httpd-cgi" is a CGI script.
4091 */
4092
4093 cupsdSetString(&con->command, filename);
4094
4095 if (options)
4096 cupsdSetStringf(&con->options, " %s", options);
4097
4098 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4099 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4100 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4101 type ? type->super : "unknown",
4102 type ? type->type : "unknown");
4103 return (1);
4104 }
4105 #ifdef HAVE_JAVA
4106 else if (!strcasecmp(type->type, "x-httpd-java"))
4107 {
4108 /*
4109 * "application/x-httpd-java" is a Java servlet.
4110 */
4111
4112 cupsdSetString(&con->command, CUPS_JAVA);
4113
4114 if (options)
4115 cupsdSetStringf(&con->options, " %s %s", filename, options);
4116 else
4117 cupsdSetStringf(&con->options, " %s", filename);
4118
4119 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4120 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4121 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4122 type ? type->super : "unknown",
4123 type ? type->type : "unknown");
4124 return (1);
4125 }
4126 #endif /* HAVE_JAVA */
4127 #ifdef HAVE_PERL
4128 else if (!strcasecmp(type->type, "x-httpd-perl"))
4129 {
4130 /*
4131 * "application/x-httpd-perl" is a Perl page.
4132 */
4133
4134 cupsdSetString(&con->command, CUPS_PERL);
4135
4136 if (options)
4137 cupsdSetStringf(&con->options, " %s %s", filename, options);
4138 else
4139 cupsdSetStringf(&con->options, " %s", filename);
4140
4141 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4142 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4143 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4144 type ? type->super : "unknown",
4145 type ? type->type : "unknown");
4146 return (1);
4147 }
4148 #endif /* HAVE_PERL */
4149 #ifdef HAVE_PHP
4150 else if (!strcasecmp(type->type, "x-httpd-php"))
4151 {
4152 /*
4153 * "application/x-httpd-php" is a PHP page.
4154 */
4155
4156 cupsdSetString(&con->command, CUPS_PHP);
4157
4158 if (options)
4159 cupsdSetStringf(&con->options, " %s %s", filename, options);
4160 else
4161 cupsdSetStringf(&con->options, " %s", filename);
4162
4163 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4164 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4165 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4166 type ? type->super : "unknown",
4167 type ? type->type : "unknown");
4168 return (1);
4169 }
4170 #endif /* HAVE_PHP */
4171 #ifdef HAVE_PYTHON
4172 else if (!strcasecmp(type->type, "x-httpd-python"))
4173 {
4174 /*
4175 * "application/x-httpd-python" is a Python page.
4176 */
4177
4178 cupsdSetString(&con->command, CUPS_PYTHON);
4179
4180 if (options)
4181 cupsdSetStringf(&con->options, " %s %s", filename, options);
4182 else
4183 cupsdSetStringf(&con->options, " %s", filename);
4184
4185 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4186 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4187 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4188 type ? type->super : "unknown",
4189 type ? type->type : "unknown");
4190 return (1);
4191 }
4192 #endif /* HAVE_PYTHON */
4193
4194 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4195 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4196 "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
4197 type ? type->super : "unknown",
4198 type ? type->type : "unknown");
4199 return (0);
4200 }
4201
4202
4203 /*
4204 * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
4205 */
4206
4207 static int /* O - 0 if relative, 1 if absolute */
4208 is_path_absolute(const char *path) /* I - Input path */
4209 {
4210 /*
4211 * Check for a leading slash...
4212 */
4213
4214 if (path[0] != '/')
4215 return (0);
4216
4217 /*
4218 * Check for "/.." in the path...
4219 */
4220
4221 while ((path = strstr(path, "/..")) != NULL)
4222 {
4223 if (!path[3] || path[3] == '/')
4224 return (0);
4225
4226 path ++;
4227 }
4228
4229 /*
4230 * If we haven't found any relative paths, return 1 indicating an
4231 * absolute path...
4232 */
4233
4234 return (1);
4235 }
4236
4237
4238 #ifdef HAVE_SSL
4239 /*
4240 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
4241 */
4242
4243 static int /* O - 1 on success, 0 on failure */
4244 make_certificate(cupsd_client_t *con) /* I - Client connection */
4245 {
4246 #if defined(HAVE_LIBSSL) && defined(HAVE_WAITPID)
4247 int pid, /* Process ID of command */
4248 status; /* Status of command */
4249 char command[1024], /* Command */
4250 *argv[12], /* Command-line arguments */
4251 *envp[MAX_ENV + 1], /* Environment variables */
4252 infofile[1024], /* Type-in information for cert */
4253 seedfile[1024]; /* Random number seed file */
4254 int envc, /* Number of environment variables */
4255 bytes; /* Bytes written */
4256 cups_file_t *fp; /* Seed/info file */
4257 int infofd; /* Info file descriptor */
4258
4259
4260 /*
4261 * Run the "openssl" command to seed the random number generator and
4262 * generate a self-signed certificate that is good for 10 years:
4263 *
4264 * openssl rand -rand seedfile 1
4265 *
4266 * openssl req -new -x509 -keyout ServerKey \
4267 * -out ServerCertificate -days 3650 -nodes
4268 *
4269 * The seeding step is crucial in ensuring that the openssl command
4270 * does not block on systems without sufficient entropy...
4271 */
4272
4273 if (!cupsFileFind("openssl", getenv("PATH"), 1, command, sizeof(command)))
4274 {
4275 cupsdLogMessage(CUPSD_LOG_ERROR,
4276 "No SSL certificate and openssl command not found!");
4277 return (0);
4278 }
4279
4280 if (access("/dev/urandom", 0))
4281 {
4282 /*
4283 * If the system doesn't provide /dev/urandom, then any random source
4284 * will probably be blocking-style, so generate some random data to
4285 * use as a seed for the certificate. Note that we have already
4286 * seeded the random number generator in cupsdInitCerts()...
4287 */
4288
4289 cupsdLogMessage(CUPSD_LOG_INFO,
4290 "Seeding the random number generator...");
4291
4292 /*
4293 * Write the seed file...
4294 */
4295
4296 if ((fp = cupsTempFile2(seedfile, sizeof(seedfile))) == NULL)
4297 {
4298 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create seed file %s - %s",
4299 seedfile, strerror(errno));
4300 return (0);
4301 }
4302
4303 for (bytes = 0; bytes < 262144; bytes ++)
4304 cupsFilePutChar(fp, random());
4305
4306 cupsFileClose(fp);
4307
4308 /*
4309 * Run the openssl command to seed its random number generator...
4310 */
4311
4312 argv[0] = "openssl";
4313 argv[1] = "rand";
4314 argv[2] = "-rand";
4315 argv[3] = seedfile;
4316 argv[4] = "1";
4317 argv[5] = NULL;
4318
4319 envc = cupsdLoadEnv(envp, MAX_ENV);
4320 envp[envc] = NULL;
4321
4322 if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL,
4323 NULL, &pid))
4324 {
4325 unlink(seedfile);
4326 return (0);
4327 }
4328
4329 while (waitpid(pid, &status, 0) < 0)
4330 if (errno != EINTR)
4331 {
4332 status = 1;
4333 break;
4334 }
4335
4336 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4337
4338 /*
4339 * Remove the seed file, as it is no longer needed...
4340 */
4341
4342 unlink(seedfile);
4343
4344 if (status)
4345 {
4346 if (WIFEXITED(status))
4347 cupsdLogMessage(CUPSD_LOG_ERROR,
4348 "Unable to seed random number generator - "
4349 "the openssl command stopped with status %d!",
4350 WEXITSTATUS(status));
4351 else
4352 cupsdLogMessage(CUPSD_LOG_ERROR,
4353 "Unable to seed random number generator - "
4354 "the openssl command crashed on signal %d!",
4355 WTERMSIG(status));
4356
4357 return (0);
4358 }
4359 }
4360
4361 /*
4362 * Create a file with the certificate information fields...
4363 *
4364 * Note: This assumes that the default questions are asked by the openssl
4365 * command...
4366 */
4367
4368 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
4369 {
4370 cupsdLogMessage(CUPSD_LOG_ERROR,
4371 "Unable to create certificate information file %s - %s",
4372 infofile, strerror(errno));
4373 return (0);
4374 }
4375
4376 cupsFilePrintf(fp, ".\n.\n.\n%s\n.\n%s\n%s\n",
4377 ServerName, ServerName, ServerAdmin);
4378 cupsFileClose(fp);
4379
4380 cupsdLogMessage(CUPSD_LOG_INFO,
4381 "Generating SSL server key and certificate...");
4382
4383 argv[0] = "openssl";
4384 argv[1] = "req";
4385 argv[2] = "-new";
4386 argv[3] = "-x509";
4387 argv[4] = "-keyout";
4388 argv[5] = ServerKey;
4389 argv[6] = "-out";
4390 argv[7] = ServerCertificate;
4391 argv[8] = "-days";
4392 argv[9] = "3650";
4393 argv[10] = "-nodes";
4394 argv[11] = NULL;
4395
4396 cupsdLoadEnv(envp, MAX_ENV);
4397
4398 infofd = open(infofile, O_RDONLY);
4399
4400 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
4401 NULL, &pid))
4402 {
4403 close(infofd);
4404 unlink(infofile);
4405 return (0);
4406 }
4407
4408 close(infofd);
4409 unlink(infofile);
4410
4411 while (waitpid(pid, &status, 0) < 0)
4412 if (errno != EINTR)
4413 {
4414 status = 1;
4415 break;
4416 }
4417
4418 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4419
4420 if (status)
4421 {
4422 if (WIFEXITED(status))
4423 cupsdLogMessage(CUPSD_LOG_ERROR,
4424 "Unable to create SSL server key and certificate - "
4425 "the openssl command stopped with status %d!",
4426 WEXITSTATUS(status));
4427 else
4428 cupsdLogMessage(CUPSD_LOG_ERROR,
4429 "Unable to create SSL server key and certificate - "
4430 "the openssl command crashed on signal %d!",
4431 WTERMSIG(status));
4432 }
4433 else
4434 {
4435 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
4436 ServerKey);
4437 cupsdLogMessage(CUPSD_LOG_INFO,
4438 "Created SSL server certificate file \"%s\"...",
4439 ServerCertificate);
4440 }
4441
4442 return (!status);
4443
4444 #elif defined(HAVE_GNUTLS)
4445 gnutls_x509_crt crt; /* Self-signed certificate */
4446 gnutls_x509_privkey key; /* Encryption key */
4447 cups_lang_t *language; /* Default language info */
4448 cups_file_t *fp; /* Key/cert file */
4449 unsigned char buffer[8192]; /* Buffer for x509 data */
4450 size_t bytes; /* Number of bytes of data */
4451 unsigned char serial[4]; /* Serial number buffer */
4452 time_t curtime; /* Current time */
4453 int result; /* Result of GNU TLS calls */
4454
4455
4456 /*
4457 * Create the encryption key...
4458 */
4459
4460 cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key...");
4461
4462 gnutls_x509_privkey_init(&key);
4463 gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
4464
4465 /*
4466 * Save it...
4467 */
4468
4469 bytes = sizeof(buffer);
4470
4471 if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM,
4472 buffer, &bytes)) < 0)
4473 {
4474 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s",
4475 gnutls_strerror(result));
4476 gnutls_x509_privkey_deinit(key);
4477 return (0);
4478 }
4479 else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL)
4480 {
4481 cupsFileWrite(fp, (char *)buffer, bytes);
4482 cupsFileClose(fp);
4483
4484 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
4485 ServerKey);
4486 }
4487 else
4488 {
4489 cupsdLogMessage(CUPSD_LOG_ERROR,
4490 "Unable to create SSL server key file \"%s\" - %s",
4491 ServerKey, strerror(errno));
4492 gnutls_x509_privkey_deinit(key);
4493 return (0);
4494 }
4495
4496 /*
4497 * Create the self-signed certificate...
4498 */
4499
4500 cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate...");
4501
4502 language = cupsLangDefault();
4503 curtime = time(NULL);
4504 serial[0] = curtime >> 24;
4505 serial[1] = curtime >> 16;
4506 serial[2] = curtime >> 8;
4507 serial[3] = curtime;
4508
4509 gnutls_x509_crt_init(&crt);
4510 if (strlen(language->language) == 5)
4511 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
4512 language->language + 3, 2);
4513 else
4514 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
4515 "US", 2);
4516 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
4517 ServerName, strlen(ServerName));
4518 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
4519 ServerName, strlen(ServerName));
4520 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
4521 0, "Unknown", 7);
4522 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
4523 "Unknown", 7);
4524 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
4525 "Unknown", 7);
4526 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
4527 ServerAdmin, strlen(ServerAdmin));
4528 gnutls_x509_crt_set_key(crt, key);
4529 gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
4530 gnutls_x509_crt_set_activation_time(crt, curtime);
4531 gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
4532 gnutls_x509_crt_set_ca_status(crt, 0);
4533 gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME,
4534 ServerName);
4535 gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
4536 gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
4537 gnutls_x509_crt_set_version(crt, 3);
4538
4539 bytes = sizeof(buffer);
4540 if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
4541 gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
4542
4543 gnutls_x509_crt_sign(crt, crt, key);
4544
4545 /*
4546 * Save it...
4547 */
4548
4549 bytes = sizeof(buffer);
4550 if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM,
4551 buffer, &bytes)) < 0)
4552 cupsdLogMessage(CUPSD_LOG_ERROR,
4553 "Unable to export SSL server certificate - %s",
4554 gnutls_strerror(result));
4555 else if ((fp = cupsFileOpen(ServerCertificate, "w")) != NULL)
4556 {
4557 cupsFileWrite(fp, (char *)buffer, bytes);
4558 cupsFileClose(fp);
4559
4560 cupsdLogMessage(CUPSD_LOG_INFO,
4561 "Created SSL server certificate file \"%s\"...",
4562 ServerCertificate);
4563 }
4564 else
4565 cupsdLogMessage(CUPSD_LOG_ERROR,
4566 "Unable to create SSL server certificate file \"%s\" - %s",
4567 ServerCertificate, strerror(errno));
4568
4569 /*
4570 * Cleanup...
4571 */
4572
4573 gnutls_x509_crt_deinit(crt);
4574 gnutls_x509_privkey_deinit(key);
4575
4576 return (1);
4577
4578 #elif defined(HAVE_CDSASSL) && defined(HAVE_WAITPID)
4579 int pid, /* Process ID of command */
4580 status; /* Status of command */
4581 char command[1024], /* Command */
4582 *argv[4], /* Command-line arguments */
4583 *envp[MAX_ENV + 1], /* Environment variables */
4584 keychain[1024], /* Keychain argument */
4585 infofile[1024], /* Type-in information for cert */
4586 localname[1024], /* Local hostname */
4587 *servername; /* Name of server in cert */
4588 cups_file_t *fp; /* Seed/info file */
4589 int infofd; /* Info file descriptor */
4590
4591
4592 if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
4593 {
4594 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
4595 servername = localname;
4596 }
4597 else
4598 servername = con->servername;
4599
4600 /*
4601 * Run the "certtool" command to generate a self-signed certificate...
4602 */
4603
4604 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
4605 {
4606 cupsdLogMessage(CUPSD_LOG_ERROR,
4607 "No SSL certificate and certtool command not found!");
4608 return (0);
4609 }
4610
4611 /*
4612 * Create a file with the certificate information fields...
4613 *
4614 * Note: This assumes that the default questions are asked by the certtool
4615 * command...
4616 */
4617
4618 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
4619 {
4620 cupsdLogMessage(CUPSD_LOG_ERROR,
4621 "Unable to create certificate information file %s - %s",
4622 infofile, strerror(errno));
4623 return (0);
4624 }
4625
4626 cupsFilePrintf(fp, "%s\nr\n\ny\nb\ns\ny\n%s\n\n\n\n\n%s\ny\n",
4627 servername, servername, ServerAdmin);
4628 cupsFileClose(fp);
4629
4630 cupsdLogMessage(CUPSD_LOG_INFO,
4631 "Generating SSL server key and certificate...");
4632
4633 snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
4634
4635 argv[0] = "certtool";
4636 argv[1] = "c";
4637 argv[2] = keychain;
4638 argv[3] = NULL;
4639
4640 cupsdLoadEnv(envp, MAX_ENV);
4641
4642 infofd = open(infofile, O_RDONLY);
4643
4644 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
4645 NULL, &pid))
4646 {
4647 close(infofd);
4648 unlink(infofile);
4649 return (0);
4650 }
4651
4652 close(infofd);
4653 unlink(infofile);
4654
4655 while (waitpid(pid, &status, 0) < 0)
4656 if (errno != EINTR)
4657 {
4658 status = 1;
4659 break;
4660 }
4661
4662 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4663
4664 if (status)
4665 {
4666 if (WIFEXITED(status))
4667 cupsdLogMessage(CUPSD_LOG_ERROR,
4668 "Unable to create SSL server key and certificate - "
4669 "the certtool command stopped with status %d!",
4670 WEXITSTATUS(status));
4671 else
4672 cupsdLogMessage(CUPSD_LOG_ERROR,
4673 "Unable to create SSL server key and certificate - "
4674 "the certtool command crashed on signal %d!",
4675 WTERMSIG(status));
4676 }
4677 else
4678 {
4679 cupsdLogMessage(CUPSD_LOG_INFO,
4680 "Created SSL server certificate file \"%s\"...",
4681 ServerCertificate);
4682 }
4683
4684 return (!status);
4685
4686 #else
4687 return (0);
4688 #endif /* HAVE_LIBSSL && HAVE_WAITPID */
4689 }
4690 #endif /* HAVE_SSL */
4691
4692
4693 /*
4694 * 'pipe_command()' - Pipe the output of a command to the remote client.
4695 */
4696
4697 static int /* O - Process ID */
4698 pipe_command(cupsd_client_t *con, /* I - Client connection */
4699 int infile, /* I - Standard input for command */
4700 int *outfile, /* O - Standard output for command */
4701 char *command, /* I - Command to run */
4702 char *options, /* I - Options for command */
4703 int root) /* I - Run as root? */
4704 {
4705 int i; /* Looping var */
4706 int pid; /* Process ID */
4707 char *commptr, /* Command string pointer */
4708 commch; /* Command string character */
4709 char *uriptr; /* URI string pointer */
4710 int fds[2]; /* Pipe FDs */
4711 int argc; /* Number of arguments */
4712 int envc; /* Number of environment variables */
4713 char argbuf[10240], /* Argument buffer */
4714 *argv[100], /* Argument strings */
4715 *envp[MAX_ENV + 20]; /* Environment variables */
4716 char auth_type[256], /* AUTH_TYPE environment variable */
4717 content_length[1024], /* CONTENT_LENGTH environment variable */
4718 content_type[1024], /* CONTENT_TYPE environment variable */
4719 http_cookie[32768], /* HTTP_COOKIE environment variable */
4720 http_referer[1024], /* HTTP_REFERER environment variable */
4721 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
4722 lang[1024], /* LANG environment variable */
4723 path_info[1024], /* PATH_INFO environment variable */
4724 remote_addr[1024], /* REMOTE_ADDR environment variable */
4725 remote_host[1024], /* REMOTE_HOST environment variable */
4726 remote_user[1024], /* REMOTE_USER environment variable */
4727 script_filename[1024], /* SCRIPT_FILENAME environment variable */
4728 script_name[1024], /* SCRIPT_NAME environment variable */
4729 server_name[1024], /* SERVER_NAME environment variable */
4730 server_port[1024]; /* SERVER_PORT environment variable */
4731 ipp_attribute_t *attr; /* attributes-natural-language attribute */
4732 void *ccache = NULL; /* Kerberos credentials */
4733
4734
4735 /*
4736 * Parse a copy of the options string, which is of the form:
4737 *
4738 * argument+argument+argument
4739 * ?argument+argument+argument
4740 * param=value&param=value
4741 * ?param=value&param=value
4742 * /name?argument+argument+argument
4743 * /name?param=value&param=value
4744 *
4745 * If the string contains an "=" character after the initial name,
4746 * then we treat it as a HTTP GET form request and make a copy of
4747 * the remaining string for the environment variable.
4748 *
4749 * The string is always parsed out as command-line arguments, to
4750 * be consistent with Apache...
4751 */
4752
4753 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4754 "pipe_command(con=%p(%d), infile=%d, outfile=%p, "
4755 "command=\"%s\", options=\"%s\", root=%d)",
4756 con, con->http.fd, infile, outfile, command,
4757 options ? options : "(null)", root);
4758
4759 argv[0] = command;
4760
4761 if (options)
4762 {
4763 commptr = options;
4764 if (*commptr == ' ')
4765 commptr ++;
4766 strlcpy(argbuf, commptr, sizeof(argbuf));
4767 }
4768 else
4769 argbuf[0] = '\0';
4770
4771 if (argbuf[0] == '/')
4772 {
4773 /*
4774 * Found some trailing path information, set PATH_INFO...
4775 */
4776
4777 if ((commptr = strchr(argbuf, '?')) == NULL)
4778 commptr = argbuf + strlen(argbuf);
4779
4780 commch = *commptr;
4781 *commptr = '\0';
4782 snprintf(path_info, sizeof(path_info), "PATH_INFO=%s", argbuf);
4783 *commptr = commch;
4784 }
4785 else
4786 {
4787 commptr = argbuf;
4788 path_info[0] = '\0';
4789
4790 if (*commptr == ' ')
4791 commptr ++;
4792 }
4793
4794 if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
4795 {
4796 commptr ++;
4797 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr);
4798 }
4799
4800 argc = 1;
4801
4802 if (*commptr)
4803 {
4804 argv[argc ++] = commptr;
4805
4806 for (; *commptr && argc < 99; commptr ++)
4807 {
4808 /*
4809 * Break arguments whenever we see a + or space...
4810 */
4811
4812 if (*commptr == ' ' || *commptr == '+')
4813 {
4814 while (*commptr == ' ' || *commptr == '+')
4815 *commptr++ = '\0';
4816
4817 /*
4818 * If we don't have a blank string, save it as another argument...
4819 */
4820
4821 if (*commptr)
4822 {
4823 argv[argc] = commptr;
4824 argc ++;
4825 }
4826 else
4827 break;
4828 }
4829 else if (*commptr == '%' && isxdigit(commptr[1] & 255) &&
4830 isxdigit(commptr[2] & 255))
4831 {
4832 /*
4833 * Convert the %xx notation to the individual character.
4834 */
4835
4836 if (commptr[1] >= '0' && commptr[1] <= '9')
4837 *commptr = (commptr[1] - '0') << 4;
4838 else
4839 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
4840
4841 if (commptr[2] >= '0' && commptr[2] <= '9')
4842 *commptr |= commptr[2] - '0';
4843 else
4844 *commptr |= tolower(commptr[2]) - 'a' + 10;
4845
4846 _cups_strcpy(commptr + 1, commptr + 3);
4847
4848 /*
4849 * Check for a %00 and break if that is the case...
4850 */
4851
4852 if (!*commptr)
4853 break;
4854 }
4855 }
4856 }
4857
4858 argv[argc] = NULL;
4859
4860 /*
4861 * Setup the environment variables as needed...
4862 */
4863
4864 if (con->username[0])
4865 {
4866 snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s",
4867 httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION));
4868
4869 if ((uriptr = strchr(auth_type + 10, ' ')) != NULL)
4870 *uriptr = '\0';
4871 }
4872 else
4873 auth_type[0] = '\0';
4874
4875 if (con->request &&
4876 (attr = ippFindAttribute(con->request, "attributes-natural-language",
4877 IPP_TAG_LANGUAGE)) != NULL)
4878 {
4879 switch (strlen(attr->values[0].string.text))
4880 {
4881 default :
4882 /*
4883 * This is an unknown or badly formatted language code; use
4884 * the POSIX locale...
4885 */
4886
4887 strcpy(lang, "LANG=C");
4888 break;
4889
4890 case 2 :
4891 /*
4892 * Just the language code (ll)...
4893 */
4894
4895 snprintf(lang, sizeof(lang), "LANG=%s.UTF8",
4896 attr->values[0].string.text);
4897 break;
4898
4899 case 5 :
4900 /*
4901 * Language and country code (ll-cc)...
4902 */
4903
4904 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF8",
4905 attr->values[0].string.text[0],
4906 attr->values[0].string.text[1],
4907 toupper(attr->values[0].string.text[3] & 255),
4908 toupper(attr->values[0].string.text[4] & 255));
4909 break;
4910 }
4911 }
4912 else if (con->language)
4913 snprintf(lang, sizeof(lang), "LANG=%s.UTF8", con->language->language);
4914 else
4915 strcpy(lang, "LANG=C");
4916
4917 strcpy(remote_addr, "REMOTE_ADDR=");
4918 httpAddrString(con->http.hostaddr, remote_addr + 12,
4919 sizeof(remote_addr) - 12);
4920
4921 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s",
4922 con->http.hostname);
4923
4924 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
4925 if ((uriptr = strchr(script_name, '?')) != NULL)
4926 *uriptr = '\0';
4927
4928 snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s",
4929 DocumentRoot, script_name + 12);
4930
4931 sprintf(server_port, "SERVER_PORT=%d", con->serverport);
4932
4933 if (con->http.fields[HTTP_FIELD_HOST][0])
4934 {
4935 char *nameptr; /* Pointer to ":port" */
4936
4937 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4938 con->http.fields[HTTP_FIELD_HOST]);
4939 if ((nameptr = strrchr(server_name, ':')) != NULL && !strchr(nameptr, ']'))
4940 *nameptr = '\0'; /* Strip trailing ":port" */
4941 }
4942 else
4943 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4944 con->servername);
4945
4946 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
4947
4948 if (auth_type[0])
4949 envp[envc ++] = auth_type;
4950
4951 envp[envc ++] = lang;
4952 envp[envc ++] = "REDIRECT_STATUS=1";
4953 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
4954 envp[envc ++] = server_name;
4955 envp[envc ++] = server_port;
4956 envp[envc ++] = remote_addr;
4957 envp[envc ++] = remote_host;
4958 envp[envc ++] = script_name;
4959 envp[envc ++] = script_filename;
4960
4961 if (path_info[0])
4962 envp[envc ++] = path_info;
4963
4964 if (con->username[0])
4965 {
4966 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
4967
4968 envp[envc ++] = remote_user;
4969
4970 /*
4971 * Save Kerberos credentials, if any...
4972 */
4973
4974 #ifdef HAVE_GSSAPI
4975 if (con->gss_creds)
4976 ccache = cupsdCopyKrb5Creds(con);
4977 #endif /* HAVE_GSSAPI */
4978 }
4979
4980 if (con->http.version == HTTP_1_1)
4981 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
4982 else if (con->http.version == HTTP_1_0)
4983 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
4984 else
4985 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
4986
4987 if (con->http.cookie)
4988 {
4989 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
4990 con->http.cookie);
4991 envp[envc ++] = http_cookie;
4992 }
4993
4994 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
4995 {
4996 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
4997 con->http.fields[HTTP_FIELD_USER_AGENT]);
4998 envp[envc ++] = http_user_agent;
4999 }
5000
5001 if (con->http.fields[HTTP_FIELD_REFERER][0])
5002 {
5003 snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s",
5004 con->http.fields[HTTP_FIELD_REFERER]);
5005 envp[envc ++] = http_referer;
5006 }
5007
5008 if (con->operation == HTTP_GET)
5009 {
5010 envp[envc ++] = "REQUEST_METHOD=GET";
5011
5012 if (con->query_string)
5013 {
5014 /*
5015 * Add GET form variables after ?...
5016 */
5017
5018 envp[envc ++] = con->query_string;
5019 }
5020 else
5021 envp[envc ++] = "QUERY_STRING=";
5022 }
5023 else
5024 {
5025 sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT,
5026 CUPS_LLCAST con->bytes);
5027 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
5028 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
5029
5030 envp[envc ++] = "REQUEST_METHOD=POST";
5031 envp[envc ++] = content_length;
5032 envp[envc ++] = content_type;
5033 }
5034
5035 /*
5036 * Tell the CGI if we are using encryption...
5037 */
5038
5039 if (con->http.tls)
5040 envp[envc ++] = "HTTPS=ON";
5041
5042 /*
5043 * Terminate the environment array...
5044 */
5045
5046 envp[envc] = NULL;
5047
5048 if (LogLevel >= CUPSD_LOG_DEBUG)
5049 {
5050 for (i = 0; i < argc; i ++)
5051 cupsdLogMessage(CUPSD_LOG_DEBUG,
5052 "[CGI] argv[%d] = \"%s\"", i, argv[i]);
5053 for (i = 0; i < envc; i ++)
5054 cupsdLogMessage(CUPSD_LOG_DEBUG,
5055 "[CGI] envp[%d] = \"%s\"", i, envp[i]);
5056 }
5057
5058 /*
5059 * Create a pipe for the output...
5060 */
5061
5062 if (cupsdOpenPipe(fds))
5063 {
5064 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s",
5065 argv[0], strerror(errno));
5066 return (0);
5067 }
5068
5069 /*
5070 * Then execute the command...
5071 */
5072
5073 if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
5074 -1, -1, root, DefaultProfile, NULL, &pid) < 0)
5075 {
5076 /*
5077 * Error - can't fork!
5078 */
5079
5080 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0],
5081 strerror(errno));
5082
5083 cupsdClosePipe(fds);
5084 pid = 0;
5085 }
5086 else
5087 {
5088 /*
5089 * Fork successful - return the PID...
5090 */
5091
5092 if (con->username[0])
5093 cupsdAddCert(pid, con->username, ccache);
5094
5095 cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] Started %s (PID %d)", command, pid);
5096
5097 *outfile = fds[0];
5098 close(fds[1]);
5099 }
5100
5101 return (pid);
5102 }
5103
5104
5105 /*
5106 * 'valid_host()' - Is the Host: field valid?
5107 */
5108
5109 static int /* O - 1 if valid, 0 if not */
5110 valid_host(cupsd_client_t *con) /* I - Client connection */
5111 {
5112 cupsd_alias_t *a; /* Current alias */
5113 cupsd_netif_t *netif; /* Current network interface */
5114 const char *host, /* Host field */
5115 *end; /* End character */
5116
5117
5118 host = con->http.fields[HTTP_FIELD_HOST];
5119
5120 if (httpAddrLocalhost(con->http.hostaddr))
5121 {
5122 /*
5123 * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical
5124 * addresses when accessing CUPS via the loopback interface...
5125 */
5126
5127 return (!strcasecmp(host, "localhost") ||
5128 !strncasecmp(host, "localhost:", 10) ||
5129 !strcasecmp(host, "localhost.") ||
5130 !strncasecmp(host, "localhost.:", 11) ||
5131 #ifdef __linux
5132 !strcasecmp(host, "localhost.localdomain") ||
5133 !strncasecmp(host, "localhost.localdomain:", 22) ||
5134 #endif /* __linux */
5135 !strcmp(host, "127.0.0.1") ||
5136 !strncmp(host, "127.0.0.1:", 10) ||
5137 !strcmp(host, "[::1]") ||
5138 !strncmp(host, "[::1]:", 6));
5139 }
5140
5141 #ifdef HAVE_DNSSD
5142 /*
5143 * Check if the hostname is something.local (Bonjour); if so, allow it.
5144 */
5145
5146 if ((end = strrchr(host, '.')) != NULL &&
5147 (!strcasecmp(end, ".local") || !strncasecmp(end, ".local:", 7) ||
5148 !strcasecmp(end, ".local.") || !strncasecmp(end, ".local.:", 8)))
5149 return (1);
5150 #endif /* HAVE_DNSSD */
5151
5152 /*
5153 * Check if the hostname is an IP address...
5154 */
5155
5156 if (isdigit(*host & 255) || *host == '[')
5157 {
5158 /*
5159 * Possible IPv4/IPv6 address...
5160 */
5161
5162 char temp[1024], /* Temporary string */
5163 *ptr; /* Pointer into temporary string */
5164 http_addrlist_t *addrlist; /* List of addresses */
5165
5166
5167 strlcpy(temp, host, sizeof(temp));
5168 if ((ptr = strrchr(temp, ':')) != NULL && !strchr(ptr, ']'))
5169 *ptr = '\0'; /* Strip :port from host value */
5170
5171 if ((addrlist = httpAddrGetList(temp, AF_UNSPEC, NULL)) != NULL)
5172 {
5173 /*
5174 * Good IPv4/IPv6 address...
5175 */
5176
5177 httpAddrFreeList(addrlist);
5178 return (1);
5179 }
5180 }
5181
5182 /*
5183 * Check for (alias) name matches...
5184 */
5185
5186 for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias);
5187 a;
5188 a = (cupsd_alias_t *)cupsArrayNext(ServerAlias))
5189 {
5190 /*
5191 * "ServerAlias *" allows all host values through...
5192 */
5193
5194 if (!strcmp(a->name, "*"))
5195 return (1);
5196
5197 if (!strncasecmp(host, a->name, a->namelen))
5198 {
5199 /*
5200 * Prefix matches; check the character at the end - it must be ":", ".",
5201 * ".:", or nul...
5202 */
5203
5204 end = host + a->namelen;
5205
5206 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5207 return (1);
5208 }
5209 }
5210
5211 #ifdef HAVE_DNSSD
5212 for (a = (cupsd_alias_t *)cupsArrayFirst(DNSSDAlias);
5213 a;
5214 a = (cupsd_alias_t *)cupsArrayNext(DNSSDAlias))
5215 {
5216 /*
5217 * "ServerAlias *" allows all host values through...
5218 */
5219
5220 if (!strcmp(a->name, "*"))
5221 return (1);
5222
5223 if (!strncasecmp(host, a->name, a->namelen))
5224 {
5225 /*
5226 * Prefix matches; check the character at the end - it must be ":", ".",
5227 * ".:", or nul...
5228 */
5229
5230 end = host + a->namelen;
5231
5232 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5233 return (1);
5234 }
5235 }
5236 #endif /* HAVE_DNSSD */
5237
5238 /*
5239 * Check for interface hostname matches...
5240 */
5241
5242 for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
5243 netif;
5244 netif = (cupsd_netif_t *)cupsArrayNext(NetIFList))
5245 {
5246 if (!strncasecmp(host, netif->hostname, netif->hostlen))
5247 {
5248 /*
5249 * Prefix matches; check the character at the end - it must be ":", ".",
5250 * ".:", or nul...
5251 */
5252
5253 end = host + netif->hostlen;
5254
5255 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5256 return (1);
5257 }
5258 }
5259
5260 return (0);
5261 }
5262
5263
5264 /*
5265 * 'write_file()' - Send a file via HTTP.
5266 */
5267
5268 static int /* O - 0 on failure, 1 on success */
5269 write_file(cupsd_client_t *con, /* I - Client connection */
5270 http_status_t code, /* I - HTTP status */
5271 char *filename, /* I - Filename */
5272 char *type, /* I - File type */
5273 struct stat *filestats) /* O - File information */
5274 {
5275 con->file = open(filename, O_RDONLY);
5276
5277 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5278 "write_file(con=%p(%d), code=%d, filename=\"%s\" (%d), "
5279 "type=\"%s\", filestats=%p)", con, con->http.fd,
5280 code, filename, con->file, type ? type : "(null)", filestats);
5281
5282 if (con->file < 0)
5283 return (0);
5284
5285 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
5286
5287 con->pipe_pid = 0;
5288
5289 if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
5290 return (0);
5291
5292 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
5293 httpGetDateString(filestats->st_mtime)) < 0)
5294 return (0);
5295 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n",
5296 CUPS_LLCAST filestats->st_size) < 0)
5297 return (0);
5298 if (httpPrintf(HTTP(con), "\r\n") < 0)
5299 return (0);
5300
5301 if (cupsdFlushHeader(con) < 0)
5302 return (0);
5303
5304 con->http.data_encoding = HTTP_ENCODE_LENGTH;
5305 con->http.data_remaining = filestats->st_size;
5306
5307 if (con->http.data_remaining <= INT_MAX)
5308 con->http._data_remaining = con->http.data_remaining;
5309 else
5310 con->http._data_remaining = INT_MAX;
5311
5312 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
5313 (cupsd_selfunc_t)cupsdWriteClient, con);
5314
5315 return (1);
5316 }
5317
5318
5319 /*
5320 * 'write_pipe()' - Flag that data is available on the CGI pipe.
5321 */
5322
5323 static void
5324 write_pipe(cupsd_client_t *con) /* I - Client connection */
5325 {
5326 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5327 "write_pipe(con=%p(%d)) CGI output on fd %d",
5328 con, con->http.fd, con->file);
5329
5330 con->file_ready = 1;
5331
5332 cupsdRemoveSelect(con->file);
5333 cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
5334 }
5335
5336
5337 /*
5338 * End of "$Id: client.c 7950 2008-09-17 00:21:59Z mike $".
5339 */