1/* 2 * "$Id: client.c 12131 2014-08-28 23:38:16Z msweet $" 3 * 4 * Client routines for the CUPS scheduler. 5 * 6 * Copyright 2007-2014 by Apple Inc. 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. 11 * 12 * These coded instructions, statements, and computer programs are the 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/". 17 */ 18 19/* 20 * Include necessary headers... 21 */ 22 23#define _CUPS_NO_DEPRECATED 24#define _HTTP_NO_PRIVATE 25#include "cupsd.h" 26 27#ifdef __APPLE__ 28# include <libproc.h> 29#endif /* __APPLE__ */ 30#ifdef HAVE_TCPD_H 31# include <tcpd.h> 32#endif /* HAVE_TCPD_H */ 33 34 35/* 36 * Local functions... 37 */ 38 39static int check_if_modified(cupsd_client_t *con, 40 struct stat *filestats); 41static int compare_clients(cupsd_client_t *a, cupsd_client_t *b, 42 void *data); 43#ifdef HAVE_SSL 44static int cupsd_start_tls(cupsd_client_t *con, http_encryption_t e); 45#endif /* HAVE_SSL */ 46static char *get_file(cupsd_client_t *con, struct stat *filestats, 47 char *filename, size_t len); 48static http_status_t install_cupsd_conf(cupsd_client_t *con); 49static int is_cgi(cupsd_client_t *con, const char *filename, 50 struct stat *filestats, mime_type_t *type); 51static int is_path_absolute(const char *path); 52static int pipe_command(cupsd_client_t *con, int infile, int *outfile, 53 char *command, char *options, int root); 54static int valid_host(cupsd_client_t *con); 55static int write_file(cupsd_client_t *con, http_status_t code, 56 char *filename, char *type, 57 struct stat *filestats); 58static void write_pipe(cupsd_client_t *con); 59 60 61/* 62 * 'cupsdAcceptClient()' - Accept a new client. 63 */ 64 65void 66cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ 67{ 68 const char *hostname; /* Hostname of client */ 69 char name[256]; /* Hostname of client */ 70 int count; /* Count of connections on a host */ 71 cupsd_client_t *con, /* New client pointer */ 72 *tempcon; /* Temporary client pointer */ 73 socklen_t addrlen; /* Length of address */ 74 http_addr_t temp; /* Temporary address variable */ 75 static time_t last_dos = 0; /* Time of last DoS attack */ 76#ifdef HAVE_TCPD_H 77 struct request_info wrap_req; /* TCP wrappers request information */ 78#endif /* HAVE_TCPD_H */ 79 80 81 cupsdLogMessage(CUPSD_LOG_DEBUG2, 82 "cupsdAcceptClient(lis=%p(%d)) Clients=%d", 83 lis, lis->fd, cupsArrayCount(Clients)); 84 85 /* 86 * Make sure we don't have a full set of clients already... 87 */ 88 89 if (cupsArrayCount(Clients) == MaxClients) 90 return; 91 92 /* 93 * Get a pointer to the next available client... 94 */ 95 96 if (!Clients) 97 Clients = cupsArrayNew(NULL, NULL); 98 99 if (!Clients) 100 { 101 cupsdLogMessage(CUPSD_LOG_ERROR, 102 "Unable to allocate memory for clients array!"); 103 cupsdPauseListening(); 104 return; 105 } 106 107 if (!ActiveClients) 108 ActiveClients = cupsArrayNew((cups_array_func_t)compare_clients, NULL); 109 110 if (!ActiveClients) 111 { 112 cupsdLogMessage(CUPSD_LOG_ERROR, 113 "Unable to allocate memory for active clients array!"); 114 cupsdPauseListening(); 115 return; 116 } 117 118 if ((con = calloc(1, sizeof(cupsd_client_t))) == NULL) 119 { 120 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for client!"); 121 cupsdPauseListening(); 122 return; 123 } 124 125 /* 126 * Accept the client and get the remote address... 127 */ 128 129 con->number = ++ LastClientNumber; 130 con->file = -1; 131 132 if ((con->http = httpAcceptConnection(lis->fd, 0)) == NULL) 133 { 134 if (errno == ENFILE || errno == EMFILE) 135 cupsdPauseListening(); 136 137 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.", 138 strerror(errno)); 139 free(con); 140 141 return; 142 } 143 144 /* 145 * Save the connected address and port number... 146 */ 147 148 con->clientaddr = lis->address; 149 150 /* 151 * Check the number of clients on the same address... 152 */ 153 154 for (count = 0, tempcon = (cupsd_client_t *)cupsArrayFirst(Clients); 155 tempcon; 156 tempcon = (cupsd_client_t *)cupsArrayNext(Clients)) 157 if (httpAddrEqual(httpGetAddress(tempcon->http), httpGetAddress(con->http))) 158 { 159 count ++; 160 if (count >= MaxClientsPerHost) 161 break; 162 } 163 164 if (count >= MaxClientsPerHost) 165 { 166 if ((time(NULL) - last_dos) >= 60) 167 { 168 last_dos = time(NULL); 169 cupsdLogMessage(CUPSD_LOG_WARN, 170 "Possible DoS attack - more than %d clients connecting " 171 "from %s.", 172 MaxClientsPerHost, 173 httpGetHostname(con->http, name, sizeof(name))); 174 } 175 176 httpClose(con->http); 177 free(con); 178 return; 179 } 180 181 /* 182 * Get the hostname or format the IP address as needed... 183 */ 184 185 if (HostNameLookups) 186 hostname = httpResolveHostname(con->http, NULL, 0); 187 else 188 hostname = httpGetHostname(con->http, NULL, 0); 189 190 if (hostname == NULL && HostNameLookups == 2) 191 { 192 /* 193 * Can't have an unresolved IP address with double-lookups enabled... 194 */ 195 196 httpClose(con->http); 197 198 cupsdLogClient(con, CUPSD_LOG_WARN, 199 "Name lookup failed - connection from %s closed!", 200 httpGetHostname(con->http, NULL, 0)); 201 202 free(con); 203 return; 204 } 205 206 if (HostNameLookups == 2) 207 { 208 /* 209 * Do double lookups as needed... 210 */ 211 212 http_addrlist_t *addrlist, /* List of addresses */ 213 *addr; /* Current address */ 214 215 if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, NULL)) != NULL) 216 { 217 /* 218 * See if the hostname maps to the same IP address... 219 */ 220 221 for (addr = addrlist; addr; addr = addr->next) 222 if (httpAddrEqual(httpGetAddress(con->http), &(addr->addr))) 223 break; 224 } 225 else 226 addr = NULL; 227 228 httpAddrFreeList(addrlist); 229 230 if (!addr) 231 { 232 /* 233 * Can't have a hostname that doesn't resolve to the same IP address 234 * with double-lookups enabled... 235 */ 236 237 httpClose(con->http); 238 239 cupsdLogClient(con, CUPSD_LOG_WARN, 240 "IP lookup failed - connection from %s closed!", 241 httpGetHostname(con->http, NULL, 0)); 242 free(con); 243 return; 244 } 245 } 246 247#ifdef HAVE_TCPD_H 248 /* 249 * See if the connection is denied by TCP wrappers... 250 */ 251 252 request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, httpGetFd(con->http), 253 NULL); 254 fromhost(&wrap_req); 255 256 if (!hosts_access(&wrap_req)) 257 { 258 httpClose(con->http); 259 260 cupsdLogClient(con, CUPSD_LOG_WARN, 261 "Connection from %s refused by /etc/hosts.allow and " 262 "/etc/hosts.deny rules.", httpGetHostname(con->http, NULL, 0)); 263 free(con); 264 return; 265 } 266#endif /* HAVE_TCPD_H */ 267 268#ifdef AF_LOCAL 269 if (httpAddrFamily(httpGetAddress(con->http)) == AF_LOCAL) 270 { 271# ifdef __APPLE__ 272 socklen_t peersize; /* Size of peer credentials */ 273 pid_t peerpid; /* Peer process ID */ 274 char peername[256]; /* Name of process */ 275 276 peersize = sizeof(peerpid); 277 if (!getsockopt(httpGetFd(con->http), SOL_LOCAL, LOCAL_PEERPID, &peerpid, 278 &peersize)) 279 { 280 if (!proc_name((int)peerpid, peername, sizeof(peername))) 281 cupsdLogClient(con, CUPSD_LOG_DEBUG, 282 "Accepted from %s (Domain ???[%d])", 283 httpGetHostname(con->http, NULL, 0), (int)peerpid); 284 else 285 cupsdLogClient(con, CUPSD_LOG_DEBUG, 286 "Accepted from %s (Domain %s[%d])", 287 httpGetHostname(con->http, NULL, 0), peername, (int)peerpid); 288 } 289 else 290# endif /* __APPLE__ */ 291 292 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Accepted from %s (Domain)", 293 httpGetHostname(con->http, NULL, 0)); 294 } 295 else 296#endif /* AF_LOCAL */ 297 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Accepted from %s:%d (IPv%d)", 298 httpGetHostname(con->http, NULL, 0), 299 httpAddrPort(httpGetAddress(con->http)), 300 httpAddrFamily(httpGetAddress(con->http)) == AF_INET ? 4 : 6); 301 302 /* 303 * Get the local address the client connected to... 304 */ 305 306 addrlen = sizeof(temp); 307 if (getsockname(httpGetFd(con->http), (struct sockaddr *)&temp, &addrlen)) 308 { 309 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to get local address - %s", 310 strerror(errno)); 311 312 strlcpy(con->servername, "localhost", sizeof(con->servername)); 313 con->serverport = LocalPort; 314 } 315#ifdef AF_LOCAL 316 else if (httpAddrFamily(&temp) == AF_LOCAL) 317 { 318 strlcpy(con->servername, "localhost", sizeof(con->servername)); 319 con->serverport = LocalPort; 320 } 321#endif /* AF_LOCAL */ 322 else 323 { 324 if (httpAddrLocalhost(&temp)) 325 strlcpy(con->servername, "localhost", sizeof(con->servername)); 326 else if (HostNameLookups) 327 httpAddrLookup(&temp, con->servername, sizeof(con->servername)); 328 else 329 httpAddrString(&temp, con->servername, sizeof(con->servername)); 330 331 con->serverport = httpAddrPort(&(lis->address)); 332 } 333 334 /* 335 * Add the connection to the array of active clients... 336 */ 337 338 cupsArrayAdd(Clients, con); 339 340 /* 341 * Add the socket to the server select. 342 */ 343 344 cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, NULL, 345 con); 346 347 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for request."); 348 349 /* 350 * Temporarily suspend accept()'s until we lose a client... 351 */ 352 353 if (cupsArrayCount(Clients) == MaxClients) 354 cupsdPauseListening(); 355 356#ifdef HAVE_SSL 357 /* 358 * See if we are connecting on a secure port... 359 */ 360 361 if (lis->encryption == HTTP_ENCRYPTION_ALWAYS) 362 { 363 /* 364 * https connection; go secure... 365 */ 366 367 if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS)) 368 cupsdCloseClient(con); 369 } 370 else 371 con->auto_ssl = 1; 372#endif /* HAVE_SSL */ 373} 374 375 376/* 377 * 'cupsdCloseAllClients()' - Close all remote clients immediately. 378 */ 379 380void 381cupsdCloseAllClients(void) 382{ 383 cupsd_client_t *con; /* Current client */ 384 385 386 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d", 387 cupsArrayCount(Clients)); 388 389 for (con = (cupsd_client_t *)cupsArrayFirst(Clients); 390 con; 391 con = (cupsd_client_t *)cupsArrayNext(Clients)) 392 if (cupsdCloseClient(con)) 393 cupsdCloseClient(con); 394} 395 396 397/* 398 * 'cupsdCloseClient()' - Close a remote client. 399 */ 400 401int /* O - 1 if partial close, 0 if fully closed */ 402cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ 403{ 404 int partial; /* Do partial close for SSL? */ 405 406 407 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing connection."); 408 409 /* 410 * Flush pending writes before closing... 411 */ 412 413 httpFlushWrite(con->http); 414 415 partial = 0; 416 417 if (con->pipe_pid != 0) 418 { 419 /* 420 * Stop any CGI process... 421 */ 422 423 cupsdEndProcess(con->pipe_pid, 1); 424 con->pipe_pid = 0; 425 } 426 427 if (con->file >= 0) 428 { 429 cupsdRemoveSelect(con->file); 430 431 close(con->file); 432 con->file = -1; 433 } 434 435 /* 436 * Close the socket and clear the file from the input set for select()... 437 */ 438 439 if (httpGetFd(con->http) >= 0) 440 { 441 cupsArrayRemove(ActiveClients, con); 442 cupsdSetBusyState(); 443 444#ifdef HAVE_SSL 445 /* 446 * Shutdown encryption as needed... 447 */ 448 449 if (httpIsEncrypted(con->http)) 450 partial = 1; 451#endif /* HAVE_SSL */ 452 453 if (partial) 454 { 455 /* 456 * Only do a partial close so that the encrypted client gets everything. 457 */ 458 459 httpShutdown(con->http); 460 cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, 461 NULL, con); 462 463 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for socket close."); 464 } 465 else 466 { 467 /* 468 * Shut the socket down fully... 469 */ 470 471 cupsdRemoveSelect(httpGetFd(con->http)); 472 httpClose(con->http); 473 con->http = NULL; 474 } 475 } 476 477 if (!partial) 478 { 479 /* 480 * Free memory... 481 */ 482 483 cupsdRemoveSelect(httpGetFd(con->http)); 484 485 httpClose(con->http); 486 487 cupsdClearString(&con->filename); 488 cupsdClearString(&con->command); 489 cupsdClearString(&con->options); 490 cupsdClearString(&con->query_string); 491 492 if (con->request) 493 { 494 ippDelete(con->request); 495 con->request = NULL; 496 } 497 498 if (con->response) 499 { 500 ippDelete(con->response); 501 con->response = NULL; 502 } 503 504 if (con->language) 505 { 506 cupsLangFree(con->language); 507 con->language = NULL; 508 } 509 510#ifdef HAVE_AUTHORIZATION_H 511 if (con->authref) 512 { 513 AuthorizationFree(con->authref, kAuthorizationFlagDefaults); 514 con->authref = NULL; 515 } 516#endif /* HAVE_AUTHORIZATION_H */ 517 518 /* 519 * Re-enable new client connections if we are going back under the 520 * limit... 521 */ 522 523 if (cupsArrayCount(Clients) == MaxClients) 524 cupsdResumeListening(); 525 526 /* 527 * Compact the list of clients as necessary... 528 */ 529 530 cupsArrayRemove(Clients, con); 531 532 free(con); 533 } 534 535 return (partial); 536} 537 538 539/* 540 * 'cupsdReadClient()' - Read data from a client. 541 */ 542 543void 544cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ 545{ 546 char line[32768], /* Line from client... */ 547 locale[64], /* Locale */ 548 *ptr; /* Pointer into strings */ 549 http_status_t status; /* Transfer status */ 550 ipp_state_t ipp_state; /* State of IPP transfer */ 551 int bytes; /* Number of bytes to POST */ 552 char *filename; /* Name of file for GET/HEAD */ 553 char buf[1024]; /* Buffer for real filename */ 554 struct stat filestats; /* File information */ 555 mime_type_t *type; /* MIME type of file */ 556 cupsd_printer_t *p; /* Printer */ 557 static unsigned request_id = 0; /* Request ID for temp files */ 558 559 560 status = HTTP_STATUS_CONTINUE; 561 562 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 563 "cupsdReadClient " 564 "error=%d, " 565 "used=%d, " 566 "state=%s, " 567 "data_encoding=HTTP_ENCODING_%s, " 568 "data_remaining=" CUPS_LLFMT ", " 569 "request=%p(%s), " 570 "file=%d", 571 httpError(con->http), (int)httpGetReady(con->http), 572 httpStateString(httpGetState(con->http)), 573 httpIsChunked(con->http) ? "CHUNKED" : "LENGTH", 574 CUPS_LLCAST httpGetRemaining(con->http), 575 con->request, 576 con->request ? ippStateString(ippGetState(con->request)) : "", 577 con->file); 578 579 if (httpGetState(con->http) == HTTP_STATE_GET_SEND || 580 httpGetState(con->http) == HTTP_STATE_POST_SEND || 581 httpGetState(con->http) == HTTP_STATE_STATUS) 582 { 583 /* 584 * If we get called in the wrong state, then something went wrong with the 585 * connection and we need to shut it down... 586 */ 587 588 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP read state %s.", 589 httpStateString(httpGetState(con->http))); 590 cupsdCloseClient(con); 591 return; 592 } 593 594#ifdef HAVE_SSL 595 if (con->auto_ssl) 596 { 597 /* 598 * Automatically check for a SSL/TLS handshake... 599 */ 600 601 con->auto_ssl = 0; 602 603 if (recv(httpGetFd(con->http), buf, 1, MSG_PEEK) == 1 && 604 (!buf[0] || !strchr("DGHOPT", buf[0]))) 605 { 606 /* 607 * Encrypt this connection... 608 */ 609 610 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 611 "Saw first byte %02X, auto-negotiating " 612 "SSL/TLS session.", buf[0] & 255); 613 614 if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS)) 615 cupsdCloseClient(con); 616 617 return; 618 } 619 } 620#endif /* HAVE_SSL */ 621 622 switch (httpGetState(con->http)) 623 { 624 case HTTP_STATE_WAITING : 625 /* 626 * See if we've received a request line... 627 */ 628 629 con->operation = httpReadRequest(con->http, con->uri, sizeof(con->uri)); 630 if (con->operation == HTTP_STATE_ERROR || 631 con->operation == HTTP_STATE_UNKNOWN_METHOD || 632 con->operation == HTTP_STATE_UNKNOWN_VERSION) 633 { 634 if (httpError(con->http)) 635 cupsdLogClient(con, CUPSD_LOG_DEBUG, 636 "HTTP_STATE_WAITING Closing for error %d (%s)", 637 httpError(con->http), strerror(httpError(con->http))); 638 else 639 cupsdLogClient(con, CUPSD_LOG_DEBUG, 640 "HTTP_STATE_WAITING Closing on error: %s", 641 cupsLastErrorString()); 642 643 cupsdCloseClient(con); 644 return; 645 } 646 647 /* 648 * Ignore blank request lines... 649 */ 650 651 if (con->operation == HTTP_STATE_WAITING) 652 break; 653 654 /* 655 * Clear other state variables... 656 */ 657 658 con->bytes = 0; 659 con->file = -1; 660 con->file_ready = 0; 661 con->pipe_pid = 0; 662 con->username[0] = '\0'; 663 con->password[0] = '\0'; 664 665 cupsdClearString(&con->command); 666 cupsdClearString(&con->options); 667 cupsdClearString(&con->query_string); 668 669 if (con->request) 670 { 671 ippDelete(con->request); 672 con->request = NULL; 673 } 674 675 if (con->response) 676 { 677 ippDelete(con->response); 678 con->response = NULL; 679 } 680 681 if (con->language) 682 { 683 cupsLangFree(con->language); 684 con->language = NULL; 685 } 686 687#ifdef HAVE_GSSAPI 688 con->have_gss = 0; 689 con->gss_uid = 0; 690#endif /* HAVE_GSSAPI */ 691 692 /* 693 * Handle full URLs in the request line... 694 */ 695 696 if (strcmp(con->uri, "*")) 697 { 698 char scheme[HTTP_MAX_URI], /* Method/scheme */ 699 userpass[HTTP_MAX_URI], /* Username:password */ 700 hostname[HTTP_MAX_URI], /* Hostname */ 701 resource[HTTP_MAX_URI]; /* Resource path */ 702 int port; /* Port number */ 703 704 /* 705 * Separate the URI into its components... 706 */ 707 708 if (httpSeparateURI(HTTP_URI_CODING_MOST, con->uri, 709 scheme, sizeof(scheme), 710 userpass, sizeof(userpass), 711 hostname, sizeof(hostname), &port, 712 resource, sizeof(resource)) < HTTP_URI_STATUS_OK) 713 { 714 cupsdLogClient(con, CUPSD_LOG_ERROR, "Bad URI \"%s\" in request.", 715 con->uri); 716 cupsdSendError(con, HTTP_STATUS_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE); 717 cupsdCloseClient(con); 718 return; 719 } 720 721 /* 722 * Only allow URIs with the servername, localhost, or an IP 723 * address... 724 */ 725 726 if (strcmp(scheme, "file") && 727 _cups_strcasecmp(hostname, ServerName) && 728 _cups_strcasecmp(hostname, "localhost") && 729 !cupsArrayFind(ServerAlias, hostname) && 730 !isdigit(hostname[0]) && hostname[0] != '[') 731 { 732 /* 733 * Nope, we don't do proxies... 734 */ 735 736 cupsdLogClient(con, CUPSD_LOG_ERROR, "Bad URI \"%s\" in request.", 737 con->uri); 738 cupsdSendError(con, HTTP_STATUS_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE); 739 cupsdCloseClient(con); 740 return; 741 } 742 743 /* 744 * Copy the resource portion back into the URI; both resource and 745 * con->uri are HTTP_MAX_URI bytes in size... 746 */ 747 748 strlcpy(con->uri, resource, sizeof(con->uri)); 749 } 750 751 /* 752 * Process the request... 753 */ 754 755 gettimeofday(&(con->start), NULL); 756 757 cupsdLogClient(con, CUPSD_LOG_DEBUG, "%s %s HTTP/%d.%d", 758 httpStateString(con->operation) + 11, con->uri, 759 httpGetVersion(con->http) / 100, 760 httpGetVersion(con->http) % 100); 761 762 if (!cupsArrayFind(ActiveClients, con)) 763 { 764 cupsArrayAdd(ActiveClients, con); 765 cupsdSetBusyState(); 766 } 767 768 case HTTP_STATE_OPTIONS : 769 case HTTP_STATE_DELETE : 770 case HTTP_STATE_GET : 771 case HTTP_STATE_HEAD : 772 case HTTP_STATE_POST : 773 case HTTP_STATE_PUT : 774 case HTTP_STATE_TRACE : 775 /* 776 * Parse incoming parameters until the status changes... 777 */ 778 779 while ((status = httpUpdate(con->http)) == HTTP_STATUS_CONTINUE) 780 if (!httpGetReady(con->http)) 781 break; 782 783 if (status != HTTP_STATUS_OK && status != HTTP_STATUS_CONTINUE) 784 { 785 if (httpError(con->http) && httpError(con->http) != EPIPE) 786 cupsdLogClient(con, CUPSD_LOG_DEBUG, 787 "Closing for error %d (%s) while reading headers.", 788 httpError(con->http), strerror(httpError(con->http))); 789 else 790 cupsdLogClient(con, CUPSD_LOG_DEBUG, 791 "Closing on EOF while reading headers."); 792 793 cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE); 794 cupsdCloseClient(con); 795 return; 796 } 797 break; 798 799 default : 800 if (!httpGetReady(con->http) && recv(httpGetFd(con->http), buf, 1, MSG_PEEK) < 1) 801 { 802 /* 803 * Connection closed... 804 */ 805 806 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on EOF."); 807 cupsdCloseClient(con); 808 return; 809 } 810 break; /* Anti-compiler-warning-code */ 811 } 812 813 /* 814 * Handle new transfers... 815 */ 816 817 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Read: status=%d", status); 818 819 if (status == HTTP_STATUS_OK) 820 { 821 if (httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE)[0]) 822 { 823 /* 824 * Figure out the locale from the Accept-Language and Content-Type 825 * fields... 826 */ 827 828 if ((ptr = strchr(httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE), 829 ',')) != NULL) 830 *ptr = '\0'; 831 832 if ((ptr = strchr(httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE), 833 ';')) != NULL) 834 *ptr = '\0'; 835 836 if ((ptr = strstr(httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE), 837 "charset=")) != NULL) 838 { 839 /* 840 * Combine language and charset, and trim any extra params in the 841 * content-type. 842 */ 843 844 snprintf(locale, sizeof(locale), "%s.%s", 845 httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE), ptr + 8); 846 847 if ((ptr = strchr(locale, ',')) != NULL) 848 *ptr = '\0'; 849 } 850 else 851 snprintf(locale, sizeof(locale), "%s.UTF-8", 852 httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE)); 853 854 con->language = cupsLangGet(locale); 855 } 856 else 857 con->language = cupsLangGet(DefaultLocale); 858 859 cupsdAuthorize(con); 860 861 if (!_cups_strncasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), 862 "Keep-Alive", 10) && KeepAlive) 863 httpSetKeepAlive(con->http, HTTP_KEEPALIVE_ON); 864 else if (!_cups_strncasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), 865 "close", 5)) 866 httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); 867 868 if (!httpGetField(con->http, HTTP_FIELD_HOST)[0] && 869 httpGetVersion(con->http) >= HTTP_VERSION_1_1) 870 { 871 /* 872 * HTTP/1.1 and higher require the "Host:" field... 873 */ 874 875 if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) 876 { 877 cupsdLogClient(con, CUPSD_LOG_ERROR, "Missing Host: field in request."); 878 cupsdCloseClient(con); 879 return; 880 } 881 } 882 else if (!valid_host(con)) 883 { 884 /* 885 * Access to localhost must use "localhost" or the corresponding IPv4 886 * or IPv6 values in the Host: field. 887 */ 888 889 cupsdLogClient(con, CUPSD_LOG_ERROR, 890 "Request from \"%s\" using invalid Host: field \"%s\".", 891 httpGetHostname(con->http, NULL, 0), httpGetField(con->http, HTTP_FIELD_HOST)); 892 893 if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) 894 { 895 cupsdCloseClient(con); 896 return; 897 } 898 } 899 else if (con->operation == HTTP_STATE_OPTIONS) 900 { 901 /* 902 * Do OPTIONS command... 903 */ 904 905 if (con->best && con->best->type != CUPSD_AUTH_NONE) 906 { 907 httpClearFields(con->http); 908 909 if (!cupsdSendHeader(con, HTTP_STATUS_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE)) 910 { 911 cupsdCloseClient(con); 912 return; 913 } 914 } 915 916 if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), "Upgrade") && strstr(httpGetField(con->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(con->http)) 917 { 918#ifdef HAVE_SSL 919 /* 920 * Do encryption stuff... 921 */ 922 923 httpClearFields(con->http); 924 925 if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE)) 926 { 927 cupsdCloseClient(con); 928 return; 929 } 930 931 if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED)) 932 { 933 cupsdCloseClient(con); 934 return; 935 } 936#else 937 if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) 938 { 939 cupsdCloseClient(con); 940 return; 941 } 942#endif /* HAVE_SSL */ 943 } 944 945 httpClearFields(con->http); 946 httpSetField(con->http, HTTP_FIELD_ALLOW, 947 "GET, HEAD, OPTIONS, POST, PUT"); 948 httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); 949 950 if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE)) 951 { 952 cupsdCloseClient(con); 953 return; 954 } 955 } 956 else if (!is_path_absolute(con->uri)) 957 { 958 /* 959 * Protect against malicious users! 960 */ 961 962 cupsdLogClient(con, CUPSD_LOG_ERROR, 963 "Request for non-absolute resource \"%s\".", con->uri); 964 965 if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) 966 { 967 cupsdCloseClient(con); 968 return; 969 } 970 } 971 else 972 { 973 if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), 974 "Upgrade") && !httpIsEncrypted(con->http)) 975 { 976#ifdef HAVE_SSL 977 /* 978 * Do encryption stuff... 979 */ 980 981 httpClearFields(con->http); 982 983 if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, 984 CUPSD_AUTH_NONE)) 985 { 986 cupsdCloseClient(con); 987 return; 988 } 989 990 if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED)) 991 { 992 cupsdCloseClient(con); 993 return; 994 } 995#else 996 if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) 997 { 998 cupsdCloseClient(con); 999 return; 1000 } 1001#endif /* HAVE_SSL */ 1002 } 1003 1004 if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_STATUS_OK) 1005 { 1006 cupsdSendError(con, status, CUPSD_AUTH_NONE); 1007 cupsdCloseClient(con); 1008 return; 1009 } 1010 1011 if (httpGetExpect(con->http) && 1012 (con->operation == HTTP_STATE_POST || con->operation == HTTP_STATE_PUT)) 1013 { 1014 if (httpGetExpect(con->http) == HTTP_STATUS_CONTINUE) 1015 { 1016 /* 1017 * Send 100-continue header... 1018 */ 1019 1020 if (httpWriteResponse(con->http, HTTP_STATUS_CONTINUE)) 1021 { 1022 cupsdCloseClient(con); 1023 return; 1024 } 1025 } 1026 else 1027 { 1028 /* 1029 * Send 417-expectation-failed header... 1030 */ 1031 1032 httpClearFields(con->http); 1033 httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); 1034 1035 cupsdSendError(con, HTTP_STATUS_EXPECTATION_FAILED, CUPSD_AUTH_NONE); 1036 cupsdCloseClient(con); 1037 return; 1038 } 1039 } 1040 1041 switch (httpGetState(con->http)) 1042 { 1043 case HTTP_STATE_GET_SEND : 1044 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Processing GET %s", con->uri); 1045 1046 if ((!strncmp(con->uri, "/ppd/", 5) || 1047 !strncmp(con->uri, "/printers/", 10) || 1048 !strncmp(con->uri, "/classes/", 9)) && 1049 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd")) 1050 { 1051 /* 1052 * Send PPD file - get the real printer name since printer 1053 * names are not case sensitive but filenames can be... 1054 */ 1055 1056 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ 1057 1058 if (!strncmp(con->uri, "/ppd/", 5)) 1059 p = cupsdFindPrinter(con->uri + 5); 1060 else if (!strncmp(con->uri, "/printers/", 10)) 1061 p = cupsdFindPrinter(con->uri + 10); 1062 else 1063 { 1064 p = cupsdFindClass(con->uri + 9); 1065 1066 if (p) 1067 { 1068 int i; /* Looping var */ 1069 1070 for (i = 0; i < p->num_printers; i ++) 1071 { 1072 if (!(p->printers[i]->type & CUPS_PRINTER_CLASS)) 1073 { 1074 char ppdname[1024];/* PPD filename */ 1075 1076 snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", 1077 ServerRoot, p->printers[i]->name); 1078 if (!access(ppdname, 0)) 1079 { 1080 p = p->printers[i]; 1081 break; 1082 } 1083 } 1084 } 1085 1086 if (i >= p->num_printers) 1087 p = NULL; 1088 } 1089 } 1090 1091 if (p) 1092 { 1093 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); 1094 } 1095 else 1096 { 1097 if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) 1098 { 1099 cupsdCloseClient(con); 1100 return; 1101 } 1102 1103 break; 1104 } 1105 } 1106 else if ((!strncmp(con->uri, "/icons/", 7) || 1107 !strncmp(con->uri, "/printers/", 10) || 1108 !strncmp(con->uri, "/classes/", 9)) && 1109 !strcmp(con->uri + strlen(con->uri) - 4, ".png")) 1110 { 1111 /* 1112 * Send icon file - get the real queue name since queue names are 1113 * not case sensitive but filenames can be... 1114 */ 1115 1116 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".png" */ 1117 1118 if (!strncmp(con->uri, "/icons/", 7)) 1119 p = cupsdFindPrinter(con->uri + 7); 1120 else if (!strncmp(con->uri, "/printers/", 10)) 1121 p = cupsdFindPrinter(con->uri + 10); 1122 else 1123 { 1124 p = cupsdFindClass(con->uri + 9); 1125 1126 if (p) 1127 { 1128 int i; /* Looping var */ 1129 1130 for (i = 0; i < p->num_printers; i ++) 1131 { 1132 if (!(p->printers[i]->type & CUPS_PRINTER_CLASS)) 1133 { 1134 char ppdname[1024];/* PPD filename */ 1135 1136 snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", 1137 ServerRoot, p->printers[i]->name); 1138 if (!access(ppdname, 0)) 1139 { 1140 p = p->printers[i]; 1141 break; 1142 } 1143 } 1144 } 1145 1146 if (i >= p->num_printers) 1147 p = NULL; 1148 } 1149 } 1150 1151 if (p) 1152 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name); 1153 else 1154 { 1155 if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) 1156 { 1157 cupsdCloseClient(con); 1158 return; 1159 } 1160 1161 break; 1162 } 1163 } 1164 else if (!WebInterface) 1165 { 1166 /* 1167 * Web interface is disabled. Show an appropriate message... 1168 */ 1169 1170 if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE)) 1171 { 1172 cupsdCloseClient(con); 1173 return; 1174 } 1175 1176 break; 1177 } 1178 1179 if ((!strncmp(con->uri, "/admin", 6) && 1180 strncmp(con->uri, "/admin/conf/", 12) && 1181 strncmp(con->uri, "/admin/log/", 11)) || 1182 !strncmp(con->uri, "/printers", 9) || 1183 !strncmp(con->uri, "/classes", 8) || 1184 !strncmp(con->uri, "/help", 5) || 1185 !strncmp(con->uri, "/jobs", 5)) 1186 { 1187 /* 1188 * Send CGI output... 1189 */ 1190 1191 if (!strncmp(con->uri, "/admin", 6)) 1192 { 1193 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", 1194 ServerBin); 1195 1196 cupsdSetString(&con->options, strchr(con->uri + 6, '?')); 1197 } 1198 else if (!strncmp(con->uri, "/printers", 9)) 1199 { 1200 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", 1201 ServerBin); 1202 1203 if (con->uri[9] && con->uri[10]) 1204 cupsdSetString(&con->options, con->uri + 9); 1205 else 1206 cupsdSetString(&con->options, NULL); 1207 } 1208 else if (!strncmp(con->uri, "/classes", 8)) 1209 { 1210 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", 1211 ServerBin); 1212 1213 if (con->uri[8] && con->uri[9]) 1214 cupsdSetString(&con->options, con->uri + 8); 1215 else 1216 cupsdSetString(&con->options, NULL); 1217 } 1218 else if (!strncmp(con->uri, "/jobs", 5)) 1219 { 1220 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", 1221 ServerBin); 1222 1223 if (con->uri[5] && con->uri[6]) 1224 cupsdSetString(&con->options, con->uri + 5); 1225 else 1226 cupsdSetString(&con->options, NULL); 1227 } 1228 else 1229 { 1230 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", 1231 ServerBin); 1232 1233 if (con->uri[5] && con->uri[6]) 1234 cupsdSetString(&con->options, con->uri + 5); 1235 else 1236 cupsdSetString(&con->options, NULL); 1237 } 1238 1239 if (!cupsdSendCommand(con, con->command, con->options, 0)) 1240 { 1241 if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) 1242 { 1243 cupsdCloseClient(con); 1244 return; 1245 } 1246 } 1247 else 1248 cupsdLogRequest(con, HTTP_STATUS_OK); 1249 1250 if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) 1251 httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); 1252 } 1253 else if ((!strncmp(con->uri, "/admin/conf/", 12) && 1254 (strchr(con->uri + 12, '/') || 1255 strlen(con->uri) == 12)) || 1256 (!strncmp(con->uri, "/admin/log/", 11) && 1257 (strchr(con->uri + 11, '/') || 1258 strlen(con->uri) == 11))) 1259 { 1260 /* 1261 * GET can only be done to configuration files directly under 1262 * /admin/conf... 1263 */ 1264 1265 cupsdLogClient(con, CUPSD_LOG_ERROR, 1266 "Request for subdirectory \"%s\"!", con->uri); 1267 1268 if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) 1269 { 1270 cupsdCloseClient(con); 1271 return; 1272 } 1273 1274 break; 1275 } 1276 else 1277 { 1278 /* 1279 * Serve a file... 1280 */ 1281 1282 if ((filename = get_file(con, &filestats, buf, 1283 sizeof(buf))) == NULL) 1284 { 1285 if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) 1286 { 1287 cupsdCloseClient(con); 1288 return; 1289 } 1290 1291 break; 1292 } 1293 1294 type = mimeFileType(MimeDatabase, filename, NULL, NULL); 1295 1296 cupsdLogClient(con, CUPSD_LOG_DEBUG, "filename=\"%s\", type=%s/%s", filename, type ? type->super : "", type ? type->type : ""); 1297 1298 if (is_cgi(con, filename, &filestats, type)) 1299 { 1300 /* 1301 * Note: con->command and con->options were set by 1302 * is_cgi()... 1303 */ 1304 1305 if (!cupsdSendCommand(con, con->command, con->options, 0)) 1306 { 1307 if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) 1308 { 1309 cupsdCloseClient(con); 1310 return; 1311 } 1312 } 1313 else 1314 cupsdLogRequest(con, HTTP_STATUS_OK); 1315 1316 if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) 1317 httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); 1318 break; 1319 } 1320 1321 if (!check_if_modified(con, &filestats)) 1322 { 1323 if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) 1324 { 1325 cupsdCloseClient(con); 1326 return; 1327 } 1328 } 1329 else 1330 { 1331 if (type == NULL) 1332 strlcpy(line, "text/plain", sizeof(line)); 1333 else 1334 snprintf(line, sizeof(line), "%s/%s", type->super, type->type); 1335 1336 if (!write_file(con, HTTP_STATUS_OK, filename, line, &filestats)) 1337 { 1338 cupsdCloseClient(con); 1339 return; 1340 } 1341 } 1342 } 1343 break; 1344 1345 case HTTP_STATE_POST_RECV : 1346 /* 1347 * See if the POST request includes a Content-Length field, and if 1348 * so check the length against any limits that are set... 1349 */ 1350 1351 if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && 1352 MaxRequestSize > 0 && 1353 httpGetLength2(con->http) > MaxRequestSize) 1354 { 1355 /* 1356 * Request too large... 1357 */ 1358 1359 if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) 1360 { 1361 cupsdCloseClient(con); 1362 return; 1363 } 1364 1365 break; 1366 } 1367 else if (httpGetLength2(con->http) < 0) 1368 { 1369 /* 1370 * Negative content lengths are invalid! 1371 */ 1372 1373 if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) 1374 { 1375 cupsdCloseClient(con); 1376 return; 1377 } 1378 1379 break; 1380 } 1381 1382 /* 1383 * See what kind of POST request this is; for IPP requests the 1384 * content-type field will be "application/ipp"... 1385 */ 1386 1387 if (!strcmp(httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE), 1388 "application/ipp")) 1389 con->request = ippNew(); 1390 else if (!WebInterface) 1391 { 1392 /* 1393 * Web interface is disabled. Show an appropriate message... 1394 */ 1395 1396 if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE)) 1397 { 1398 cupsdCloseClient(con); 1399 return; 1400 } 1401 1402 break; 1403 } 1404 else if ((!strncmp(con->uri, "/admin", 6) && 1405 strncmp(con->uri, "/admin/conf/", 12) && 1406 strncmp(con->uri, "/admin/log/", 11)) || 1407 !strncmp(con->uri, "/printers", 9) || 1408 !strncmp(con->uri, "/classes", 8) || 1409 !strncmp(con->uri, "/help", 5) || 1410 !strncmp(con->uri, "/jobs", 5)) 1411 { 1412 /* 1413 * CGI request... 1414 */ 1415 1416 if (!strncmp(con->uri, "/admin", 6)) 1417 { 1418 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", 1419 ServerBin); 1420 1421 cupsdSetString(&con->options, strchr(con->uri + 6, '?')); 1422 } 1423 else if (!strncmp(con->uri, "/printers", 9)) 1424 { 1425 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", 1426 ServerBin); 1427 1428 if (con->uri[9] && con->uri[10]) 1429 cupsdSetString(&con->options, con->uri + 9); 1430 else 1431 cupsdSetString(&con->options, NULL); 1432 } 1433 else if (!strncmp(con->uri, "/classes", 8)) 1434 { 1435 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", 1436 ServerBin); 1437 1438 if (con->uri[8] && con->uri[9]) 1439 cupsdSetString(&con->options, con->uri + 8); 1440 else 1441 cupsdSetString(&con->options, NULL); 1442 } 1443 else if (!strncmp(con->uri, "/jobs", 5)) 1444 { 1445 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", 1446 ServerBin); 1447 1448 if (con->uri[5] && con->uri[6]) 1449 cupsdSetString(&con->options, con->uri + 5); 1450 else 1451 cupsdSetString(&con->options, NULL); 1452 } 1453 else 1454 { 1455 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", 1456 ServerBin); 1457 1458 if (con->uri[5] && con->uri[6]) 1459 cupsdSetString(&con->options, con->uri + 5); 1460 else 1461 cupsdSetString(&con->options, NULL); 1462 } 1463 1464 if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) 1465 httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); 1466 } 1467 else 1468 { 1469 /* 1470 * POST to a file... 1471 */ 1472 1473 if ((filename = get_file(con, &filestats, buf, 1474 sizeof(buf))) == NULL) 1475 { 1476 if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) 1477 { 1478 cupsdCloseClient(con); 1479 return; 1480 } 1481 1482 break; 1483 } 1484 1485 type = mimeFileType(MimeDatabase, filename, NULL, NULL); 1486 1487 if (!is_cgi(con, filename, &filestats, type)) 1488 { 1489 /* 1490 * Only POST to CGI's... 1491 */ 1492 1493 if (!cupsdSendError(con, HTTP_STATUS_UNAUTHORIZED, CUPSD_AUTH_NONE)) 1494 { 1495 cupsdCloseClient(con); 1496 return; 1497 } 1498 } 1499 } 1500 break; 1501 1502 case HTTP_STATE_PUT_RECV : 1503 /* 1504 * Validate the resource name... 1505 */ 1506 1507 if (strcmp(con->uri, "/admin/conf/cupsd.conf")) 1508 { 1509 /* 1510 * PUT can only be done to the cupsd.conf file... 1511 */ 1512 1513 cupsdLogClient(con, CUPSD_LOG_ERROR, 1514 "Disallowed PUT request for \"%s\".", con->uri); 1515 1516 if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) 1517 { 1518 cupsdCloseClient(con); 1519 return; 1520 } 1521 1522 break; 1523 } 1524 1525 /* 1526 * See if the PUT request includes a Content-Length field, and if 1527 * so check the length against any limits that are set... 1528 */ 1529 1530 if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && 1531 MaxRequestSize > 0 && 1532 httpGetLength2(con->http) > MaxRequestSize) 1533 { 1534 /* 1535 * Request too large... 1536 */ 1537 1538 if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) 1539 { 1540 cupsdCloseClient(con); 1541 return; 1542 } 1543 1544 break; 1545 } 1546 else if (httpGetLength2(con->http) < 0) 1547 { 1548 /* 1549 * Negative content lengths are invalid! 1550 */ 1551 1552 if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) 1553 { 1554 cupsdCloseClient(con); 1555 return; 1556 } 1557 1558 break; 1559 } 1560 1561 /* 1562 * Open a temporary file to hold the request... 1563 */ 1564 1565 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, 1566 request_id ++); 1567 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); 1568 1569 if (con->file < 0) 1570 { 1571 cupsdLogClient(con, CUPSD_LOG_ERROR, 1572 "Unable to create request file \"%s\": %s", 1573 con->filename, strerror(errno)); 1574 1575 if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) 1576 { 1577 cupsdCloseClient(con); 1578 return; 1579 } 1580 } 1581 1582 fchmod(con->file, 0640); 1583 fchown(con->file, RunUser, Group); 1584 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); 1585 break; 1586 1587 case HTTP_STATE_DELETE : 1588 case HTTP_STATE_TRACE : 1589 cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE); 1590 cupsdCloseClient(con); 1591 return; 1592 1593 case HTTP_STATE_HEAD : 1594 if (!strncmp(con->uri, "/printers/", 10) && 1595 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd")) 1596 { 1597 /* 1598 * Send PPD file - get the real printer name since printer 1599 * names are not case sensitive but filenames can be... 1600 */ 1601 1602 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ 1603 1604 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL) 1605 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); 1606 else 1607 { 1608 if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) 1609 { 1610 cupsdCloseClient(con); 1611 return; 1612 } 1613 1614 cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); 1615 break; 1616 } 1617 } 1618 else if (!strncmp(con->uri, "/printers/", 10) && 1619 !strcmp(con->uri + strlen(con->uri) - 4, ".png")) 1620 { 1621 /* 1622 * Send PNG file - get the real printer name since printer 1623 * names are not case sensitive but filenames can be... 1624 */ 1625 1626 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ 1627 1628 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL) 1629 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name); 1630 else 1631 { 1632 if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) 1633 { 1634 cupsdCloseClient(con); 1635 return; 1636 } 1637 1638 cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); 1639 break; 1640 } 1641 } 1642 else if (!WebInterface) 1643 { 1644 httpClearFields(con->http); 1645 1646 if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE)) 1647 { 1648 cupsdCloseClient(con); 1649 return; 1650 } 1651 1652 cupsdLogRequest(con, HTTP_STATUS_OK); 1653 break; 1654 } 1655 1656 if ((!strncmp(con->uri, "/admin", 6) && 1657 strncmp(con->uri, "/admin/conf/", 12) && 1658 strncmp(con->uri, "/admin/log/", 11)) || 1659 !strncmp(con->uri, "/printers", 9) || 1660 !strncmp(con->uri, "/classes", 8) || 1661 !strncmp(con->uri, "/help", 5) || 1662 !strncmp(con->uri, "/jobs", 5)) 1663 { 1664 /* 1665 * CGI output... 1666 */ 1667 1668 httpClearFields(con->http); 1669 1670 if (!cupsdSendHeader(con, HTTP_STATUS_OK, "text/html", CUPSD_AUTH_NONE)) 1671 { 1672 cupsdCloseClient(con); 1673 return; 1674 } 1675 1676 cupsdLogRequest(con, HTTP_STATUS_OK); 1677 } 1678 else if ((!strncmp(con->uri, "/admin/conf/", 12) && 1679 (strchr(con->uri + 12, '/') || 1680 strlen(con->uri) == 12)) || 1681 (!strncmp(con->uri, "/admin/log/", 11) && 1682 (strchr(con->uri + 11, '/') || 1683 strlen(con->uri) == 11))) 1684 { 1685 /* 1686 * HEAD can only be done to configuration files under 1687 * /admin/conf... 1688 */ 1689 1690 cupsdLogClient(con, CUPSD_LOG_ERROR, 1691 "Request for subdirectory \"%s\".", con->uri); 1692 1693 if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) 1694 { 1695 cupsdCloseClient(con); 1696 return; 1697 } 1698 1699 cupsdLogRequest(con, HTTP_STATUS_FORBIDDEN); 1700 break; 1701 } 1702 else if ((filename = get_file(con, &filestats, buf, 1703 sizeof(buf))) == NULL) 1704 { 1705 httpClearFields(con->http); 1706 1707 if (!cupsdSendHeader(con, HTTP_STATUS_NOT_FOUND, "text/html", 1708 CUPSD_AUTH_NONE)) 1709 { 1710 cupsdCloseClient(con); 1711 return; 1712 } 1713 1714 cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); 1715 } 1716 else if (!check_if_modified(con, &filestats)) 1717 { 1718 if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) 1719 { 1720 cupsdCloseClient(con); 1721 return; 1722 } 1723 1724 cupsdLogRequest(con, HTTP_STATUS_NOT_MODIFIED); 1725 } 1726 else 1727 { 1728 /* 1729 * Serve a file... 1730 */ 1731 1732 type = mimeFileType(MimeDatabase, filename, NULL, NULL); 1733 if (type == NULL) 1734 strlcpy(line, "text/plain", sizeof(line)); 1735 else 1736 snprintf(line, sizeof(line), "%s/%s", type->super, type->type); 1737 1738 httpClearFields(con->http); 1739 1740 httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, 1741 httpGetDateString(filestats.st_mtime)); 1742 httpSetLength(con->http, (size_t)filestats.st_size); 1743 1744 if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE)) 1745 { 1746 cupsdCloseClient(con); 1747 return; 1748 } 1749 1750 cupsdLogRequest(con, HTTP_STATUS_OK); 1751 } 1752 break; 1753 1754 default : 1755 break; /* Anti-compiler-warning-code */ 1756 } 1757 } 1758 } 1759 1760 /* 1761 * Handle any incoming data... 1762 */ 1763 1764 switch (httpGetState(con->http)) 1765 { 1766 case HTTP_STATE_PUT_RECV : 1767 do 1768 { 1769 if ((bytes = httpRead2(con->http, line, sizeof(line))) < 0) 1770 { 1771 if (httpError(con->http) && httpError(con->http) != EPIPE) 1772 cupsdLogClient(con, CUPSD_LOG_DEBUG, 1773 "HTTP_STATE_PUT_RECV Closing for error %d (%s)", 1774 httpError(con->http), strerror(httpError(con->http))); 1775 else 1776 cupsdLogClient(con, CUPSD_LOG_DEBUG, 1777 "HTTP_STATE_PUT_RECV Closing on EOF."); 1778 1779 cupsdCloseClient(con); 1780 return; 1781 } 1782 else if (bytes > 0) 1783 { 1784 con->bytes += bytes; 1785 1786 if (write(con->file, line, (size_t)bytes) < bytes) 1787 { 1788 cupsdLogClient(con, CUPSD_LOG_ERROR, 1789 "Unable to write %d bytes to \"%s\": %s", bytes, 1790 con->filename, strerror(errno)); 1791 1792 close(con->file); 1793 con->file = -1; 1794 unlink(con->filename); 1795 cupsdClearString(&con->filename); 1796 1797 if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) 1798 { 1799 cupsdCloseClient(con); 1800 return; 1801 } 1802 } 1803 } 1804 } 1805 while (httpGetState(con->http) == HTTP_STATE_PUT_RECV && httpGetReady(con->http)); 1806 1807 if (httpGetState(con->http) == HTTP_STATE_STATUS) 1808 { 1809 /* 1810 * End of file, see how big it is... 1811 */ 1812 1813 fstat(con->file, &filestats); 1814 1815 close(con->file); 1816 con->file = -1; 1817 1818 if (filestats.st_size > MaxRequestSize && 1819 MaxRequestSize > 0) 1820 { 1821 /* 1822 * Request is too big; remove it and send an error... 1823 */ 1824 1825 unlink(con->filename); 1826 cupsdClearString(&con->filename); 1827 1828 if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) 1829 { 1830 cupsdCloseClient(con); 1831 return; 1832 } 1833 } 1834 1835 /* 1836 * Install the configuration file... 1837 */ 1838 1839 status = install_cupsd_conf(con); 1840 1841 /* 1842 * Return the status to the client... 1843 */ 1844 1845 if (!cupsdSendError(con, status, CUPSD_AUTH_NONE)) 1846 { 1847 cupsdCloseClient(con); 1848 return; 1849 } 1850 } 1851 break; 1852 1853 case HTTP_STATE_POST_RECV : 1854 do 1855 { 1856 if (con->request && con->file < 0) 1857 { 1858 /* 1859 * Grab any request data from the connection... 1860 */ 1861 1862 if (!httpWait(con->http, 0)) 1863 return; 1864 1865 if ((ipp_state = ippRead(con->http, con->request)) == IPP_STATE_ERROR) 1866 { 1867 cupsdLogClient(con, CUPSD_LOG_ERROR, "IPP read error: %s", 1868 cupsLastErrorString()); 1869 1870 cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE); 1871 cupsdCloseClient(con); 1872 return; 1873 } 1874 else if (ipp_state != IPP_STATE_DATA) 1875 { 1876 if (httpGetState(con->http) == HTTP_STATE_POST_SEND) 1877 { 1878 cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE); 1879 cupsdCloseClient(con); 1880 return; 1881 } 1882 1883 if (httpGetReady(con->http)) 1884 continue; 1885 break; 1886 } 1887 else 1888 { 1889 cupsdLogClient(con, CUPSD_LOG_DEBUG, "%d.%d %s %d", 1890 con->request->request.op.version[0], 1891 con->request->request.op.version[1], 1892 ippOpString(con->request->request.op.operation_id), 1893 con->request->request.op.request_id); 1894 con->bytes += (off_t)ippLength(con->request); 1895 } 1896 } 1897 1898 if (con->file < 0 && httpGetState(con->http) != HTTP_STATE_POST_SEND) 1899 { 1900 /* 1901 * Create a file as needed for the request data... 1902 */ 1903 1904 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, 1905 request_id ++); 1906 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); 1907 1908 if (con->file < 0) 1909 { 1910 cupsdLogClient(con, CUPSD_LOG_ERROR, 1911 "Unable to create request file \"%s\": %s", 1912 con->filename, strerror(errno)); 1913 1914 if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) 1915 { 1916 cupsdCloseClient(con); 1917 return; 1918 } 1919 } 1920 1921 fchmod(con->file, 0640); 1922 fchown(con->file, RunUser, Group); 1923 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); 1924 } 1925 1926 if (httpGetState(con->http) != HTTP_STATE_POST_SEND) 1927 { 1928 if (!httpWait(con->http, 0)) 1929 return; 1930 else if ((bytes = httpRead2(con->http, line, sizeof(line))) < 0) 1931 { 1932 if (httpError(con->http) && httpError(con->http) != EPIPE) 1933 cupsdLogClient(con, CUPSD_LOG_DEBUG, 1934 "HTTP_STATE_POST_SEND Closing for error %d (%s)", 1935 httpError(con->http), strerror(httpError(con->http))); 1936 else 1937 cupsdLogClient(con, CUPSD_LOG_DEBUG, 1938 "HTTP_STATE_POST_SEND Closing on EOF."); 1939 1940 cupsdCloseClient(con); 1941 return; 1942 } 1943 else if (bytes > 0) 1944 { 1945 con->bytes += bytes; 1946 1947 if (write(con->file, line, (size_t)bytes) < bytes) 1948 { 1949 cupsdLogClient(con, CUPSD_LOG_ERROR, 1950 "Unable to write %d bytes to \"%s\": %s", 1951 bytes, con->filename, strerror(errno)); 1952 1953 close(con->file); 1954 con->file = -1; 1955 unlink(con->filename); 1956 cupsdClearString(&con->filename); 1957 1958 if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, 1959 CUPSD_AUTH_NONE)) 1960 { 1961 cupsdCloseClient(con); 1962 return; 1963 } 1964 } 1965 } 1966 else if (httpGetState(con->http) == HTTP_STATE_POST_RECV) 1967 return; 1968 else if (httpGetState(con->http) != HTTP_STATE_POST_SEND) 1969 { 1970 cupsdLogClient(con, CUPSD_LOG_DEBUG, 1971 "Closing on unexpected state %s.", 1972 httpStateString(httpGetState(con->http))); 1973 cupsdCloseClient(con); 1974 return; 1975 } 1976 } 1977 } 1978 while (httpGetState(con->http) == HTTP_STATE_POST_RECV && httpGetReady(con->http)); 1979 1980 if (httpGetState(con->http) == HTTP_STATE_POST_SEND) 1981 { 1982 /* 1983 * Don't listen for activity until we decide to do something with this... 1984 */ 1985 1986 cupsdAddSelect(httpGetFd(con->http), NULL, NULL, con); 1987 1988 if (con->file >= 0) 1989 { 1990 fstat(con->file, &filestats); 1991 1992 close(con->file); 1993 con->file = -1; 1994 1995 if (filestats.st_size > MaxRequestSize && 1996 MaxRequestSize > 0) 1997 { 1998 /* 1999 * Request is too big; remove it and send an error... 2000 */ 2001 2002 unlink(con->filename); 2003 cupsdClearString(&con->filename); 2004 2005 if (con->request) 2006 { 2007 /* 2008 * Delete any IPP request data... 2009 */ 2010 2011 ippDelete(con->request); 2012 con->request = NULL; 2013 } 2014 2015 if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) 2016 { 2017 cupsdCloseClient(con); 2018 return; 2019 } 2020 } 2021 else if (filestats.st_size == 0) 2022 { 2023 /* 2024 * Don't allow empty file... 2025 */ 2026 2027 unlink(con->filename); 2028 cupsdClearString(&con->filename); 2029 } 2030 2031 if (con->command) 2032 { 2033 if (!cupsdSendCommand(con, con->command, con->options, 0)) 2034 { 2035 if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) 2036 { 2037 cupsdCloseClient(con); 2038 return; 2039 } 2040 } 2041 else 2042 cupsdLogRequest(con, HTTP_STATUS_OK); 2043 } 2044 } 2045 2046 if (con->request) 2047 { 2048 cupsdProcessIPPRequest(con); 2049 2050 if (con->filename) 2051 { 2052 unlink(con->filename); 2053 cupsdClearString(&con->filename); 2054 } 2055 2056 return; 2057 } 2058 } 2059 break; 2060 2061 default : 2062 break; /* Anti-compiler-warning-code */ 2063 } 2064 2065 if (httpGetState(con->http) == HTTP_STATE_WAITING) 2066 { 2067 if (!httpGetKeepAlive(con->http)) 2068 { 2069 cupsdLogClient(con, CUPSD_LOG_DEBUG, 2070 "Closing because Keep-Alive is disabled."); 2071 cupsdCloseClient(con); 2072 } 2073 else 2074 { 2075 cupsArrayRemove(ActiveClients, con); 2076 cupsdSetBusyState(); 2077 } 2078 } 2079} 2080 2081 2082/* 2083 * 'cupsdSendCommand()' - Send output from a command via HTTP. 2084 */ 2085 2086int /* O - 1 on success, 0 on failure */ 2087cupsdSendCommand( 2088 cupsd_client_t *con, /* I - Client connection */ 2089 char *command, /* I - Command to run */ 2090 char *options, /* I - Command-line options */ 2091 int root) /* I - Run as root? */ 2092{ 2093 int fd; /* Standard input file descriptor */ 2094 2095 2096 if (con->filename) 2097 { 2098 fd = open(con->filename, O_RDONLY); 2099 2100 if (fd < 0) 2101 { 2102 cupsdLogClient(con, CUPSD_LOG_ERROR, 2103 "Unable to open \"%s\" for reading: %s", 2104 con->filename ? con->filename : "/dev/null", 2105 strerror(errno)); 2106 return (0); 2107 } 2108 2109 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 2110 } 2111 else 2112 fd = -1; 2113 2114 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root); 2115 con->pipe_status = HTTP_STATUS_OK; 2116 2117 httpClearFields(con->http); 2118 2119 if (fd >= 0) 2120 close(fd); 2121 2122 cupsdLogClient(con, CUPSD_LOG_INFO, "Started \"%s\" (pid=%d, file=%d)", 2123 command, con->pipe_pid, con->file); 2124 2125 if (con->pipe_pid == 0) 2126 return (0); 2127 2128 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); 2129 2130 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con); 2131 2132 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for CGI data."); 2133 2134 con->sent_header = 0; 2135 con->file_ready = 0; 2136 con->got_fields = 0; 2137 con->header_used = 0; 2138 2139 return (1); 2140} 2141 2142 2143/* 2144 * 'cupsdSendError()' - Send an error message via HTTP. 2145 */ 2146 2147int /* O - 1 if successful, 0 otherwise */ 2148cupsdSendError(cupsd_client_t *con, /* I - Connection */ 2149 http_status_t code, /* I - Error code */ 2150 int auth_type)/* I - Authentication type */ 2151{ 2152 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdSendError code=%d, auth_type=%d", 2153 code, auth_type); 2154 2155#ifdef HAVE_SSL 2156 /* 2157 * Force client to upgrade for authentication if that is how the 2158 * server is configured... 2159 */ 2160 2161 if (code == HTTP_STATUS_UNAUTHORIZED && 2162 DefaultEncryption == HTTP_ENCRYPTION_REQUIRED && 2163 _cups_strcasecmp(httpGetHostname(con->http, NULL, 0), "localhost") && 2164 !httpIsEncrypted(con->http)) 2165 { 2166 code = HTTP_STATUS_UPGRADE_REQUIRED; 2167 } 2168#endif /* HAVE_SSL */ 2169 2170 /* 2171 * Put the request in the access_log file... 2172 */ 2173 2174 cupsdLogRequest(con, code); 2175 2176 /* 2177 * To work around bugs in some proxies, don't use Keep-Alive for some 2178 * error messages... 2179 * 2180 * Kerberos authentication doesn't work without Keep-Alive, so 2181 * never disable it in that case. 2182 */ 2183 2184 httpClearFields(con->http); 2185 2186 if (code >= HTTP_STATUS_BAD_REQUEST && con->type != CUPSD_AUTH_NEGOTIATE) 2187 httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); 2188 2189 if (httpGetVersion(con->http) >= HTTP_VERSION_1_1 && 2190 httpGetKeepAlive(con->http) == HTTP_KEEPALIVE_OFF) 2191 httpSetField(con->http, HTTP_FIELD_CONNECTION, "close"); 2192 2193 if (code >= HTTP_STATUS_BAD_REQUEST) 2194 { 2195 /* 2196 * Send a human-readable error message. 2197 */ 2198 2199 char message[4096], /* Message for user */ 2200 urltext[1024], /* URL redirection text */ 2201 redirect[1024]; /* Redirection link */ 2202 const char *text; /* Status-specific text */ 2203 2204 2205 redirect[0] = '\0'; 2206 2207 if (code == HTTP_STATUS_UNAUTHORIZED) 2208 text = _cupsLangString(con->language, 2209 _("Enter your username and password or the " 2210 "root username and password to access this " 2211 "page. If you are using Kerberos authentication, " 2212 "make sure you have a valid Kerberos ticket.")); 2213 else if (code == HTTP_STATUS_UPGRADE_REQUIRED) 2214 { 2215 text = urltext; 2216 2217 snprintf(urltext, sizeof(urltext), 2218 _cupsLangString(con->language, 2219 _("You must access this page using the URL " 2220 "<A HREF=\"https://%s:%d%s\">" 2221 "https://%s:%d%s</A>.")), 2222 con->servername, con->serverport, con->uri, 2223 con->servername, con->serverport, con->uri); 2224 2225 snprintf(redirect, sizeof(redirect), 2226 "<META HTTP-EQUIV=\"Refresh\" " 2227 "CONTENT=\"3;URL=https://%s:%d%s\">\n", 2228 con->servername, con->serverport, con->uri); 2229 } 2230 else if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED) 2231 text = _cupsLangString(con->language, 2232 _("The web interface is currently disabled. Run " 2233 "\"cupsctl WebInterface=yes\" to enable it.")); 2234 else 2235 text = ""; 2236 2237 snprintf(message, sizeof(message), 2238 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" " 2239 "\"http://www.w3.org/TR/html4/loose.dtd\">\n" 2240 "<HTML>\n" 2241 "<HEAD>\n" 2242 "\t<META HTTP-EQUIV=\"Content-Type\" " 2243 "CONTENT=\"text/html; charset=utf-8\">\n" 2244 "\t<TITLE>%s - " CUPS_SVERSION "</TITLE>\n" 2245 "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" " 2246 "HREF=\"/cups.css\">\n" 2247 "%s" 2248 "</HEAD>\n" 2249 "<BODY>\n" 2250 "<H1>%s</H1>\n" 2251 "<P>%s</P>\n" 2252 "</BODY>\n" 2253 "</HTML>\n", 2254 _httpStatus(con->language, code), redirect, 2255 _httpStatus(con->language, code), text); 2256 2257 /* 2258 * Send an error message back to the client. If the error code is a 2259 * 400 or 500 series, make sure the message contains some text, too! 2260 */ 2261 2262 size_t length = strlen(message); /* Length of message */ 2263 2264 httpSetLength(con->http, length); 2265 2266 if (!cupsdSendHeader(con, code, "text/html", auth_type)) 2267 return (0); 2268 2269 if (httpWrite2(con->http, message, length) < 0) 2270 return (0); 2271 2272 if (httpFlushWrite(con->http) < 0) 2273 return (0); 2274 } 2275 else 2276 { 2277 httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); 2278 2279 if (!cupsdSendHeader(con, code, NULL, auth_type)) 2280 return (0); 2281 } 2282 2283 return (1); 2284} 2285 2286 2287/* 2288 * 'cupsdSendHeader()' - Send an HTTP request. 2289 */ 2290 2291int /* O - 1 on success, 0 on failure */ 2292cupsdSendHeader( 2293 cupsd_client_t *con, /* I - Client to send to */ 2294 http_status_t code, /* I - HTTP status code */ 2295 char *type, /* I - MIME type of document */ 2296 int auth_type) /* I - Type of authentication */ 2297{ 2298 char auth_str[1024]; /* Authorization string */ 2299 2300 2301 cupsdLogClient(con, CUPSD_LOG_DEBUG, "cupsdSendHeader: code=%d, type=\"%s\", auth_type=%d", code, type, auth_type); 2302 2303 /* 2304 * Send the HTTP status header... 2305 */ 2306 2307 if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED) 2308 { 2309 /* 2310 * Treat our special "web interface is disabled" status as "200 OK" for web 2311 * browsers. 2312 */ 2313 2314 code = HTTP_STATUS_OK; 2315 } 2316 2317 if (ServerHeader) 2318 httpSetField(con->http, HTTP_FIELD_SERVER, ServerHeader); 2319 2320 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED) 2321 httpSetField(con->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST, PUT"); 2322 2323 if (code == HTTP_STATUS_UNAUTHORIZED) 2324 { 2325 if (auth_type == CUPSD_AUTH_NONE) 2326 { 2327 if (!con->best || con->best->type <= CUPSD_AUTH_NONE) 2328 auth_type = cupsdDefaultAuthType(); 2329 else 2330 auth_type = con->best->type; 2331 } 2332 2333 auth_str[0] = '\0'; 2334 2335 if (auth_type == CUPSD_AUTH_BASIC) 2336 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str)); 2337#ifdef HAVE_GSSAPI 2338 else if (auth_type == CUPSD_AUTH_NEGOTIATE) 2339 { 2340# ifdef AF_LOCAL 2341 if (httpAddrFamily(httpGetAddress(con->http)) == AF_LOCAL) 2342 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str)); 2343 else 2344# endif /* AF_LOCAL */ 2345 strlcpy(auth_str, "Negotiate", sizeof(auth_str)); 2346 } 2347#endif /* HAVE_GSSAPI */ 2348 2349 if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE && 2350 !_cups_strcasecmp(httpGetHostname(con->http, NULL, 0), "localhost")) 2351 { 2352 /* 2353 * Add a "trc" (try root certification) parameter for local non-Kerberos 2354 * requests when the request requires system group membership - then the 2355 * client knows the root certificate can/should be used. 2356 * 2357 * Also, for OS X we also look for @AUTHKEY and add an "authkey" 2358 * parameter as needed... 2359 */ 2360 2361 char *name, /* Current user name */ 2362 *auth_key; /* Auth key buffer */ 2363 size_t auth_size; /* Size of remaining buffer */ 2364 2365 auth_key = auth_str + strlen(auth_str); 2366 auth_size = sizeof(auth_str) - (size_t)(auth_key - auth_str); 2367 2368 for (name = (char *)cupsArrayFirst(con->best->names); 2369 name; 2370 name = (char *)cupsArrayNext(con->best->names)) 2371 { 2372#ifdef HAVE_AUTHORIZATION_H 2373 if (!_cups_strncasecmp(name, "@AUTHKEY(", 9)) 2374 { 2375 snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9); 2376 /* end parenthesis is stripped in conf.c */ 2377 break; 2378 } 2379 else 2380#endif /* HAVE_AUTHORIZATION_H */ 2381 if (!_cups_strcasecmp(name, "@SYSTEM")) 2382 { 2383#ifdef HAVE_AUTHORIZATION_H 2384 if (SystemGroupAuthKey) 2385 snprintf(auth_key, auth_size, 2386 ", authkey=\"%s\"", 2387 SystemGroupAuthKey); 2388 else 2389#else 2390 strlcpy(auth_key, ", trc=\"y\"", auth_size); 2391#endif /* HAVE_AUTHORIZATION_H */ 2392 break; 2393 } 2394 } 2395 } 2396 2397 if (auth_str[0]) 2398 { 2399 cupsdLogClient(con, CUPSD_LOG_DEBUG, "WWW-Authenticate: %s", auth_str); 2400 2401 httpSetField(con->http, HTTP_FIELD_WWW_AUTHENTICATE, auth_str); 2402 } 2403 } 2404 2405 if (con->language && strcmp(con->language->language, "C")) 2406 httpSetField(con->http, HTTP_FIELD_CONTENT_LANGUAGE, con->language->language); 2407 2408 if (type) 2409 { 2410 if (!strcmp(type, "text/html")) 2411 httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, "text/html; charset=utf-8"); 2412 else 2413 httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, type); 2414 } 2415 2416 return (!httpWriteResponse(con->http, code)); 2417} 2418 2419 2420/* 2421 * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs. 2422 */ 2423 2424void 2425cupsdUpdateCGI(void) 2426{ 2427 char *ptr, /* Pointer to end of line in buffer */ 2428 message[1024]; /* Pointer to message text */ 2429 int loglevel; /* Log level for message */ 2430 2431 2432 while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel, 2433 message, sizeof(message))) != NULL) 2434 { 2435 if (loglevel == CUPSD_LOG_INFO) 2436 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message); 2437 2438 if (!strchr(CGIStatusBuffer->buffer, '\n')) 2439 break; 2440 } 2441 2442 if (ptr == NULL && !CGIStatusBuffer->bufused) 2443 { 2444 /* 2445 * Fatal error on pipe - should never happen! 2446 */ 2447 2448 cupsdLogMessage(CUPSD_LOG_CRIT, 2449 "cupsdUpdateCGI: error reading from CGI error pipe - %s", 2450 strerror(errno)); 2451 } 2452} 2453 2454 2455/* 2456 * 'cupsdWriteClient()' - Write data to a client as needed. 2457 */ 2458 2459void 2460cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ 2461{ 2462 int bytes, /* Number of bytes written */ 2463 field_col; /* Current column */ 2464 char *bufptr, /* Pointer into buffer */ 2465 *bufend; /* Pointer to end of buffer */ 2466 ipp_state_t ipp_state; /* IPP state value */ 2467 2468 2469 cupsdLogClient(con, CUPSD_LOG_DEBUG, "con->http=%p", con->http); 2470 cupsdLogClient(con, CUPSD_LOG_DEBUG, 2471 "cupsdWriteClient " 2472 "error=%d, " 2473 "used=%d, " 2474 "state=%s, " 2475 "data_encoding=HTTP_ENCODING_%s, " 2476 "data_remaining=" CUPS_LLFMT ", " 2477 "response=%p(%s), " 2478 "pipe_pid=%d, " 2479 "file=%d", 2480 httpError(con->http), (int)httpGetReady(con->http), 2481 httpStateString(httpGetState(con->http)), 2482 httpIsChunked(con->http) ? "CHUNKED" : "LENGTH", 2483 CUPS_LLCAST httpGetLength2(con->http), 2484 con->response, 2485 con->response ? ippStateString(ippGetState(con->request)) : "", 2486 con->pipe_pid, con->file); 2487 2488 if (httpGetState(con->http) != HTTP_STATE_GET_SEND && 2489 httpGetState(con->http) != HTTP_STATE_POST_SEND) 2490 { 2491 /* 2492 * If we get called in the wrong state, then something went wrong with the 2493 * connection and we need to shut it down... 2494 */ 2495 2496 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP write state %s.", 2497 httpStateString(httpGetState(con->http))); 2498 cupsdCloseClient(con); 2499 return; 2500 } 2501 2502 if (con->pipe_pid) 2503 { 2504 /* 2505 * Make sure we select on the CGI output... 2506 */ 2507 2508 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con); 2509 2510 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for CGI data."); 2511 2512 if (!con->file_ready) 2513 { 2514 /* 2515 * Try again later when there is CGI output available... 2516 */ 2517 2518 cupsdRemoveSelect(httpGetFd(con->http)); 2519 return; 2520 } 2521 2522 con->file_ready = 0; 2523 } 2524 2525 bytes = (ssize_t)(sizeof(con->header) - (size_t)con->header_used); 2526 2527 if (!con->pipe_pid && bytes > (ssize_t)httpGetRemaining(con->http)) 2528 { 2529 /* 2530 * Limit GET bytes to original size of file (STR #3265)... 2531 */ 2532 2533 bytes = (ssize_t)httpGetRemaining(con->http); 2534 } 2535 2536 if (con->response && con->response->state != IPP_STATE_DATA) 2537 { 2538 size_t wused = httpGetPending(con->http); /* Previous write buffer use */ 2539 2540 do 2541 { 2542 /* 2543 * Write a single attribute or the IPP message header... 2544 */ 2545 2546 ipp_state = ippWrite(con->http, con->response); 2547 2548 /* 2549 * If the write buffer has been flushed, stop buffering up attributes... 2550 */ 2551 2552 if (httpGetPending(con->http) <= wused) 2553 break; 2554 } 2555 while (ipp_state != IPP_STATE_DATA && ipp_state != IPP_STATE_ERROR); 2556 2557 cupsdLogClient(con, CUPSD_LOG_DEBUG, 2558 "Writing IPP response, ipp_state=%s, old " 2559 "wused=" CUPS_LLFMT ", new wused=" CUPS_LLFMT, 2560 ippStateString(ipp_state), 2561 CUPS_LLCAST wused, CUPS_LLCAST httpGetPending(con->http)); 2562 2563 if (httpGetPending(con->http) > 0) 2564 httpFlushWrite(con->http); 2565 2566 bytes = ipp_state != IPP_STATE_ERROR && 2567 (con->file >= 0 || ipp_state != IPP_STATE_DATA); 2568 2569 cupsdLogClient(con, CUPSD_LOG_DEBUG, 2570 "bytes=%d, http_state=%d, data_remaining=" CUPS_LLFMT, 2571 (int)bytes, httpGetState(con->http), 2572 CUPS_LLCAST httpGetLength2(con->http)); 2573 } 2574 else if ((bytes = read(con->file, con->header + con->header_used, (size_t)bytes)) > 0) 2575 { 2576 con->header_used += bytes; 2577 2578 if (con->pipe_pid && !con->got_fields) 2579 { 2580 /* 2581 * Inspect the data for Content-Type and other fields. 2582 */ 2583 2584 for (bufptr = con->header, bufend = con->header + con->header_used, 2585 field_col = 0; 2586 !con->got_fields && bufptr < bufend; 2587 bufptr ++) 2588 { 2589 if (*bufptr == '\n') 2590 { 2591 /* 2592 * Send line to client... 2593 */ 2594 2595 if (bufptr > con->header && bufptr[-1] == '\r') 2596 bufptr[-1] = '\0'; 2597 *bufptr++ = '\0'; 2598 2599 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Script header: %s", con->header); 2600 2601 if (!con->sent_header) 2602 { 2603 /* 2604 * Handle redirection and CGI status codes... 2605 */ 2606 2607 http_field_t field; /* HTTP field */ 2608 char *value = strchr(con->header, ':'); 2609 /* Value of field */ 2610 2611 if (value) 2612 { 2613 *value++ = '\0'; 2614 while (isspace(*value & 255)) 2615 value ++; 2616 } 2617 2618 field = httpFieldValue(con->header); 2619 2620 if (field != HTTP_FIELD_UNKNOWN && value) 2621 { 2622 httpSetField(con->http, field, value); 2623 2624 if (field == HTTP_FIELD_LOCATION) 2625 { 2626 con->pipe_status = HTTP_STATUS_SEE_OTHER; 2627 con->sent_header = 2; 2628 } 2629 else 2630 con->sent_header = 1; 2631 } 2632 else if (!_cups_strcasecmp(con->header, "Status") && value) 2633 { 2634 con->pipe_status = (http_status_t)atoi(value); 2635 con->sent_header = 2; 2636 } 2637 else if (!_cups_strcasecmp(con->header, "Set-Cookie") && value) 2638 { 2639 httpSetCookie(con->http, value); 2640 con->sent_header = 1; 2641 } 2642 } 2643 2644 /* 2645 * Update buffer... 2646 */ 2647 2648 con->header_used -= bufptr - con->header; 2649 2650 if (con->header_used > 0) 2651 memmove(con->header, bufptr, (size_t)con->header_used); 2652 2653 bufptr = con->header - 1; 2654 2655 /* 2656 * See if the line was empty... 2657 */ 2658 2659 if (field_col == 0) 2660 { 2661 con->got_fields = 1; 2662 2663 if (httpGetVersion(con->http) == HTTP_VERSION_1_1 && 2664 !httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0]) 2665 httpSetLength(con->http, 0); 2666 2667 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending status %d for CGI.", con->pipe_status); 2668 2669 if (con->pipe_status == HTTP_STATUS_OK) 2670 { 2671 if (!cupsdSendHeader(con, con->pipe_status, NULL, CUPSD_AUTH_NONE)) 2672 { 2673 cupsdCloseClient(con); 2674 return; 2675 } 2676 } 2677 else 2678 { 2679 if (!cupsdSendError(con, con->pipe_status, CUPSD_AUTH_NONE)) 2680 { 2681 cupsdCloseClient(con); 2682 return; 2683 } 2684 } 2685 } 2686 else 2687 field_col = 0; 2688 } 2689 else if (*bufptr != '\r') 2690 field_col ++; 2691 } 2692 2693 if (!con->got_fields) 2694 return; 2695 } 2696 2697 if (con->header_used > 0) 2698 { 2699 if (httpWrite2(con->http, con->header, (size_t)con->header_used) < 0) 2700 { 2701 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)", 2702 httpError(con->http), strerror(httpError(con->http))); 2703 cupsdCloseClient(con); 2704 return; 2705 } 2706 2707 if (httpIsChunked(con->http)) 2708 httpFlushWrite(con->http); 2709 2710 con->bytes += con->header_used; 2711 2712 if (httpGetState(con->http) == HTTP_STATE_WAITING) 2713 bytes = 0; 2714 else 2715 bytes = con->header_used; 2716 2717 con->header_used = 0; 2718 } 2719 } 2720 2721 if (bytes <= 0 || 2722 (httpGetState(con->http) != HTTP_STATE_GET_SEND && 2723 httpGetState(con->http) != HTTP_STATE_POST_SEND)) 2724 { 2725 if (!con->sent_header && con->pipe_pid) 2726 cupsdSendError(con, HTTP_STATUS_SERVER_ERROR, CUPSD_AUTH_NONE); 2727 else 2728 { 2729 cupsdLogRequest(con, HTTP_STATUS_OK); 2730 2731 if (httpIsChunked(con->http) && (!con->pipe_pid || con->sent_header > 0)) 2732 { 2733 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending 0-length chunk."); 2734 2735 if (httpWrite2(con->http, "", 0) < 0) 2736 { 2737 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)", 2738 httpError(con->http), strerror(httpError(con->http))); 2739 cupsdCloseClient(con); 2740 return; 2741 } 2742 } 2743 2744 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Flushing write buffer."); 2745 httpFlushWrite(con->http); 2746 cupsdLogClient(con, CUPSD_LOG_DEBUG, "New state is %s", httpStateString(httpGetState(con->http))); 2747 } 2748 2749 cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, NULL, con); 2750 2751 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for request."); 2752 2753 if (con->file >= 0) 2754 { 2755 cupsdRemoveSelect(con->file); 2756 2757 if (con->pipe_pid) 2758 cupsdEndProcess(con->pipe_pid, 0); 2759 2760 close(con->file); 2761 con->file = -1; 2762 con->pipe_pid = 0; 2763 } 2764 2765 if (con->filename) 2766 { 2767 unlink(con->filename); 2768 cupsdClearString(&con->filename); 2769 } 2770 2771 if (con->request) 2772 { 2773 ippDelete(con->request); 2774 con->request = NULL; 2775 } 2776 2777 if (con->response) 2778 { 2779 ippDelete(con->response); 2780 con->response = NULL; 2781 } 2782 2783 cupsdClearString(&con->command); 2784 cupsdClearString(&con->options); 2785 cupsdClearString(&con->query_string); 2786 2787 if (!httpGetKeepAlive(con->http)) 2788 { 2789 cupsdLogClient(con, CUPSD_LOG_DEBUG, 2790 "Closing because Keep-Alive is disabled."); 2791 cupsdCloseClient(con); 2792 return; 2793 } 2794 else 2795 { 2796 cupsArrayRemove(ActiveClients, con); 2797 cupsdSetBusyState(); 2798 } 2799 } 2800} 2801 2802 2803/* 2804 * 'check_if_modified()' - Decode an "If-Modified-Since" line. 2805 */ 2806 2807static int /* O - 1 if modified since */ 2808check_if_modified( 2809 cupsd_client_t *con, /* I - Client connection */ 2810 struct stat *filestats) /* I - File information */ 2811{ 2812 const char *ptr; /* Pointer into field */ 2813 time_t date; /* Time/date value */ 2814 off_t size; /* Size/length value */ 2815 2816 2817 size = 0; 2818 date = 0; 2819 ptr = httpGetField(con->http, HTTP_FIELD_IF_MODIFIED_SINCE); 2820 2821 if (*ptr == '\0') 2822 return (1); 2823 2824 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 2825 "check_if_modified " 2826 "filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"", 2827 filestats, CUPS_LLCAST filestats->st_size, 2828 (int)filestats->st_mtime, ptr); 2829 2830 while (*ptr != '\0') 2831 { 2832 while (isspace(*ptr) || *ptr == ';') 2833 ptr ++; 2834 2835 if (_cups_strncasecmp(ptr, "length=", 7) == 0) 2836 { 2837 ptr += 7; 2838 size = strtoll(ptr, NULL, 10); 2839 2840 while (isdigit(*ptr)) 2841 ptr ++; 2842 } 2843 else if (isalpha(*ptr)) 2844 { 2845 date = httpGetDateTime(ptr); 2846 while (*ptr != '\0' && *ptr != ';') 2847 ptr ++; 2848 } 2849 else 2850 ptr ++; 2851 } 2852 2853 return ((size != filestats->st_size && size != 0) || 2854 (date < filestats->st_mtime && date != 0) || 2855 (size == 0 && date == 0)); 2856} 2857 2858 2859/* 2860 * 'compare_clients()' - Compare two client connections. 2861 */ 2862 2863static int /* O - Result of comparison */ 2864compare_clients(cupsd_client_t *a, /* I - First client */ 2865 cupsd_client_t *b, /* I - Second client */ 2866 void *data) /* I - User data (not used) */ 2867{ 2868 (void)data; 2869 2870 if (a == b) 2871 return (0); 2872 else if (a < b) 2873 return (-1); 2874 else 2875 return (1); 2876} 2877 2878 2879#ifdef HAVE_SSL 2880/* 2881 * 'cupsd_start_tls()' - Start encryption on a connection. 2882 */ 2883 2884static int /* O - 0 on success, -1 on error */ 2885cupsd_start_tls(cupsd_client_t *con, /* I - Client connection */ 2886 http_encryption_t e) /* I - Encryption mode */ 2887{ 2888 if (httpEncryption(con->http, e)) 2889 { 2890 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to encrypt connection: %s", 2891 cupsLastErrorString()); 2892 return (-1); 2893 } 2894 2895 cupsdLogClient(con, CUPSD_LOG_INFO, "Connection now encrypted."); 2896 return (0); 2897} 2898#endif /* HAVE_SSL */ 2899 2900 2901/* 2902 * 'get_file()' - Get a filename and state info. 2903 */ 2904 2905static char * /* O - Real filename */ 2906get_file(cupsd_client_t *con, /* I - Client connection */ 2907 struct stat *filestats, /* O - File information */ 2908 char *filename, /* IO - Filename buffer */ 2909 size_t len) /* I - Buffer length */ 2910{ 2911 int status; /* Status of filesystem calls */ 2912 char *ptr; /* Pointer info filename */ 2913 size_t plen; /* Remaining length after pointer */ 2914 char language[7]; /* Language subdirectory, if any */ 2915 int perm_check = 1; /* Do permissions check? */ 2916 2917 2918 /* 2919 * Figure out the real filename... 2920 */ 2921 2922 language[0] = '\0'; 2923 2924 if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/')) 2925 { 2926 snprintf(filename, len, "%s%s", ServerRoot, con->uri); 2927 2928 perm_check = 0; 2929 } 2930 else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/')) 2931 { 2932 snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7); 2933 if (access(filename, F_OK) < 0) 2934 snprintf(filename, len, "%s/images/generic.png", DocumentRoot); 2935 2936 perm_check = 0; 2937 } 2938 else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/')) 2939 snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5); 2940 else if (!strcmp(con->uri, "/admin/conf/cupsd.conf")) 2941 { 2942 strlcpy(filename, ConfigurationFile, len); 2943 2944 perm_check = 0; 2945 } 2946 else if (!strncmp(con->uri, "/admin/log/", 11)) 2947 { 2948 if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/') 2949 strlcpy(filename, AccessLog, len); 2950 else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/') 2951 strlcpy(filename, ErrorLog, len); 2952 else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/') 2953 strlcpy(filename, PageLog, len); 2954 else 2955 return (NULL); 2956 2957 perm_check = 0; 2958 } 2959 else if (con->language) 2960 { 2961 snprintf(language, sizeof(language), "/%s", con->language->language); 2962 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri); 2963 } 2964 else 2965 snprintf(filename, len, "%s%s", DocumentRoot, con->uri); 2966 2967 if ((ptr = strchr(filename, '?')) != NULL) 2968 *ptr = '\0'; 2969 2970 /* 2971 * Grab the status for this language; if there isn't a language-specific file 2972 * then fallback to the default one... 2973 */ 2974 2975 if ((status = lstat(filename, filestats)) != 0 && language[0] && 2976 strncmp(con->uri, "/icons/", 7) && 2977 strncmp(con->uri, "/ppd/", 5) && 2978 strncmp(con->uri, "/rss/", 5) && 2979 strncmp(con->uri, "/admin/conf/", 12) && 2980 strncmp(con->uri, "/admin/log/", 11)) 2981 { 2982 /* 2983 * Drop the country code... 2984 */ 2985 2986 language[3] = '\0'; 2987 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri); 2988 2989 if ((ptr = strchr(filename, '?')) != NULL) 2990 *ptr = '\0'; 2991 2992 if ((status = lstat(filename, filestats)) != 0) 2993 { 2994 /* 2995 * Drop the language prefix and try the root directory... 2996 */ 2997 2998 language[0] = '\0'; 2999 snprintf(filename, len, "%s%s", DocumentRoot, con->uri); 3000 3001 if ((ptr = strchr(filename, '?')) != NULL) 3002 *ptr = '\0'; 3003 3004 status = lstat(filename, filestats); 3005 } 3006 } 3007 3008 /* 3009 * If we've found a symlink, 404 the sucker to avoid disclosing information. 3010 */ 3011 3012 if (!status && S_ISLNK(filestats->st_mode)) 3013 { 3014 cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename); 3015 return (NULL); 3016 } 3017 3018 /* 3019 * Similarly, if the file/directory does not have world read permissions, do 3020 * not allow access... 3021 */ 3022 3023 if (!status && perm_check && !(filestats->st_mode & S_IROTH)) 3024 { 3025 cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename); 3026 return (NULL); 3027 } 3028 3029 /* 3030 * If we've found a directory, get the index.html file instead... 3031 */ 3032 3033 if (!status && S_ISDIR(filestats->st_mode)) 3034 { 3035 /* 3036 * Make sure the URI ends with a slash... 3037 */ 3038 3039 if (con->uri[strlen(con->uri) - 1] != '/') 3040 strlcat(con->uri, "/", sizeof(con->uri)); 3041 3042 /* 3043 * Find the directory index file, trying every language... 3044 */ 3045 3046 do 3047 { 3048 if (status && language[0]) 3049 { 3050 /* 3051 * Try a different language subset... 3052 */ 3053 3054 if (language[3]) 3055 language[0] = '\0'; /* Strip country code */ 3056 else 3057 language[0] = '\0'; /* Strip language */ 3058 } 3059 3060 /* 3061 * Look for the index file... 3062 */ 3063 3064 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri); 3065 3066 if ((ptr = strchr(filename, '?')) != NULL) 3067 *ptr = '\0'; 3068 3069 ptr = filename + strlen(filename); 3070 plen = len - (size_t)(ptr - filename); 3071 3072 strlcpy(ptr, "index.html", plen); 3073 status = lstat(filename, filestats); 3074 3075#ifdef HAVE_JAVA 3076 if (status) 3077 { 3078 strlcpy(ptr, "index.class", plen); 3079 status = lstat(filename, filestats); 3080 } 3081#endif /* HAVE_JAVA */ 3082 3083#ifdef HAVE_PERL 3084 if (status) 3085 { 3086 strlcpy(ptr, "index.pl", plen); 3087 status = lstat(filename, filestats); 3088 } 3089#endif /* HAVE_PERL */ 3090 3091#ifdef HAVE_PHP 3092 if (status) 3093 { 3094 strlcpy(ptr, "index.php", plen); 3095 status = lstat(filename, filestats); 3096 } 3097#endif /* HAVE_PHP */ 3098 3099#ifdef HAVE_PYTHON 3100 if (status) 3101 { 3102 strlcpy(ptr, "index.pyc", plen); 3103 status = lstat(filename, filestats); 3104 } 3105 3106 if (status) 3107 { 3108 strlcpy(ptr, "index.py", plen); 3109 status = lstat(filename, filestats); 3110 } 3111#endif /* HAVE_PYTHON */ 3112 3113 } 3114 while (status && language[0]); 3115 3116 /* 3117 * If we've found a symlink, 404 the sucker to avoid disclosing information. 3118 */ 3119 3120 if (!status && S_ISLNK(filestats->st_mode)) 3121 { 3122 cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename); 3123 return (NULL); 3124 } 3125 3126 /* 3127 * Similarly, if the file/directory does not have world read permissions, do 3128 * not allow access... 3129 */ 3130 3131 if (!status && perm_check && !(filestats->st_mode & S_IROTH)) 3132 { 3133 cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename); 3134 return (NULL); 3135 } 3136 } 3137 3138 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "get_file filestats=%p, filename=%p, len=" CUPS_LLFMT ", returning \"%s\".", filestats, filename, CUPS_LLCAST len, status ? "(null)" : filename); 3139 3140 if (status) 3141 return (NULL); 3142 else 3143 return (filename); 3144} 3145 3146 3147/* 3148 * 'install_cupsd_conf()' - Install a configuration file. 3149 */ 3150 3151static http_status_t /* O - Status */ 3152install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ 3153{ 3154 char filename[1024]; /* Configuration filename */ 3155 cups_file_t *in, /* Input file */ 3156 *out; /* Output file */ 3157 char buffer[16384]; /* Copy buffer */ 3158 ssize_t bytes; /* Number of bytes */ 3159 3160 3161 /* 3162 * Open the request file... 3163 */ 3164 3165 if ((in = cupsFileOpen(con->filename, "rb")) == NULL) 3166 { 3167 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s", 3168 con->filename, strerror(errno)); 3169 return (HTTP_STATUS_SERVER_ERROR); 3170 } 3171 3172 /* 3173 * Open the new config file... 3174 */ 3175 3176 if ((out = cupsdCreateConfFile(ConfigurationFile, ConfigFilePerm)) == NULL) 3177 { 3178 cupsFileClose(in); 3179 return (HTTP_STATUS_SERVER_ERROR); 3180 } 3181 3182 cupsdLogClient(con, CUPSD_LOG_INFO, "Installing config file \"%s\"...", 3183 ConfigurationFile); 3184 3185 /* 3186 * Copy from the request to the new config file... 3187 */ 3188 3189 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0) 3190 if (cupsFileWrite(out, buffer, (size_t)bytes) < bytes) 3191 { 3192 cupsdLogClient(con, CUPSD_LOG_ERROR, 3193 "Unable to copy to config file \"%s\": %s", 3194 ConfigurationFile, strerror(errno)); 3195 3196 cupsFileClose(in); 3197 cupsFileClose(out); 3198 3199 snprintf(filename, sizeof(filename), "%s.N", ConfigurationFile); 3200 cupsdUnlinkOrRemoveFile(filename); 3201 3202 return (HTTP_STATUS_SERVER_ERROR); 3203 } 3204 3205 /* 3206 * Close the files... 3207 */ 3208 3209 cupsFileClose(in); 3210 3211 if (cupsdCloseCreatedConfFile(out, ConfigurationFile)) 3212 return (HTTP_STATUS_SERVER_ERROR); 3213 3214 /* 3215 * Remove the request file... 3216 */ 3217 3218 cupsdUnlinkOrRemoveFile(con->filename); 3219 cupsdClearString(&con->filename); 3220 3221 /* 3222 * Set the NeedReload flag... 3223 */ 3224 3225 NeedReload = RELOAD_CUPSD; 3226 ReloadTime = time(NULL); 3227 3228 /* 3229 * Return that the file was created successfully... 3230 */ 3231 3232 return (HTTP_STATUS_CREATED); 3233} 3234 3235 3236/* 3237 * 'is_cgi()' - Is the resource a CGI script/program? 3238 */ 3239 3240static int /* O - 1 = CGI, 0 = file */ 3241is_cgi(cupsd_client_t *con, /* I - Client connection */ 3242 const char *filename, /* I - Real filename */ 3243 struct stat *filestats, /* I - File information */ 3244 mime_type_t *type) /* I - MIME type */ 3245{ 3246 const char *options; /* Options on URL */ 3247 3248 3249 /* 3250 * Get the options, if any... 3251 */ 3252 3253 if ((options = strchr(con->uri, '?')) != NULL) 3254 { 3255 options ++; 3256 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options); 3257 } 3258 3259 /* 3260 * Check for known types... 3261 */ 3262 3263 if (!type || _cups_strcasecmp(type->super, "application")) 3264 { 3265 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 3266 "is_cgi filename=\"%s\", filestats=%p, " 3267 "type=%s/%s, returning 0", filename, 3268 filestats, type ? type->super : "unknown", 3269 type ? type->type : "unknown"); 3270 return (0); 3271 } 3272 3273 if (!_cups_strcasecmp(type->type, "x-httpd-cgi") && 3274 (filestats->st_mode & 0111)) 3275 { 3276 /* 3277 * "application/x-httpd-cgi" is a CGI script. 3278 */ 3279 3280 cupsdSetString(&con->command, filename); 3281 3282 if (options) 3283 cupsdSetStringf(&con->options, " %s", options); 3284 3285 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 3286 "is_cgi filename=\"%s\", filestats=%p, " 3287 "type=%s/%s, returning 1", filename, 3288 filestats, type->super, type->type); 3289 return (1); 3290 } 3291#ifdef HAVE_JAVA 3292 else if (!_cups_strcasecmp(type->type, "x-httpd-java")) 3293 { 3294 /* 3295 * "application/x-httpd-java" is a Java servlet. 3296 */ 3297 3298 cupsdSetString(&con->command, CUPS_JAVA); 3299 3300 if (options) 3301 cupsdSetStringf(&con->options, " %s %s", filename, options); 3302 else 3303 cupsdSetStringf(&con->options, " %s", filename); 3304 3305 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 3306 "is_cgi filename=\"%s\", filestats=%p, " 3307 "type=%s/%s, returning 1", filename, 3308 filestats, type->super, type->type); 3309 return (1); 3310 } 3311#endif /* HAVE_JAVA */ 3312#ifdef HAVE_PERL 3313 else if (!_cups_strcasecmp(type->type, "x-httpd-perl")) 3314 { 3315 /* 3316 * "application/x-httpd-perl" is a Perl page. 3317 */ 3318 3319 cupsdSetString(&con->command, CUPS_PERL); 3320 3321 if (options) 3322 cupsdSetStringf(&con->options, " %s %s", filename, options); 3323 else 3324 cupsdSetStringf(&con->options, " %s", filename); 3325 3326 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 3327 "is_cgi filename=\"%s\", filestats=%p, " 3328 "type=%s/%s, returning 1", filename, 3329 filestats, type->super, type->type); 3330 return (1); 3331 } 3332#endif /* HAVE_PERL */ 3333#ifdef HAVE_PHP 3334 else if (!_cups_strcasecmp(type->type, "x-httpd-php")) 3335 { 3336 /* 3337 * "application/x-httpd-php" is a PHP page. 3338 */ 3339 3340 cupsdSetString(&con->command, CUPS_PHP); 3341 3342 if (options) 3343 cupsdSetStringf(&con->options, " %s %s", filename, options); 3344 else 3345 cupsdSetStringf(&con->options, " %s", filename); 3346 3347 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 3348 "is_cgi filename=\"%s\", filestats=%p, " 3349 "type=%s/%s, returning 1", filename, 3350 filestats, type->super, type->type); 3351 return (1); 3352 } 3353#endif /* HAVE_PHP */ 3354#ifdef HAVE_PYTHON 3355 else if (!_cups_strcasecmp(type->type, "x-httpd-python")) 3356 { 3357 /* 3358 * "application/x-httpd-python" is a Python page. 3359 */ 3360 3361 cupsdSetString(&con->command, CUPS_PYTHON); 3362 3363 if (options) 3364 cupsdSetStringf(&con->options, " %s %s", filename, options); 3365 else 3366 cupsdSetStringf(&con->options, " %s", filename); 3367 3368 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 3369 "is_cgi filename=\"%s\", filestats=%p, " 3370 "type=%s/%s, returning 1", filename, 3371 filestats, type->super, type->type); 3372 return (1); 3373 } 3374#endif /* HAVE_PYTHON */ 3375 3376 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 3377 "is_cgi filename=\"%s\", filestats=%p, " 3378 "type=%s/%s, returning 0", filename, 3379 filestats, type->super, type->type); 3380 return (0); 3381} 3382 3383 3384/* 3385 * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. ".."). 3386 */ 3387 3388static int /* O - 0 if relative, 1 if absolute */ 3389is_path_absolute(const char *path) /* I - Input path */ 3390{ 3391 /* 3392 * Check for a leading slash... 3393 */ 3394 3395 if (path[0] != '/') 3396 return (0); 3397 3398 /* 3399 * Check for "<" or quotes in the path and reject since this is probably 3400 * someone trying to inject HTML... 3401 */ 3402 3403 if (strchr(path, '<') != NULL || strchr(path, '\"') != NULL || strchr(path, '\'') != NULL) 3404 return (0); 3405 3406 /* 3407 * Check for "/.." in the path... 3408 */ 3409 3410 while ((path = strstr(path, "/..")) != NULL) 3411 { 3412 if (!path[3] || path[3] == '/') 3413 return (0); 3414 3415 path ++; 3416 } 3417 3418 /* 3419 * If we haven't found any relative paths, return 1 indicating an 3420 * absolute path... 3421 */ 3422 3423 return (1); 3424} 3425 3426 3427/* 3428 * 'pipe_command()' - Pipe the output of a command to the remote client. 3429 */ 3430 3431static int /* O - Process ID */ 3432pipe_command(cupsd_client_t *con, /* I - Client connection */ 3433 int infile, /* I - Standard input for command */ 3434 int *outfile, /* O - Standard output for command */ 3435 char *command, /* I - Command to run */ 3436 char *options, /* I - Options for command */ 3437 int root) /* I - Run as root? */ 3438{ 3439 int i; /* Looping var */ 3440 int pid; /* Process ID */ 3441 char *commptr, /* Command string pointer */ 3442 commch; /* Command string character */ 3443 char *uriptr; /* URI string pointer */ 3444 int fds[2]; /* Pipe FDs */ 3445 int argc; /* Number of arguments */ 3446 int envc; /* Number of environment variables */ 3447 char argbuf[10240], /* Argument buffer */ 3448 *argv[100], /* Argument strings */ 3449 *envp[MAX_ENV + 20]; /* Environment variables */ 3450 char auth_type[256], /* AUTH_TYPE environment variable */ 3451 content_length[1024], /* CONTENT_LENGTH environment variable */ 3452 content_type[1024], /* CONTENT_TYPE environment variable */ 3453 http_cookie[32768], /* HTTP_COOKIE environment variable */ 3454 http_referer[1024], /* HTTP_REFERER environment variable */ 3455 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */ 3456 lang[1024], /* LANG environment variable */ 3457 path_info[1024], /* PATH_INFO environment variable */ 3458 remote_addr[1024], /* REMOTE_ADDR environment variable */ 3459 remote_host[1024], /* REMOTE_HOST environment variable */ 3460 remote_user[1024], /* REMOTE_USER environment variable */ 3461 script_filename[1024], /* SCRIPT_FILENAME environment variable */ 3462 script_name[1024], /* SCRIPT_NAME environment variable */ 3463 server_name[1024], /* SERVER_NAME environment variable */ 3464 server_port[1024]; /* SERVER_PORT environment variable */ 3465 ipp_attribute_t *attr; /* attributes-natural-language attribute */ 3466 3467 3468 /* 3469 * Parse a copy of the options string, which is of the form: 3470 * 3471 * argument+argument+argument 3472 * ?argument+argument+argument 3473 * param=value¶m=value 3474 * ?param=value¶m=value 3475 * /name?argument+argument+argument 3476 * /name?param=value¶m=value 3477 * 3478 * If the string contains an "=" character after the initial name, 3479 * then we treat it as a HTTP GET form request and make a copy of 3480 * the remaining string for the environment variable. 3481 * 3482 * The string is always parsed out as command-line arguments, to 3483 * be consistent with Apache... 3484 */ 3485 3486 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 3487 "pipe_command infile=%d, outfile=%p, " 3488 "command=\"%s\", options=\"%s\", root=%d", 3489 infile, outfile, command, 3490 options ? options : "(null)", root); 3491 3492 argv[0] = command; 3493 3494 if (options) 3495 strlcpy(argbuf, options, sizeof(argbuf)); 3496 else 3497 argbuf[0] = '\0'; 3498 3499 if (argbuf[0] == '/') 3500 { 3501 /* 3502 * Found some trailing path information, set PATH_INFO... 3503 */ 3504 3505 if ((commptr = strchr(argbuf, '?')) == NULL) 3506 commptr = argbuf + strlen(argbuf); 3507 3508 commch = *commptr; 3509 *commptr = '\0'; 3510 snprintf(path_info, sizeof(path_info), "PATH_INFO=%s", argbuf); 3511 *commptr = commch; 3512 } 3513 else 3514 { 3515 commptr = argbuf; 3516 path_info[0] = '\0'; 3517 3518 if (*commptr == ' ') 3519 commptr ++; 3520 } 3521 3522 if (*commptr == '?' && con->operation == HTTP_STATE_GET && !con->query_string) 3523 { 3524 commptr ++; 3525 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr); 3526 } 3527 3528 argc = 1; 3529 3530 if (*commptr) 3531 { 3532 argv[argc ++] = commptr; 3533 3534 for (; *commptr && argc < 99; commptr ++) 3535 { 3536 /* 3537 * Break arguments whenever we see a + or space... 3538 */ 3539 3540 if (*commptr == ' ' || *commptr == '+') 3541 { 3542 while (*commptr == ' ' || *commptr == '+') 3543 *commptr++ = '\0'; 3544 3545 /* 3546 * If we don't have a blank string, save it as another argument... 3547 */ 3548 3549 if (*commptr) 3550 { 3551 argv[argc] = commptr; 3552 argc ++; 3553 } 3554 else 3555 break; 3556 } 3557 else if (*commptr == '%' && isxdigit(commptr[1] & 255) && 3558 isxdigit(commptr[2] & 255)) 3559 { 3560 /* 3561 * Convert the %xx notation to the individual character. 3562 */ 3563 3564 if (commptr[1] >= '0' && commptr[1] <= '9') 3565 *commptr = (char)((commptr[1] - '0') << 4); 3566 else 3567 *commptr = (char)((tolower(commptr[1]) - 'a' + 10) << 4); 3568 3569 if (commptr[2] >= '0' && commptr[2] <= '9') 3570 *commptr |= commptr[2] - '0'; 3571 else 3572 *commptr |= tolower(commptr[2]) - 'a' + 10; 3573 3574 _cups_strcpy(commptr + 1, commptr + 3); 3575 3576 /* 3577 * Check for a %00 and break if that is the case... 3578 */ 3579 3580 if (!*commptr) 3581 break; 3582 } 3583 } 3584 } 3585 3586 argv[argc] = NULL; 3587 3588 /* 3589 * Setup the environment variables as needed... 3590 */ 3591 3592 if (con->username[0]) 3593 { 3594 snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s", 3595 httpGetField(con->http, HTTP_FIELD_AUTHORIZATION)); 3596 3597 if ((uriptr = strchr(auth_type + 10, ' ')) != NULL) 3598 *uriptr = '\0'; 3599 } 3600 else 3601 auth_type[0] = '\0'; 3602 3603 if (con->request && 3604 (attr = ippFindAttribute(con->request, "attributes-natural-language", 3605 IPP_TAG_LANGUAGE)) != NULL) 3606 { 3607 switch (strlen(attr->values[0].string.text)) 3608 { 3609 default : 3610 /* 3611 * This is an unknown or badly formatted language code; use 3612 * the POSIX locale... 3613 */ 3614 3615 strlcpy(lang, "LANG=C", sizeof(lang)); 3616 break; 3617 3618 case 2 : 3619 /* 3620 * Just the language code (ll)... 3621 */ 3622 3623 snprintf(lang, sizeof(lang), "LANG=%s.UTF8", 3624 attr->values[0].string.text); 3625 break; 3626 3627 case 5 : 3628 /* 3629 * Language and country code (ll-cc)... 3630 */ 3631 3632 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF8", 3633 attr->values[0].string.text[0], 3634 attr->values[0].string.text[1], 3635 toupper(attr->values[0].string.text[3] & 255), 3636 toupper(attr->values[0].string.text[4] & 255)); 3637 break; 3638 } 3639 } 3640 else if (con->language) 3641 snprintf(lang, sizeof(lang), "LANG=%s.UTF8", con->language->language); 3642 else 3643 strlcpy(lang, "LANG=C", sizeof(lang)); 3644 3645 strlcpy(remote_addr, "REMOTE_ADDR=", sizeof(remote_addr)); 3646 httpAddrString(httpGetAddress(con->http), remote_addr + 12, 3647 sizeof(remote_addr) - 12); 3648 3649 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", 3650 httpGetHostname(con->http, NULL, 0)); 3651 3652 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri); 3653 if ((uriptr = strchr(script_name, '?')) != NULL) 3654 *uriptr = '\0'; 3655 3656 snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s", 3657 DocumentRoot, script_name + 12); 3658 3659 sprintf(server_port, "SERVER_PORT=%d", con->serverport); 3660 3661 if (httpGetField(con->http, HTTP_FIELD_HOST)[0]) 3662 { 3663 char *nameptr; /* Pointer to ":port" */ 3664 3665 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", 3666 httpGetField(con->http, HTTP_FIELD_HOST)); 3667 if ((nameptr = strrchr(server_name, ':')) != NULL && !strchr(nameptr, ']')) 3668 *nameptr = '\0'; /* Strip trailing ":port" */ 3669 } 3670 else 3671 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", 3672 con->servername); 3673 3674 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0]))); 3675 3676 if (auth_type[0]) 3677 envp[envc ++] = auth_type; 3678 3679 envp[envc ++] = lang; 3680 envp[envc ++] = "REDIRECT_STATUS=1"; 3681 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1"; 3682 envp[envc ++] = server_name; 3683 envp[envc ++] = server_port; 3684 envp[envc ++] = remote_addr; 3685 envp[envc ++] = remote_host; 3686 envp[envc ++] = script_name; 3687 envp[envc ++] = script_filename; 3688 3689 if (path_info[0]) 3690 envp[envc ++] = path_info; 3691 3692 if (con->username[0]) 3693 { 3694 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username); 3695 3696 envp[envc ++] = remote_user; 3697 } 3698 3699 if (httpGetVersion(con->http) == HTTP_VERSION_1_1) 3700 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1"; 3701 else if (httpGetVersion(con->http) == HTTP_VERSION_1_0) 3702 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0"; 3703 else 3704 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9"; 3705 3706 if (httpGetCookie(con->http)) 3707 { 3708 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s", 3709 httpGetCookie(con->http)); 3710 envp[envc ++] = http_cookie; 3711 } 3712 3713 if (httpGetField(con->http, HTTP_FIELD_USER_AGENT)[0]) 3714 { 3715 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s", 3716 httpGetField(con->http, HTTP_FIELD_USER_AGENT)); 3717 envp[envc ++] = http_user_agent; 3718 } 3719 3720 if (httpGetField(con->http, HTTP_FIELD_REFERER)[0]) 3721 { 3722 snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s", 3723 httpGetField(con->http, HTTP_FIELD_REFERER)); 3724 envp[envc ++] = http_referer; 3725 } 3726 3727 if (con->operation == HTTP_STATE_GET) 3728 { 3729 envp[envc ++] = "REQUEST_METHOD=GET"; 3730 3731 if (con->query_string) 3732 { 3733 /* 3734 * Add GET form variables after ?... 3735 */ 3736 3737 envp[envc ++] = con->query_string; 3738 } 3739 else 3740 envp[envc ++] = "QUERY_STRING="; 3741 } 3742 else 3743 { 3744 sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT, 3745 CUPS_LLCAST con->bytes); 3746 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s", 3747 httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE)); 3748 3749 envp[envc ++] = "REQUEST_METHOD=POST"; 3750 envp[envc ++] = content_length; 3751 envp[envc ++] = content_type; 3752 } 3753 3754 /* 3755 * Tell the CGI if we are using encryption... 3756 */ 3757 3758 if (httpIsEncrypted(con->http)) 3759 envp[envc ++] = "HTTPS=ON"; 3760 3761 /* 3762 * Terminate the environment array... 3763 */ 3764 3765 envp[envc] = NULL; 3766 3767 if (LogLevel >= CUPSD_LOG_DEBUG) 3768 { 3769 for (i = 0; i < argc; i ++) 3770 cupsdLogMessage(CUPSD_LOG_DEBUG, 3771 "[CGI] argv[%d] = \"%s\"", i, argv[i]); 3772 for (i = 0; i < envc; i ++) 3773 cupsdLogMessage(CUPSD_LOG_DEBUG, 3774 "[CGI] envp[%d] = \"%s\"", i, envp[i]); 3775 } 3776 3777 /* 3778 * Create a pipe for the output... 3779 */ 3780 3781 if (cupsdOpenPipe(fds)) 3782 { 3783 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s", 3784 argv[0], strerror(errno)); 3785 return (0); 3786 } 3787 3788 /* 3789 * Then execute the command... 3790 */ 3791 3792 if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1], 3793 -1, -1, root, DefaultProfile, NULL, &pid) < 0) 3794 { 3795 /* 3796 * Error - can't fork! 3797 */ 3798 3799 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0], 3800 strerror(errno)); 3801 3802 cupsdClosePipe(fds); 3803 pid = 0; 3804 } 3805 else 3806 { 3807 /* 3808 * Fork successful - return the PID... 3809 */ 3810 3811 if (con->username[0]) 3812 cupsdAddCert(pid, con->username, con->type); 3813 3814 cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] Started %s (PID %d)", command, pid); 3815 3816 *outfile = fds[0]; 3817 close(fds[1]); 3818 } 3819 3820 return (pid); 3821} 3822 3823 3824/* 3825 * 'valid_host()' - Is the Host: field valid? 3826 */ 3827 3828static int /* O - 1 if valid, 0 if not */ 3829valid_host(cupsd_client_t *con) /* I - Client connection */ 3830{ 3831 cupsd_alias_t *a; /* Current alias */ 3832 cupsd_netif_t *netif; /* Current network interface */ 3833 const char *end; /* End character */ 3834 char *ptr; /* Pointer into host value */ 3835 3836 3837 /* 3838 * Copy the Host: header for later use... 3839 */ 3840 3841 strlcpy(con->clientname, httpGetField(con->http, HTTP_FIELD_HOST), 3842 sizeof(con->clientname)); 3843 if ((ptr = strrchr(con->clientname, ':')) != NULL && !strchr(ptr, ']')) 3844 { 3845 *ptr++ = '\0'; 3846 con->clientport = atoi(ptr); 3847 } 3848 else 3849 con->clientport = con->serverport; 3850 3851 /* 3852 * Then validate... 3853 */ 3854 3855 if (httpAddrLocalhost(httpGetAddress(con->http))) 3856 { 3857 /* 3858 * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical 3859 * addresses when accessing CUPS via the loopback interface... 3860 */ 3861 3862 return (!_cups_strcasecmp(con->clientname, "localhost") || 3863 !_cups_strcasecmp(con->clientname, "localhost.") || 3864#ifdef __linux 3865 !_cups_strcasecmp(con->clientname, "localhost.localdomain") || 3866#endif /* __linux */ 3867 !strcmp(con->clientname, "127.0.0.1") || 3868 !strcmp(con->clientname, "[::1]")); 3869 } 3870 3871#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 3872 /* 3873 * Check if the hostname is something.local (Bonjour); if so, allow it. 3874 */ 3875 3876 if ((end = strrchr(con->clientname, '.')) != NULL && end > con->clientname && 3877 !end[1]) 3878 { 3879 /* 3880 * "." on end, work back to second-to-last "."... 3881 */ 3882 3883 for (end --; end > con->clientname && *end != '.'; end --); 3884 } 3885 3886 if (end && (!_cups_strcasecmp(end, ".local") || 3887 !_cups_strcasecmp(end, ".local."))) 3888 return (1); 3889#endif /* HAVE_DNSSD || HAVE_AVAHI */ 3890 3891 /* 3892 * Check if the hostname is an IP address... 3893 */ 3894 3895 if (isdigit(con->clientname[0] & 255) || con->clientname[0] == '[') 3896 { 3897 /* 3898 * Possible IPv4/IPv6 address... 3899 */ 3900 3901 http_addrlist_t *addrlist; /* List of addresses */ 3902 3903 3904 if ((addrlist = httpAddrGetList(con->clientname, AF_UNSPEC, NULL)) != NULL) 3905 { 3906 /* 3907 * Good IPv4/IPv6 address... 3908 */ 3909 3910 httpAddrFreeList(addrlist); 3911 return (1); 3912 } 3913 } 3914 3915 /* 3916 * Check for (alias) name matches... 3917 */ 3918 3919 for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias); 3920 a; 3921 a = (cupsd_alias_t *)cupsArrayNext(ServerAlias)) 3922 { 3923 /* 3924 * "ServerAlias *" allows all host values through... 3925 */ 3926 3927 if (!strcmp(a->name, "*")) 3928 return (1); 3929 3930 if (!_cups_strncasecmp(con->clientname, a->name, a->namelen)) 3931 { 3932 /* 3933 * Prefix matches; check the character at the end - it must be "." or nul. 3934 */ 3935 3936 end = con->clientname + a->namelen; 3937 3938 if (!*end || (*end == '.' && !end[1])) 3939 return (1); 3940 } 3941 } 3942 3943#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 3944 for (a = (cupsd_alias_t *)cupsArrayFirst(DNSSDAlias); 3945 a; 3946 a = (cupsd_alias_t *)cupsArrayNext(DNSSDAlias)) 3947 { 3948 /* 3949 * "ServerAlias *" allows all host values through... 3950 */ 3951 3952 if (!strcmp(a->name, "*")) 3953 return (1); 3954 3955 if (!_cups_strncasecmp(con->clientname, a->name, a->namelen)) 3956 { 3957 /* 3958 * Prefix matches; check the character at the end - it must be "." or nul. 3959 */ 3960 3961 end = con->clientname + a->namelen; 3962 3963 if (!*end || (*end == '.' && !end[1])) 3964 return (1); 3965 } 3966 } 3967#endif /* HAVE_DNSSD || HAVE_AVAHI */ 3968 3969 /* 3970 * Check for interface hostname matches... 3971 */ 3972 3973 for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList); 3974 netif; 3975 netif = (cupsd_netif_t *)cupsArrayNext(NetIFList)) 3976 { 3977 if (!_cups_strncasecmp(con->clientname, netif->hostname, netif->hostlen)) 3978 { 3979 /* 3980 * Prefix matches; check the character at the end - it must be "." or nul. 3981 */ 3982 3983 end = con->clientname + netif->hostlen; 3984 3985 if (!*end || (*end == '.' && !end[1])) 3986 return (1); 3987 } 3988 } 3989 3990 return (0); 3991} 3992 3993 3994/* 3995 * 'write_file()' - Send a file via HTTP. 3996 */ 3997 3998static int /* O - 0 on failure, 1 on success */ 3999write_file(cupsd_client_t *con, /* I - Client connection */ 4000 http_status_t code, /* I - HTTP status */ 4001 char *filename, /* I - Filename */ 4002 char *type, /* I - File type */ 4003 struct stat *filestats) /* O - File information */ 4004{ 4005 con->file = open(filename, O_RDONLY); 4006 4007 cupsdLogClient(con, CUPSD_LOG_DEBUG2, 4008 "write_file code=%d, filename=\"%s\" (%d), " 4009 "type=\"%s\", filestats=%p", 4010 code, filename, con->file, type ? type : "(null)", filestats); 4011 4012 if (con->file < 0) 4013 return (0); 4014 4015 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); 4016 4017 con->pipe_pid = 0; 4018 con->sent_header = 1; 4019 4020 httpClearFields(con->http); 4021 4022 httpSetLength(con->http, (size_t)filestats->st_size); 4023 4024 httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, 4025 httpGetDateString(filestats->st_mtime)); 4026 4027 if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE)) 4028 return (0); 4029 4030 cupsdAddSelect(httpGetFd(con->http), NULL, (cupsd_selfunc_t)cupsdWriteClient, con); 4031 4032 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending file."); 4033 4034 return (1); 4035} 4036 4037 4038/* 4039 * 'write_pipe()' - Flag that data is available on the CGI pipe. 4040 */ 4041 4042static void 4043write_pipe(cupsd_client_t *con) /* I - Client connection */ 4044{ 4045 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_pipe CGI output on fd %d", 4046 con->file); 4047 4048 con->file_ready = 1; 4049 4050 cupsdRemoveSelect(con->file); 4051 cupsdAddSelect(httpGetFd(con->http), NULL, (cupsd_selfunc_t)cupsdWriteClient, con); 4052 4053 cupsdLogClient(con, CUPSD_LOG_DEBUG, "CGI data ready to be sent."); 4054} 4055 4056 4057/* 4058 * End of "$Id: client.c 12131 2014-08-28 23:38:16Z msweet $". 4059 */ 4060