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