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