X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scheduler%2Fclient.c;h=cfb0e1b9bd0869be444563da88cb41dde5055061;hb=e14894bd8db665c70ae5303d43473147c7ecf612;hp=1e277881502dc3a0aca3f634c0cc44cee9265dc0;hpb=ebeb3268e4f91c0003cf3c56338dac8f46810341;p=thirdparty%2Fcups.git diff --git a/scheduler/client.c b/scheduler/client.c index 1e2778815..cfb0e1b9b 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -1,19 +1,14 @@ /* - * "$Id$" - * * Client routines for the CUPS scheduler. * - * Copyright 2007-2013 by Apple Inc. - * Copyright 1997-2007 by Easy Software Products, all rights reserved. + * Copyright © 2007-2018 by Apple Inc. + * Copyright © 1997-2007 by Easy Software Products, all rights reserved. * * This file contains Kerberos support code, copyright 2006 by * Jelmer Vernooij. * - * These coded instructions, statements, and computer programs are the - * property of Apple Inc. and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "LICENSE.txt" - * which should have been included with this file. If this file is - * file is missing or damaged, see the license at "http://www.cups.org/". + * Licensed under Apache License v2.0. See the file "LICENSE" for more + * information. */ /* @@ -44,7 +39,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 +73,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... @@ -89,6 +82,8 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ if (cupsArrayCount(Clients) == MaxClients) return; + cupsdSetBusyState(1); + /* * Get a pointer to the next available client... */ @@ -145,7 +140,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 +274,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 +383,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; @@ -439,7 +438,7 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ if (httpGetFd(con->http) >= 0) { cupsArrayRemove(ActiveClients, con); - cupsdSetBusyState(); + cupsdSetBusyState(0); #ifdef HAVE_SSL /* @@ -484,7 +483,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); @@ -553,28 +557,12 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ char buf[1024]; /* Buffer for real filename */ struct stat filestats; /* File information */ mime_type_t *type; /* MIME type of file */ - cupsd_printer_t *p; /* Printer */ static unsigned request_id = 0; /* Request ID for temp files */ 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,13 +573,22 @@ 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; } - #ifdef HAVE_SSL if (con->auto_ssl) { @@ -608,9 +605,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); @@ -763,7 +758,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (!cupsArrayFind(ActiveClients, con)) { cupsArrayAdd(ActiveClients, con); - cupsdSetBusyState(); + cupsdSetBusyState(0); } case HTTP_STATE_OPTIONS : @@ -815,10 +810,22 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Handle new transfers... */ - cupsdLogClient(con, CUPSD_LOG_DEBUG, "Read: status=%d", status); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Read: status=%d, state=%d", status, httpGetState(con->http)); if (status == HTTP_STATUS_OK) { + /* + * Record whether the client is a web browser. "Mozilla" was the original + * and it seems that every web browser in existence now uses that as the + * prefix with additional information identifying *which* browser. + * + * Chrome (at least) has problems with multiple WWW-Authenticate values in + * a single header, so we only report Basic or Negotiate to web browsers and + * leave the multiple choices to the native CUPS client... + */ + + con->is_browser = !strncmp(httpGetField(con->http, HTTP_FIELD_USER_AGENT), "Mozilla/", 8); + if (httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE)[0]) { /* @@ -914,8 +921,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } } - 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 /* @@ -936,7 +942,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; @@ -995,7 +1001,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; @@ -1045,116 +1051,65 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ 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)) && - !strcmp(con->uri + strlen(con->uri) - 4, ".ppd")) - { - /* - * Send PPD file - get the real printer name since printer - * names are not case sensitive but filenames can be... - */ + if ((filename = get_file(con, &filestats, buf, sizeof(buf))) != NULL) + { + type = mimeFileType(MimeDatabase, filename, NULL, NULL); - con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ + cupsdLogClient(con, CUPSD_LOG_DEBUG, "filename=\"%s\", type=%s/%s", filename, type ? type->super : "", type ? type->type : ""); - if (!strncmp(con->uri, "/ppd/", 5)) - p = cupsdFindPrinter(con->uri + 5); - else if (!strncmp(con->uri, "/printers/", 10)) - p = cupsdFindPrinter(con->uri + 10); - else + if (is_cgi(con, filename, &filestats, type)) { - p = cupsdFindClass(con->uri + 9); + /* + * Note: con->command and con->options were set by is_cgi()... + */ - if (p) + if (!cupsdSendCommand(con, con->command, con->options, 0)) { - int i; /* Looping var */ - - for (i = 0; i < p->num_printers; i ++) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { - if (!(p->printers[i]->type & CUPS_PRINTER_CLASS)) - { - char ppdname[1024];/* PPD filename */ - - snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", - ServerRoot, p->printers[i]->name); - if (!access(ppdname, 0)) - { - p = p->printers[i]; - break; - } - } + cupsdCloseClient(con); + return; } + } + else + cupsdLogRequest(con, HTTP_STATUS_OK); - if (i >= p->num_printers) - p = NULL; - } + if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); + break; } - if (p) - { - snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); - } - else - { - if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!check_if_modified(con, &filestats)) + { + if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - - break; } - } - 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")) - { - /* - * Send icon file - get the real queue name since queue names are - * not case sensitive but filenames can be... - */ - - con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".png" */ - - if (!strncmp(con->uri, "/icons/", 7)) - p = cupsdFindPrinter(con->uri + 7); - else if (!strncmp(con->uri, "/printers/", 10)) - p = cupsdFindPrinter(con->uri + 10); - else + else { - p = cupsdFindClass(con->uri + 9); + if (type == NULL) + strlcpy(line, "text/plain", sizeof(line)); + else + snprintf(line, sizeof(line), "%s/%s", type->super, type->type); - if (p) + if (!write_file(con, HTTP_STATUS_OK, filename, line, &filestats)) { - int i; /* Looping var */ - - for (i = 0; i < p->num_printers; i ++) - { - if (!(p->printers[i]->type & CUPS_PRINTER_CLASS)) - { - char ppdname[1024];/* PPD filename */ - - snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", - ServerRoot, p->printers[i]->name); - if (!access(ppdname, 0)) - { - p = p->printers[i]; - break; - } - } - } - - if (i >= p->num_printers) - p = NULL; + cupsdCloseClient(con); + return; } } - - if (p) - snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name); - else + } + 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)) + { + if (!WebInterface) { - if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) + /* + * Web interface is disabled. Show an appropriate message... + */ + + if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1162,46 +1117,19 @@ 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)) || - !strncmp(con->uri, "/printers", 9) || - !strncmp(con->uri, "/classes", 8) || - !strncmp(con->uri, "/help", 5) || - !strncmp(con->uri, "/jobs", 5)) - { /* * Send CGI output... */ if (!strncmp(con->uri, "/admin", 6)) { - cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin); cupsdSetString(&con->options, strchr(con->uri + 6, '?')); } else if (!strncmp(con->uri, "/printers", 9)) { - cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin); if (con->uri[9] && con->uri[10]) cupsdSetString(&con->options, con->uri + 9); else @@ -1209,9 +1137,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else if (!strncmp(con->uri, "/classes", 8)) { - cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin); if (con->uri[8] && con->uri[9]) cupsdSetString(&con->options, con->uri + 8); else @@ -1219,9 +1145,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else if (!strncmp(con->uri, "/jobs", 5)) { - cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin); if (con->uri[5] && con->uri[6]) cupsdSetString(&con->options, con->uri + 5); else @@ -1229,9 +1153,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else { - cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", ServerBin); if (con->uri[5] && con->uri[6]) cupsdSetString(&con->options, con->uri + 5); else @@ -1252,95 +1174,13 @@ 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 { - /* - * GET can only be done to configuration files directly under - * /admin/conf... - */ - - cupsdLogClient(con, CUPSD_LOG_ERROR, - "Request for subdirectory \"%s\"!", con->uri); - - if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - - break; - } - else - { - /* - * Serve a file... - */ - - if ((filename = get_file(con, &filestats, buf, - sizeof(buf))) == NULL) - { - if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - break; - } - - 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)) - { - /* - * Note: con->command and con->options were set by - * is_cgi()... - */ - - if (!cupsdSendCommand(con, con->command, con->options, 0)) - { - if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - } - else - cupsdLogRequest(con, HTTP_STATUS_OK); - - if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) - httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); - break; - } - - if (!check_if_modified(con, &filestats)) - { - if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - } - else - { - if (type == NULL) - strlcpy(line, "text/plain", sizeof(line)); - else - snprintf(line, sizeof(line), "%s/%s", type->super, type->type); - - if (!write_file(con, HTTP_STATUS_OK, filename, line, &filestats)) - { - cupsdCloseClient(con); - return; - } - } } break; @@ -1350,9 +1190,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * so check the length against any limits that are set... */ - if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && - MaxRequestSize > 0 && - httpGetLength2(con->http) > MaxRequestSize) + if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && MaxRequestSize > 0 && httpGetLength2(con->http) > MaxRequestSize) { /* * Request too large... @@ -1386,9 +1224,11 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * content-type field will be "application/ipp"... */ - if (!strcmp(httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE), - "application/ipp")) + if (!strcmp(httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE), "application/ipp")) + { con->request = ippNew(); + break; + } else if (!WebInterface) { /* @@ -1403,13 +1243,29 @@ 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)) || - !strncmp(con->uri, "/printers", 9) || - !strncmp(con->uri, "/classes", 8) || - !strncmp(con->uri, "/help", 5) || - !strncmp(con->uri, "/jobs", 5)) + + if ((filename = get_file(con, &filestats, buf, sizeof(buf))) != NULL) + { + /* + * POST to a file... + */ + + type = mimeFileType(MimeDatabase, filename, NULL, NULL); + + if (!is_cgi(con, filename, &filestats, type)) + { + /* + * Only POST to CGI's... + */ + + if (!cupsdSendError(con, HTTP_STATUS_UNAUTHORIZED, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + } + } + 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)) { /* * CGI request... @@ -1417,16 +1273,12 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (!strncmp(con->uri, "/admin", 6)) { - cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin); cupsdSetString(&con->options, strchr(con->uri + 6, '?')); } else if (!strncmp(con->uri, "/printers", 9)) { - cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin); if (con->uri[9] && con->uri[10]) cupsdSetString(&con->options, con->uri + 9); else @@ -1434,9 +1286,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else if (!strncmp(con->uri, "/classes", 8)) { - cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin); if (con->uri[8] && con->uri[9]) cupsdSetString(&con->options, con->uri + 8); else @@ -1444,9 +1294,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else if (!strncmp(con->uri, "/jobs", 5)) { - cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin); if (con->uri[5] && con->uri[6]) cupsdSetString(&con->options, con->uri + 5); else @@ -1454,9 +1302,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else { - cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", - ServerBin); - + cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", ServerBin); if (con->uri[5] && con->uri[6]) cupsdSetString(&con->options, con->uri + 5); else @@ -1468,35 +1314,10 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else { - /* - * POST to a file... - */ - - if ((filename = get_file(con, &filestats, buf, - sizeof(buf))) == NULL) - { - if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - break; - } - - type = mimeFileType(MimeDatabase, filename, NULL, NULL); - - if (!is_cgi(con, filename, &filestats, type)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { - /* - * Only POST to CGI's... - */ - - if (!cupsdSendError(con, HTTP_STATUS_UNAUTHORIZED, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } + cupsdCloseClient(con); + return; } } break; @@ -1512,8 +1333,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * PUT can only be done to the cupsd.conf file... */ - cupsdLogClient(con, CUPSD_LOG_ERROR, - "Disallowed PUT request for \"%s\".", con->uri); + cupsdLogClient(con, CUPSD_LOG_ERROR, "Disallowed PUT request for \"%s\".", con->uri); if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) { @@ -1529,9 +1349,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * so check the length against any limits that are set... */ - if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && - MaxRequestSize > 0 && - httpGetLength2(con->http) > MaxRequestSize) + if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && MaxRequestSize > 0 && httpGetLength2(con->http) > MaxRequestSize) { /* * Request too large... @@ -1564,15 +1382,12 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Open a temporary file to hold the request... */ - cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, - request_id ++); + cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++); con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); if (con->file < 0) { - cupsdLogClient(con, CUPSD_LOG_ERROR, - "Unable to create request file \"%s\": %s", - con->filename, strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to create request file \"%s\": %s", con->filename, strerror(errno)); if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { @@ -1593,163 +1408,85 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ return; case HTTP_STATE_HEAD : - if (!strncmp(con->uri, "/printers/", 10) && - !strcmp(con->uri + strlen(con->uri) - 4, ".ppd")) - { - /* - * Send PPD file - get the real printer name since printer - * names are not case sensitive but filenames can be... - */ - - con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ - - if ((p = cupsdFindPrinter(con->uri + 10)) != NULL) - snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); - else + if ((filename = get_file(con, &filestats, buf, sizeof(buf))) != NULL) + { + if (!check_if_modified(con, &filestats)) { - if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); - break; + cupsdLogRequest(con, HTTP_STATUS_NOT_MODIFIED); } - } - else if (!strncmp(con->uri, "/printers/", 10) && - !strcmp(con->uri + strlen(con->uri) - 4, ".png")) - { - /* - * Send PNG file - get the real printer name since printer - * names are not case sensitive but filenames can be... - */ - - con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ - - if ((p = cupsdFindPrinter(con->uri + 10)) != NULL) - snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name); else { - if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) + /* + * Serve a file... + */ + + type = mimeFileType(MimeDatabase, filename, NULL, NULL); + if (type == NULL) + strlcpy(line, "text/plain", sizeof(line)); + else + snprintf(line, sizeof(line), "%s/%s", type->super, type->type); + + httpClearFields(con->http); + + httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, httpGetDateString(filestats.st_mtime)); + httpSetLength(con->http, (size_t)filestats.st_size); + + if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); - break; + cupsdLogRequest(con, HTTP_STATUS_OK); } - } + } else if (!WebInterface) { httpClearFields(con->http); - if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - cupsdLogRequest(con, HTTP_STATUS_OK); - break; - } - - if ((!strncmp(con->uri, "/admin", 6) && - strncmp(con->uri, "/admin/conf/", 12) && - 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)) - { - /* - * CGI output... - */ - - httpClearFields(con->http); - - 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))) - { - /* - * HEAD can only be done to configuration files under - * /admin/conf... - */ - - cupsdLogClient(con, CUPSD_LOG_ERROR, - "Request for subdirectory \"%s\".", con->uri); - - if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) + if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_STATUS_FORBIDDEN); + cupsdLogRequest(con, HTTP_STATUS_OK); break; } - else if ((filename = get_file(con, &filestats, buf, - sizeof(buf))) == NULL) + + 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)) { - httpClearFields(con->http); + /* + * CGI output... + */ - if (!cupsdSendHeader(con, HTTP_STATUS_NOT_FOUND, "text/html", - CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } + httpClearFields(con->http); - cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); - } - else if (!check_if_modified(con, &filestats)) - { - if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) + if (!cupsdSendHeader(con, HTTP_STATUS_OK, "text/html", CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_STATUS_NOT_MODIFIED); + cupsdLogRequest(con, HTTP_STATUS_OK); } else { - /* - * Serve a file... - */ - - type = mimeFileType(MimeDatabase, filename, NULL, NULL); - if (type == NULL) - strlcpy(line, "text/plain", sizeof(line)); - else - snprintf(line, sizeof(line), "%s/%s", type->super, type->type); - httpClearFields(con->http); - httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, - httpGetDateString(filestats.st_mtime)); - httpSetLength(con->http, filestats.st_size); - - if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE)) + if (!cupsdSendHeader(con, HTTP_STATUS_NOT_FOUND, "text/html", CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_STATUS_OK); + cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); } break; @@ -1785,7 +1522,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, @@ -1803,6 +1554,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)); @@ -1861,6 +1617,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", @@ -1890,7 +1649,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); } } @@ -1943,7 +1702,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", @@ -2066,7 +1839,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ else { cupsArrayRemove(ActiveClients, con); - cupsdSetBusyState(); + cupsdSetBusyState(0); } } } @@ -2142,8 +1915,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 /* @@ -2174,8 +1949,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); @@ -2291,6 +2070,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... */ @@ -2323,50 +2104,59 @@ 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) { -# ifdef AF_LOCAL +#if defined(SO_PEERCRED) && defined(AF_LOCAL) if (httpAddrFamily(httpGetAddress(con->http)) == AF_LOCAL) - strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str)); + strlcpy(auth_str, "PeerCred", sizeof(auth_str)); else -# endif /* AF_LOCAL */ +#endif /* SO_PEERCRED && AF_LOCAL */ strlcpy(auth_str, "Negotiate", sizeof(auth_str)); } -#endif /* HAVE_GSSAPI */ - if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE && - !_cups_strcasecmp(httpGetHostname(con->http, NULL, 0), "localhost")) + if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE && !con->is_browser && !_cups_strcasecmp(httpGetHostname(con->http, NULL, 0), "localhost")) { /* * Add a "trc" (try root certification) parameter for local non-Kerberos * requests when the request requires system group membership - then the * client knows the root certificate can/should be used. * - * Also, for OS X we also look for @AUTHKEY and add an "authkey" - * parameter as needed... + * Also, for macOS we also look for @AUTHKEY and add an "AuthRef key=foo" + * method as needed... */ char *name, /* Current user name */ *auth_key; /* Auth key buffer */ size_t auth_size; /* Size of remaining buffer */ + int need_local = 1; /* Do we need to list "Local" method? */ 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); + +#if defined(SO_PEERCRED) && defined(AF_LOCAL) + if (httpAddrFamily(httpGetAddress(con->http)) == AF_LOCAL) + { + strlcpy(auth_key, ", PeerCred", auth_size); + auth_key += 10; + auth_size -= 10; + } +#endif /* SO_PEERCRED && AF_LOCAL */ for (name = (char *)cupsArrayFirst(con->best->names); name; name = (char *)cupsArrayNext(con->best->names)) { + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdSendHeader: require \"%s\"", name); + #ifdef HAVE_AUTHORIZATION_H if (!_cups_strncasecmp(name, "@AUTHKEY(", 9)) { - snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9); + snprintf(auth_key, auth_size, ", AuthRef key=\"%s\", Local trc=\"y\"", name + 9); + need_local = 0; /* end parenthesis is stripped in conf.c */ break; } @@ -2376,16 +2166,17 @@ cupsdSendHeader( { #ifdef HAVE_AUTHORIZATION_H if (SystemGroupAuthKey) - snprintf(auth_key, auth_size, - ", authkey=\"%s\"", - SystemGroupAuthKey); + snprintf(auth_key, auth_size, ", AuthRef key=\"%s\", Local trc=\"y\"", SystemGroupAuthKey); else -#else - strlcpy(auth_key, ", trc=\"y\"", auth_size); #endif /* HAVE_AUTHORIZATION_H */ + strlcpy(auth_key, ", Local trc=\"y\"", auth_size); + need_local = 0; break; } } + + if (need_local) + strlcat(auth_key, ", Local", auth_size); } if (auth_str[0]) @@ -2516,6 +2307,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 */ @@ -2554,8 +2356,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; @@ -2620,12 +2421,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; } @@ -2638,7 +2433,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; @@ -2654,10 +2449,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 @@ -2673,7 +2481,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))); @@ -2705,7 +2513,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."); @@ -2771,7 +2579,7 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ else { cupsArrayRemove(ActiveClients, con); - cupsdSetBusyState(); + cupsdSetBusyState(0); } } } @@ -2798,11 +2606,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') { @@ -2869,7 +2673,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 */ @@ -2883,12 +2687,15 @@ 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? */ + cupsd_printer_t *p; /* Printer */ /* @@ -2897,18 +2704,110 @@ get_file(cupsd_client_t *con, /* I - Client connection */ language[0] = '\0'; - if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/')) - snprintf(filename, len, "%s%s", ServerRoot, con->uri); - else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/')) + if ((!strncmp(con->uri, "/ppd/", 5) || !strncmp(con->uri, "/printers/", 10) || !strncmp(con->uri, "/classes/", 9)) && !strcmp(con->uri + strlen(con->uri) - 4, ".ppd")) + { + strlcpy(dest, strchr(con->uri + 1, '/') + 1, sizeof(dest)); + dest[strlen(dest) - 4] = '\0'; /* Strip .ppd */ + + if ((p = cupsdFindDest(dest)) == NULL) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "No destination \"%s\" found.", dest); + return (NULL); + } + + if (p->type & CUPS_PRINTER_CLASS) + { + int i; /* Looping var */ + + for (i = 0; i < p->num_printers; i ++) + { + if (!(p->printers[i]->type & CUPS_PRINTER_CLASS)) + { + snprintf(filename, len, "%s/ppd/%s.ppd", ServerRoot, p->printers[i]->name); + if (!access(filename, 0)) + { + p = p->printers[i]; + break; + } + } + } + + if (i >= p->num_printers) + p = NULL; + } + else + snprintf(filename, len, "%s/ppd/%s.ppd", ServerRoot, p->name); + + perm_check = 0; + } + 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")) { - snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7); + strlcpy(dest, strchr(con->uri + 1, '/') + 1, sizeof(dest)); + dest[strlen(dest) - 4] = '\0'; /* Strip .png */ + + if ((p = cupsdFindDest(dest)) == NULL) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "No destination \"%s\" found.", dest); + return (NULL); + } + + if (p->type & CUPS_PRINTER_CLASS) + { + int i; /* Looping var */ + + for (i = 0; i < p->num_printers; i ++) + { + if (!(p->printers[i]->type & CUPS_PRINTER_CLASS)) + { + snprintf(filename, len, "%s/images/%s.png", CacheDir, p->printers[i]->name); + if (!access(filename, 0)) + { + p = p->printers[i]; + break; + } + } + } + + if (i >= p->num_printers) + p = NULL; + } + else + snprintf(filename, len, "%s/images/%s.png", CacheDir, p->name); + 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 (!strncmp(con->uri, "/strings/", 9) && !strcmp(con->uri + strlen(con->uri) - 8, ".strings")) + { + strlcpy(dest, con->uri + 9, sizeof(dest)); + dest[strlen(dest) - 8] = '\0'; + + if ((p = cupsdFindDest(dest)) == NULL) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "No destination \"%s\" found.", dest); + return (NULL); + } + + if (!p->strings) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "No strings files for \"%s\".", dest); + return (NULL); + } + + strlcpy(filename, p->strings, len); + + perm_check = 0; + } + 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] == '/') @@ -2919,6 +2818,8 @@ get_file(cupsd_client_t *con, /* I - Client connection */ strlcpy(filename, PageLog, len); else return (NULL); + + perm_check = 0; } else if (con->language) { @@ -2936,10 +2837,11 @@ 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) && + strncmp(con->uri, "/strings/", 9) && strncmp(con->uri, "/admin/conf/", 12) && strncmp(con->uri, "/admin/log/", 11)) { @@ -2953,7 +2855,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... @@ -2965,12 +2867,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)) @@ -3010,57 +2933,36 @@ 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); - -#ifdef HAVE_JAVA - if (status) - { - strlcpy(ptr, "index.class", plen); - status = stat(filename, filestats); - } -#endif /* HAVE_JAVA */ - -#ifdef HAVE_PERL - if (status) - { - strlcpy(ptr, "index.pl", plen); - status = stat(filename, filestats); - } -#endif /* HAVE_PERL */ + status = lstat(filename, filestats); + } + while (status && language[0]); -#ifdef HAVE_PHP - if (status) - { - strlcpy(ptr, "index.php", plen); - status = stat(filename, filestats); - } -#endif /* HAVE_PHP */ + /* + * If we've found a symlink, 404 the sucker to avoid disclosing information. + */ -#ifdef HAVE_PYTHON - if (status) - { - strlcpy(ptr, "index.pyc", plen); - status = stat(filename, filestats); - } + if (!status && S_ISLNK(filestats->st_mode)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename); + return (NULL); + } - if (status) - { - strlcpy(ptr, "index.py", plen); - status = stat(filename, filestats); - } -#endif /* HAVE_PYTHON */ + /* + * 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); } - while (status && language[0]); } - 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); @@ -3091,7 +2993,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; } /* @@ -3101,7 +3003,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\"...", @@ -3112,7 +3014,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", @@ -3124,7 +3026,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; } /* @@ -3134,7 +3036,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... @@ -3155,6 +3057,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); } @@ -3187,11 +3100,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); } @@ -3207,101 +3116,11 @@ 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); - return (1); - } -#ifdef HAVE_JAVA - else if (!_cups_strcasecmp(type->type, "x-httpd-java")) - { - /* - * "application/x-httpd-java" is a Java servlet. - */ - - cupsdSetString(&con->command, CUPS_JAVA); - - if (options) - cupsdSetStringf(&con->options, " %s %s", filename, options); - 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); - return (1); - } -#endif /* HAVE_JAVA */ -#ifdef HAVE_PERL - else if (!_cups_strcasecmp(type->type, "x-httpd-perl")) - { - /* - * "application/x-httpd-perl" is a Perl page. - */ - - cupsdSetString(&con->command, CUPS_PERL); - - if (options) - cupsdSetStringf(&con->options, " %s %s", filename, options); - 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); - return (1); - } -#endif /* HAVE_PERL */ -#ifdef HAVE_PHP - else if (!_cups_strcasecmp(type->type, "x-httpd-php")) - { - /* - * "application/x-httpd-php" is a PHP page. - */ - - cupsdSetString(&con->command, CUPS_PHP); - - if (options) - cupsdSetStringf(&con->options, " %s %s", filename, options); - 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); - return (1); - } -#endif /* HAVE_PHP */ -#ifdef HAVE_PYTHON - else if (!_cups_strcasecmp(type->type, "x-httpd-python")) - { - /* - * "application/x-httpd-python" is a Python page. - */ - - cupsdSetString(&con->command, CUPS_PYTHON); - - if (options) - cupsdSetStringf(&con->options, " %s %s", filename, options); - 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); } @@ -3320,6 +3139,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... */ @@ -3400,21 +3227,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'; @@ -3484,9 +3302,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'; @@ -3522,42 +3340,12 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ else auth_type[0] = '\0'; - if (con->request && - (attr = ippFindAttribute(con->request, "attributes-natural-language", - IPP_TAG_LANGUAGE)) != NULL) + if (con->request && (attr = ippFindAttribute(con->request, "attributes-natural-language", IPP_TAG_LANGUAGE)) != NULL) { - switch (strlen(attr->values[0].string.text)) - { - default : - /* - * This is an unknown or badly formatted language code; use - * the POSIX locale... - */ + cups_lang_t *language = cupsLangGet(ippGetString(attr, 0, NULL)); - strlcpy(lang, "LANG=C", sizeof(lang)); - break; - - case 2 : - /* - * Just the language code (ll)... - */ - - snprintf(lang, sizeof(lang), "LANG=%s.UTF8", - attr->values[0].string.text); - break; - - case 5 : - /* - * Language and country code (ll-cc)... - */ - - snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF8", - attr->values[0].string.text[0], - attr->values[0].string.text[1], - toupper(attr->values[0].string.text[3] & 255), - toupper(attr->values[0].string.text[4] & 255)); - break; - } + snprintf(lang, sizeof(lang), "LANG=%s.UTF8", language->language); + cupsLangFree(language); } else if (con->language) snprintf(lang, sizeof(lang), "LANG=%s.UTF8", con->language->language); @@ -3578,7 +3366,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]) { @@ -3783,9 +3571,6 @@ valid_host(cupsd_client_t *con) /* I - Client connection */ return (!_cups_strcasecmp(con->clientname, "localhost") || !_cups_strcasecmp(con->clientname, "localhost.") || -#ifdef __linux - !_cups_strcasecmp(con->clientname, "localhost.localdomain") || -#endif /* __linux */ !strcmp(con->clientname, "127.0.0.1") || !strcmp(con->clientname, "[::1]")); } @@ -3926,10 +3711,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); @@ -3941,7 +3723,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)); @@ -3964,8 +3746,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; @@ -3974,8 +3755,3 @@ write_pipe(cupsd_client_t *con) /* I - Client connection */ cupsdLogClient(con, CUPSD_LOG_DEBUG, "CGI data ready to be sent."); } - - -/* - * End of "$Id$". - */