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