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