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