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