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