X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fcups.git;a=blobdiff_plain;f=scheduler%2Fclient.c;h=02e5fafb8c9e2afffff519f2ffa9ff67d7f0be0f;hp=dffcf99f07cfd8368d05fd4e343b846da71efcd8;hb=503b54c9302c8de6207e079a80a89a787eb612ea;hpb=a80b2360c9b982cb6c8514744ffd53f6f9476c5a diff --git a/scheduler/client.c b/scheduler/client.c index dffcf99f0..02e5fafb8 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -1,9 +1,7 @@ /* - * "$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 @@ -44,7 +42,7 @@ static int compare_clients(cupsd_client_t *a, cupsd_client_t *b, 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); @@ -78,9 +76,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ #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... @@ -145,7 +141,12 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * Save the connected address and port number... */ - con->clientaddr = lis->address; + addrlen = sizeof(con->clientaddr); + + if (getsockname(httpGetFd(con->http), (struct sockaddr *)&con->clientaddr, &addrlen) || addrlen == 0) + con->clientaddr = lis->address; + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Server address is \"%s\".", httpAddrString(&con->clientaddr, name, sizeof(name))); /* * Check the number of clients on the same address... @@ -274,17 +275,17 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ 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__ */ @@ -383,8 +384,7 @@ cupsdCloseAllClients(void) 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; @@ -484,7 +484,12 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ 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); @@ -559,22 +564,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ 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 ? ippStateString(ippGetState(con->request)) : "", - 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 || @@ -585,8 +575,18 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * connection and we need to shut it down... */ - cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP read state %s.", - httpStateString(httpGetState(con->http))); + 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; } @@ -607,9 +607,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * 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 (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS)) cupsdCloseClient(con); @@ -934,7 +932,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ return; } #else - if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -993,7 +991,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ return; } #else - if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1161,29 +1159,28 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ 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... */ @@ -1250,20 +1247,14 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ 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)) { @@ -1401,9 +1392,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ 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) || @@ -1653,9 +1642,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ 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) || @@ -1675,12 +1662,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ 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 @@ -1739,7 +1721,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, httpGetDateString(filestats.st_mtime)); - httpSetLength(con->http, filestats.st_size); + httpSetLength(con->http, (size_t)filestats.st_size); if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE)) { @@ -1783,7 +1765,21 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ { 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, @@ -1801,6 +1797,11 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } } } + else if (httpGetState(con->http) == HTTP_STATE_PUT_RECV) + { + cupsdCloseClient(con); + return; + } } while (httpGetState(con->http) == HTTP_STATE_PUT_RECV && httpGetReady(con->http)); @@ -1859,6 +1860,9 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * 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", @@ -1888,7 +1892,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ 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); } } @@ -1941,7 +1945,21 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ { 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", @@ -2140,8 +2158,10 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ 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 /* @@ -2172,8 +2192,12 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ * never disable it in that case. */ + strlcpy(location, httpGetField(con->http, HTTP_FIELD_LOCATION), sizeof(location)); + httpClearFields(con->http); + httpSetField(con->http, HTTP_FIELD_LOCATION, location); + if (code >= HTTP_STATUS_BAD_REQUEST && con->type != CUPSD_AUTH_NEGOTIATE) httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); @@ -2289,6 +2313,8 @@ cupsdSendHeader( 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... */ @@ -2321,11 +2347,8 @@ cupsdSendHeader( 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) { @@ -2355,7 +2378,7 @@ cupsdSendHeader( 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; @@ -2514,6 +2537,17 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ 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 */ @@ -2552,8 +2586,7 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ (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; @@ -2618,12 +2651,6 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ } else if (!_cups_strcasecmp(con->header, "Set-Cookie") && value) { - char *sep = strchr(value, ';'); - /* Separator between name=value and the rest */ - - if (sep) - *sep = '\0'; - httpSetCookie(con->http, value); con->sent_header = 1; } @@ -2636,7 +2663,7 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ 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; @@ -2652,10 +2679,23 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ !httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0]) httpSetLength(con->http, 0); - if (!cupsdSendHeader(con, con->pipe_status, NULL, CUPSD_AUTH_NONE)) + 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; + } } } else @@ -2671,7 +2711,7 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ 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))); @@ -2703,7 +2743,7 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ { cupsdLogRequest(con, HTTP_STATUS_OK); - if (httpIsChunked(con->http) && (!con->pipe_pid || con->sent_header == 1)) + if (httpIsChunked(con->http) && (!con->pipe_pid || con->sent_header > 0)) { cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending 0-length chunk."); @@ -2796,11 +2836,7 @@ check_if_modified( 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') { @@ -2867,7 +2903,7 @@ cupsd_start_tls(cupsd_client_t *con, /* I - Client connection */ return (-1); } - cupsdLogClient(con, CUPSD_LOG_INFO, "Connection now encrypted."); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Connection now encrypted."); return (0); } #endif /* HAVE_SSL */ @@ -2881,12 +2917,14 @@ static char * /* O - Real filename */ 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? */ /* @@ -2896,17 +2934,59 @@ get_file(cupsd_client_t *con, /* I - Client connection */ 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] == '/') @@ -2917,6 +2997,8 @@ get_file(cupsd_client_t *con, /* I - Client connection */ strlcpy(filename, PageLog, len); else return (NULL); + + perm_check = 0; } else if (con->language) { @@ -2934,7 +3016,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ * 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) && @@ -2951,7 +3033,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ 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... @@ -2963,12 +3045,33 @@ get_file(cupsd_client_t *con, /* I - Client connection */ 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)) @@ -3008,16 +3111,16 @@ get_file(cupsd_client_t *con, /* I - Client connection */ *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 */ @@ -3025,7 +3128,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if (status) { strlcpy(ptr, "index.pl", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_PERL */ @@ -3033,7 +3136,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if (status) { strlcpy(ptr, "index.php", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_PHP */ @@ -3041,24 +3144,42 @@ get_file(cupsd_client_t *con, /* I - Client connection */ 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); @@ -3089,7 +3210,7 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ { cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s", con->filename, strerror(errno)); - return (HTTP_STATUS_SERVER_ERROR); + goto server_error; } /* @@ -3099,7 +3220,7 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ 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\"...", @@ -3110,7 +3231,7 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ */ 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", @@ -3122,7 +3243,7 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ snprintf(filename, sizeof(filename), "%s.N", ConfigurationFile); cupsdUnlinkOrRemoveFile(filename); - return (HTTP_STATUS_SERVER_ERROR); + goto server_error; } /* @@ -3132,7 +3253,7 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ cupsFileClose(in); if (cupsdCloseCreatedConfFile(out, ConfigurationFile)) - return (HTTP_STATUS_SERVER_ERROR); + goto server_error; /* * Remove the request file... @@ -3153,6 +3274,17 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ */ return (HTTP_STATUS_CREATED); + + /* + * Common exit for errors... + */ + + server_error: + + cupsdUnlinkOrRemoveFile(con->filename); + cupsdClearString(&con->filename); + + return (HTTP_STATUS_SERVER_ERROR); } @@ -3185,11 +3317,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ 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); } @@ -3205,10 +3333,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ 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 @@ -3225,10 +3350,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ 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 */ @@ -3246,10 +3368,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ 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 */ @@ -3267,10 +3386,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ 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 */ @@ -3288,18 +3404,12 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ 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); } @@ -3318,6 +3428,14 @@ is_path_absolute(const char *path) /* I - Input path */ 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... */ @@ -3398,21 +3516,12 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ * 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'; @@ -3482,9 +3591,9 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ */ 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'; @@ -3576,7 +3685,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ 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]) { @@ -3924,10 +4033,7 @@ write_file(cupsd_client_t *con, /* I - Client connection */ { 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); @@ -3939,7 +4045,7 @@ write_file(cupsd_client_t *con, /* I - Client connection */ httpClearFields(con->http); - httpSetLength(con->http, filestats->st_size); + httpSetLength(con->http, (size_t)filestats->st_size); httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, httpGetDateString(filestats->st_mtime)); @@ -3962,8 +4068,7 @@ write_file(cupsd_client_t *con, /* I - Client connection */ 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; @@ -3972,8 +4077,3 @@ write_pipe(cupsd_client_t *con) /* I - Client connection */ cupsdLogClient(con, CUPSD_LOG_DEBUG, "CGI data ready to be sent."); } - - -/* - * End of "$Id$". - */