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