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