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