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