/*
- * "$Id$"
- *
* Client routines for the CUPS scheduler.
*
- * Copyright 2007-2013 by Apple Inc.
+ * Copyright 2007-2015 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* This file contains Kerberos support code, copyright 2006 by
#endif /* HAVE_TCPD_H */
-/*
- * Local globals...
- */
-
-static const char * const ipp_states[] =
- { /* IPP state strings */
- "IPP_IDLE",
- "IPP_HEADER",
- "IPP_ATTRIBUTE",
- "IPP_STATE_DATA"
- };
-
-
/*
* Local functions...
*/
struct stat *filestats);
static int compare_clients(cupsd_client_t *a, cupsd_client_t *b,
void *data);
+#ifdef HAVE_SSL
+static int cupsd_start_tls(cupsd_client_t *con, http_encryption_t e);
+#endif /* HAVE_SSL */
static char *get_file(cupsd_client_t *con, struct stat *filestats,
- char *filename, int len);
+ char *filename, size_t len);
static http_status_t install_cupsd_conf(cupsd_client_t *con);
static int is_cgi(cupsd_client_t *con, const char *filename,
struct stat *filestats, mime_type_t *type);
#endif /* HAVE_TCPD_H */
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdAcceptClient(lis=%p(%d)) Clients=%d",
- lis, lis->fd, cupsArrayCount(Clients));
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAcceptClient(lis=%p(%d)) Clients=%d", lis, lis->fd, cupsArrayCount(Clients));
/*
* Make sure we don't have a full set of clients already...
* Save the connected address and port number...
*/
- con->clientaddr = lis->address;
+ addrlen = sizeof(con->clientaddr);
-#if 0 /* ifdef AF_INET6 */
- /* FIXME: I don't believe this is recommended any longer, and we specifically
- * disable IPv4-over-IPv6 when we listen...
- */
- /*
- * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we
- * can more easily use...
- */
+ if (getsockname(httpGetFd(con->http), (struct sockaddr *)&con->clientaddr, &addrlen) || addrlen == 0)
+ con->clientaddr = lis->address;
- if (lis->address.addr.sa_family == AF_INET6 &&
- httpGetAddress(con->http)->ipv6.sin6_addr.s6_addr32[0] == 0 &&
- httpGetAddress(con->http)->ipv6.sin6_addr.s6_addr32[1] == 0 &&
- ntohl(httpGetAddress(con->http)->ipv6.sin6_addr.s6_addr32[2]) == 0xffff)
- httpGetAddress(con->http)->ipv6.sin6_addr.s6_addr32[2] = 0;
-#endif /* AF_INET6 */
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Server address is \"%s\".", httpAddrString(&con->clientaddr, name, sizeof(name)));
/*
* Check the number of clients on the same address...
char peername[256]; /* Name of process */
peersize = sizeof(peerpid);
- if (!getsockopt(con->number, SOL_LOCAL, LOCAL_PEERPID, &peerpid,
+ if (!getsockopt(httpGetFd(con->http), SOL_LOCAL, LOCAL_PEERPID, &peerpid,
&peersize))
{
- if (!proc_name(peerpid, peername, sizeof(peername)))
+ if (!proc_name((int)peerpid, peername, sizeof(peername)))
cupsdLogClient(con, CUPSD_LOG_DEBUG,
"Accepted from %s (Domain ???[%d])",
httpGetHostname(con->http, NULL, 0), (int)peerpid);
else
cupsdLogClient(con, CUPSD_LOG_DEBUG,
"Accepted from %s (Domain %s[%d])",
- httpGetHostname(con->http, NULL, 0), name, (int)peerpid);
+ httpGetHostname(con->http, NULL, 0), peername, (int)peerpid);
}
else
# endif /* __APPLE__ */
* https connection; go secure...
*/
- if (!cupsdStartTLS(con))
+ if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS))
cupsdCloseClient(con);
}
else
cupsd_client_t *con; /* Current client */
- cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d",
- cupsArrayCount(Clients));
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d", cupsArrayCount(Clients));
for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
con;
cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */
{
int partial; /* Do partial close for SSL? */
-#ifdef HAVE_LIBSSL
-#elif defined(HAVE_GNUTLS)
-# elif defined(HAVE_CDSASSL)
-#endif /* HAVE_LIBSSL */
cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing connection.");
httpClose(con->http);
- cupsdClearString(&con->filename);
+ if (con->filename)
+ {
+ unlink(con->filename);
+ cupsdClearString(&con->filename);
+ }
+
cupsdClearString(&con->command);
cupsdClearString(&con->options);
cupsdClearString(&con->query_string);
}
-#if 0
-/*
- * 'cupsdFlushHeader()' - Flush the header fields to the client.
- */
-
-int /* I - Bytes written or -1 on error */
-cupsdFlushHeader(cupsd_client_t *con) /* I - Client to flush to */
-{
- int bytes = httpFlushWrite(con->http);
-
- // TODO: Need to use httpSendResponse
- con->http->data_encoding = HTTP_ENCODING_LENGTH;
-
- return (bytes);
-}
-#endif /* 0 */
-
-
/*
* 'cupsdReadClient()' - Read data from a client.
*/
cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */
{
char line[32768], /* Line from client... */
- operation[64], /* Operation code from socket */
locale[64], /* Locale */
*ptr; /* Pointer into strings */
http_status_t status; /* Transfer status */
status = HTTP_STATUS_CONTINUE;
- 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 ? ipp_states[con->request->state] : "",
- con->file);
+ 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);
+
+ if (httpGetState(con->http) == HTTP_STATE_GET_SEND ||
+ httpGetState(con->http) == HTTP_STATE_POST_SEND ||
+ httpGetState(con->http) == HTTP_STATE_STATUS)
+ {
+ /*
+ * If we get called in the wrong state, then something went wrong with the
+ * connection and we need to shut it down...
+ */
+
+ if (!httpGetReady(con->http) && recv(httpGetFd(con->http), buf, 1, MSG_PEEK) < 1)
+ {
+ /*
+ * Connection closed...
+ */
+
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on EOF.");
+ cupsdCloseClient(con);
+ return;
+ }
+
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP read state %s.", httpStateString(httpGetState(con->http)));
+ cupsdCloseClient(con);
+ return;
+ }
#ifdef HAVE_SSL
if (con->auto_ssl)
* Encrypt this connection...
*/
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "Saw first byte %02X, auto-negotiating "
- "SSL/TLS session.", buf[0] & 255);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "Saw first byte %02X, auto-negotiating SSL/TLS session.", buf[0] & 255);
- if (!cupsdStartTLS(con))
+ if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS))
cupsdCloseClient(con);
return;
gettimeofday(&(con->start), NULL);
cupsdLogClient(con, CUPSD_LOG_DEBUG, "%s %s HTTP/%d.%d",
- operation, con->uri,
+ httpStateString(con->operation) + 11, con->uri,
httpGetVersion(con->http) / 100,
httpGetVersion(con->http) % 100);
* Handle new transfers...
*/
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Read: status=%d", status);
+
if (status == HTTP_STATUS_OK)
{
if (httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE)[0])
if (con->best && con->best->type != CUPSD_AUTH_NONE)
{
+ httpClearFields(con->http);
+
if (!cupsdSendHeader(con, HTTP_STATUS_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
{
cupsdCloseClient(con);
}
}
- if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), "Upgrade") &&
- !httpIsEncrypted(con->http))
+ if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), "Upgrade") && strstr(httpGetField(con->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(con->http))
{
#ifdef HAVE_SSL
/*
* Do encryption stuff...
*/
+ httpClearFields(con->http);
+
if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
{
cupsdCloseClient(con);
return;
}
- httpPrintf(con->http, "Connection: Upgrade\r\n");
- httpPrintf(con->http, "Upgrade: TLS/1.2,TLS/1.1,TLS/1.0\r\n");
- httpPrintf(con->http, "Content-Length: 0\r\n");
- httpPrintf(con->http, "\r\n");
-
- if (cupsdFlushHeader(con) < 0)
- {
- cupsdCloseClient(con);
- return;
- }
-
- if (!cupsdStartTLS(con))
+ if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED))
{
cupsdCloseClient(con);
return;
}
#else
- if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
+ if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
{
cupsdCloseClient(con);
return;
#endif /* HAVE_SSL */
}
- if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
- {
- cupsdCloseClient(con);
- return;
- }
-
- httpPrintf(con->http, "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
- httpPrintf(con->http, "Content-Length: 0\r\n");
- httpPrintf(con->http, "\r\n");
+ httpClearFields(con->http);
+ httpSetField(con->http, HTTP_FIELD_ALLOW,
+ "GET, HEAD, OPTIONS, POST, PUT");
+ httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0");
- if (cupsdFlushHeader(con) < 0)
+ if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
{
cupsdCloseClient(con);
return;
* Do encryption stuff...
*/
+ httpClearFields(con->http);
+
if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL,
CUPSD_AUTH_NONE))
{
return;
}
- httpPrintf(con->http, "Connection: Upgrade\r\n");
- httpPrintf(con->http, "Upgrade: TLS/1.2,TLS/1.1,TLS/1.0\r\n");
- httpPrintf(con->http, "Content-Length: 0\r\n");
- httpPrintf(con->http, "\r\n");
-
- if (cupsdFlushHeader(con) < 0)
- {
- cupsdCloseClient(con);
- return;
- }
-
- if (!cupsdStartTLS(con))
+ if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED))
{
cupsdCloseClient(con);
return;
}
#else
- if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
+ if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
{
cupsdCloseClient(con);
return;
* Send 100-continue header...
*/
- if (!cupsdSendHeader(con, HTTP_STATUS_CONTINUE, NULL, CUPSD_AUTH_NONE))
+ if (httpWriteResponse(con->http, HTTP_STATUS_CONTINUE))
{
cupsdCloseClient(con);
return;
* Send 417-expectation-failed header...
*/
- if (!cupsdSendHeader(con, HTTP_STATUS_EXPECTATION_FAILED, NULL,
- CUPSD_AUTH_NONE))
- {
- cupsdCloseClient(con);
- return;
- }
-
- httpPrintf(con->http, "Content-Length: 0\r\n");
- httpPrintf(con->http, "\r\n");
+ httpClearFields(con->http);
+ httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0");
- if (cupsdFlushHeader(con) < 0)
- {
- cupsdCloseClient(con);
- return;
- }
+ cupsdSendError(con, HTTP_STATUS_EXPECTATION_FAILED, CUPSD_AUTH_NONE);
+ cupsdCloseClient(con);
+ return;
}
}
switch (httpGetState(con->http))
{
case HTTP_STATE_GET_SEND :
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Processing GET %s", con->uri);
+
if ((!strncmp(con->uri, "/ppd/", 5) ||
!strncmp(con->uri, "/printers/", 10) ||
!strncmp(con->uri, "/classes/", 9)) &&
break;
}
}
- else if (!WebInterface)
- {
- /*
- * Web interface is disabled. Show an appropriate message...
- */
-
- if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE))
- {
- cupsdCloseClient(con);
- return;
- }
-
- break;
- }
- if ((!strncmp(con->uri, "/admin", 6) &&
- strncmp(con->uri, "/admin/conf/", 12) &&
- strncmp(con->uri, "/admin/log/", 11)) ||
+ if ((!strncmp(con->uri, "/admin", 6) && strcmp(con->uri, "/admin/conf/cupsd.conf") && strncmp(con->uri, "/admin/log/", 11)) ||
!strncmp(con->uri, "/printers", 9) ||
!strncmp(con->uri, "/classes", 8) ||
!strncmp(con->uri, "/help", 5) ||
!strncmp(con->uri, "/jobs", 5))
{
+ if (!WebInterface)
+ {
+ /*
+ * Web interface is disabled. Show an appropriate message...
+ */
+
+ if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE))
+ {
+ cupsdCloseClient(con);
+ return;
+ }
+
+ break;
+ }
+
/*
* Send CGI output...
*/
if (httpGetVersion(con->http) <= HTTP_VERSION_1_0)
httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
}
- else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
- (strchr(con->uri + 12, '/') ||
- strlen(con->uri) == 12)) ||
- (!strncmp(con->uri, "/admin/log/", 11) &&
- (strchr(con->uri + 11, '/') ||
- strlen(con->uri) == 11)))
+ else if (!strncmp(con->uri, "/admin/log/", 11) && (strchr(con->uri + 11, '/') || strlen(con->uri) == 11))
{
/*
* GET can only be done to configuration files directly under
* /admin/conf...
*/
- cupsdLogClient(con, CUPSD_LOG_ERROR,
- "Request for subdirectory \"%s\"!", con->uri);
+ cupsdLogClient(con, CUPSD_LOG_ERROR, "Request for subdirectory \"%s\".", con->uri);
if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE))
{
type = mimeFileType(MimeDatabase, filename, NULL, NULL);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "filename=\"%s\", type=%s/%s", filename, type ? type->super : "", type ? type->type : "");
+
if (is_cgi(con, filename, &filestats, type))
{
/*
break;
}
- else if ((!strncmp(con->uri, "/admin", 6) &&
- strncmp(con->uri, "/admin/conf/", 12) &&
- strncmp(con->uri, "/admin/log/", 11)) ||
+ else if ((!strncmp(con->uri, "/admin", 6) && strncmp(con->uri, "/admin/log/", 11)) ||
!strncmp(con->uri, "/printers", 9) ||
!strncmp(con->uri, "/classes", 8) ||
!strncmp(con->uri, "/help", 5) ||
return;
}
+ cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND);
break;
}
}
return;
}
+ cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND);
break;
}
}
else if (!WebInterface)
{
- if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
- {
- cupsdCloseClient(con);
- return;
- }
-
- if (httpPrintf(con->http, "\r\n") < 0)
- {
- cupsdCloseClient(con);
- return;
- }
+ httpClearFields(con->http);
- if (cupsdFlushHeader(con) < 0)
+ if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
{
cupsdCloseClient(con);
return;
}
-// con->http->state = HTTP_STATE_WAITING;
- DEBUG_puts("cupsdReadClient: Set state to HTTP_STATE_WAITING "
- "after HEAD.");
+ cupsdLogRequest(con, HTTP_STATUS_OK);
break;
}
- if ((!strncmp(con->uri, "/admin", 6) &&
- strncmp(con->uri, "/admin/conf/", 12) &&
- strncmp(con->uri, "/admin/log/", 11)) ||
+ if ((!strncmp(con->uri, "/admin", 6) && strcmp(con->uri, "/admin/conf/cupsd.conf") && strncmp(con->uri, "/admin/log/", 11)) ||
!strncmp(con->uri, "/printers", 9) ||
!strncmp(con->uri, "/classes", 8) ||
!strncmp(con->uri, "/help", 5) ||
* CGI output...
*/
- if (!cupsdSendHeader(con, HTTP_STATUS_OK, "text/html", CUPSD_AUTH_NONE))
- {
- cupsdCloseClient(con);
- return;
- }
+ httpClearFields(con->http);
- if (httpPrintf(con->http, "\r\n") < 0)
- {
- cupsdCloseClient(con);
- return;
- }
-
- if (cupsdFlushHeader(con) < 0)
+ if (!cupsdSendHeader(con, HTTP_STATUS_OK, "text/html", CUPSD_AUTH_NONE))
{
cupsdCloseClient(con);
return;
cupsdLogRequest(con, HTTP_STATUS_OK);
}
- else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
- (strchr(con->uri + 12, '/') ||
- strlen(con->uri) == 12)) ||
- (!strncmp(con->uri, "/admin/log/", 11) &&
- (strchr(con->uri + 11, '/') ||
- strlen(con->uri) == 11)))
+ else if (!strncmp(con->uri, "/admin/log/", 11) && (strchr(con->uri + 11, '/') || strlen(con->uri) == 11))
{
/*
* HEAD can only be done to configuration files under
return;
}
+ cupsdLogRequest(con, HTTP_STATUS_FORBIDDEN);
break;
}
else if ((filename = get_file(con, &filestats, buf,
sizeof(buf))) == NULL)
{
+ httpClearFields(con->http);
+
if (!cupsdSendHeader(con, HTTP_STATUS_NOT_FOUND, "text/html",
CUPSD_AUTH_NONE))
{
else
snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
- if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE))
- {
- cupsdCloseClient(con);
- return;
- }
+ httpClearFields(con->http);
- if (httpPrintf(con->http, "Last-Modified: %s\r\n",
- httpGetDateString(filestats.st_mtime)) < 0)
- {
- cupsdCloseClient(con);
- return;
- }
+ httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED,
+ httpGetDateString(filestats.st_mtime));
+ httpSetLength(con->http, (size_t)filestats.st_size);
- if (httpPrintf(con->http, "Content-Length: %lu\r\n",
- (unsigned long)filestats.st_size) < 0)
+ if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE))
{
cupsdCloseClient(con);
return;
cupsdLogRequest(con, HTTP_STATUS_OK);
}
-
- if (httpPrintf(con->http, "\r\n") < 0)
- {
- cupsdCloseClient(con);
- return;
- }
-
- if (cupsdFlushHeader(con) < 0)
- {
- cupsdCloseClient(con);
- return;
- }
-
-// con->http->state = HTTP_STATE_WAITING;
- DEBUG_puts("cupsdReadClient: Set state to HTTP_STATE_WAITING "
- "after HEAD.");
break;
default :
{
con->bytes += bytes;
- if (write(con->file, line, bytes) < bytes)
+ if (MaxRequestSize > 0 && con->bytes > MaxRequestSize)
+ {
+ close(con->file);
+ con->file = -1;
+ unlink(con->filename);
+ cupsdClearString(&con->filename);
+
+ if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
+ {
+ cupsdCloseClient(con);
+ return;
+ }
+ }
+
+ if (write(con->file, line, (size_t)bytes) < bytes)
{
cupsdLogClient(con, CUPSD_LOG_ERROR,
"Unable to write %d bytes to \"%s\": %s", bytes,
}
}
}
+ else if (httpGetState(con->http) == HTTP_STATE_PUT_RECV)
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
while (httpGetState(con->http) == HTTP_STATE_PUT_RECV && httpGetReady(con->http));
* Grab any request data from the connection...
*/
+ if (!httpWait(con->http, 0))
+ return;
+
if ((ipp_state = ippRead(con->http, con->request)) == IPP_STATE_ERROR)
{
cupsdLogClient(con, CUPSD_LOG_ERROR, "IPP read error: %s",
con->request->request.op.version[1],
ippOpString(con->request->request.op.operation_id),
con->request->request.op.request_id);
- con->bytes += ippLength(con->request);
+ con->bytes += (off_t)ippLength(con->request);
}
}
{
con->bytes += bytes;
- if (write(con->file, line, bytes) < bytes)
+ if (MaxRequestSize > 0 && con->bytes > MaxRequestSize)
+ {
+ close(con->file);
+ con->file = -1;
+ unlink(con->filename);
+ cupsdClearString(&con->filename);
+
+ if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
+ {
+ cupsdCloseClient(con);
+ return;
+ }
+ }
+
+ if (write(con->file, line, (size_t)bytes) < bytes)
{
cupsdLogClient(con, CUPSD_LOG_ERROR,
"Unable to write %d bytes to \"%s\": %s",
else
fd = -1;
- con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
+ con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
+ con->pipe_status = HTTP_STATUS_OK;
+
+ httpClearFields(con->http);
if (fd >= 0)
close(fd);
http_status_t code, /* I - Error code */
int auth_type)/* I - Authentication type */
{
- cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdSendError code=%d, auth_type=%d",
- code, auth_type);
+ char location[HTTP_MAX_VALUE]; /* Location field */
+
+
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdSendError code=%d, auth_type=%d", code, auth_type);
#ifdef HAVE_SSL
/*
* never disable it in that case.
*/
- if (code >= HTTP_STATUS_BAD_REQUEST && con->type != CUPSD_AUTH_NEGOTIATE)
- httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
+ strlcpy(location, httpGetField(con->http, HTTP_FIELD_LOCATION), sizeof(location));
- /*
- * Send an error message back to the client. If the error code is a
- * 400 or 500 series, make sure the message contains some text, too!
- */
+ httpClearFields(con->http);
- if (!cupsdSendHeader(con, code, NULL, auth_type))
- return (0);
-
-#ifdef HAVE_SSL
- if (code == HTTP_STATUS_UPGRADE_REQUIRED)
- if (httpPrintf(con->http, "Connection: Upgrade\r\n") < 0)
- return (0);
+ httpSetField(con->http, HTTP_FIELD_LOCATION, location);
- if (httpPrintf(con->http, "Upgrade: TLS/1.2,TLS/1.1,TLS/1.0\r\n") < 0)
- return (0);
-#endif /* HAVE_SSL */
+ if (code >= HTTP_STATUS_BAD_REQUEST && con->type != CUPSD_AUTH_NEGOTIATE)
+ httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
if (httpGetVersion(con->http) >= HTTP_VERSION_1_1 &&
httpGetKeepAlive(con->http) == HTTP_KEEPALIVE_OFF)
- {
- if (httpPrintf(con->http, "Connection: close\r\n") < 0)
- return (0);
- }
+ httpSetField(con->http, HTTP_FIELD_CONNECTION, "close");
if (code >= HTTP_STATUS_BAD_REQUEST)
{
_httpStatus(con->language, code), redirect,
_httpStatus(con->language, code), text);
- if (httpPrintf(con->http, "Content-Type: text/html; charset=utf-8\r\n") < 0)
- return (0);
- if (httpPrintf(con->http, "Content-Length: %d\r\n",
- (int)strlen(message)) < 0)
+ /*
+ * Send an error message back to the client. If the error code is a
+ * 400 or 500 series, make sure the message contains some text, too!
+ */
+
+ size_t length = strlen(message); /* Length of message */
+
+ httpSetLength(con->http, length);
+
+ if (!cupsdSendHeader(con, code, "text/html", auth_type))
return (0);
- if (httpPrintf(con->http, "\r\n") < 0)
+
+ if (httpWrite2(con->http, message, length) < 0)
return (0);
- if (httpPrintf(con->http, "%s", message) < 0)
+
+ if (httpFlushWrite(con->http) < 0)
return (0);
}
- else if (httpPrintf(con->http, "\r\n") < 0)
- return (0);
-
- if (cupsdFlushHeader(con) < 0)
- return (0);
-
-// con->http->state = HTTP_STATE_WAITING;
+ else
+ {
+ httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0");
- DEBUG_puts("cupsdSendError: Set state to HTTP_STATE_WAITING.");
+ if (!cupsdSendHeader(con, code, NULL, auth_type))
+ return (0);
+ }
return (1);
}
char auth_str[1024]; /* Authorization string */
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "cupsdSendHeader: code=%d, type=\"%s\", auth_type=%d", code, type, auth_type);
+
/*
* Send the HTTP status header...
*/
- if (code == HTTP_STATUS_CONTINUE)
- {
- /*
- * 100-continue doesn't send any headers...
- */
-
- return (httpPrintf(con->http, "HTTP/%d.%d 100 Continue\r\n\r\n",
- httpGetVersion(con->http) / 100,
- httpGetVersion(con->http) % 100) > 0);
- }
- else if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED)
+ if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED)
{
/*
* Treat our special "web interface is disabled" status as "200 OK" for web
code = HTTP_STATUS_OK;
}
- httpFlushWrite(con->http);
-
-// con->http->data_encoding = HTTP_ENCODING_FIELDS;
-
- if (httpPrintf(con->http, "HTTP/%d.%d %d %s\r\n", httpGetVersion(con->http) / 100,
- httpGetVersion(con->http) % 100, code, httpStatus(code)) < 0)
- return (0);
- if (httpPrintf(con->http, "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
- return (0);
if (ServerHeader)
- if (httpPrintf(con->http, "Server: %s\r\n", ServerHeader) < 0)
- return (0);
- if (httpGetKeepAlive(con->http) && httpGetVersion(con->http) >= HTTP_VERSION_1_0)
- {
- if (httpPrintf(con->http, "Connection: Keep-Alive\r\n") < 0)
- return (0);
- if (httpPrintf(con->http, "Keep-Alive: timeout=%d\r\n",
- KeepAliveTimeout) < 0)
- return (0);
- }
- else if (httpPrintf(con->http, "Connection: close\r\n") < 0)
- return (0);
+ httpSetField(con->http, HTTP_FIELD_SERVER, ServerHeader);
if (code == HTTP_STATUS_METHOD_NOT_ALLOWED)
- if (httpPrintf(con->http, "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0)
- return (0);
+ httpSetField(con->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST, PUT");
if (code == HTTP_STATUS_UNAUTHORIZED)
{
auth_str[0] = '\0';
- if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
+ if (auth_type == CUPSD_AUTH_BASIC)
strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
- else if (auth_type == CUPSD_AUTH_DIGEST)
- snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
- httpGetHostname(con->http, NULL, 0));
#ifdef HAVE_GSSAPI
else if (auth_type == CUPSD_AUTH_NEGOTIATE)
{
size_t auth_size; /* Size of remaining buffer */
auth_key = auth_str + strlen(auth_str);
- auth_size = sizeof(auth_str) - (auth_key - auth_str);
+ auth_size = sizeof(auth_str) - (size_t)(auth_key - auth_str);
for (name = (char *)cupsArrayFirst(con->best->names);
name;
{
cupsdLogClient(con, CUPSD_LOG_DEBUG, "WWW-Authenticate: %s", auth_str);
- if (httpPrintf(con->http, "WWW-Authenticate: %s\r\n", auth_str) < 0)
- return (0);
+ httpSetField(con->http, HTTP_FIELD_WWW_AUTHENTICATE, auth_str);
}
}
if (con->language && strcmp(con->language->language, "C"))
- {
- if (httpPrintf(con->http, "Content-Language: %s\r\n",
- con->language->language) < 0)
- return (0);
- }
+ httpSetField(con->http, HTTP_FIELD_CONTENT_LANGUAGE, con->language->language);
if (type)
{
if (!strcmp(type, "text/html"))
- {
- if (httpPrintf(con->http,
- "Content-Type: text/html; charset=utf-8\r\n") < 0)
- return (0);
- }
- else if (httpPrintf(con->http, "Content-Type: %s\r\n", type) < 0)
- return (0);
+ httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, "text/html; charset=utf-8");
+ else
+ httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, type);
}
- return (1);
+ return (!httpWriteResponse(con->http, code));
}
httpIsChunked(con->http) ? "CHUNKED" : "LENGTH",
CUPS_LLCAST httpGetLength2(con->http),
con->response,
- con->response ? ipp_states[con->response->state] : "",
+ con->response ? ippStateString(ippGetState(con->request)) : "",
con->pipe_pid, con->file);
if (httpGetState(con->http) != HTTP_STATE_GET_SEND &&
* connection and we need to shut it down...
*/
- cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP state %s.",
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP write state %s.",
httpStateString(httpGetState(con->http)));
cupsdCloseClient(con);
return;
con->file_ready = 0;
}
+ bytes = (ssize_t)(sizeof(con->header) - (size_t)con->header_used);
+
+ if (!con->pipe_pid && bytes > (ssize_t)httpGetRemaining(con->http))
+ {
+ /*
+ * Limit GET bytes to original size of file (STR #3265)...
+ */
+
+ bytes = (ssize_t)httpGetRemaining(con->http);
+ }
+
if (con->response && con->response->state != IPP_STATE_DATA)
{
size_t wused = httpGetPending(con->http); /* Previous write buffer use */
(int)bytes, httpGetState(con->http),
CUPS_LLCAST httpGetLength2(con->http));
}
- else if ((bytes = read(con->file, con->header + con->header_used,
- sizeof(con->header) - con->header_used)) > 0)
+ else if ((bytes = read(con->file, con->header + con->header_used, (size_t)bytes)) > 0)
{
con->header_used += bytes;
* Handle redirection and CGI status codes...
*/
- if (!_cups_strncasecmp(con->header, "Location:", 9))
+ http_field_t field; /* HTTP field */
+ char *value = strchr(con->header, ':');
+ /* Value of field */
+
+ if (value)
{
- if (!cupsdSendHeader(con, HTTP_STATUS_SEE_OTHER, NULL, CUPSD_AUTH_NONE))
- {
- cupsdCloseClient(con);
- return;
- }
+ *value++ = '\0';
+ while (isspace(*value & 255))
+ value ++;
+ }
- con->sent_header = 2;
+ field = httpFieldValue(con->header);
- if (httpPrintf(con->http, "Content-Length: 0\r\n") < 0)
- return;
+ if (field != HTTP_FIELD_UNKNOWN && value)
+ {
+ httpSetField(con->http, field, value);
+
+ if (field == HTTP_FIELD_LOCATION)
+ {
+ con->pipe_status = HTTP_STATUS_SEE_OTHER;
+ con->sent_header = 2;
+ }
+ else
+ con->sent_header = 1;
}
- else if (!_cups_strncasecmp(con->header, "Status:", 7))
+ else if (!_cups_strcasecmp(con->header, "Status") && value)
{
- cupsdSendError(con, (http_status_t)atoi(con->header + 7),
- CUPSD_AUTH_NONE);
+ con->pipe_status = (http_status_t)atoi(value);
con->sent_header = 2;
}
- else
+ else if (!_cups_strcasecmp(con->header, "Set-Cookie") && value)
{
- if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
- {
- cupsdCloseClient(con);
- return;
- }
-
+ httpSetCookie(con->http, value);
con->sent_header = 1;
-
- if (httpGetVersion(con->http) == HTTP_VERSION_1_1)
- {
- if (httpPrintf(con->http, "Transfer-Encoding: chunked\r\n") < 0)
- return;
- }
- }
+ }
}
- if (_cups_strncasecmp(con->header, "Status:", 7))
- httpPrintf(con->http, "%s\r\n", con->header);
-
/*
* Update buffer...
*/
con->header_used -= bufptr - con->header;
if (con->header_used > 0)
- memmove(con->header, bufptr, con->header_used);
+ memmove(con->header, bufptr, (size_t)con->header_used);
bufptr = con->header - 1;
{
con->got_fields = 1;
- if (cupsdFlushHeader(con) < 0)
+ if (httpGetVersion(con->http) == HTTP_VERSION_1_1 &&
+ !httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0])
+ httpSetLength(con->http, 0);
+
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending status %d for CGI.", con->pipe_status);
+
+ if (con->pipe_status == HTTP_STATUS_OK)
{
- cupsdCloseClient(con);
- return;
+ if (!cupsdSendHeader(con, con->pipe_status, NULL, CUPSD_AUTH_NONE))
+ {
+ cupsdCloseClient(con);
+ return;
+ }
+ }
+ else
+ {
+ if (!cupsdSendError(con, con->pipe_status, CUPSD_AUTH_NONE))
+ {
+ cupsdCloseClient(con);
+ return;
+ }
}
-
- if (httpGetVersion(con->http) == HTTP_VERSION_1_1)
- httpSetLength(con->http, 0);
-// con->http->data_encoding = HTTP_ENCODING_CHUNKED;
}
else
field_col = 0;
}
if (!con->got_fields)
- {
-// con->http->activity = time(NULL);
return;
- }
}
if (con->header_used > 0)
{
- if (httpWrite2(con->http, con->header, con->header_used) < 0)
+ if (httpWrite2(con->http, con->header, (size_t)con->header_used) < 0)
{
cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)",
httpError(con->http), strerror(httpError(con->http)));
{
cupsdLogRequest(con, HTTP_STATUS_OK);
- httpFlushWrite(con->http);
-
- if (httpIsChunked(con->http) && con->sent_header == 1)
+ if (httpIsChunked(con->http) && (!con->pipe_pid || con->sent_header > 0))
{
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending 0-length chunk.");
+
if (httpWrite2(con->http, "", 0) < 0)
{
cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)",
return;
}
}
- }
-// con->http->state = HTTP_STATE_WAITING;
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Flushing write buffer.");
+ httpFlushWrite(con->http);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "New state is %s", httpStateString(httpGetState(con->http)));
+ }
cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, NULL, con);
cupsdSetBusyState();
}
}
-
-// con->http->activity = time(NULL);
}
if (*ptr == '\0')
return (1);
- 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);
+ 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);
while (*ptr != '\0')
{
}
+#ifdef HAVE_SSL
+/*
+ * 'cupsd_start_tls()' - Start encryption on a connection.
+ */
+
+static int /* O - 0 on success, -1 on error */
+cupsd_start_tls(cupsd_client_t *con, /* I - Client connection */
+ http_encryption_t e) /* I - Encryption mode */
+{
+ if (httpEncryption(con->http, e))
+ {
+ cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to encrypt connection: %s",
+ cupsLastErrorString());
+ return (-1);
+ }
+
+ cupsdLogClient(con, CUPSD_LOG_DEBUG, "Connection now encrypted.");
+ return (0);
+}
+#endif /* HAVE_SSL */
+
+
/*
* 'get_file()' - Get a filename and state info.
*/
get_file(cupsd_client_t *con, /* I - Client connection */
struct stat *filestats, /* O - File information */
char *filename, /* IO - Filename buffer */
- int len) /* I - Buffer length */
+ size_t len) /* I - Buffer length */
{
int status; /* Status of filesystem calls */
char *ptr; /* Pointer info filename */
- int plen; /* Remaining length after pointer */
- char language[7]; /* Language subdirectory, if any */
+ size_t plen; /* Remaining length after pointer */
+ char language[7], /* Language subdirectory, if any */
+ dest[1024]; /* Destination name */
+ int perm_check = 1; /* Do permissions check? */
/*
language[0] = '\0';
if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/'))
+ {
+ strlcpy(dest, con->uri + 5, sizeof(dest));
+ ptr = dest + strlen(dest) - 4;
+
+ if (ptr <= dest || strcmp(ptr, ".ppd"))
+ {
+ cupsdLogClient(con, CUPSD_LOG_INFO, "Disallowed path \"%s\".", con->uri);
+ return (NULL);
+ }
+
+ *ptr = '\0';
+ if (!cupsdFindPrinter(dest))
+ {
+ cupsdLogClient(con, CUPSD_LOG_INFO, "No printer \"%s\" found.", dest);
+ return (NULL);
+ }
+
snprintf(filename, len, "%s%s", ServerRoot, con->uri);
+
+ perm_check = 0;
+ }
else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/'))
{
- snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7);
+ strlcpy(dest, con->uri + 7, sizeof(dest));
+ ptr = dest + strlen(dest) - 4;
+
+ if (ptr <= dest || strcmp(ptr, ".png"))
+ {
+ cupsdLogClient(con, CUPSD_LOG_INFO, "Disallowed path \"%s\".", con->uri);
+ return (NULL);
+ }
+
+ *ptr = '\0';
+ if (!cupsdFindDest(dest))
+ {
+ cupsdLogClient(con, CUPSD_LOG_INFO, "No printer \"%s\" found.", dest);
+ return (NULL);
+ }
+
+ snprintf(filename, len, "%s/%s.png", CacheDir, dest);
if (access(filename, F_OK) < 0)
snprintf(filename, len, "%s/images/generic.png", DocumentRoot);
+
+ perm_check = 0;
}
else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5);
- else if (!strncmp(con->uri, "/admin/conf/", 12))
- snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
+ else if (!strcmp(con->uri, "/admin/conf/cupsd.conf"))
+ {
+ strlcpy(filename, ConfigurationFile, len);
+
+ perm_check = 0;
+ }
else if (!strncmp(con->uri, "/admin/log/", 11))
{
if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/')
strlcpy(filename, PageLog, len);
else
return (NULL);
+
+ perm_check = 0;
}
else if (con->language)
{
* then fallback to the default one...
*/
- if ((status = stat(filename, filestats)) != 0 && language[0] &&
+ if ((status = lstat(filename, filestats)) != 0 && language[0] &&
strncmp(con->uri, "/icons/", 7) &&
strncmp(con->uri, "/ppd/", 5) &&
strncmp(con->uri, "/rss/", 5) &&
if ((ptr = strchr(filename, '?')) != NULL)
*ptr = '\0';
- if ((status = stat(filename, filestats)) != 0)
+ if ((status = lstat(filename, filestats)) != 0)
{
/*
* Drop the language prefix and try the root directory...
if ((ptr = strchr(filename, '?')) != NULL)
*ptr = '\0';
- status = stat(filename, filestats);
+ status = lstat(filename, filestats);
}
}
/*
- * If we're found a directory, get the index.html file instead...
+ * If we've found a symlink, 404 the sucker to avoid disclosing information.
+ */
+
+ if (!status && S_ISLNK(filestats->st_mode))
+ {
+ cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename);
+ return (NULL);
+ }
+
+ /*
+ * Similarly, if the file/directory does not have world read permissions, do
+ * not allow access...
+ */
+
+ if (!status && perm_check && !(filestats->st_mode & S_IROTH))
+ {
+ cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename);
+ return (NULL);
+ }
+
+ /*
+ * If we've found a directory, get the index.html file instead...
*/
if (!status && S_ISDIR(filestats->st_mode))
*ptr = '\0';
ptr = filename + strlen(filename);
- plen = len - (ptr - filename);
+ plen = len - (size_t)(ptr - filename);
strlcpy(ptr, "index.html", plen);
- status = stat(filename, filestats);
+ status = lstat(filename, filestats);
#ifdef HAVE_JAVA
if (status)
{
strlcpy(ptr, "index.class", plen);
- status = stat(filename, filestats);
+ status = lstat(filename, filestats);
}
#endif /* HAVE_JAVA */
if (status)
{
strlcpy(ptr, "index.pl", plen);
- status = stat(filename, filestats);
+ status = lstat(filename, filestats);
}
#endif /* HAVE_PERL */
if (status)
{
strlcpy(ptr, "index.php", plen);
- status = stat(filename, filestats);
+ status = lstat(filename, filestats);
}
#endif /* HAVE_PHP */
if (status)
{
strlcpy(ptr, "index.pyc", plen);
- status = stat(filename, filestats);
+ status = lstat(filename, filestats);
}
if (status)
{
strlcpy(ptr, "index.py", plen);
- status = stat(filename, filestats);
+ status = lstat(filename, filestats);
}
#endif /* HAVE_PYTHON */
}
while (status && language[0]);
+
+ /*
+ * If we've found a symlink, 404 the sucker to avoid disclosing information.
+ */
+
+ if (!status && S_ISLNK(filestats->st_mode))
+ {
+ cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename);
+ return (NULL);
+ }
+
+ /*
+ * Similarly, if the file/directory does not have world read permissions, do
+ * not allow access...
+ */
+
+ if (!status && perm_check && !(filestats->st_mode & S_IROTH))
+ {
+ cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename);
+ return (NULL);
+ }
}
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "get_file filestats=%p, filename=%p, len=%d, "
- "returning \"%s\".", filestats, filename, len,
- status ? "(null)" : filename);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "get_file: filestats=%p, filename=%p, len=" CUPS_LLFMT ", returning \"%s\".", filestats, filename, CUPS_LLCAST len, status ? "(null)" : filename);
if (status)
return (NULL);
{
cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s",
con->filename, strerror(errno));
- return (HTTP_STATUS_SERVER_ERROR);
+ goto server_error;
}
/*
if ((out = cupsdCreateConfFile(ConfigurationFile, ConfigFilePerm)) == NULL)
{
cupsFileClose(in);
- return (HTTP_STATUS_SERVER_ERROR);
+ goto server_error;
}
cupsdLogClient(con, CUPSD_LOG_INFO, "Installing config file \"%s\"...",
*/
while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
- if (cupsFileWrite(out, buffer, bytes) < bytes)
+ if (cupsFileWrite(out, buffer, (size_t)bytes) < bytes)
{
cupsdLogClient(con, CUPSD_LOG_ERROR,
"Unable to copy to config file \"%s\": %s",
snprintf(filename, sizeof(filename), "%s.N", ConfigurationFile);
cupsdUnlinkOrRemoveFile(filename);
- return (HTTP_STATUS_SERVER_ERROR);
+ goto server_error;
}
/*
cupsFileClose(in);
if (cupsdCloseCreatedConfFile(out, ConfigurationFile))
- return (HTTP_STATUS_SERVER_ERROR);
+ goto server_error;
/*
* Remove the request file...
*/
return (HTTP_STATUS_CREATED);
+
+ /*
+ * Common exit for errors...
+ */
+
+ server_error:
+
+ cupsdUnlinkOrRemoveFile(con->filename);
+ cupsdClearString(&con->filename);
+
+ return (HTTP_STATUS_SERVER_ERROR);
}
if (!type || _cups_strcasecmp(type->super, "application"))
{
- 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");
+ 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");
return (0);
}
if (options)
cupsdSetStringf(&con->options, " %s", options);
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "is_cgi filename=\"%s\", filestats=%p, "
- "type=%s/%s, returning 1", filename,
- filestats, type->super, type->type);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
return (1);
}
#ifdef HAVE_JAVA
else
cupsdSetStringf(&con->options, " %s", filename);
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "is_cgi filename=\"%s\", filestats=%p, "
- "type=%s/%s, returning 1", filename,
- filestats, type->super, type->type);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
return (1);
}
#endif /* HAVE_JAVA */
else
cupsdSetStringf(&con->options, " %s", filename);
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "is_cgi filename=\"%s\", filestats=%p, "
- "type=%s/%s, returning 1", filename,
- filestats, type->super, type->type);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
return (1);
}
#endif /* HAVE_PERL */
else
cupsdSetStringf(&con->options, " %s", filename);
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "is_cgi filename=\"%s\", filestats=%p, "
- "type=%s/%s, returning 1", filename,
- filestats, type->super, type->type);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
return (1);
}
#endif /* HAVE_PHP */
else
cupsdSetStringf(&con->options, " %s", filename);
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "is_cgi filename=\"%s\", filestats=%p, "
- "type=%s/%s, returning 1", filename,
- filestats, type->super, type->type);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
return (1);
}
#endif /* HAVE_PYTHON */
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "is_cgi filename=\"%s\", filestats=%p, "
- "type=%s/%s, returning 0", filename,
- filestats, type->super, type->type);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 0.", filename, filestats, type->super, type->type);
return (0);
}
if (path[0] != '/')
return (0);
+ /*
+ * Check for "<" or quotes in the path and reject since this is probably
+ * someone trying to inject HTML...
+ */
+
+ if (strchr(path, '<') != NULL || strchr(path, '\"') != NULL || strchr(path, '\'') != NULL)
+ return (0);
+
/*
* Check for "/.." in the path...
*/
* be consistent with Apache...
*/
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "pipe_command infile=%d, outfile=%p, "
- "command=\"%s\", options=\"%s\", root=%d",
- infile, outfile, command,
- options ? options : "(null)", root);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "pipe_command: infile=%d, outfile=%p, command=\"%s\", options=\"%s\", root=%d", infile, outfile, command, options ? options : "(null)", root);
argv[0] = command;
if (options)
- {
- commptr = options;
- if (*commptr == ' ')
- commptr ++;
- strlcpy(argbuf, commptr, sizeof(argbuf));
- }
+ strlcpy(argbuf, options, sizeof(argbuf));
else
argbuf[0] = '\0';
*/
if (commptr[1] >= '0' && commptr[1] <= '9')
- *commptr = (commptr[1] - '0') << 4;
+ *commptr = (char)((commptr[1] - '0') << 4);
else
- *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
+ *commptr = (char)((tolower(commptr[1]) - 'a' + 10) << 4);
if (commptr[2] >= '0' && commptr[2] <= '9')
*commptr |= commptr[2] - '0';
snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s",
DocumentRoot, script_name + 12);
- sprintf(server_port, "SERVER_PORT=%d", con->serverport);
+ snprintf(server_port, sizeof(server_port), "SERVER_PORT=%d", con->serverport);
if (httpGetField(con->http, HTTP_FIELD_HOST)[0])
{
{
con->file = open(filename, O_RDONLY);
- cupsdLogClient(con, CUPSD_LOG_DEBUG2,
- "write_file code=%d, filename=\"%s\" (%d), "
- "type=\"%s\", filestats=%p",
- code, filename, con->file, type ? type : "(null)", filestats);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_file: code=%d, filename=\"%s\" (%d), type=\"%s\", filestats=%p.", code, filename, con->file, type ? type : "(null)", filestats);
if (con->file < 0)
return (0);
fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
- con->pipe_pid = 0;
+ con->pipe_pid = 0;
+ con->sent_header = 1;
- if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
- return (0);
+ httpClearFields(con->http);
- if (httpPrintf(con->http, "Last-Modified: %s\r\n",
- httpGetDateString(filestats->st_mtime)) < 0)
- return (0);
- if (httpPrintf(con->http, "Content-Length: " CUPS_LLFMT "\r\n",
- CUPS_LLCAST filestats->st_size) < 0)
- return (0);
- if (httpPrintf(con->http, "\r\n") < 0)
- return (0);
+ httpSetLength(con->http, (size_t)filestats->st_size);
- if (cupsdFlushHeader(con) < 0)
- return (0);
+ httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED,
+ httpGetDateString(filestats->st_mtime));
- httpSetLength(con->http, filestats->st_size);
+ if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
+ return (0);
- cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient,
- (cupsd_selfunc_t)cupsdWriteClient, con);
+ cupsdAddSelect(httpGetFd(con->http), NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending file.");
static void
write_pipe(cupsd_client_t *con) /* I - Client connection */
{
- cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_pipe CGI output on fd %d",
- con->file);
+ cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_pipe: CGI output on fd %d.", con->file);
con->file_ready = 1;
cupsdLogClient(con, CUPSD_LOG_DEBUG, "CGI data ready to be sent.");
}
-
-
-/*
- * End of "$Id$".
- */