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