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