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