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