]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/client.c
Merge changes from CUPS 1.5svn-r9675.
[thirdparty/cups.git] / scheduler / client.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: client.c 7950 2008-09-17 00:21:59Z mike $"
ef416fc2 3 *
10d09e33 4 * Client routines for the CUPS scheduler.
ef416fc2 5 *
0268488e 6 * Copyright 2007-2011 by Apple Inc.
f7deaa1a 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * This file contains Kerberos support code, copyright 2006 by
10 * Jelmer Vernooij.
ef416fc2 11 *
12 * These coded instructions, statements, and computer programs are the
bc44d920 13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
15 * which should have been included with this file. If this file is
16 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 17 *
18 * Contents:
19 *
94da7e34
MS
20 * cupsdAcceptClient() - Accept a new client.
21 * cupsdCloseAllClients() - Close all remote clients immediately.
22 * cupsdCloseClient() - Close a remote client.
23 * cupsdFlushHeader() - Flush the header fields to the client.
24 * cupsdReadClient() - Read data from a client.
25 * cupsdSendCommand() - Send output from a command via HTTP.
26 * cupsdSendError() - Send an error message via HTTP.
27 * cupsdSendHeader() - Send an HTTP request.
28 * cupsdUpdateCGI() - Read status messages from CGI scripts and
29 * programs.
30 * cupsdWriteClient() - Write data to a client as needed.
31 * check_if_modified() - Decode an "If-Modified-Since" line.
32 * compare_clients() - Compare two client connections.
33 * data_ready() - Check whether data is available from a client.
34 * encrypt_client() - Enable encryption for the client...
35 * get_cdsa_certificate() - Get a SSL/TLS certificate from the System
36 * keychain.
37 * get_file() - Get a filename and state info.
38 * install_conf_file() - Install a configuration file.
39 * is_cgi() - Is the resource a CGI script/program?
40 * is_path_absolute() - Is a path absolute and free of relative elements
41 * (i.e. "..").
42 * make_certificate() - Make a self-signed SSL/TLS certificate.
43 * pipe_command() - Pipe the output of a command to the remote client.
e07d4801 44 * valid_host() - Is the Host: field valid?
94da7e34
MS
45 * write_file() - Send a file via HTTP.
46 * write_pipe() - Flag that data is available on the CGI pipe.
ef416fc2 47 */
48
49/*
50 * Include necessary headers...
51 */
52
ef416fc2 53#include "cupsd.h"
54
749b1e90
MS
55#ifdef HAVE_TCPD_H
56# include <tcpd.h>
57#endif /* HAVE_TCPD_H */
58
ef416fc2 59
60/*
61 * Local functions...
62 */
63
64static int check_if_modified(cupsd_client_t *con,
65 struct stat *filestats);
3dfe78b3
MS
66static int compare_clients(cupsd_client_t *a, cupsd_client_t *b,
67 void *data);
ee571f26 68static int data_ready(cupsd_client_t *con);
80ca4592 69#ifdef HAVE_SSL
e1d6a774 70static int encrypt_client(cupsd_client_t *con);
80ca4592 71#endif /* HAVE_SSL */
ef416fc2 72#ifdef HAVE_CDSASSL
b86bc4cf 73static CFArrayRef get_cdsa_certificate(cupsd_client_t *con);
ef416fc2 74#endif /* HAVE_CDSASSL */
f7deaa1a 75static char *get_file(cupsd_client_t *con, struct stat *filestats,
ef416fc2 76 char *filename, int len);
77static http_status_t install_conf_file(cupsd_client_t *con);
e1d6a774 78static int is_cgi(cupsd_client_t *con, const char *filename,
79 struct stat *filestats, mime_type_t *type);
ef416fc2 80static int is_path_absolute(const char *path);
8ca02f3c 81#ifdef HAVE_SSL
b86bc4cf 82static int make_certificate(cupsd_client_t *con);
8ca02f3c 83#endif /* HAVE_SSL */
ef416fc2 84static int pipe_command(cupsd_client_t *con, int infile, int *outfile,
85 char *command, char *options, int root);
e07d4801 86static int valid_host(cupsd_client_t *con);
a74454a7 87static int write_file(cupsd_client_t *con, http_status_t code,
88 char *filename, char *type,
89 struct stat *filestats);
f899b121 90static void write_pipe(cupsd_client_t *con);
ef416fc2 91
92
93/*
94 * 'cupsdAcceptClient()' - Accept a new client.
95 */
96
97void
98cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
99{
ef416fc2 100 int count; /* Count of connections on a host */
101 int val; /* Parameter value */
bd7854cb 102 cupsd_client_t *con, /* New client pointer */
103 *tempcon; /* Temporary client pointer */
ef416fc2 104 http_addrlist_t *addrlist, /* List of adddresses for host */
105 *addr; /* Current address */
106 socklen_t addrlen; /* Length of address */
107 char *hostname; /* Hostname for address */
108 http_addr_t temp; /* Temporary address variable */
109 static time_t last_dos = 0; /* Time of last DoS attack */
749b1e90
MS
110#ifdef HAVE_TCPD_H
111 struct request_info wrap_req; /* TCP wrappers request information */
112#endif /* HAVE_TCPD_H */
ef416fc2 113
114
115 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1 116 "cupsdAcceptClient(lis=%p(%d)) Clients=%d",
bd7854cb 117 lis, lis->fd, cupsArrayCount(Clients));
ef416fc2 118
119 /*
120 * Make sure we don't have a full set of clients already...
121 */
122
bd7854cb 123 if (cupsArrayCount(Clients) == MaxClients)
ef416fc2 124 return;
125
126 /*
127 * Get a pointer to the next available client...
128 */
129
bd7854cb 130 if (!Clients)
131 Clients = cupsArrayNew(NULL, NULL);
132
133 if (!Clients)
91c84a35
MS
134 {
135 cupsdLogMessage(CUPSD_LOG_ERROR,
3dfe78b3
MS
136 "Unable to allocate memory for clients array!");
137 cupsdPauseListening();
138 return;
139 }
140
141 if (!ActiveClients)
142 ActiveClients = cupsArrayNew((cups_array_func_t)compare_clients, NULL);
143
144 if (!ActiveClients)
145 {
146 cupsdLogMessage(CUPSD_LOG_ERROR,
147 "Unable to allocate memory for active clients array!");
91c84a35 148 cupsdPauseListening();
bd7854cb 149 return;
91c84a35 150 }
bd7854cb 151
91c84a35
MS
152 if ((con = calloc(1, sizeof(cupsd_client_t))) == NULL)
153 {
154 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for client!");
155 cupsdPauseListening();
156 return;
157 }
ef416fc2 158
ef416fc2 159 con->http.activity = time(NULL);
160 con->file = -1;
161 con->http.hostaddr = &(con->clientaddr);
162
163 /*
164 * Accept the client and get the remote address...
165 */
166
167 addrlen = sizeof(http_addr_t);
168
169 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)con->http.hostaddr,
170 &addrlen)) < 0)
171 {
76cd9e37
MS
172 if (errno == ENFILE || errno == EMFILE)
173 cupsdPauseListening();
174
ef416fc2 175 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.",
176 strerror(errno));
bd7854cb 177 free(con);
76cd9e37 178
ef416fc2 179 return;
180 }
181
182#ifdef AF_INET6
183 if (lis->address.addr.sa_family == AF_INET6)
184 {
185 /*
186 * Save the connected port number...
187 */
188
189 con->http.hostaddr->ipv6.sin6_port = lis->address.ipv6.sin6_port;
190
191 /*
192 * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we
193 * can more easily use...
194 */
195
196 if (con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0] == 0 &&
197 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1] == 0 &&
198 ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]) == 0xffff)
199 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2] = 0;
200 }
201 else
202#endif /* AF_INET6 */
203 if (lis->address.addr.sa_family == AF_INET)
204 con->http.hostaddr->ipv4.sin_port = lis->address.ipv4.sin_port;
205
206 /*
207 * Check the number of clients on the same address...
208 */
209
bd7854cb 210 for (count = 0, tempcon = (cupsd_client_t *)cupsArrayFirst(Clients);
211 tempcon;
212 tempcon = (cupsd_client_t *)cupsArrayNext(Clients))
213 if (httpAddrEqual(tempcon->http.hostaddr, con->http.hostaddr))
ef416fc2 214 {
215 count ++;
216 if (count >= MaxClientsPerHost)
217 break;
218 }
219
220 if (count >= MaxClientsPerHost)
221 {
222 if ((time(NULL) - last_dos) >= 60)
223 {
224 last_dos = time(NULL);
225 cupsdLogMessage(CUPSD_LOG_WARN,
bd7854cb 226 "Possible DoS attack - more than %d clients connecting "
227 "from %s!",
749b1e90
MS
228 MaxClientsPerHost,
229 httpAddrString(con->http.hostaddr, con->http.hostname,
230 sizeof(con->http.hostname)));
ef416fc2 231 }
232
233#ifdef WIN32
234 closesocket(con->http.fd);
235#else
236 close(con->http.fd);
237#endif /* WIN32 */
238
bd7854cb 239 free(con);
ef416fc2 240 return;
241 }
242
243 /*
244 * Get the hostname or format the IP address as needed...
245 */
246
247 if (httpAddrLocalhost(con->http.hostaddr))
248 {
249 /*
250 * Map accesses from the loopback interface to "localhost"...
251 */
252
253 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
254 hostname = con->http.hostname;
255 }
256 else
257 {
258 /*
259 * Map accesses from the same host to the server name.
260 */
261
d1c13e16 262 if (HostNameLookups)
ef416fc2 263 hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname,
264 sizeof(con->http.hostname));
265 else
266 {
267 hostname = NULL;
268 httpAddrString(con->http.hostaddr, con->http.hostname,
269 sizeof(con->http.hostname));
270 }
271 }
272
273 if (hostname == NULL && HostNameLookups == 2)
274 {
275 /*
276 * Can't have an unresolved IP address with double-lookups enabled...
277 */
278
ef416fc2 279#ifdef WIN32
280 closesocket(con->http.fd);
281#else
282 close(con->http.fd);
283#endif /* WIN32 */
284
285 cupsdLogMessage(CUPSD_LOG_WARN,
286 "Name lookup failed - connection from %s closed!",
287 con->http.hostname);
bd7854cb 288
289 free(con);
ef416fc2 290 return;
291 }
292
293 if (HostNameLookups == 2)
294 {
295 /*
296 * Do double lookups as needed...
297 */
298
749b1e90
MS
299 if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL))
300 != NULL)
ef416fc2 301 {
302 /*
303 * See if the hostname maps to the same IP address...
304 */
305
306 for (addr = addrlist; addr; addr = addr->next)
307 if (httpAddrEqual(con->http.hostaddr, &(addr->addr)))
308 break;
309 }
310 else
311 addr = NULL;
312
313 httpAddrFreeList(addrlist);
314
315 if (!addr)
316 {
317 /*
318 * Can't have a hostname that doesn't resolve to the same IP address
319 * with double-lookups enabled...
320 */
321
ef416fc2 322#ifdef WIN32
323 closesocket(con->http.fd);
324#else
325 close(con->http.fd);
326#endif /* WIN32 */
327
328 cupsdLogMessage(CUPSD_LOG_WARN,
329 "IP lookup failed - connection from %s closed!",
330 con->http.hostname);
bd7854cb 331 free(con);
ef416fc2 332 return;
333 }
334 }
335
749b1e90
MS
336#ifdef HAVE_TCPD_H
337 /*
338 * See if the connection is denied by TCP wrappers...
339 */
340
341 request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, con->http.fd, NULL);
342 fromhost(&wrap_req);
343
344 if (!hosts_access(&wrap_req))
345 {
749b1e90
MS
346#ifdef WIN32
347 closesocket(con->http.fd);
348#else
349 close(con->http.fd);
350#endif /* WIN32 */
351
352 cupsdLogMessage(CUPSD_LOG_WARN,
353 "Connection from %s refused by /etc/hosts.allow and "
354 "/etc/hosts.deny rules.", con->http.hostname);
355 free(con);
356 return;
357 }
358#endif /* HAVE_TCPD_H */
359
ef416fc2 360#ifdef AF_INET6
361 if (con->http.hostaddr->addr.sa_family == AF_INET6)
362 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv6)",
363 con->http.fd, con->http.hostname,
364 ntohs(con->http.hostaddr->ipv6.sin6_port));
365 else
366#endif /* AF_INET6 */
367#ifdef AF_LOCAL
368 if (con->http.hostaddr->addr.sa_family == AF_LOCAL)
369 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s (Domain)",
370 con->http.fd, con->http.hostname);
371 else
372#endif /* AF_LOCAL */
373 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv4)",
374 con->http.fd, con->http.hostname,
375 ntohs(con->http.hostaddr->ipv4.sin_port));
376
377 /*
378 * Get the local address the client connected to...
379 */
380
381 addrlen = sizeof(temp);
382 if (getsockname(con->http.fd, (struct sockaddr *)&temp, &addrlen))
383 {
384 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get local address - %s",
385 strerror(errno));
386
387 strcpy(con->servername, "localhost");
388 con->serverport = LocalPort;
389 }
390 else
391 {
392#ifdef AF_INET6
393 if (temp.addr.sa_family == AF_INET6)
394 {
e07d4801 395 if (httpAddrLocalhost(&temp))
0af14961 396 strlcpy(con->servername, "localhost", sizeof(con->servername));
f11a948a 397 else if (HostNameLookups || RemotePort)
e07d4801 398 httpAddrLookup(&temp, con->servername, sizeof(con->servername));
b19ccc9e
MS
399 else
400 httpAddrString(&temp, con->servername, sizeof(con->servername));
401
ef416fc2 402 con->serverport = ntohs(lis->address.ipv6.sin6_port);
403 }
404 else
405#endif /* AF_INET6 */
406 if (temp.addr.sa_family == AF_INET)
407 {
e07d4801 408 if (httpAddrLocalhost(&temp))
0af14961 409 strlcpy(con->servername, "localhost", sizeof(con->servername));
f11a948a 410 else if (HostNameLookups || RemotePort)
e07d4801 411 httpAddrLookup(&temp, con->servername, sizeof(con->servername));
b19ccc9e
MS
412 else
413 httpAddrString(&temp, con->servername, sizeof(con->servername));
414
ef416fc2 415 con->serverport = ntohs(lis->address.ipv4.sin_port);
416 }
417 else
418 {
419 strcpy(con->servername, "localhost");
420 con->serverport = LocalPort;
421 }
422 }
423
bd7854cb 424 cupsArrayAdd(Clients, con);
425
ef416fc2 426 /*
b9faaae1
MS
427 * Using TCP_NODELAY improves responsiveness, especially on systems with a slow
428 * loopback interface. Since we write large buffers when sending print files
429 * and requests there shouldn't be any performance penalty for this...
ef416fc2 430 */
431
432 val = 1;
f7deaa1a 433 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
ef416fc2 434
435 /*
436 * Close this file on all execs...
437 */
438
439 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
440
441 /*
b9faaae1 442 * Add the socket to the server select.
ef416fc2 443 */
444
f7deaa1a 445 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
ef416fc2 446
ef416fc2 447 /*
448 * Temporarily suspend accept()'s until we lose a client...
449 */
450
bd7854cb 451 if (cupsArrayCount(Clients) == MaxClients)
ef416fc2 452 cupsdPauseListening();
453
454#ifdef HAVE_SSL
455 /*
456 * See if we are connecting on a secure port...
457 */
458
459 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
460 {
461 /*
462 * https connection; go secure...
463 */
464
465 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
466
411affcf 467 if (!encrypt_client(con))
468 cupsdCloseClient(con);
ef416fc2 469 }
470 else
471 con->auto_ssl = 1;
472#endif /* HAVE_SSL */
473}
474
475
476/*
477 * 'cupsdCloseAllClients()' - Close all remote clients immediately.
478 */
479
480void
481cupsdCloseAllClients(void)
482{
bd7854cb 483 cupsd_client_t *con; /* Current client */
484
485
b9faaae1
MS
486 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d",
487 cupsArrayCount(Clients));
488
bd7854cb 489 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
490 con;
491 con = (cupsd_client_t *)cupsArrayNext(Clients))
49d87452
MS
492 if (cupsdCloseClient(con))
493 cupsdCloseClient(con);
ef416fc2 494}
495
496
497/*
498 * 'cupsdCloseClient()' - Close a remote client.
499 */
500
501int /* O - 1 if partial close, 0 if fully closed */
502cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */
503{
504 int partial; /* Do partial close for SSL? */
505#ifdef HAVE_LIBSSL
506 SSL_CTX *context; /* Context for encryption */
ef416fc2 507 unsigned long error; /* Error code */
508#elif defined(HAVE_GNUTLS)
89d46774 509 int error; /* Error code */
ef416fc2 510 gnutls_certificate_server_credentials *credentials;
511 /* TLS credentials */
89d46774 512# elif defined(HAVE_CDSASSL)
ef416fc2 513#endif /* HAVE_LIBSSL */
514
515
f11a948a 516 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCloseClient: %d", con->http.fd);
ef416fc2 517
518 /*
519 * Flush pending writes before closing...
520 */
521
522 httpFlushWrite(HTTP(con));
523
524 partial = 0;
525
526#ifdef HAVE_SSL
527 /*
528 * Shutdown encryption as needed...
529 */
530
531 if (con->http.tls)
532 {
533 partial = 1;
534
535# ifdef HAVE_LIBSSL
7cf5915e 536 context = SSL_get_SSL_CTX(con->http.tls);
ef416fc2 537
7cf5915e 538 switch (SSL_shutdown(con->http.tls))
ef416fc2 539 {
540 case 1 :
c934a06c
MS
541 cupsdLogMessage(CUPSD_LOG_DEBUG,
542 "SSL shutdown successful!");
ef416fc2 543 break;
544 case -1 :
545 cupsdLogMessage(CUPSD_LOG_ERROR,
c934a06c 546 "Fatal error during SSL shutdown!");
ef416fc2 547 default :
548 while ((error = ERR_get_error()) != 0)
c934a06c 549 cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s",
ef416fc2 550 ERR_error_string(error, NULL));
551 break;
552 }
553
554 SSL_CTX_free(context);
7cf5915e 555 SSL_free(con->http.tls);
ef416fc2 556
557# elif defined(HAVE_GNUTLS)
7cf5915e 558 credentials = (gnutls_certificate_server_credentials *)(con->http.tls_credentials);
ef416fc2 559
7cf5915e 560 error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
ef416fc2 561 switch (error)
562 {
563 case GNUTLS_E_SUCCESS:
c934a06c
MS
564 cupsdLogMessage(CUPSD_LOG_DEBUG,
565 "SSL shutdown successful!");
ef416fc2 566 break;
567 default:
568 cupsdLogMessage(CUPSD_LOG_ERROR,
c934a06c 569 "SSL shutdown failed: %s", gnutls_strerror(error));
ef416fc2 570 break;
571 }
572
7cf5915e 573 gnutls_deinit(con->http.tls);
ef416fc2 574 gnutls_certificate_free_credentials(*credentials);
575 free(credentials);
ef416fc2 576
577# elif defined(HAVE_CDSASSL)
7cf5915e 578 while (SSLClose(con->http.tls) == errSSLWouldBlock)
b423cd4c 579 usleep(1000);
580
7cf5915e 581 SSLDisposeContext(con->http.tls);
89d46774 582
7cf5915e
MS
583 if (con->http.tls_credentials)
584 CFRelease(con->http.tls_credentials);
89d46774 585
ef416fc2 586# endif /* HAVE_LIBSSL */
587
588 con->http.tls = NULL;
589 }
590#endif /* HAVE_SSL */
591
592 if (con->pipe_pid != 0)
593 {
594 /*
595 * Stop any CGI process...
596 */
597
ef416fc2 598 cupsdEndProcess(con->pipe_pid, 1);
599 con->pipe_pid = 0;
600 }
601
602 if (con->file >= 0)
603 {
f7deaa1a 604 cupsdRemoveSelect(con->file);
ef416fc2 605
ef416fc2 606 close(con->file);
607 con->file = -1;
608 }
609
610 /*
611 * Close the socket and clear the file from the input set for select()...
612 */
613
3dfe78b3 614 if (con->http.fd >= 0)
ef416fc2 615 {
3dfe78b3
MS
616 cupsArrayRemove(ActiveClients, con);
617 cupsdSetBusyState();
618
ef416fc2 619 if (partial)
620 {
621 /*
622 * Only do a partial close so that the encrypted client gets everything.
623 */
624
ef416fc2 625 shutdown(con->http.fd, 0);
f7deaa1a 626 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
ef416fc2 627 }
628 else
629 {
630 /*
631 * Shut the socket down fully...
632 */
633
f7deaa1a 634 cupsdRemoveSelect(con->http.fd);
ef416fc2 635 close(con->http.fd);
ef416fc2 636 con->http.fd = -1;
637 }
638 }
639
640 if (!partial)
641 {
642 /*
643 * Free memory...
644 */
645
646 if (con->http.input_set)
647 free(con->http.input_set);
648
649 httpClearCookie(HTTP(con));
9f5eb9be 650 httpClearFields(HTTP(con));
ef416fc2 651
652 cupsdClearString(&con->filename);
653 cupsdClearString(&con->command);
654 cupsdClearString(&con->options);
b86bc4cf 655 cupsdClearString(&con->query_string);
ef416fc2 656
657 if (con->request)
658 {
659 ippDelete(con->request);
660 con->request = NULL;
661 }
662
663 if (con->response)
664 {
665 ippDelete(con->response);
666 con->response = NULL;
667 }
668
669 if (con->language)
670 {
671 cupsLangFree(con->language);
672 con->language = NULL;
673 }
674
f7deaa1a 675#ifdef HAVE_AUTHORIZATION_H
676 if (con->authref)
677 {
678 AuthorizationFree(con->authref, kAuthorizationFlagDefaults);
679 con->authref = NULL;
680 }
681#endif /* HAVE_AUTHORIZATION_H */
682
ef416fc2 683 /*
684 * Re-enable new client connections if we are going back under the
685 * limit...
686 */
687
bd7854cb 688 if (cupsArrayCount(Clients) == MaxClients)
ef416fc2 689 cupsdResumeListening();
690
691 /*
692 * Compact the list of clients as necessary...
693 */
694
bd7854cb 695 cupsArrayRemove(Clients, con);
ef416fc2 696
bd7854cb 697 free(con);
ef416fc2 698 }
699
700 return (partial);
701}
702
703
d09495fa 704/*
705 * 'cupsdFlushHeader()' - Flush the header fields to the client.
706 */
707
07725fee 708int /* I - Bytes written or -1 on error */
d09495fa 709cupsdFlushHeader(cupsd_client_t *con) /* I - Client to flush to */
710{
07725fee 711 int bytes = httpFlushWrite(HTTP(con));
d09495fa 712
713 con->http.data_encoding = HTTP_ENCODE_LENGTH;
07725fee 714
715 return (bytes);
d09495fa 716}
717
718
ef416fc2 719/*
720 * 'cupsdReadClient()' - Read data from a client.
721 */
722
f7deaa1a 723void
ef416fc2 724cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */
725{
726 char line[32768], /* Line from client... */
727 operation[64], /* Operation code from socket */
728 version[64], /* HTTP version number string */
729 locale[64], /* Locale */
730 *ptr; /* Pointer into strings */
731 int major, minor; /* HTTP version numbers */
732 http_status_t status; /* Transfer status */
733 ipp_state_t ipp_state; /* State of IPP transfer */
734 int bytes; /* Number of bytes to POST */
735 char *filename; /* Name of file for GET/HEAD */
736 char buf[1024]; /* Buffer for real filename */
737 struct stat filestats; /* File information */
738 mime_type_t *type; /* MIME type of file */
739 cupsd_printer_t *p; /* Printer */
740 static unsigned request_id = 0; /* Request ID for temp files */
741
742
743 status = HTTP_CONTINUE;
744
745 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1
MS
746 "cupsdReadClient(con=%p(%d)) "
747 "con->http.error=%d "
748 "con->http.used=%d, "
749 "con->http.state=%d "
750 "con->data_encoding=HTTP_ENCODE_%s, "
751 "con->data_remaining=" CUPS_LLFMT ", "
752 "con->file=%d",
753 con, con->http.fd, con->http.error, con->http.used,
754 con->http.state,
755 con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
756 "CHUNKED" : "LENGTH",
757 CUPS_LLCAST con->http.data_remaining, con->file);
ef416fc2 758
ef416fc2 759#ifdef HAVE_SSL
760 if (con->auto_ssl)
761 {
762 /*
763 * Automatically check for a SSL/TLS handshake...
764 */
765
766 con->auto_ssl = 0;
767
768 if (recv(con->http.fd, buf, 1, MSG_PEEK) == 1 &&
769 (!buf[0] || !strchr("DGHOPT", buf[0])))
770 {
771 /*
772 * Encrypt this connection...
773 */
774
775 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1
MS
776 "cupsdReadClient: Saw first byte %02X, auto-negotiating "
777 "SSL/TLS session...", buf[0] & 255);
ef416fc2 778
411affcf 779 if (!encrypt_client(con))
f7deaa1a 780 cupsdCloseClient(con);
411affcf 781
f7deaa1a 782 return;
ef416fc2 783 }
784 }
785#endif /* HAVE_SSL */
786
787 switch (con->http.state)
788 {
789 case HTTP_WAITING :
790 /*
791 * See if we've received a request line...
792 */
793
794 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
795 {
f11a948a
MS
796 if (con->http.error && con->http.error != EPIPE)
797 cupsdLogMessage(CUPSD_LOG_DEBUG,
798 "cupsdReadClient: %d WAITING Closing for error %d "
799 "(%s)", con->http.fd, con->http.error,
800 strerror(con->http.error));
801 else
802 cupsdLogMessage(CUPSD_LOG_DEBUG,
803 "cupsdReadClient: %d WAITING Closing on EOF",
804 con->http.fd);
805
f7deaa1a 806 cupsdCloseClient(con);
807 return;
ef416fc2 808 }
809
810 /*
811 * Ignore blank request lines...
812 */
813
814 if (line[0] == '\0')
815 break;
816
817 /*
818 * Clear other state variables...
819 */
820
821 httpClearFields(HTTP(con));
822
823 con->http.activity = time(NULL);
824 con->http.version = HTTP_1_0;
825 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
826 con->http.data_encoding = HTTP_ENCODE_LENGTH;
827 con->http.data_remaining = 0;
828 con->http._data_remaining = 0;
829 con->operation = HTTP_WAITING;
830 con->bytes = 0;
831 con->file = -1;
832 con->file_ready = 0;
833 con->pipe_pid = 0;
834 con->username[0] = '\0';
835 con->password[0] = '\0';
836 con->uri[0] = '\0';
837
838 cupsdClearString(&con->command);
839 cupsdClearString(&con->options);
b86bc4cf 840 cupsdClearString(&con->query_string);
ef416fc2 841
2abf387c 842 if (con->request)
843 {
844 ippDelete(con->request);
845 con->request = NULL;
846 }
847
848 if (con->response)
849 {
850 ippDelete(con->response);
851 con->response = NULL;
852 }
853
854 if (con->language)
ef416fc2 855 {
856 cupsLangFree(con->language);
857 con->language = NULL;
858 }
859
09a101d6 860#ifdef HAVE_GSSAPI
97c9a8d7 861 con->have_gss = 0;
07ed0e9a 862 con->gss_uid = 0;
09a101d6 863#endif /* HAVE_GSSAPI */
864
ef416fc2 865 /*
866 * Grab the request line...
867 */
868
869 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
870 {
871 case 1 :
c934a06c
MS
872 if (line[0])
873 {
874 cupsdLogMessage(CUPSD_LOG_ERROR,
c8fef167
MS
875 "Bad request line \"%s\" from %s!",
876 _httpEncodeURI(buf, line, sizeof(buf)),
c934a06c
MS
877 con->http.hostname);
878 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
879 cupsdCloseClient(con);
880 }
f7deaa1a 881 return;
ef416fc2 882 case 2 :
883 con->http.version = HTTP_0_9;
884 break;
885 case 3 :
886 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
887 {
888 cupsdLogMessage(CUPSD_LOG_ERROR,
c8fef167
MS
889 "Bad request line \"%s\" from %s!",
890 _httpEncodeURI(buf, line, sizeof(buf)),
ef416fc2 891 con->http.hostname);
5bd77a73 892 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 893 cupsdCloseClient(con);
894 return;
ef416fc2 895 }
896
897 if (major < 2)
898 {
899 con->http.version = (http_version_t)(major * 100 + minor);
900 if (con->http.version == HTTP_1_1 && KeepAlive)
901 con->http.keep_alive = HTTP_KEEPALIVE_ON;
902 else
903 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
904 }
905 else
906 {
f11a948a
MS
907 cupsdLogMessage(CUPSD_LOG_ERROR,
908 "Unsupported request line \"%s\" from %s!",
c8fef167
MS
909 _httpEncodeURI(buf, line, sizeof(buf)),
910 con->http.hostname);
5bd77a73 911 cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE);
f7deaa1a 912 cupsdCloseClient(con);
913 return;
ef416fc2 914 }
915 break;
916 }
917
918 /*
919 * Handle full URLs in the request line...
920 */
921
922 if (strcmp(con->uri, "*"))
923 {
e6013cfa 924 char scheme[HTTP_MAX_URI], /* Method/scheme */
ef416fc2 925 userpass[HTTP_MAX_URI], /* Username:password */
926 hostname[HTTP_MAX_URI], /* Hostname */
927 resource[HTTP_MAX_URI]; /* Resource path */
928 int port; /* Port number */
929
930
931 /*
932 * Separate the URI into its components...
933 */
934
a4d04587 935 httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
e6013cfa 936 scheme, sizeof(scheme),
ef416fc2 937 userpass, sizeof(userpass),
938 hostname, sizeof(hostname), &port,
939 resource, sizeof(resource));
940
941 /*
942 * Only allow URIs with the servername, localhost, or an IP
943 * address...
944 */
945
e6013cfa 946 if (strcmp(scheme, "file") &&
ef416fc2 947 strcasecmp(hostname, ServerName) &&
948 strcasecmp(hostname, "localhost") &&
e6013cfa 949 !isdigit(hostname[0]) && hostname[0] != '[')
ef416fc2 950 {
951 /*
952 * Nope, we don't do proxies...
953 */
954
955 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!",
956 con->uri);
5bd77a73 957 cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE);
f7deaa1a 958 cupsdCloseClient(con);
959 return;
ef416fc2 960 }
961
962 /*
963 * Copy the resource portion back into the URI; both resource and
964 * con->uri are HTTP_MAX_URI bytes in size...
965 */
966
967 strcpy(con->uri, resource);
968 }
969
970 /*
971 * Process the request...
972 */
973
974 if (!strcmp(operation, "GET"))
975 con->http.state = HTTP_GET;
976 else if (!strcmp(operation, "PUT"))
977 con->http.state = HTTP_PUT;
978 else if (!strcmp(operation, "POST"))
979 con->http.state = HTTP_POST;
980 else if (!strcmp(operation, "DELETE"))
981 con->http.state = HTTP_DELETE;
982 else if (!strcmp(operation, "TRACE"))
983 con->http.state = HTTP_TRACE;
984 else if (!strcmp(operation, "OPTIONS"))
985 con->http.state = HTTP_OPTIONS;
986 else if (!strcmp(operation, "HEAD"))
987 con->http.state = HTTP_HEAD;
988 else
989 {
990 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation);
5bd77a73 991 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 992 cupsdCloseClient(con);
993 return;
ef416fc2 994 }
995
dfd5680b 996 gettimeofday(&(con->start), NULL);
ef416fc2 997 con->operation = con->http.state;
998
89d46774 999 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %s %s HTTP/%d.%d",
1000 con->http.fd, operation, con->uri,
ef416fc2 1001 con->http.version / 100, con->http.version % 100);
1002
1003 con->http.status = HTTP_OK;
1004
49d87452
MS
1005 if (!cupsArrayFind(ActiveClients, con))
1006 {
1007 cupsArrayAdd(ActiveClients, con);
1008 cupsdSetBusyState();
1009 }
3dfe78b3 1010
ef416fc2 1011 case HTTP_OPTIONS :
1012 case HTTP_DELETE :
1013 case HTTP_GET :
1014 case HTTP_HEAD :
1015 case HTTP_POST :
1016 case HTTP_PUT :
1017 case HTTP_TRACE :
1018 /*
1019 * Parse incoming parameters until the status changes...
1020 */
1021
f7deaa1a 1022 while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
ee571f26 1023 if (!data_ready(con))
f7deaa1a 1024 break;
ef416fc2 1025
1026 if (status != HTTP_OK && status != HTTP_CONTINUE)
1027 {
f11a948a
MS
1028 if (con->http.error && con->http.error != EPIPE)
1029 cupsdLogMessage(CUPSD_LOG_DEBUG,
1030 "cupsdReadClient: %d FIELDS Closing for error %d "
1031 "(%s)", con->http.fd, con->http.error,
1032 strerror(con->http.error));
1033 else
1034 cupsdLogMessage(CUPSD_LOG_DEBUG,
1035 "cupsdReadClient: %d FIELDS Closing on EOF",
1036 con->http.fd);
1037
5bd77a73 1038 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 1039 cupsdCloseClient(con);
1040 return;
ef416fc2 1041 }
1042 break;
1043
1044 default :
e6013cfa
MS
1045 if (!data_ready(con) && recv(con->http.fd, buf, 1, MSG_PEEK) < 1)
1046 {
1047 /*
1048 * Connection closed...
1049 */
1050
f11a948a
MS
1051 cupsdLogMessage(CUPSD_LOG_DEBUG,
1052 "cupsdReadClient: %d Closing on EOF", con->http.fd);
e6013cfa
MS
1053 cupsdCloseClient(con);
1054 return;
1055 }
ef416fc2 1056 break; /* Anti-compiler-warning-code */
1057 }
1058
1059 /*
1060 * Handle new transfers...
1061 */
1062
1063 if (status == HTTP_OK)
1064 {
1065 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
1066 {
1067 /*
1068 * Figure out the locale from the Accept-Language and Content-Type
1069 * fields...
1070 */
1071
f11a948a
MS
1072 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1073 ',')) != NULL)
ef416fc2 1074 *ptr = '\0';
1075
f11a948a
MS
1076 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1077 ';')) != NULL)
ef416fc2 1078 *ptr = '\0';
1079
f11a948a
MS
1080 if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1081 "charset=")) != NULL)
ef416fc2 1082 {
1083 /*
1084 * Combine language and charset, and trim any extra params in the
1085 * content-type.
1086 */
1087
1088 snprintf(locale, sizeof(locale), "%s.%s",
1089 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
1090
1091 if ((ptr = strchr(locale, ',')) != NULL)
1092 *ptr = '\0';
1093 }
1094 else
f8b3a85b
MS
1095 snprintf(locale, sizeof(locale), "%s.UTF-8",
1096 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
d6ae789d 1097
ef416fc2 1098 con->language = cupsLangGet(locale);
1099 }
1100 else
1101 con->language = cupsLangGet(DefaultLocale);
1102
1103 cupsdAuthorize(con);
1104
f11a948a
MS
1105 if (!strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive",
1106 10) && KeepAlive)
ef416fc2 1107 con->http.keep_alive = HTTP_KEEPALIVE_ON;
52f6f666
MS
1108 else if (!strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "close", 5))
1109 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
ef416fc2 1110
1111 if (!con->http.fields[HTTP_FIELD_HOST][0] &&
1112 con->http.version >= HTTP_1_1)
1113 {
1114 /*
1115 * HTTP/1.1 and higher require the "Host:" field...
1116 */
1117
5bd77a73 1118 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
f7deaa1a 1119 {
f11a948a 1120 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing Host: field in request!");
f7deaa1a 1121 cupsdCloseClient(con);
1122 return;
1123 }
ef416fc2 1124 }
e07d4801 1125 else if (!valid_host(con))
e6013cfa
MS
1126 {
1127 /*
1128 * Access to localhost must use "localhost" or the corresponding IPv4
1129 * or IPv6 values in the Host: field.
1130 */
1131
f11a948a 1132 cupsdLogMessage(CUPSD_LOG_ERROR,
e6013cfa
MS
1133 "Request from \"%s\" using invalid Host: field \"%s\"",
1134 con->http.hostname, con->http.fields[HTTP_FIELD_HOST]);
1135
1136 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1137 {
1138 cupsdCloseClient(con);
1139 return;
1140 }
1141 }
ef416fc2 1142 else if (con->operation == HTTP_OPTIONS)
1143 {
1144 /*
1145 * Do OPTIONS command...
1146 */
1147
5bd77a73 1148 if (con->best && con->best->type != CUPSD_AUTH_NONE)
ef416fc2 1149 {
5bd77a73 1150 if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1151 {
1152 cupsdCloseClient(con);
1153 return;
1154 }
ef416fc2 1155 }
1156
1157 if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1158 con->http.tls == NULL)
1159 {
1160#ifdef HAVE_SSL
1161 /*
1162 * Do encryption stuff...
1163 */
1164
5bd77a73 1165 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1166 {
1167 cupsdCloseClient(con);
1168 return;
1169 }
ef416fc2 1170
1171 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1172 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1173 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1174 httpPrintf(HTTP(con), "\r\n");
07725fee 1175
1176 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1177 {
1178 cupsdCloseClient(con);
1179 return;
1180 }
ef416fc2 1181
411affcf 1182 if (!encrypt_client(con))
f7deaa1a 1183 {
1184 cupsdCloseClient(con);
1185 return;
1186 }
ef416fc2 1187#else
5bd77a73 1188 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
f7deaa1a 1189 {
1190 cupsdCloseClient(con);
1191 return;
1192 }
ef416fc2 1193#endif /* HAVE_SSL */
1194 }
1195
5bd77a73 1196 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1197 {
1198 cupsdCloseClient(con);
1199 return;
1200 }
ef416fc2 1201
1202 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1203 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1204 httpPrintf(HTTP(con), "\r\n");
07725fee 1205
1206 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1207 {
1208 cupsdCloseClient(con);
1209 return;
1210 }
ef416fc2 1211 }
1212 else if (!is_path_absolute(con->uri))
1213 {
1214 /*
1215 * Protect against malicious users!
1216 */
1217
bf3816c7
MS
1218 cupsdLogMessage(CUPSD_LOG_ERROR,
1219 "Request for non-absolute resource \"%s\"!", con->uri);
1220
5bd77a73 1221 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1222 {
1223 cupsdCloseClient(con);
1224 return;
1225 }
ef416fc2 1226 }
1227 else
1228 {
1229 if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1230 con->http.tls == NULL)
1231 {
1232#ifdef HAVE_SSL
1233 /*
1234 * Do encryption stuff...
1235 */
1236
5bd77a73 1237 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1238 {
1239 cupsdCloseClient(con);
1240 return;
1241 }
ef416fc2 1242
1243 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1244 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1245 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1246 httpPrintf(HTTP(con), "\r\n");
07725fee 1247
1248 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1249 {
1250 cupsdCloseClient(con);
1251 return;
1252 }
ef416fc2 1253
411affcf 1254 if (!encrypt_client(con))
f7deaa1a 1255 {
1256 cupsdCloseClient(con);
1257 return;
1258 }
ef416fc2 1259#else
5bd77a73 1260 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
f7deaa1a 1261 {
1262 cupsdCloseClient(con);
1263 return;
1264 }
ef416fc2 1265#endif /* HAVE_SSL */
1266 }
1267
1268 if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK)
1269 {
5bd77a73 1270 cupsdSendError(con, status, CUPSD_AUTH_NONE);
f7deaa1a 1271 cupsdCloseClient(con);
1272 return;
ef416fc2 1273 }
1274
b423cd4c 1275 if (con->http.expect &&
1276 (con->operation == HTTP_POST || con->operation == HTTP_PUT))
ef416fc2 1277 {
b423cd4c 1278 if (con->http.expect == HTTP_CONTINUE)
1279 {
1280 /*
1281 * Send 100-continue header...
1282 */
1283
5bd77a73 1284 if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1285 {
1286 cupsdCloseClient(con);
1287 return;
1288 }
b423cd4c 1289 }
1290 else
1291 {
1292 /*
1293 * Send 417-expectation-failed header...
1294 */
1295
f11a948a
MS
1296 if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL,
1297 CUPSD_AUTH_NONE))
f7deaa1a 1298 {
1299 cupsdCloseClient(con);
1300 return;
1301 }
b423cd4c 1302
1303 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1304 httpPrintf(HTTP(con), "\r\n");
07725fee 1305
1306 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1307 {
1308 cupsdCloseClient(con);
1309 return;
1310 }
b423cd4c 1311 }
ef416fc2 1312 }
1313
1314 switch (con->http.state)
1315 {
1316 case HTTP_GET_SEND :
1317 if (!strncmp(con->uri, "/printers/", 10) &&
1318 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1319 {
1320 /*
1321 * Send PPD file - get the real printer name since printer
1322 * names are not case sensitive but filenames can be...
1323 */
1324
1325 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1326
1327 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1328 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1329 else
1330 {
5bd77a73 1331 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1332 {
1333 cupsdCloseClient(con);
1334 return;
1335 }
ef416fc2 1336
1337 break;
1338 }
1339 }
7cf5915e
MS
1340 else if ((!strncmp(con->uri, "/printers/", 10) ||
1341 !strncmp(con->uri, "/classes/", 9)) &&
1342 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1343 {
1344 /*
1345 * Send icon file - get the real queue name since queue names are
1346 * not case sensitive but filenames can be...
1347 */
1348
1349 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".png" */
1350
1351 if (!strncmp(con->uri, "/printers/", 10))
1352 p = cupsdFindPrinter(con->uri + 10);
1353 else
1354 p = cupsdFindClass(con->uri + 9);
1355
1356 if (p)
1357 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1358 else
1359 {
1360 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1361 {
1362 cupsdCloseClient(con);
1363 return;
1364 }
1365
1366 break;
1367 }
1368 }
0268488e 1369 else if (!WebInterface)
229681c1
MS
1370 {
1371 /*
1372 * Web interface is disabled. Show an appropriate message...
1373 */
1374
1375 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1376 {
1377 cupsdCloseClient(con);
1378 return;
1379 }
1380
1381 break;
1382 }
0268488e
MS
1383
1384 if ((!strncmp(con->uri, "/admin", 6) &&
1385 strncmp(con->uri, "/admin/conf/", 12) &&
1386 strncmp(con->uri, "/admin/log/", 11)) ||
1387 !strncmp(con->uri, "/printers", 9) ||
1388 !strncmp(con->uri, "/classes", 8) ||
1389 !strncmp(con->uri, "/help", 5) ||
1390 !strncmp(con->uri, "/jobs", 5))
ef416fc2 1391 {
1392 /*
1393 * Send CGI output...
1394 */
1395
1396 if (!strncmp(con->uri, "/admin", 6))
1397 {
1398 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1399 ServerBin);
1400
b423cd4c 1401 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
ef416fc2 1402 }
1403 else if (!strncmp(con->uri, "/printers", 9))
1404 {
1405 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1406 ServerBin);
b423cd4c 1407
1408 if (con->uri[9] && con->uri[10])
1409 cupsdSetString(&con->options, con->uri + 9);
1410 else
1411 cupsdSetString(&con->options, NULL);
ef416fc2 1412 }
1413 else if (!strncmp(con->uri, "/classes", 8))
1414 {
1415 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1416 ServerBin);
b423cd4c 1417
1418 if (con->uri[8] && con->uri[9])
1419 cupsdSetString(&con->options, con->uri + 8);
1420 else
1421 cupsdSetString(&con->options, NULL);
ef416fc2 1422 }
1423 else if (!strncmp(con->uri, "/jobs", 5))
1424 {
1425 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1426 ServerBin);
b423cd4c 1427
1428 if (con->uri[5] && con->uri[6])
1429 cupsdSetString(&con->options, con->uri + 5);
1430 else
1431 cupsdSetString(&con->options, NULL);
ef416fc2 1432 }
1433 else
1434 {
1435 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1436 ServerBin);
ef416fc2 1437
b423cd4c 1438 if (con->uri[5] && con->uri[6])
1439 cupsdSetString(&con->options, con->uri + 5);
1440 else
1441 cupsdSetString(&con->options, NULL);
1442 }
ef416fc2 1443
1444 if (!cupsdSendCommand(con, con->command, con->options, 0))
1445 {
5bd77a73 1446 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1447 {
1448 cupsdCloseClient(con);
1449 return;
1450 }
ef416fc2 1451 }
1452 else
1453 cupsdLogRequest(con, HTTP_OK);
1454
1455 if (con->http.version <= HTTP_1_0)
1456 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1457 }
1458 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1459 (strchr(con->uri + 12, '/') ||
1460 strlen(con->uri) == 12)) ||
1461 (!strncmp(con->uri, "/admin/log/", 11) &&
1462 (strchr(con->uri + 11, '/') ||
1463 strlen(con->uri) == 11)))
1464 {
1465 /*
bf3816c7 1466 * GET can only be done to configuration files directly under
ef416fc2 1467 * /admin/conf...
1468 */
1469
bf3816c7
MS
1470 cupsdLogMessage(CUPSD_LOG_ERROR,
1471 "Request for subdirectory \"%s\"!", con->uri);
1472
5bd77a73 1473 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1474 {
1475 cupsdCloseClient(con);
1476 return;
1477 }
ef416fc2 1478
1479 break;
1480 }
1481 else
1482 {
1483 /*
1484 * Serve a file...
1485 */
1486
1487 if ((filename = get_file(con, &filestats, buf,
1488 sizeof(buf))) == NULL)
1489 {
5bd77a73 1490 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1491 {
1492 cupsdCloseClient(con);
1493 return;
1494 }
ef416fc2 1495
1496 break;
1497 }
1498
4400e98d 1499 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
ef416fc2 1500
e1d6a774 1501 if (is_cgi(con, filename, &filestats, type))
ef416fc2 1502 {
1503 /*
1504 * Note: con->command and con->options were set by
e1d6a774 1505 * is_cgi()...
ef416fc2 1506 */
1507
1508 if (!cupsdSendCommand(con, con->command, con->options, 0))
1509 {
5bd77a73 1510 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1511 {
1512 cupsdCloseClient(con);
1513 return;
1514 }
ef416fc2 1515 }
1516 else
1517 cupsdLogRequest(con, HTTP_OK);
1518
1519 if (con->http.version <= HTTP_1_0)
1520 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1521 break;
1522 }
1523
1524 if (!check_if_modified(con, &filestats))
1525 {
5bd77a73 1526 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
f7deaa1a 1527 {
1528 cupsdCloseClient(con);
1529 return;
1530 }
ef416fc2 1531 }
1532 else
1533 {
1534 if (type == NULL)
1535 strcpy(line, "text/plain");
1536 else
1537 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1538
a74454a7 1539 if (!write_file(con, HTTP_OK, filename, line, &filestats))
f7deaa1a 1540 {
1541 cupsdCloseClient(con);
1542 return;
1543 }
ef416fc2 1544 }
1545 }
1546 break;
1547
1548 case HTTP_POST_RECV :
1549 /*
1550 * See if the POST request includes a Content-Length field, and if
1551 * so check the length against any limits that are set...
1552 */
1553
ef416fc2 1554 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1555 MaxRequestSize > 0 &&
1556 con->http.data_remaining > MaxRequestSize)
1557 {
1558 /*
1559 * Request too large...
1560 */
1561
5bd77a73 1562 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 1563 {
1564 cupsdCloseClient(con);
1565 return;
1566 }
ef416fc2 1567
1568 break;
1569 }
839a51c8
MS
1570 else if (con->http.data_remaining < 0 ||
1571 (!con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1572 con->http.data_encoding == HTTP_ENCODE_LENGTH))
ef416fc2 1573 {
1574 /*
1575 * Negative content lengths are invalid!
1576 */
1577
5bd77a73 1578 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
f7deaa1a 1579 {
1580 cupsdCloseClient(con);
1581 return;
1582 }
ef416fc2 1583
1584 break;
1585 }
1586
1587 /*
1588 * See what kind of POST request this is; for IPP requests the
1589 * content-type field will be "application/ipp"...
1590 */
1591
1592 if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1593 "application/ipp"))
1594 con->request = ippNew();
229681c1
MS
1595 else if (!WebInterface)
1596 {
1597 /*
1598 * Web interface is disabled. Show an appropriate message...
1599 */
1600
1601 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1602 {
1603 cupsdCloseClient(con);
1604 return;
1605 }
1606
1607 break;
1608 }
ef416fc2 1609 else if ((!strncmp(con->uri, "/admin", 6) &&
1610 strncmp(con->uri, "/admin/conf/", 12) &&
1611 strncmp(con->uri, "/admin/log/", 11)) ||
1612 !strncmp(con->uri, "/printers", 9) ||
1613 !strncmp(con->uri, "/classes", 8) ||
1614 !strncmp(con->uri, "/help", 5) ||
1615 !strncmp(con->uri, "/jobs", 5))
1616 {
1617 /*
1618 * CGI request...
1619 */
1620
1621 if (!strncmp(con->uri, "/admin", 6))
1622 {
1623 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1624 ServerBin);
1625
b423cd4c 1626 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
ef416fc2 1627 }
1628 else if (!strncmp(con->uri, "/printers", 9))
1629 {
1630 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1631 ServerBin);
b423cd4c 1632
1633 if (con->uri[9] && con->uri[10])
1634 cupsdSetString(&con->options, con->uri + 9);
1635 else
1636 cupsdSetString(&con->options, NULL);
ef416fc2 1637 }
1638 else if (!strncmp(con->uri, "/classes", 8))
1639 {
1640 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1641 ServerBin);
b423cd4c 1642
1643 if (con->uri[8] && con->uri[9])
1644 cupsdSetString(&con->options, con->uri + 8);
1645 else
1646 cupsdSetString(&con->options, NULL);
ef416fc2 1647 }
1648 else if (!strncmp(con->uri, "/jobs", 5))
1649 {
1650 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1651 ServerBin);
b423cd4c 1652
1653 if (con->uri[5] && con->uri[6])
1654 cupsdSetString(&con->options, con->uri + 5);
1655 else
1656 cupsdSetString(&con->options, NULL);
ef416fc2 1657 }
1658 else
1659 {
1660 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1661 ServerBin);
ef416fc2 1662
b423cd4c 1663 if (con->uri[5] && con->uri[6])
1664 cupsdSetString(&con->options, con->uri + 5);
1665 else
1666 cupsdSetString(&con->options, NULL);
1667 }
ef416fc2 1668
ef416fc2 1669 if (con->http.version <= HTTP_1_0)
1670 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1671 }
1672 else
1673 {
1674 /*
1675 * POST to a file...
1676 */
1677
1678 if ((filename = get_file(con, &filestats, buf,
1679 sizeof(buf))) == NULL)
1680 {
5bd77a73 1681 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1682 {
1683 cupsdCloseClient(con);
1684 return;
1685 }
ef416fc2 1686
1687 break;
1688 }
1689
4400e98d 1690 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
ef416fc2 1691
e1d6a774 1692 if (!is_cgi(con, filename, &filestats, type))
ef416fc2 1693 {
1694 /*
1695 * Only POST to CGI's...
1696 */
1697
5bd77a73 1698 if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE))
f7deaa1a 1699 {
1700 cupsdCloseClient(con);
1701 return;
1702 }
ef416fc2 1703 }
1704 }
1705 break;
1706
1707 case HTTP_PUT_RECV :
1708 /*
1709 * Validate the resource name...
1710 */
1711
1712 if (strncmp(con->uri, "/admin/conf/", 12) ||
1713 strchr(con->uri + 12, '/') ||
1714 strlen(con->uri) == 12)
1715 {
1716 /*
1717 * PUT can only be done to configuration files under
1718 * /admin/conf...
1719 */
1720
bf3816c7
MS
1721 cupsdLogMessage(CUPSD_LOG_ERROR,
1722 "Request for subdirectory \"%s\"!", con->uri);
1723
5bd77a73 1724 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1725 {
1726 cupsdCloseClient(con);
1727 return;
1728 }
ef416fc2 1729
1730 break;
1731 }
1732
1733 /*
1734 * See if the PUT request includes a Content-Length field, and if
1735 * so check the length against any limits that are set...
1736 */
1737
ef416fc2 1738 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1739 MaxRequestSize > 0 &&
1740 con->http.data_remaining > MaxRequestSize)
1741 {
1742 /*
1743 * Request too large...
1744 */
1745
5bd77a73 1746 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 1747 {
1748 cupsdCloseClient(con);
1749 return;
1750 }
ef416fc2 1751
1752 break;
1753 }
1754 else if (con->http.data_remaining < 0)
1755 {
1756 /*
1757 * Negative content lengths are invalid!
1758 */
1759
5bd77a73 1760 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
f7deaa1a 1761 {
1762 cupsdCloseClient(con);
1763 return;
1764 }
ef416fc2 1765
1766 break;
1767 }
1768
1769 /*
1770 * Open a temporary file to hold the request...
1771 */
1772
1773 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
1774 request_id ++);
1775 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1776
ef416fc2 1777 if (con->file < 0)
1778 {
a4924f6c
MS
1779 cupsdLogMessage(CUPSD_LOG_ERROR,
1780 "Unable to create request file %s: %s",
1781 con->filename, strerror(errno));
1782
5bd77a73 1783 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 1784 {
1785 cupsdCloseClient(con);
1786 return;
1787 }
ef416fc2 1788 }
1789
1790 fchmod(con->file, 0640);
1791 fchown(con->file, RunUser, Group);
1792 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1793 break;
1794
1795 case HTTP_DELETE :
1796 case HTTP_TRACE :
5bd77a73 1797 cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE);
f7deaa1a 1798 cupsdCloseClient(con);
1799 return;
ef416fc2 1800
1801 case HTTP_HEAD :
1802 if (!strncmp(con->uri, "/printers/", 10) &&
1803 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1804 {
1805 /*
1806 * Send PPD file - get the real printer name since printer
1807 * names are not case sensitive but filenames can be...
1808 */
1809
1810 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1811
1812 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1813 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1814 else
1815 {
5bd77a73 1816 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1817 {
1818 cupsdCloseClient(con);
1819 return;
1820 }
ef416fc2 1821
1822 break;
1823 }
1824 }
229681c1
MS
1825 else if (!strncmp(con->uri, "/printers/", 10) &&
1826 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1827 {
1828 /*
1829 * Send PNG file - get the real printer name since printer
1830 * names are not case sensitive but filenames can be...
1831 */
1832
1833 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1834
1835 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1836 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1837 else
1838 {
1839 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1840 {
1841 cupsdCloseClient(con);
1842 return;
1843 }
1844
1845 break;
1846 }
1847 }
1848 else if (!WebInterface)
1849 {
1850 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
1851 {
1852 cupsdCloseClient(con);
1853 return;
1854 }
1855
1856 if (httpPrintf(HTTP(con), "\r\n") < 0)
1857 {
1858 cupsdCloseClient(con);
1859 return;
1860 }
1861
1862 if (cupsdFlushHeader(con) < 0)
1863 {
1864 cupsdCloseClient(con);
1865 return;
1866 }
1867
1868 con->http.state = HTTP_WAITING;
1869 break;
1870 }
ef416fc2 1871
1872 if ((!strncmp(con->uri, "/admin", 6) &&
1873 strncmp(con->uri, "/admin/conf/", 12) &&
1874 strncmp(con->uri, "/admin/log/", 11)) ||
1875 !strncmp(con->uri, "/printers", 9) ||
1876 !strncmp(con->uri, "/classes", 8) ||
1877 !strncmp(con->uri, "/help", 5) ||
1878 !strncmp(con->uri, "/jobs", 5))
1879 {
1880 /*
1881 * CGI output...
1882 */
1883
5bd77a73 1884 if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE))
f7deaa1a 1885 {
1886 cupsdCloseClient(con);
1887 return;
1888 }
ef416fc2 1889
1890 if (httpPrintf(HTTP(con), "\r\n") < 0)
f7deaa1a 1891 {
1892 cupsdCloseClient(con);
1893 return;
1894 }
ef416fc2 1895
07725fee 1896 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1897 {
1898 cupsdCloseClient(con);
1899 return;
1900 }
d09495fa 1901
ef416fc2 1902 cupsdLogRequest(con, HTTP_OK);
1903 }
1904 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1905 (strchr(con->uri + 12, '/') ||
1906 strlen(con->uri) == 12)) ||
1907 (!strncmp(con->uri, "/admin/log/", 11) &&
1908 (strchr(con->uri + 11, '/') ||
1909 strlen(con->uri) == 11)))
1910 {
1911 /*
1912 * HEAD can only be done to configuration files under
1913 * /admin/conf...
1914 */
1915
bf3816c7
MS
1916 cupsdLogMessage(CUPSD_LOG_ERROR,
1917 "Request for subdirectory \"%s\"!", con->uri);
1918
5bd77a73 1919 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1920 {
1921 cupsdCloseClient(con);
1922 return;
1923 }
ef416fc2 1924
1925 break;
1926 }
1927 else if ((filename = get_file(con, &filestats, buf,
1928 sizeof(buf))) == NULL)
1929 {
f11a948a
MS
1930 if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html",
1931 CUPSD_AUTH_NONE))
f7deaa1a 1932 {
1933 cupsdCloseClient(con);
1934 return;
1935 }
ef416fc2 1936
1937 cupsdLogRequest(con, HTTP_NOT_FOUND);
1938 }
1939 else if (!check_if_modified(con, &filestats))
1940 {
5bd77a73 1941 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
f7deaa1a 1942 {
1943 cupsdCloseClient(con);
1944 return;
1945 }
ef416fc2 1946
1947 cupsdLogRequest(con, HTTP_NOT_MODIFIED);
1948 }
1949 else
1950 {
1951 /*
1952 * Serve a file...
1953 */
1954
4400e98d 1955 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
ef416fc2 1956 if (type == NULL)
1957 strcpy(line, "text/plain");
1958 else
1959 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1960
5bd77a73 1961 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
f7deaa1a 1962 {
1963 cupsdCloseClient(con);
1964 return;
1965 }
ef416fc2 1966
1967 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1968 httpGetDateString(filestats.st_mtime)) < 0)
f7deaa1a 1969 {
1970 cupsdCloseClient(con);
1971 return;
1972 }
ef416fc2 1973
1974 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1975 (unsigned long)filestats.st_size) < 0)
f7deaa1a 1976 {
1977 cupsdCloseClient(con);
1978 return;
1979 }
ef416fc2 1980
1981 cupsdLogRequest(con, HTTP_OK);
1982 }
1983
1984 if (httpPrintf(HTTP(con), "\r\n") < 0)
f7deaa1a 1985 {
1986 cupsdCloseClient(con);
1987 return;
1988 }
ef416fc2 1989
07725fee 1990 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1991 {
1992 cupsdCloseClient(con);
1993 return;
1994 }
d09495fa 1995
ef416fc2 1996 con->http.state = HTTP_WAITING;
1997 break;
1998
1999 default :
2000 break; /* Anti-compiler-warning-code */
2001 }
2002 }
2003 }
2004
2005 /*
2006 * Handle any incoming data...
2007 */
2008
2009 switch (con->http.state)
2010 {
2011 case HTTP_PUT_RECV :
f7deaa1a 2012 do
ef416fc2 2013 {
f7deaa1a 2014 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
2015 {
f11a948a
MS
2016 if (con->http.error && con->http.error != EPIPE)
2017 cupsdLogMessage(CUPSD_LOG_DEBUG,
2018 "cupsdReadClient: %d PUT_RECV Closing for error "
2019 "%d (%s)", con->http.fd, con->http.error,
2020 strerror(con->http.error));
2021 else
2022 cupsdLogMessage(CUPSD_LOG_DEBUG,
2023 "cupsdReadClient: %d PUT_RECV Closing on EOF",
2024 con->http.fd);
2025
f7deaa1a 2026 cupsdCloseClient(con);
2027 return;
2028 }
2029 else if (bytes > 0)
2030 {
2031 con->bytes += bytes;
ef416fc2 2032
f7deaa1a 2033 if (write(con->file, line, bytes) < bytes)
2034 {
2035 cupsdLogMessage(CUPSD_LOG_ERROR,
2036 "cupsdReadClient: Unable to write %d bytes to %s: %s",
2037 bytes, con->filename, strerror(errno));
ef416fc2 2038
f7deaa1a 2039 close(con->file);
2040 con->file = -1;
2041 unlink(con->filename);
2042 cupsdClearString(&con->filename);
ef416fc2 2043
5bd77a73 2044 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2045 {
2046 cupsdCloseClient(con);
2047 return;
2048 }
2049 }
ef416fc2 2050 }
f7deaa1a 2051 }
ee571f26 2052 while (con->http.state == HTTP_PUT_RECV && data_ready(con));
ef416fc2 2053
2054 if (con->http.state == HTTP_WAITING)
2055 {
2056 /*
2057 * End of file, see how big it is...
2058 */
2059
2060 fstat(con->file, &filestats);
2061
ef416fc2 2062 close(con->file);
2063 con->file = -1;
2064
2065 if (filestats.st_size > MaxRequestSize &&
2066 MaxRequestSize > 0)
2067 {
2068 /*
2069 * Request is too big; remove it and send an error...
2070 */
2071
ef416fc2 2072 unlink(con->filename);
2073 cupsdClearString(&con->filename);
2074
5bd77a73 2075 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2076 {
2077 cupsdCloseClient(con);
2078 return;
2079 }
ef416fc2 2080 }
2081
2082 /*
2083 * Install the configuration file...
2084 */
2085
2086 status = install_conf_file(con);
2087
2088 /*
2089 * Return the status to the client...
2090 */
2091
5bd77a73 2092 if (!cupsdSendError(con, status, CUPSD_AUTH_NONE))
f7deaa1a 2093 {
2094 cupsdCloseClient(con);
2095 return;
2096 }
ef416fc2 2097 }
2098 break;
2099
2100 case HTTP_POST_RECV :
f7deaa1a 2101 do
ef416fc2 2102 {
f11a948a 2103 if (con->request && con->file < 0)
ef416fc2 2104 {
f7deaa1a 2105 /*
2106 * Grab any request data from the connection...
2107 */
ef416fc2 2108
f7deaa1a 2109 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
ef416fc2 2110 {
f7deaa1a 2111 cupsdLogMessage(CUPSD_LOG_ERROR,
2112 "cupsdReadClient: %d IPP Read Error!",
2113 con->http.fd);
2114
5bd77a73 2115 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 2116 cupsdCloseClient(con);
2117 return;
ef416fc2 2118 }
f7deaa1a 2119 else if (ipp_state != IPP_DATA)
2120 {
2121 if (con->http.state == HTTP_POST_SEND)
2122 {
5bd77a73 2123 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 2124 cupsdCloseClient(con);
2125 return;
2126 }
ef416fc2 2127
f7deaa1a 2128 break;
2129 }
2130 else
f11a948a
MS
2131 {
2132 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %d.%d %s %d",
2133 con->http.fd, con->request->request.op.version[0],
2134 con->request->request.op.version[1],
2135 ippOpString(con->request->request.op.operation_id),
2136 con->request->request.op.request_id);
f7deaa1a 2137 con->bytes += ippLength(con->request);
f11a948a 2138 }
f7deaa1a 2139 }
ef416fc2 2140
f7deaa1a 2141 if (con->file < 0 && con->http.state != HTTP_POST_SEND)
2142 {
2143 /*
2144 * Create a file as needed for the request data...
2145 */
ef416fc2 2146
f11a948a
MS
2147 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
2148 request_id ++);
f7deaa1a 2149 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
ef416fc2 2150
f7deaa1a 2151 if (con->file < 0)
2152 {
a4924f6c
MS
2153 cupsdLogMessage(CUPSD_LOG_ERROR,
2154 "Unable to create request file %s: %s",
2155 con->filename, strerror(errno));
2156
5bd77a73 2157 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2158 {
2159 cupsdCloseClient(con);
2160 return;
2161 }
2162 }
ef416fc2 2163
f7deaa1a 2164 fchmod(con->file, 0640);
2165 fchown(con->file, RunUser, Group);
2166 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2167 }
ef416fc2 2168
f7deaa1a 2169 if (con->http.state != HTTP_POST_SEND)
ef416fc2 2170 {
f7deaa1a 2171 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
2172 {
f11a948a
MS
2173 if (con->http.error && con->http.error != EPIPE)
2174 cupsdLogMessage(CUPSD_LOG_DEBUG,
2175 "cupsdReadClient: %d POST_SEND Closing for "
2176 "error %d (%s)", con->http.fd, con->http.error,
2177 strerror(con->http.error));
2178 else
2179 cupsdLogMessage(CUPSD_LOG_DEBUG,
2180 "cupsdReadClient: %d POST_SEND Closing on EOF",
2181 con->http.fd);
2182
f7deaa1a 2183 cupsdCloseClient(con);
2184 return;
2185 }
2186 else if (bytes > 0)
2187 {
2188 con->bytes += bytes;
ef416fc2 2189
f7deaa1a 2190 if (write(con->file, line, bytes) < bytes)
2191 {
2192 cupsdLogMessage(CUPSD_LOG_ERROR,
f11a948a
MS
2193 "cupsdReadClient: Unable to write %d bytes to "
2194 "%s: %s", bytes, con->filename,
2195 strerror(errno));
ef416fc2 2196
f7deaa1a 2197 close(con->file);
2198 con->file = -1;
2199 unlink(con->filename);
2200 cupsdClearString(&con->filename);
ef416fc2 2201
f11a948a
MS
2202 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE,
2203 CUPSD_AUTH_NONE))
f7deaa1a 2204 {
2205 cupsdCloseClient(con);
2206 return;
2207 }
2208 }
2209 }
2210 else if (con->http.state == HTTP_POST_RECV)
2211 return;
2212 else if (con->http.state != HTTP_POST_SEND)
2213 {
f11a948a
MS
2214 cupsdLogMessage(CUPSD_LOG_DEBUG,
2215 "cupsdReadClient: %d Closing on unknown HTTP "
2216 "state %d", con->http.fd, con->http.state);
f7deaa1a 2217 cupsdCloseClient(con);
2218 return;
ef416fc2 2219 }
2220 }
f7deaa1a 2221 }
ee571f26 2222 while (con->http.state == HTTP_POST_RECV && data_ready(con));
ef416fc2 2223
2224 if (con->http.state == HTTP_POST_SEND)
2225 {
2226 if (con->file >= 0)
2227 {
2228 fstat(con->file, &filestats);
2229
ef416fc2 2230 close(con->file);
2231 con->file = -1;
2232
2233 if (filestats.st_size > MaxRequestSize &&
2234 MaxRequestSize > 0)
2235 {
2236 /*
2237 * Request is too big; remove it and send an error...
2238 */
2239
ef416fc2 2240 unlink(con->filename);
2241 cupsdClearString(&con->filename);
2242
2243 if (con->request)
2244 {
2245 /*
2246 * Delete any IPP request data...
2247 */
2248
2249 ippDelete(con->request);
2250 con->request = NULL;
2251 }
2252
5bd77a73 2253 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2254 {
2255 cupsdCloseClient(con);
2256 return;
2257 }
ef416fc2 2258 }
f8b3a85b
MS
2259 else if (filestats.st_size == 0)
2260 {
2261 /*
2262 * Don't allow empty file...
2263 */
2264
2265 unlink(con->filename);
2266 cupsdClearString(&con->filename);
2267 }
ef416fc2 2268
2269 if (con->command)
2270 {
2271 if (!cupsdSendCommand(con, con->command, con->options, 0))
2272 {
5bd77a73 2273 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 2274 {
2275 cupsdCloseClient(con);
2276 return;
2277 }
ef416fc2 2278 }
2279 else
2280 cupsdLogRequest(con, HTTP_OK);
2281 }
2282 }
2283
2284 if (con->request)
f7deaa1a 2285 {
2286 cupsdProcessIPPRequest(con);
bc44d920 2287
2288 if (con->filename)
2289 {
bc44d920 2290 unlink(con->filename);
2291 cupsdClearString(&con->filename);
2292 }
2293
f7deaa1a 2294 return;
2295 }
ef416fc2 2296 }
2297 break;
2298
2299 default :
2300 break; /* Anti-compiler-warning-code */
2301 }
2302
3dfe78b3
MS
2303 if (con->http.state == HTTP_WAITING)
2304 {
2305 if (!con->http.keep_alive)
f11a948a
MS
2306 {
2307 cupsdLogMessage(CUPSD_LOG_DEBUG,
2308 "cupsdReadClient: %d Closing because Keep-Alive disabled",
2309 con->http.fd);
3dfe78b3 2310 cupsdCloseClient(con);
f11a948a 2311 }
3dfe78b3
MS
2312 else
2313 {
2314 cupsArrayRemove(ActiveClients, con);
2315 cupsdSetBusyState();
2316 }
2317 }
ef416fc2 2318}
2319
2320
2321/*
2322 * 'cupsdSendCommand()' - Send output from a command via HTTP.
2323 */
2324
2325int /* O - 1 on success, 0 on failure */
2326cupsdSendCommand(
2327 cupsd_client_t *con, /* I - Client connection */
2328 char *command, /* I - Command to run */
2329 char *options, /* I - Command-line options */
2330 int root) /* I - Run as root? */
2331{
2332 int fd; /* Standard input file descriptor */
2333
2334
2335 if (con->filename)
ed486911 2336 {
ef416fc2 2337 fd = open(con->filename, O_RDONLY);
ef416fc2 2338
ed486911 2339 if (fd < 0)
2340 {
2341 cupsdLogMessage(CUPSD_LOG_ERROR,
2342 "cupsdSendCommand: %d Unable to open \"%s\" for reading: %s",
2343 con->http.fd, con->filename ? con->filename : "/dev/null",
2344 strerror(errno));
2345 return (0);
2346 }
ef416fc2 2347
ed486911 2348 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
2349 }
2350 else
2351 fd = -1;
ef416fc2 2352
2353 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
2354
ed486911 2355 if (fd >= 0)
2356 close(fd);
ef416fc2 2357
2358 cupsdLogMessage(CUPSD_LOG_INFO, "Started \"%s\" (pid=%d)", command,
2359 con->pipe_pid);
2360
2361 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendCommand: %d file=%d",
2362 con->http.fd, con->file);
2363
2364 if (con->pipe_pid == 0)
2365 return (0);
2366
2367 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2368
f899b121 2369 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
ef416fc2 2370
2371 con->sent_header = 0;
2372 con->file_ready = 0;
2373 con->got_fields = 0;
68b10830 2374 con->header_used = 0;
ef416fc2 2375
2376 return (1);
2377}
2378
2379
2380/*
2381 * 'cupsdSendError()' - Send an error message via HTTP.
2382 */
2383
2384int /* O - 1 if successful, 0 otherwise */
2385cupsdSendError(cupsd_client_t *con, /* I - Connection */
f899b121 2386 http_status_t code, /* I - Error code */
2387 int auth_type)/* I - Authentication type */
ef416fc2 2388{
b9faaae1
MS
2389 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2390 "cupsdSendError(con=%p(%d), code=%d, auth_type=%d", con,
2391 con->http.fd, code, auth_type);
2392
4744bd90 2393#ifdef HAVE_SSL
2394 /*
2395 * Force client to upgrade for authentication if that is how the
2396 * server is configured...
2397 */
2398
2399 if (code == HTTP_UNAUTHORIZED &&
2400 DefaultEncryption == HTTP_ENCRYPT_REQUIRED &&
2401 strcasecmp(con->http.hostname, "localhost") &&
2402 !con->http.tls)
2403 {
4744bd90 2404 code = HTTP_UPGRADE_REQUIRED;
2405 }
2406#endif /* HAVE_SSL */
2407
ef416fc2 2408 /*
2409 * Put the request in the access_log file...
2410 */
2411
2412 cupsdLogRequest(con, code);
2413
ef416fc2 2414 /*
2415 * To work around bugs in some proxies, don't use Keep-Alive for some
2416 * error messages...
f7deaa1a 2417 *
2418 * Kerberos authentication doesn't work without Keep-Alive, so
2419 * never disable it in that case.
ef416fc2 2420 */
2421
5bd77a73 2422 if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE)
ef416fc2 2423 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2424
2425 /*
2426 * Send an error message back to the client. If the error code is a
2427 * 400 or 500 series, make sure the message contains some text, too!
2428 */
2429
f899b121 2430 if (!cupsdSendHeader(con, code, NULL, auth_type))
ef416fc2 2431 return (0);
2432
2433#ifdef HAVE_SSL
2434 if (code == HTTP_UPGRADE_REQUIRED)
2435 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2436 return (0);
2437
2438 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2439 return (0);
2440#endif /* HAVE_SSL */
2441
f7deaa1a 2442 if (con->http.version >= HTTP_1_1 &&
2443 con->http.keep_alive == HTTP_KEEPALIVE_OFF)
ef416fc2 2444 {
2445 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2446 return (0);
2447 }
2448
2449 if (code >= HTTP_BAD_REQUEST)
2450 {
2451 /*
2452 * Send a human-readable error message.
2453 */
2454
80ca4592 2455 char message[4096], /* Message for user */
2456 urltext[1024], /* URL redirection text */
2457 redirect[1024]; /* Redirection link */
ef416fc2 2458 const char *text; /* Status-specific text */
2459
80ca4592 2460
2461 redirect[0] = '\0';
2462
ef416fc2 2463 if (code == HTTP_UNAUTHORIZED)
2464 text = _cupsLangString(con->language,
2465 _("Enter your username and password or the "
2466 "root username and password to access this "
f7deaa1a 2467 "page. If you are using Kerberos authentication, "
2468 "make sure you have a valid Kerberos ticket."));
ef416fc2 2469 else if (code == HTTP_UPGRADE_REQUIRED)
80ca4592 2470 {
2471 text = urltext;
2472
2473 snprintf(urltext, sizeof(urltext),
2474 _cupsLangString(con->language,
2475 _("You must access this page using the URL "
2476 "<A HREF=\"https://%s:%d%s\">"
2477 "https://%s:%d%s</A>.")),
2478 con->servername, con->serverport, con->uri,
2479 con->servername, con->serverport, con->uri);
2480
2481 snprintf(redirect, sizeof(redirect),
b86bc4cf 2482 "<META HTTP-EQUIV=\"Refresh\" "
2483 "CONTENT=\"3;URL=https://%s:%d%s\">\n",
80ca4592 2484 con->servername, con->serverport, con->uri);
2485 }
229681c1
MS
2486 else if (code == HTTP_WEBIF_DISABLED)
2487 text = _cupsLangString(con->language,
2488 _("The web interface is currently disabled. Run "
2489 "\"cupsctl WebInterface=yes\" to enable it."));
ef416fc2 2490 else
2491 text = "";
2492
2493 snprintf(message, sizeof(message),
745129be
MS
2494 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2495 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
ef416fc2 2496 "<HTML>\n"
2497 "<HEAD>\n"
2498 "\t<META HTTP-EQUIV=\"Content-Type\" "
2499 "CONTENT=\"text/html; charset=utf-8\">\n"
229681c1 2500 "\t<TITLE>%s - " CUPS_SVERSION "</TITLE>\n"
ef416fc2 2501 "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" "
2502 "HREF=\"/cups.css\">\n"
80ca4592 2503 "%s"
ef416fc2 2504 "</HEAD>\n"
2505 "<BODY>\n"
229681c1 2506 "<H1>%s</H1>\n"
ef416fc2 2507 "<P>%s</P>\n"
2508 "</BODY>\n"
2509 "</HTML>\n",
229681c1 2510 httpStatus(code), redirect, httpStatus(code), text);
ef416fc2 2511
2512 if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0)
2513 return (0);
2514 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2515 (int)strlen(message)) < 0)
2516 return (0);
2517 if (httpPrintf(HTTP(con), "\r\n") < 0)
2518 return (0);
2519 if (httpPrintf(HTTP(con), "%s", message) < 0)
2520 return (0);
2521 }
2522 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2523 return (0);
2524
07725fee 2525 if (cupsdFlushHeader(con) < 0)
2526 return (0);
d09495fa 2527
ef416fc2 2528 con->http.state = HTTP_WAITING;
2529
2530 return (1);
2531}
2532
2533
ef416fc2 2534/*
2535 * 'cupsdSendHeader()' - Send an HTTP request.
2536 */
2537
2538int /* O - 1 on success, 0 on failure */
f899b121 2539cupsdSendHeader(
2540 cupsd_client_t *con, /* I - Client to send to */
2541 http_status_t code, /* I - HTTP status code */
2542 char *type, /* I - MIME type of document */
2543 int auth_type) /* I - Type of authentication */
ef416fc2 2544{
5a738aea 2545 char auth_str[1024]; /* Authorization string */
07ed0e9a 2546#if 0 /* def HAVE_GSSAPI */
5a738aea
MS
2547 static char *gss_buf = NULL; /* Kerberos auth data buffer */
2548 static int gss_bufsize = 0; /* Size of Kerberos auth data buffer */
2549#endif /* HAVE_GSSAPI */
f7deaa1a 2550
2551
4744bd90 2552 /*
2553 * Send the HTTP status header...
2554 */
2555
b423cd4c 2556 if (code == HTTP_CONTINUE)
2557 {
4744bd90 2558 /*
2559 * 100-continue doesn't send any headers...
2560 */
2561
07725fee 2562 return (httpPrintf(HTTP(con), "HTTP/%d.%d 100 Continue\r\n\r\n",
2563 con->http.version / 100, con->http.version % 100) > 0);
b423cd4c 2564 }
229681c1
MS
2565 else if (code == HTTP_WEBIF_DISABLED)
2566 {
2567 /*
2568 * Treat our special "web interface is disabled" status as "200 OK" for web
2569 * browsers.
2570 */
2571
2572 code = HTTP_OK;
2573 }
b423cd4c 2574
07725fee 2575 httpFlushWrite(HTTP(con));
2576
2577 con->http.data_encoding = HTTP_ENCODE_FIELDS;
2578
2579 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
2580 con->http.version % 100, code, httpStatus(code)) < 0)
2581 return (0);
ef416fc2 2582 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2583 return (0);
2584 if (ServerHeader)
2585 if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0)
2586 return (0);
2587 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2588 {
2589 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2590 return (0);
2591 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n",
2592 KeepAliveTimeout) < 0)
2593 return (0);
2594 }
2595 if (code == HTTP_METHOD_NOT_ALLOWED)
e6013cfa 2596 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0)
ef416fc2 2597 return (0);
2598
2599 if (code == HTTP_UNAUTHORIZED)
2600 {
5bd77a73 2601 if (auth_type == CUPSD_AUTH_NONE)
f899b121 2602 {
5bd77a73 2603 if (!con->best || con->best->type <= CUPSD_AUTH_NONE)
f899b121 2604 auth_type = DefaultAuthType;
2605 else
2606 auth_type = con->best->type;
2607 }
f7deaa1a 2608
2609 auth_str[0] = '\0';
2610
5bd77a73 2611 if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
f7deaa1a 2612 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
5bd77a73 2613 else if (auth_type == CUPSD_AUTH_DIGEST)
f7deaa1a 2614 snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
2615 con->http.hostname);
2616#ifdef HAVE_GSSAPI
07ed0e9a 2617 else if (auth_type == CUPSD_AUTH_NEGOTIATE /* && con->gss_output_token.length == 0 */)
f7deaa1a 2618 strlcpy(auth_str, "Negotiate", sizeof(auth_str));
2619#endif /* HAVE_GSSAPI */
2620
e07d4801
MS
2621 if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE &&
2622 !strcasecmp(con->http.hostname, "localhost"))
ef416fc2 2623 {
e07d4801
MS
2624 /*
2625 * Add a "trc" (try root certification) parameter for local non-Kerberos
2626 * requests when the request requires system group membership - then the
2627 * client knows the root certificate can/should be used.
2628 *
2629 * Also, for Mac OS X we also look for @AUTHKEY and add an "authkey"
2630 * parameter as needed...
2631 */
2632
10d09e33
MS
2633 char *name, /* Current user name */
2634 *auth_key; /* Auth key buffer */
f7deaa1a 2635 size_t auth_size; /* Size of remaining buffer */
2636
f7deaa1a 2637 auth_key = auth_str + strlen(auth_str);
2638 auth_size = sizeof(auth_str) - (auth_key - auth_str);
2639
10d09e33
MS
2640 for (name = (char *)cupsArrayFirst(con->best->names);
2641 name;
2642 name = (char *)cupsArrayNext(con->best->names))
f7deaa1a 2643 {
e07d4801 2644#ifdef HAVE_AUTHORIZATION_H
10d09e33 2645 if (!strncasecmp(name, "@AUTHKEY(", 9))
f7deaa1a 2646 {
10d09e33 2647 snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9);
f7deaa1a 2648 /* end parenthesis is stripped in conf.c */
2649 break;
2650 }
e07d4801
MS
2651 else
2652#endif /* HAVE_AUTHORIZATION_H */
10d09e33 2653 if (!strcasecmp(name, "@SYSTEM"))
f7deaa1a 2654 {
e07d4801
MS
2655#ifdef HAVE_AUTHORIZATION_H
2656 if (SystemGroupAuthKey)
2657 snprintf(auth_key, auth_size,
07ed0e9a 2658 ", authkey=\"%s\"",
e07d4801
MS
2659 SystemGroupAuthKey);
2660 else
2661#else
f11a948a 2662 strlcpy(auth_key, ", trc=\"y\"", auth_size);
e07d4801 2663#endif /* HAVE_AUTHORIZATION_H */
f7deaa1a 2664 break;
2665 }
2666 }
ef416fc2 2667 }
f7deaa1a 2668
bc44d920 2669 if (auth_str[0])
2670 {
68b10830
MS
2671 cupsdLogMessage(CUPSD_LOG_DEBUG,
2672 "cupsdSendHeader: %d WWW-Authenticate: %s", con->http.fd,
bc44d920 2673 auth_str);
2674
2675 if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
2676 return (0);
2677 }
ef416fc2 2678 }
2679
07ed0e9a 2680#if 0 /* def HAVE_GSSAPI */
f7deaa1a 2681 /*
2682 * WWW-Authenticate: Negotiate can be included even for
2683 * non-401 replies...
2684 */
2685
5a738aea 2686 if (con->gss_output_token.length > 0 && con->gss_output_token.length <= 65536)
f7deaa1a 2687 {
f7deaa1a 2688 OM_uint32 minor_status; /* Minor status code */
5a738aea
MS
2689 int bufsize; /* Size of output token buffer */
2690
2691
2692 bufsize = con->gss_output_token.length * 4 / 3 + 2;
2693
2694 if (bufsize > gss_bufsize)
2695 {
2696 char *buf; /* New buffer */
2697
f7deaa1a 2698
5a738aea 2699 bufsize = (bufsize + 1023) & 1023;/* Round up */
f7deaa1a 2700
5a738aea
MS
2701 if (gss_buf)
2702 buf = realloc(gss_buf, bufsize);
2703 else
2704 buf = malloc(bufsize);
2705
2706 if (!buf)
2707 {
2708 cupsdLogMessage(CUPSD_LOG_ERROR,
2709 "Unable to allocate %d bytes for Kerberos credentials!",
2710 bufsize);
2711 return (0);
2712 }
2713
2714 gss_buf = buf;
2715 gss_bufsize = bufsize;
2716 }
2717
2718 httpEncode64_2(gss_buf, gss_bufsize,
2719 con->gss_output_token.value,
f7deaa1a 2720 con->gss_output_token.length);
2721 gss_release_buffer(&minor_status, &con->gss_output_token);
2722
355e94dc 2723 cupsdLogMessage(CUPSD_LOG_DEBUG,
5a738aea 2724 "cupsdSendHeader: WWW-Authenticate: Negotiate %s", gss_buf);
355e94dc 2725
5a738aea
MS
2726 if (httpPrintf(HTTP(con), "WWW-Authenticate: Negotiate %s\r\n",
2727 gss_buf) < 0)
f7deaa1a 2728 return (0);
2729 }
5a738aea
MS
2730 else if (con->gss_output_token.length > 65536)
2731 {
2732 cupsdLogMessage(CUPSD_LOG_ERROR,
2733 "Kerberos credentials larger than 64k (%d)!",
ac884b6a 2734 (int)con->gss_output_token.length);
5a738aea
MS
2735 return (0);
2736 }
f7deaa1a 2737#endif /* HAVE_GSSAPI */
2738
e1d6a774 2739 if (con->language && strcmp(con->language->language, "C"))
ef416fc2 2740 {
2741 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2742 con->language->language) < 0)
2743 return (0);
2744 }
2745
e1d6a774 2746 if (type)
ef416fc2 2747 {
2748 if (!strcmp(type, "text/html"))
2749 {
2750 if (httpPrintf(HTTP(con),
2751 "Content-Type: text/html; charset=utf-8\r\n") < 0)
2752 return (0);
2753 }
2754 else if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2755 return (0);
2756 }
2757
2758 return (1);
2759}
2760
2761
2762/*
2763 * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs.
2764 */
2765
2766void
2767cupsdUpdateCGI(void)
2768{
2769 char *ptr, /* Pointer to end of line in buffer */
2770 message[1024]; /* Pointer to message text */
2771 int loglevel; /* Log level for message */
2772
2773
2774 while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
2775 message, sizeof(message))) != NULL)
f0ab5bff
MS
2776 {
2777 if (loglevel == CUPSD_LOG_INFO)
2778 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
2779
ef416fc2 2780 if (!strchr(CGIStatusBuffer->buffer, '\n'))
2781 break;
f0ab5bff 2782 }
ef416fc2 2783
d09495fa 2784 if (ptr == NULL && !CGIStatusBuffer->bufused)
ef416fc2 2785 {
2786 /*
2787 * Fatal error on pipe - should never happen!
2788 */
2789
2790 cupsdLogMessage(CUPSD_LOG_CRIT,
2791 "cupsdUpdateCGI: error reading from CGI error pipe - %s",
2792 strerror(errno));
2793 }
2794}
2795
2796
2797/*
2798 * 'cupsdWriteClient()' - Write data to a client as needed.
2799 */
2800
f7deaa1a 2801void
ef416fc2 2802cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */
2803{
68b10830
MS
2804 int bytes, /* Number of bytes written */
2805 field_col; /* Current column */
2806 char *bufptr, /* Pointer into buffer */
2807 *bufend; /* Pointer to end of buffer */
ef416fc2 2808 ipp_state_t ipp_state; /* IPP state value */
2809
2810
ef416fc2 2811 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1 2812 "cupsdWriteClient(con=%p(%d)) response=%p(%d), file=%d "
ef416fc2 2813 "pipe_pid=%d state=%d",
ae71f5de
MS
2814 con, con->http.fd, con->response,
2815 con->response ? con->response->state : -1,
b94498cf 2816 con->file, con->pipe_pid, con->http.state);
ef416fc2 2817
2818 if (con->http.state != HTTP_GET_SEND &&
2819 con->http.state != HTTP_POST_SEND)
8b116e60
MS
2820 {
2821 /*
2822 * If we get called in the wrong state, then something went wrong with the
2823 * connection and we need to shut it down...
2824 */
2825
f11a948a
MS
2826 cupsdLogMessage(CUPSD_LOG_DEBUG,
2827 "cupsdWriteClient: %d Closing on unknown HTTP state %d",
2828 con->http.fd, con->http.state);
8b116e60 2829 cupsdCloseClient(con);
f7deaa1a 2830 return;
8b116e60 2831 }
f7deaa1a 2832
2833 if (con->pipe_pid)
2834 {
2835 /*
2836 * Make sure we select on the CGI output...
2837 */
2838
f899b121 2839 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
f7deaa1a 2840
2841 if (!con->file_ready)
2842 {
2843 /*
2844 * Try again later when there is CGI output available...
2845 */
2846
2847 cupsdRemoveSelect(con->http.fd);
2848 return;
2849 }
2850
2851 con->file_ready = 0;
2852 }
ef416fc2 2853
b94498cf 2854 if (con->response && con->response->state != IPP_DATA)
ef416fc2 2855 {
07725fee 2856 ipp_state = ippWrite(HTTP(con), con->response);
b94498cf 2857 bytes = ipp_state != IPP_ERROR &&
2858 (con->file >= 0 || ipp_state != IPP_DATA);
ef416fc2 2859 }
68b10830
MS
2860 else if ((bytes = read(con->file, con->header,
2861 sizeof(con->header) - con->header_used)) > 0)
ef416fc2 2862 {
68b10830
MS
2863 con->header_used += bytes;
2864
ef416fc2 2865 if (con->pipe_pid && !con->got_fields)
2866 {
2867 /*
2868 * Inspect the data for Content-Type and other fields.
2869 */
2870
68b10830
MS
2871 for (bufptr = con->header, bufend = con->header + con->header_used,
2872 field_col = 0;
2873 !con->got_fields && bufptr < bufend;
2874 bufptr ++)
d1c13e16 2875 {
ef416fc2 2876 if (*bufptr == '\n')
2877 {
2878 /*
2879 * Send line to client...
2880 */
2881
68b10830 2882 if (bufptr > con->header && bufptr[-1] == '\r')
ef416fc2 2883 bufptr[-1] = '\0';
2884 *bufptr++ = '\0';
2885
68b10830 2886 cupsdLogMessage(CUPSD_LOG_DEBUG, "Script header: %s", con->header);
ef416fc2 2887
2888 if (!con->sent_header)
2889 {
2890 /*
2891 * Handle redirection and CGI status codes...
2892 */
2893
68b10830 2894 if (!strncasecmp(con->header, "Location:", 9))
d6ae789d 2895 {
5bd77a73 2896 cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE);
07725fee 2897 con->sent_header = 2;
2898
d6ae789d 2899 if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
f7deaa1a 2900 return;
d6ae789d 2901 }
68b10830 2902 else if (!strncasecmp(con->header, "Status:", 7))
07725fee 2903 {
68b10830
MS
2904 cupsdSendError(con, (http_status_t)atoi(con->header + 7),
2905 CUPSD_AUTH_NONE);
07725fee 2906 con->sent_header = 2;
2907 }
ef416fc2 2908 else
4744bd90 2909 {
5bd77a73 2910 cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE);
07725fee 2911 con->sent_header = 1;
ef416fc2 2912
4744bd90 2913 if (con->http.version == HTTP_1_1)
2914 {
4744bd90 2915 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
f7deaa1a 2916 return;
4744bd90 2917 }
2918 }
ef416fc2 2919 }
2920
68b10830
MS
2921 if (strncasecmp(con->header, "Status:", 7))
2922 httpPrintf(HTTP(con), "%s\r\n", con->header);
ef416fc2 2923
ef416fc2 2924 /*
2925 * Update buffer...
2926 */
2927
68b10830 2928 con->header_used -= bufptr - con->header;
d1c13e16 2929
68b10830
MS
2930 if (con->header_used > 0)
2931 memmove(con->header, bufptr, con->header_used);
d1c13e16 2932
68b10830 2933 bufptr = con->header - 1;
ef416fc2 2934
2935 /*
2936 * See if the line was empty...
2937 */
2938
68b10830 2939 if (field_col == 0)
d09495fa 2940 {
ef416fc2 2941 con->got_fields = 1;
d09495fa 2942
07725fee 2943 if (cupsdFlushHeader(con) < 0)
2944 {
2945 cupsdCloseClient(con);
f7deaa1a 2946 return;
07725fee 2947 }
d09495fa 2948
2949 if (con->http.version == HTTP_1_1)
2950 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2951 }
ef416fc2 2952 else
68b10830 2953 field_col = 0;
ef416fc2 2954 }
2955 else if (*bufptr != '\r')
68b10830 2956 field_col ++;
d1c13e16 2957 }
ef416fc2 2958
68b10830 2959 if (!con->got_fields)
47879b8b 2960 {
ef416fc2 2961 con->http.activity = time(NULL);
47879b8b
MS
2962 return;
2963 }
ef416fc2 2964 }
2965
68b10830 2966 if (con->header_used > 0)
ef416fc2 2967 {
68b10830 2968 if (httpWrite2(HTTP(con), con->header, con->header_used) < 0)
4744bd90 2969 {
f11a948a
MS
2970 cupsdLogMessage(CUPSD_LOG_DEBUG,
2971 "cupsdWriteClient: %d Closing for error %d (%s)",
2972 con->http.fd, con->http.error,
2973 strerror(con->http.error));
4744bd90 2974 cupsdCloseClient(con);
f7deaa1a 2975 return;
4744bd90 2976 }
ef416fc2 2977
01ce6322
MS
2978 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2979 httpFlushWrite(HTTP(con));
ae71f5de 2980
68b10830 2981 con->bytes += con->header_used;
ef416fc2 2982
4744bd90 2983 if (con->http.state == HTTP_WAITING)
2984 bytes = 0;
68b10830
MS
2985 else
2986 bytes = con->header_used;
2987
2988 con->header_used = 0;
4744bd90 2989 }
ef416fc2 2990 }
2991
8b116e60
MS
2992 if (bytes <= 0 ||
2993 (con->http.state != HTTP_GET_SEND && con->http.state != HTTP_POST_SEND))
ef416fc2 2994 {
38e73f87 2995 if (!con->sent_header && con->pipe_pid)
94da7e34
MS
2996 cupsdSendError(con, HTTP_SERVER_ERROR, CUPSD_AUTH_NONE);
2997 else
2998 {
2999 cupsdLogRequest(con, HTTP_OK);
ef416fc2 3000
94da7e34 3001 httpFlushWrite(HTTP(con));
ef416fc2 3002
94da7e34 3003 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED && con->sent_header == 1)
ef416fc2 3004 {
94da7e34
MS
3005 if (httpWrite2(HTTP(con), "", 0) < 0)
3006 {
f11a948a
MS
3007 cupsdLogMessage(CUPSD_LOG_DEBUG,
3008 "cupsdWriteClient: %d Closing for error %d (%s)",
3009 con->http.fd, con->http.error,
3010 strerror(con->http.error));
94da7e34
MS
3011 cupsdCloseClient(con);
3012 return;
3013 }
ef416fc2 3014 }
3015 }
3016
3017 con->http.state = HTTP_WAITING;
3018
f7deaa1a 3019 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
ef416fc2 3020
3021 if (con->file >= 0)
3022 {
f7deaa1a 3023 cupsdRemoveSelect(con->file);
ef416fc2 3024
3025 if (con->pipe_pid)
3026 cupsdEndProcess(con->pipe_pid, 0);
3027
ef416fc2 3028 close(con->file);
3029 con->file = -1;
3030 con->pipe_pid = 0;
3031 }
3032
3033 if (con->filename)
3034 {
ef416fc2 3035 unlink(con->filename);
3036 cupsdClearString(&con->filename);
3037 }
3038
2abf387c 3039 if (con->request)
ef416fc2 3040 {
3041 ippDelete(con->request);
3042 con->request = NULL;
3043 }
3044
2abf387c 3045 if (con->response)
ef416fc2 3046 {
3047 ippDelete(con->response);
3048 con->response = NULL;
3049 }
3050
3051 cupsdClearString(&con->command);
3052 cupsdClearString(&con->options);
b86bc4cf 3053 cupsdClearString(&con->query_string);
ef416fc2 3054
3055 if (!con->http.keep_alive)
3056 {
f11a948a
MS
3057 cupsdLogMessage(CUPSD_LOG_DEBUG,
3058 "cupsdWriteClient: %d Closing because Keep-Alive disabled",
3059 con->http.fd);
ef416fc2 3060 cupsdCloseClient(con);
f7deaa1a 3061 return;
ef416fc2 3062 }
f11a948a
MS
3063 else
3064 {
3065 cupsArrayRemove(ActiveClients, con);
3066 cupsdSetBusyState();
3067 }
ef416fc2 3068 }
3069
3070 con->http.activity = time(NULL);
f7deaa1a 3071}
ef416fc2 3072
f7deaa1a 3073
e1d6a774 3074/*
3075 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
3076 */
3077
3078static int /* O - 1 if modified since */
3079check_if_modified(
3080 cupsd_client_t *con, /* I - Client connection */
3081 struct stat *filestats) /* I - File information */
3082{
3083 char *ptr; /* Pointer into field */
3084 time_t date; /* Time/date value */
3085 off_t size; /* Size/length value */
3086
3087
3088 size = 0;
3089 date = 0;
3090 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
3091
3092 if (*ptr == '\0')
3093 return (1);
3094
3095 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1
MS
3096 "check_if_modified(con=%p(%d), "
3097 "filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"",
3098 con, con->http.fd, filestats, CUPS_LLCAST filestats->st_size,
3099 (int)filestats->st_mtime, ptr);
e1d6a774 3100
3101 while (*ptr != '\0')
3102 {
3103 while (isspace(*ptr) || *ptr == ';')
3104 ptr ++;
3105
3106 if (strncasecmp(ptr, "length=", 7) == 0)
3107 {
3108 ptr += 7;
3109 size = strtoll(ptr, NULL, 10);
3110
3111 while (isdigit(*ptr))
3112 ptr ++;
3113 }
3114 else if (isalpha(*ptr))
3115 {
3116 date = httpGetDateTime(ptr);
3117 while (*ptr != '\0' && *ptr != ';')
3118 ptr ++;
3119 }
e53920b9 3120 else
3121 ptr ++;
e1d6a774 3122 }
3123
e1d6a774 3124 return ((size != filestats->st_size && size != 0) ||
3125 (date < filestats->st_mtime && date != 0) ||
3126 (size == 0 && date == 0));
3127}
3128
3129
3dfe78b3
MS
3130/*
3131 * 'compare_clients()' - Compare two client connections.
3132 */
3133
3134static int /* O - Result of comparison */
3135compare_clients(cupsd_client_t *a, /* I - First client */
3136 cupsd_client_t *b, /* I - Second client */
3137 void *data) /* I - User data (not used) */
3138{
3139 (void)data;
3140
3141 if (a == b)
3142 return (0);
3143 else if (a < b)
3144 return (-1);
3145 else
3146 return (1);
3147}
3148
3149
ee571f26
MS
3150/*
3151 * 'data_ready()' - Check whether data is available from a client.
3152 */
3153
3154static int /* O - 1 if data is ready, 0 otherwise */
3155data_ready(cupsd_client_t *con) /* I - Client */
3156{
3157 if (con->http.used > 0)
3158 return (1);
3159#ifdef HAVE_SSL
3160 else if (con->http.tls)
3161 {
3162# ifdef HAVE_LIBSSL
3163 if (SSL_pending((SSL *)(con->http.tls)))
3164 return (1);
3165# elif defined(HAVE_GNUTLS)
7cf5915e 3166 if (gnutls_record_check_pending(con->http.tls))
ee571f26
MS
3167 return (1);
3168# elif defined(HAVE_CDSASSL)
3169 size_t bytes; /* Bytes that are available */
3170
7cf5915e 3171 if (!SSLGetBufferedReadSize(con->http.tls, &bytes) && bytes > 0)
ee571f26
MS
3172 return (1);
3173# endif /* HAVE_LIBSSL */
3174 }
3175#endif /* HAVE_SSL */
3176
3177 return (0);
3178}
3179
3180
80ca4592 3181#ifdef HAVE_SSL
e1d6a774 3182/*
3183 * 'encrypt_client()' - Enable encryption for the client...
3184 */
3185
3186static int /* O - 1 on success, 0 on error */
3187encrypt_client(cupsd_client_t *con) /* I - Client to encrypt */
3188{
80ca4592 3189# ifdef HAVE_LIBSSL
e1d6a774 3190 SSL_CTX *context; /* Context for encryption */
411affcf 3191 BIO *bio; /* BIO data */
e1d6a774 3192 unsigned long error; /* Error code */
3193
3194
b9faaae1
MS
3195 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3196 con->http.fd);
3197
8ca02f3c 3198 /*
3199 * Verify that we have a certificate...
3200 */
3201
3202 if (access(ServerKey, 0) || access(ServerCertificate, 0))
3203 {
3204 /*
3205 * Nope, make a self-signed certificate...
3206 */
3207
b86bc4cf 3208 if (!make_certificate(con))
8ca02f3c 3209 return (0);
3210 }
3211
e1d6a774 3212 /*
3213 * Create the SSL context and accept the connection...
3214 */
3215
3216 context = SSL_CTX_new(SSLv23_server_method());
3217
3218 SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
b19ccc9e 3219 if (SSLOptions & CUPSD_SSL_NOEMPTY)
58dc1933 3220 SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
e1d6a774 3221 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
58dc1933 3222 SSL_CTX_use_certificate_chain_file(context, ServerCertificate);
e1d6a774 3223
411affcf 3224 bio = BIO_new(_httpBIOMethods());
3225 BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
3226
7cf5915e
MS
3227 con->http.tls = SSL_new(context);
3228 SSL_set_bio(con->http.tls, bio, bio);
e1d6a774 3229
7cf5915e 3230 if (SSL_accept(con->http.tls) != 1)
e1d6a774 3231 {
b9faaae1 3232 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to encrypt connection from %s!",
e1d6a774 3233 con->http.hostname);
3234
3235 while ((error = ERR_get_error()) != 0)
b9faaae1 3236 cupsdLogMessage(CUPSD_LOG_ERROR, "%s", ERR_error_string(error, NULL));
e1d6a774 3237
3238 SSL_CTX_free(context);
7cf5915e
MS
3239 SSL_free(con->http.tls);
3240 con->http.tls = NULL;
e1d6a774 3241 return (0);
3242 }
3243
b9faaae1
MS
3244 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3245 con->http.hostname);
e1d6a774 3246
e1d6a774 3247 return (1);
f7deaa1a 3248
80ca4592 3249# elif defined(HAVE_GNUTLS)
e1d6a774 3250 int error; /* Error code */
3251 gnutls_certificate_server_credentials *credentials;
3252 /* TLS credentials */
3253
3254
b9faaae1
MS
3255 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3256 con->http.fd);
3257
e1d6a774 3258 /*
3259 * Verify that we have a certificate...
3260 */
3261
3262 if (access(ServerKey, 0) || access(ServerCertificate, 0))
3263 {
3264 /*
3265 * Nope, make a self-signed certificate...
3266 */
3267
b86bc4cf 3268 if (!make_certificate(con))
8ca02f3c 3269 return (0);
e1d6a774 3270 }
3271
3272 /*
3273 * Create the SSL object and perform the SSL handshake...
3274 */
3275
e1d6a774 3276 credentials = (gnutls_certificate_server_credentials *)
3277 malloc(sizeof(gnutls_certificate_server_credentials));
3278 if (credentials == NULL)
3279 {
3280 cupsdLogMessage(CUPSD_LOG_ERROR,
b9faaae1
MS
3281 "Unable to encrypt connection from %s - %s",
3282 con->http.hostname, strerror(errno));
e1d6a774 3283
e1d6a774 3284 return (0);
3285 }
3286
3287 gnutls_certificate_allocate_credentials(credentials);
f7deaa1a 3288 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
e1d6a774 3289 ServerKey, GNUTLS_X509_FMT_PEM);
3290
7cf5915e
MS
3291 gnutls_init(&con->http.tls), GNUTLS_SERVER);
3292 gnutls_set_default_priority(con->http.tls);
3293 gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
3294 gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr)HTTP(con));
3295 gnutls_transport_set_pull_function(con->http.tls, _httpReadGNUTLS);
3296 gnutls_transport_set_push_function(con->http.tls, _httpWriteGNUTLS);
e1d6a774 3297
7cf5915e 3298 error = gnutls_handshake(con->http.tls);
e1d6a774 3299
3300 if (error != GNUTLS_E_SUCCESS)
3301 {
3302 cupsdLogMessage(CUPSD_LOG_ERROR,
b9faaae1
MS
3303 "Unable to encrypt connection from %s - %s",
3304 con->http.hostname, gnutls_strerror(error));
e1d6a774 3305
7cf5915e 3306 gnutls_deinit(con->http.tls);
e1d6a774 3307 gnutls_certificate_free_credentials(*credentials);
7cf5915e 3308 con->http.tls = NULL;
e1d6a774 3309 free(credentials);
3310 return (0);
3311 }
3312
b9faaae1
MS
3313 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3314 con->http.hostname);
e1d6a774 3315
7cf5915e 3316 con->http.tls_credentials = credentials;
e1d6a774 3317 return (1);
3318
80ca4592 3319# elif defined(HAVE_CDSASSL)
e60ec91f
MS
3320 OSStatus error = 0; /* Error code */
3321 CFArrayRef peerCerts; /* Peer certificates */
e1d6a774 3322
3323
b9faaae1
MS
3324 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3325 con->http.fd);
3326
7cf5915e 3327 con->http.tls_credentials = get_cdsa_certificate(con);
e1d6a774 3328
7cf5915e 3329 if (!con->http.tls_credentials)
8ca02f3c 3330 {
3331 /*
3332 * No keychain (yet), make a self-signed certificate...
3333 */
3334
b86bc4cf 3335 if (make_certificate(con))
7cf5915e 3336 con->http.tls_credentials = get_cdsa_certificate(con);
8ca02f3c 3337 }
3338
7cf5915e 3339 if (!con->http.tls_credentials)
e1d6a774 3340 {
3341 cupsdLogMessage(CUPSD_LOG_ERROR,
b9faaae1
MS
3342 "Could not find signing key in keychain \"%s\"",
3343 ServerCertificate);
e1d6a774 3344 error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */
3345 }
3346
3347 if (!error)
7cf5915e 3348 error = SSLNewContext(true, &con->http.tls);
e1d6a774 3349
3350 if (!error)
7cf5915e 3351 error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA);
89d46774 3352
3353 if (!error)
7cf5915e 3354 error = SSLSetProtocolVersionEnabled(con->http.tls, kSSLProtocol2, false);
e1d6a774 3355
3356 if (!error)
7cf5915e 3357 error = SSLSetConnection(con->http.tls, HTTP(con));
e1d6a774 3358
3359 if (!error)
7cf5915e 3360 error = SSLSetAllowsExpiredCerts(con->http.tls, true);
e1d6a774 3361
3362 if (!error)
7cf5915e 3363 error = SSLSetAllowsAnyRoot(con->http.tls, true);
ef416fc2 3364
89d46774 3365 if (!error)
7cf5915e 3366 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
ef416fc2 3367
e1d6a774 3368 if (!error)
3369 {
3370 /*
3371 * Perform SSL/TLS handshake
3372 */
ef416fc2 3373
7cf5915e 3374 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
e1d6a774 3375 usleep(1000);
3376 }
ef416fc2 3377
e1d6a774 3378 if (error)
3379 {
3380 cupsdLogMessage(CUPSD_LOG_ERROR,
b9faaae1
MS
3381 "Unable to encrypt connection from %s - %s (%d)",
3382 con->http.hostname, cssmErrorString(error), (int)error);
ef416fc2 3383
e1d6a774 3384 con->http.error = error;
3385 con->http.status = HTTP_ERROR;
ef416fc2 3386
7cf5915e
MS
3387 if (con->http.tls)
3388 {
3389 SSLDisposeContext(con->http.tls);
3390 con->http.tls = NULL;
3391 }
89d46774 3392
7cf5915e
MS
3393 if (con->http.tls_credentials)
3394 {
3395 CFRelease(con->http.tls_credentials);
3396 con->http.tls_credentials = NULL;
3397 }
ef416fc2 3398
e1d6a774 3399 return (0);
ef416fc2 3400 }
3401
b9faaae1
MS
3402 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3403 con->http.hostname);
ef416fc2 3404
e60ec91f 3405 if (!SSLCopyPeerCertificates(con->http.tls, &peerCerts) && peerCerts)
7cf5915e
MS
3406 {
3407 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!",
3408 (int)CFArrayGetCount(peerCerts));
3409 }
3410 else
3411 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!");
3412
e1d6a774 3413 return (1);
3414
80ca4592 3415# endif /* HAVE_LIBSSL */
ef416fc2 3416}
80ca4592 3417#endif /* HAVE_SSL */
ef416fc2 3418
3419
3420#ifdef HAVE_CDSASSL
3421/*
b86bc4cf 3422 * 'get_cdsa_certificate()' - Get a SSL/TLS certificate from the System keychain.
ef416fc2 3423 */
3424
7cf5915e 3425static CFArrayRef /* O - Array of certificates */
4a4b4f99 3426get_cdsa_certificate(
7cf5915e 3427 cupsd_client_t *con) /* I - Client connection */
ef416fc2 3428{
3429 OSStatus err; /* Error info */
c7017ecc
MS
3430 SecKeychainRef keychain = NULL;/* Keychain reference */
3431 SecIdentitySearchRef search = NULL; /* Search reference */
3432 SecIdentityRef identity = NULL;/* Identity */
b86bc4cf 3433 CFArrayRef certificates = NULL;
3434 /* Certificate array */
c7017ecc
MS
3435# if HAVE_SECPOLICYCREATESSL
3436 SecPolicyRef policy = NULL; /* Policy ref */
3437 CFStringRef servername = NULL;
3438 /* Server name */
3439 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
3440 char localname[1024];/* Local hostname */
3441# elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3442 SecPolicyRef policy = NULL; /* Policy ref */
3443 SecPolicySearchRef policy_search = NULL;
3444 /* Policy search ref */
3445 CSSM_DATA options; /* Policy options */
3446 CSSM_APPLE_TP_SSL_OPTIONS
3447 ssl_options; /* SSL Option for hostname */
3448 char localname[1024];/* Local hostname */
3449# endif /* HAVE_SECPOLICYCREATESSL */
3450
ef416fc2 3451
c7017ecc
MS
3452 cupsdLogMessage(CUPSD_LOG_DEBUG,
3453 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3454 con->servername);
ef416fc2 3455
b86bc4cf 3456 if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
3457 {
b9faaae1
MS
3458 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
3459 ServerCertificate, cssmErrorString(err), (int)err);
c7017ecc
MS
3460 goto cleanup;
3461 }
3462
3463# if HAVE_SECPOLICYCREATESSL
3464 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
3465 kCFStringEncodingUTF8);
3466
e60ec91f
MS
3467 policy = SecPolicyCreateSSL(1, servername);
3468
3469 if (servername)
3470 CFRelease(servername);
3471
3472 if (!policy)
c7017ecc
MS
3473 {
3474 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3475 goto cleanup;
3476 }
3477
c7017ecc
MS
3478 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3479 &kCFTypeDictionaryKeyCallBacks,
3480 &kCFTypeDictionaryValueCallBacks)))
3481 {
3482 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
3483 goto cleanup;
3484 }
3485
3486 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
3487 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
3488 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
3489 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
3490
3491 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
3492
3493 if (err && DNSSDHostName)
3494 {
3495 /*
3496 * Search for the connection server name failed; try the DNS-SD .local
3497 * hostname instead...
3498 */
3499
3500 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
3501
3502 cupsdLogMessage(CUPSD_LOG_DEBUG,
3503 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3504 localname);
3505
3506 servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
3507 kCFStringEncodingUTF8);
3508
3509 CFRelease(policy);
3510
e60ec91f
MS
3511 policy = SecPolicyCreateSSL(1, servername);
3512
3513 if (servername)
3514 CFRelease(servername);
3515
3516 if (!policy)
c7017ecc
MS
3517 {
3518 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3519 goto cleanup;
3520 }
3521
c7017ecc
MS
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
0268488e
MS
4928 if (con->http.fields[HTTP_FIELD_HOST][0])
4929 {
4930 char *nameptr; /* Pointer to ":port" */
4931
4932 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4933 con->http.fields[HTTP_FIELD_HOST]);
4934 if ((nameptr = strrchr(server_name, ':')) != NULL && !strchr(nameptr, ']'))
4935 *nameptr = '\0'; /* Strip trailing ":port" */
4936 }
4937 else
4938 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4939 con->servername);
ef416fc2 4940
4941 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
4942
b94498cf 4943 if (auth_type[0])
4944 envp[envc ++] = auth_type;
4945
ef416fc2 4946 envp[envc ++] = lang;
4947 envp[envc ++] = "REDIRECT_STATUS=1";
b94498cf 4948 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
ef416fc2 4949 envp[envc ++] = server_name;
4950 envp[envc ++] = server_port;
4951 envp[envc ++] = remote_addr;
4952 envp[envc ++] = remote_host;
4953 envp[envc ++] = script_name;
b94498cf 4954 envp[envc ++] = script_filename;
ef416fc2 4955
b423cd4c 4956 if (path_info[0])
4957 envp[envc ++] = path_info;
4958
ef416fc2 4959 if (con->username[0])
4960 {
4961 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
4962
4963 envp[envc ++] = remote_user;
4964 }
4965
4966 if (con->http.version == HTTP_1_1)
4967 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
4968 else if (con->http.version == HTTP_1_0)
4969 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
4970 else
4971 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
4972
4973 if (con->http.cookie)
4974 {
4975 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
4976 con->http.cookie);
4977 envp[envc ++] = http_cookie;
4978 }
4979
4980 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
4981 {
4982 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
4983 con->http.fields[HTTP_FIELD_USER_AGENT]);
4984 envp[envc ++] = http_user_agent;
4985 }
4986
f7deaa1a 4987 if (con->http.fields[HTTP_FIELD_REFERER][0])
4988 {
4989 snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s",
4990 con->http.fields[HTTP_FIELD_REFERER]);
4991 envp[envc ++] = http_referer;
4992 }
4993
ef416fc2 4994 if (con->operation == HTTP_GET)
4995 {
ef416fc2 4996 envp[envc ++] = "REQUEST_METHOD=GET";
4997
b86bc4cf 4998 if (con->query_string)
ef416fc2 4999 {
5000 /*
5001 * Add GET form variables after ?...
5002 */
5003
b86bc4cf 5004 envp[envc ++] = con->query_string;
ef416fc2 5005 }
3dfe78b3
MS
5006 else
5007 envp[envc ++] = "QUERY_STRING=";
ef416fc2 5008 }
5009 else
5010 {
e1d6a774 5011 sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT,
5012 CUPS_LLCAST con->bytes);
ef416fc2 5013 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
5014 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
5015
5016 envp[envc ++] = "REQUEST_METHOD=POST";
5017 envp[envc ++] = content_length;
5018 envp[envc ++] = content_type;
5019 }
5020
5021 /*
5022 * Tell the CGI if we are using encryption...
5023 */
5024
a74454a7 5025 if (con->http.tls)
ef416fc2 5026 envp[envc ++] = "HTTPS=ON";
5027
5028 /*
5029 * Terminate the environment array...
5030 */
5031
5032 envp[envc] = NULL;
5033
38e73f87 5034 if (LogLevel >= CUPSD_LOG_DEBUG)
ef416fc2 5035 {
5036 for (i = 0; i < argc; i ++)
38e73f87
MS
5037 cupsdLogMessage(CUPSD_LOG_DEBUG,
5038 "[CGI] argv[%d] = \"%s\"", i, argv[i]);
ef416fc2 5039 for (i = 0; i < envc; i ++)
38e73f87
MS
5040 cupsdLogMessage(CUPSD_LOG_DEBUG,
5041 "[CGI] envp[%d] = \"%s\"", i, envp[i]);
ef416fc2 5042 }
5043
5044 /*
5045 * Create a pipe for the output...
5046 */
5047
5048 if (cupsdOpenPipe(fds))
5049 {
38e73f87 5050 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s",
ef416fc2 5051 argv[0], strerror(errno));
5052 return (0);
5053 }
5054
5055 /*
5056 * Then execute the command...
5057 */
5058
5059 if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
38e73f87 5060 -1, -1, root, DefaultProfile, NULL, &pid) < 0)
ef416fc2 5061 {
5062 /*
5063 * Error - can't fork!
5064 */
5065
38e73f87 5066 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0],
ef416fc2 5067 strerror(errno));
5068
5069 cupsdClosePipe(fds);
5070 pid = 0;
5071 }
5072 else
5073 {
5074 /*
5075 * Fork successful - return the PID...
5076 */
5077
5078 if (con->username[0])
5bd77a73 5079 cupsdAddCert(pid, con->username, ccache);
ef416fc2 5080
38e73f87 5081 cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] Started %s (PID %d)", command, pid);
ef416fc2 5082
5083 *outfile = fds[0];
5084 close(fds[1]);
5085 }
5086
ef416fc2 5087 return (pid);
5088}
5089
5090
e07d4801
MS
5091/*
5092 * 'valid_host()' - Is the Host: field valid?
5093 */
5094
5095static int /* O - 1 if valid, 0 if not */
5096valid_host(cupsd_client_t *con) /* I - Client connection */
5097{
5098 cupsd_alias_t *a; /* Current alias */
5099 cupsd_netif_t *netif; /* Current network interface */
5100 const char *host, /* Host field */
5101 *end; /* End character */
5102
5103
5104 host = con->http.fields[HTTP_FIELD_HOST];
5105
5106 if (httpAddrLocalhost(con->http.hostaddr))
5107 {
5108 /*
5109 * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical
5110 * addresses when accessing CUPS via the loopback interface...
5111 */
5112
5113 return (!strcasecmp(host, "localhost") ||
5114 !strncasecmp(host, "localhost:", 10) ||
5115 !strcasecmp(host, "localhost.") ||
5116 !strncasecmp(host, "localhost.:", 11) ||
5117#ifdef __linux
5118 !strcasecmp(host, "localhost.localdomain") ||
5119 !strncasecmp(host, "localhost.localdomain:", 22) ||
5120#endif /* __linux */
5121 !strcmp(host, "127.0.0.1") ||
5122 !strncmp(host, "127.0.0.1:", 10) ||
5123 !strcmp(host, "[::1]") ||
5124 !strncmp(host, "[::1]:", 6));
5125 }
5126
5127#ifdef HAVE_DNSSD
5128 /*
5129 * Check if the hostname is something.local (Bonjour); if so, allow it.
5130 */
5131
5132 if ((end = strrchr(host, '.')) != NULL &&
5133 (!strcasecmp(end, ".local") || !strncasecmp(end, ".local:", 7) ||
5134 !strcasecmp(end, ".local.") || !strncasecmp(end, ".local.:", 8)))
5135 return (1);
5136#endif /* HAVE_DNSSD */
5137
5138 /*
5139 * Check if the hostname is an IP address...
5140 */
5141
5142 if (isdigit(*host & 255) || *host == '[')
5143 {
5144 /*
5145 * Possible IPv4/IPv6 address...
5146 */
5147
5148 char temp[1024], /* Temporary string */
5149 *ptr; /* Pointer into temporary string */
5150 http_addrlist_t *addrlist; /* List of addresses */
5151
5152
5153 strlcpy(temp, host, sizeof(temp));
5154 if ((ptr = strrchr(temp, ':')) != NULL && !strchr(ptr, ']'))
5155 *ptr = '\0'; /* Strip :port from host value */
5156
5157 if ((addrlist = httpAddrGetList(temp, AF_UNSPEC, NULL)) != NULL)
5158 {
5159 /*
5160 * Good IPv4/IPv6 address...
5161 */
5162
5163 httpAddrFreeList(addrlist);
5164 return (1);
5165 }
5166 }
5167
5168 /*
5169 * Check for (alias) name matches...
5170 */
5171
5172 for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias);
5173 a;
5174 a = (cupsd_alias_t *)cupsArrayNext(ServerAlias))
5175 {
5176 /*
5177 * "ServerAlias *" allows all host values through...
5178 */
5179
5180 if (!strcmp(a->name, "*"))
5181 return (1);
5182
5183 if (!strncasecmp(host, a->name, a->namelen))
5184 {
5185 /*
5186 * Prefix matches; check the character at the end - it must be ":", ".",
5187 * ".:", or nul...
5188 */
5189
5190 end = host + a->namelen;
5191
5192 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5193 return (1);
5194 }
5195 }
5196
5197#ifdef HAVE_DNSSD
5198 for (a = (cupsd_alias_t *)cupsArrayFirst(DNSSDAlias);
5199 a;
5200 a = (cupsd_alias_t *)cupsArrayNext(DNSSDAlias))
5201 {
5202 /*
5203 * "ServerAlias *" allows all host values through...
5204 */
5205
5206 if (!strcmp(a->name, "*"))
5207 return (1);
5208
5209 if (!strncasecmp(host, a->name, a->namelen))
5210 {
5211 /*
5212 * Prefix matches; check the character at the end - it must be ":", ".",
5213 * ".:", or nul...
5214 */
5215
5216 end = host + a->namelen;
5217
5218 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5219 return (1);
5220 }
5221 }
5222#endif /* HAVE_DNSSD */
5223
5224 /*
5225 * Check for interface hostname matches...
5226 */
5227
5228 for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
5229 netif;
5230 netif = (cupsd_netif_t *)cupsArrayNext(NetIFList))
5231 {
5232 if (!strncasecmp(host, netif->hostname, netif->hostlen))
5233 {
5234 /*
5235 * Prefix matches; check the character at the end - it must be ":", ".",
5236 * ".:", or nul...
5237 */
5238
5239 end = host + netif->hostlen;
5240
5241 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5242 return (1);
5243 }
5244 }
5245
5246 return (0);
5247}
5248
5249
ef416fc2 5250/*
a74454a7 5251 * 'write_file()' - Send a file via HTTP.
e1d6a774 5252 */
5253
5254static int /* O - 0 on failure, 1 on success */
a74454a7 5255write_file(cupsd_client_t *con, /* I - Client connection */
5256 http_status_t code, /* I - HTTP status */
5257 char *filename, /* I - Filename */
5258 char *type, /* I - File type */
5259 struct stat *filestats) /* O - File information */
e1d6a774 5260{
5261 con->file = open(filename, O_RDONLY);
5262
b9faaae1
MS
5263 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5264 "write_file(con=%p(%d), code=%d, filename=\"%s\" (%d), "
5265 "type=\"%s\", filestats=%p)", con, con->http.fd,
5266 code, filename, con->file, type ? type : "(null)", filestats);
e1d6a774 5267
5268 if (con->file < 0)
5269 return (0);
5270
5271 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
5272
5273 con->pipe_pid = 0;
5274
5bd77a73 5275 if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
e1d6a774 5276 return (0);
5277
5278 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
5279 httpGetDateString(filestats->st_mtime)) < 0)
5280 return (0);
5281 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n",
5282 CUPS_LLCAST filestats->st_size) < 0)
5283 return (0);
5284 if (httpPrintf(HTTP(con), "\r\n") < 0)
5285 return (0);
5286
07725fee 5287 if (cupsdFlushHeader(con) < 0)
5288 return (0);
d09495fa 5289
e1d6a774 5290 con->http.data_encoding = HTTP_ENCODE_LENGTH;
5291 con->http.data_remaining = filestats->st_size;
5292
5293 if (con->http.data_remaining <= INT_MAX)
5294 con->http._data_remaining = con->http.data_remaining;
5295 else
5296 con->http._data_remaining = INT_MAX;
5297
f7deaa1a 5298 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
5299 (cupsd_selfunc_t)cupsdWriteClient, con);
e1d6a774 5300
5301 return (1);
5302}
5303
5304
5305/*
f899b121 5306 * 'write_pipe()' - Flag that data is available on the CGI pipe.
5307 */
5308
5309static void
5310write_pipe(cupsd_client_t *con) /* I - Client connection */
5311{
b9faaae1
MS
5312 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5313 "write_pipe(con=%p(%d)) CGI output on fd %d",
5314 con, con->http.fd, con->file);
f899b121 5315
5316 con->file_ready = 1;
5317
5318 cupsdRemoveSelect(con->file);
5319 cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
5320}
5321
5322
5323/*
b19ccc9e 5324 * End of "$Id: client.c 7950 2008-09-17 00:21:59Z mike $".
ef416fc2 5325 */