X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scheduler%2Fclient.c;h=de825609becaf707cdd59df3c2d563d34c4f766e;hb=b2e108950fe59ee63a1f6d2381d67d3f06999cab;hp=cbd59a7ca30925953608f275ae41e144b9a1bca6;hpb=0a968cfb3e13a513626079dfbbc1574936ebe457;p=thirdparty%2Fcups.git diff --git a/scheduler/client.c b/scheduler/client.c index cbd59a7ca..de825609b 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -1,9 +1,9 @@ /* - * "$Id: client.c,v 1.91.2.53 2003/03/14 21:43:30 mike Exp $" + * "$Id$" * * Client routines for the Common UNIX Printing System (CUPS) scheduler. * - * Copyright 1997-2003 by Easy Software Products, all rights reserved. + * Copyright 1997-2004 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the * property of Easy Software Products and are protected by Federal @@ -15,9 +15,9 @@ * Attn: CUPS Licensing Information * Easy Software Products * 44141 Airport View Drive, Suite 204 - * Hollywood, Maryland 20636-3111 USA + * Hollywood, Maryland 20636 USA * - * Voice: (301) 373-9603 + * Voice: (301) 373-9600 * EMail: cups-info@cups.org * WWW: http://www.cups.org * @@ -33,13 +33,13 @@ * SendError() - Send an error message via HTTP. * SendFile() - Send a file via HTTP. * SendHeader() - Send an HTTP request. - * ShutdownClient() - Shutdown the receiving end of a connection. * UpdateCGI() - Read status messages from CGI scripts and programs. * WriteClient() - Write data to a client as needed. * check_if_modified() - Decode an "If-Modified-Since" line. * decode_auth() - Decode an authorization string. * get_file() - Get a filename and state info. * install_conf_file() - Install a configuration file. + * is_path_absolute() - Is a path absolute and free of relative elements. * pipe_command() - Pipe the output of a command to the remote client. * CDSAReadFunc() - Read function for CDSA decryption code. * CDSAWriteFunc() - Write function for CDSA encryption code. @@ -49,9 +49,9 @@ * Include necessary headers... */ +#include #include "cupsd.h" #include -#include /* @@ -64,6 +64,7 @@ static void decode_auth(client_t *con); static char *get_file(client_t *con, struct stat *filestats, char *filename, int len); static http_status_t install_conf_file(client_t *con); +static int is_path_absolute(const char *path); static int pipe_command(client_t *con, int infile, int *outfile, char *command, char *options); @@ -93,7 +94,7 @@ AcceptClient(listener_t *lis) /* I - Listener socket */ /* Time of last DoS attack */ - LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d", + LogMessage(L_DEBUG2, "AcceptClient(lis=%p) %d NumClients = %d", lis, lis->fd, NumClients); /* @@ -111,6 +112,7 @@ AcceptClient(listener_t *lis) /* I - Listener socket */ memset(con, 0, sizeof(client_t)); con->http.activity = time(NULL); + con->file = -1; /* * Accept the client and get the remote address... @@ -163,7 +165,7 @@ AcceptClient(listener_t *lis) /* I - Listener socket */ return; } - + /* * Get the hostname or format the IP address as needed... */ @@ -353,10 +355,10 @@ CloseAllClients(void) * 'CloseClient()' - Close a remote client. */ -void +int /* O - 1 if partial close, 0 if fully closed */ CloseClient(client_t *con) /* I - Client to close */ { - int status; /* Exit status of pipe command */ + int partial; /* Do partial close for SSL? */ #if defined(HAVE_LIBSSL) SSL_CTX *context; /* Context for encryption */ SSL *conn; /* Connection for encryption */ @@ -369,16 +371,9 @@ CloseClient(client_t *con) /* I - Client to close */ #endif /* HAVE_GNUTLS */ - LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd); - - if (con->http.input_set) - free(con->http.input_set); - - httpClearCookie(HTTP(con)); + LogMessage(L_DEBUG, "CloseClient: %d", con->http.fd); - ClearString(&con->filename); - ClearString(&con->command); - ClearString(&con->options); + partial = 0; #ifdef HAVE_SSL /* @@ -387,6 +382,8 @@ CloseClient(client_t *con) /* I - Client to close */ if (con->http.tls) { + partial = 1; + # ifdef HAVE_LIBSSL conn = (SSL *)(con->http.tls); context = SSL_get_SSL_CTX(conn); @@ -440,79 +437,113 @@ CloseClient(client_t *con) /* I - Client to close */ * Close the socket and clear the file from the input set for select()... */ - if (con->http.fd >= 0) + if (con->http.fd > 0) { - LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...", - con->http.fd); - close(con->http.fd); - FD_CLR(con->http.fd, InputSet); - FD_CLR(con->http.fd, OutputSet); - con->http.fd = 0; - } + if (partial) + { + /* + * Only do a partial close so that the encrypted client gets everything. + */ - if (con->pipe_pid != 0) - { - LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...", - con->file); - FD_CLR(con->file, InputSet); + LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from OutputSet...", + con->http.fd); + shutdown(con->http.fd, 0); + FD_CLR(con->http.fd, OutputSet); + } + else + { + /* + * Shut the socket down fully... + */ + + LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...", + con->http.fd); + close(con->http.fd); + FD_CLR(con->http.fd, InputSet); + FD_CLR(con->http.fd, OutputSet); + con->http.fd = -1; + } } - if (con->file) + if (con->pipe_pid != 0) { /* - * Close the open data file... + * Stop any CGI process... */ - if (con->pipe_pid) + LogMessage(L_DEBUG2, "CloseClient: %d Killing process ID %d...", + con->http.fd, con->pipe_pid); + kill(con->pipe_pid, SIGKILL); + } + + if (con->file >= 0) + { + if (FD_ISSET(con->file, InputSet)) { - kill(con->pipe_pid, SIGKILL); - waitpid(con->pipe_pid, &status, WNOHANG); + LogMessage(L_DEBUG2, "CloseClient: %d Removing fd %d from InputSet...", + con->http.fd, con->file); + FD_CLR(con->file, InputSet); } - LogMessage(L_DEBUG2, "CloseClient() %d Closing data file %d.", - con->http.fd, con->file); - LogMessage(L_DEBUG2, "CloseClient() %d Removing fd %d from InputSet.", + LogMessage(L_DEBUG2, "CloseClient: %d Closing data file %d.", con->http.fd, con->file); - FD_CLR(con->file, InputSet); close(con->file); - con->file = 0; + con->file = -1; } - if (con->request) + if (!partial) { - ippDelete(con->request); - con->request = NULL; - } + /* + * Free memory... + */ - if (con->response) - { - ippDelete(con->response); - con->response = NULL; - } + if (con->http.input_set) + free(con->http.input_set); - if (con->language) - { - cupsLangFree(con->language); - con->language = NULL; - } + httpClearCookie(HTTP(con)); - /* - * Re-enable new client connections if we are going back under the - * limit... - */ + ClearString(&con->filename); + ClearString(&con->command); + ClearString(&con->options); - if (NumClients == MaxClients) - ResumeListening(); + if (con->request) + { + ippDelete(con->request); + con->request = NULL; + } - /* - * Compact the list of clients as necessary... - */ + if (con->response) + { + ippDelete(con->response); + con->response = NULL; + } + + if (con->language) + { + cupsLangFree(con->language); + con->language = NULL; + } + + /* + * Re-enable new client connections if we are going back under the + * limit... + */ - NumClients --; + if (NumClients == MaxClients) + ResumeListening(); - if (con < (Clients + NumClients)) - memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t)); + /* + * Compact the list of clients as necessary... + */ + + NumClients --; + + if (con < (Clients + NumClients)) + memmove(con, con + 1, (Clients + NumClients - con) * sizeof(client_t)); + } + + return (partial); } @@ -543,6 +574,9 @@ EncryptClient(client_t *con) /* I - Client to encrypt */ SSL_set_fd(conn, con->http.fd); if (SSL_accept(conn) != 1) { + LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!", + con->http.hostname); + while ((error = ERR_get_error()) != 0) LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL)); @@ -551,6 +585,9 @@ EncryptClient(client_t *con) /* I - Client to encrypt */ return (0); } + LogMessage(L_DEBUG, "EncryptClient: %d Connection from %s now encrypted.", + con->http.fd, con->http.hostname); + con->http.tls = conn; return (1); @@ -573,6 +610,10 @@ EncryptClient(client_t *con) /* I - Client to encrypt */ malloc(sizeof(gnutls_certificate_server_credentials)); if (credentials == NULL) { + LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!", + con->http.hostname); + LogMessage(L_ERROR, "EncryptClient: %s", strerror(errno)); + free(conn); return (0); } @@ -590,7 +631,10 @@ EncryptClient(client_t *con) /* I - Client to encrypt */ if (error != GNUTLS_E_SUCCESS) { + LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!", + con->http.hostname); LogMessage(L_ERROR, "EncryptClient: %s", gnutls_strerror(error)); + gnutls_deinit(conn->session); gnutls_certificate_free_credentials(*credentials); free(conn); @@ -598,8 +642,8 @@ EncryptClient(client_t *con) /* I - Client to encrypt */ return (0); } - LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.", - con->http.fd); + LogMessage(L_DEBUG, "EncryptClient: %d Connection from %s now encrypted.", + con->http.fd, con->http.hostname); conn->credentials = credentials; con->http.tls = conn; @@ -659,7 +703,10 @@ EncryptClient(client_t *con) /* I - Client to encrypt */ if (error) { - LogMessage(L_ERROR, "EncryptClient: %d", error); + LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!", + con->http.hostname); + + LogMessage(L_ERROR, "EncryptClient: CDSA error code is %d", error); con->http.error = error; con->http.status = HTTP_ERROR; @@ -670,8 +717,9 @@ EncryptClient(client_t *con) /* I - Client to encrypt */ return (0); } - LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.", - con->http.fd); + LogMessage(L_DEBUG, "EncryptClient: %d Connection from %s now encrypted.", + con->http.fd, con->http.hostname); + con->http.tls = conn; return (1); @@ -709,7 +757,7 @@ IsCGI(client_t *con, /* I - Client connection */ * Check for known types... */ - if (strcasecmp(type->super, "application")) + if (!type || strcasecmp(type->super, "application")) { LogMessage(L_DEBUG2, "IsCGI: Returning 0..."); return (0); @@ -827,34 +875,36 @@ IsCGI(client_t *con, /* I - Client connection */ * 'ReadClient()' - Read data from a client. */ -int /* O - 1 on success, 0 on error */ -ReadClient(client_t *con) /* I - Client to read from */ +int /* O - 1 on success, 0 on error */ +ReadClient(client_t *con) /* I - Client to read from */ { - char line[32768], /* Line from client... */ - operation[64], /* Operation code from socket */ - version[64]; /* HTTP version number string */ - int major, minor; /* HTTP version numbers */ - http_status_t status; /* Transfer status */ - ipp_state_t ipp_state; /* State of IPP transfer */ - int bytes; /* Number of bytes to POST */ - char *filename; /* Name of file for GET/HEAD */ - char buf[1024]; /* Buffer for real filename */ - struct stat filestats; /* File information */ - mime_type_t *type; /* MIME type of file */ - printer_t *p; /* Printer */ - location_t *best; /* Best match for authentication */ - static unsigned request_id = 0;/* Request ID for temp files */ + char line[32768], /* Line from client... */ + operation[64], /* Operation code from socket */ + version[64], /* HTTP version number string */ + locale[64], /* Locale */ + *ptr; /* Pointer into strings */ + int major, minor; /* HTTP version numbers */ + http_status_t status; /* Transfer status */ + ipp_state_t ipp_state; /* State of IPP transfer */ + int bytes; /* Number of bytes to POST */ + char *filename; /* Name of file for GET/HEAD */ + char buf[1024]; /* Buffer for real filename */ + struct stat filestats; /* File information */ + mime_type_t *type; /* MIME type of file */ + printer_t *p; /* Printer */ + location_t *best; /* Best match for authentication */ + static unsigned request_id = 0; /* Request ID for temp files */ status = HTTP_CONTINUE; - LogMessage(L_DEBUG2, "ReadClient() %d, used=%d", con->http.fd, - con->http.used); + LogMessage(L_DEBUG2, "ReadClient: %d, used=%d, file=%d", con->http.fd, + con->http.used, con->file); if (con->http.error) { - CloseClient(con); - return (0); + LogMessage(L_DEBUG2, "ReadClient: http error seen..."); + return (CloseClient(con)); } switch (con->http.state) @@ -866,8 +916,8 @@ ReadClient(client_t *con) /* I - Client to read from */ if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL) { - CloseClient(con); - return (0); + LogMessage(L_DEBUG2, "ReadClient: httpGets returned EOF..."); + return (CloseClient(con)); } /* @@ -890,7 +940,8 @@ ReadClient(client_t *con) /* I - Client to read from */ con->http.data_remaining = 0; con->operation = HTTP_WAITING; con->bytes = 0; - con->file = 0; + con->file = -1; + con->file_ready = 0; con->pipe_pid = 0; con->username[0] = '\0'; con->password[0] = '\0'; @@ -912,20 +963,20 @@ ReadClient(client_t *con) /* I - Client to read from */ switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version)) { case 1 : - LogMessage(L_ERROR, "Bad request line \"%s\"!", line); + LogMessage(L_ERROR, "Bad request line \"%s\" from %s!", line, + con->http.hostname); SendError(con, HTTP_BAD_REQUEST); - ShutdownClient(con); - return (1); + return (CloseClient(con)); case 2 : con->http.version = HTTP_0_9; break; case 3 : if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2) { - LogMessage(L_ERROR, "Bad request line \"%s\"!", line); + LogMessage(L_ERROR, "Bad request line \"%s\" from %s!", line, + con->http.hostname); SendError(con, HTTP_BAD_REQUEST); - ShutdownClient(con); - return (1); + return (CloseClient(con)); } if (major < 2) @@ -939,8 +990,7 @@ ReadClient(client_t *con) /* I - Client to read from */ else { SendError(con, HTTP_NOT_SUPPORTED); - ShutdownClient(con); - return (1); + return (CloseClient(con)); } break; } @@ -979,8 +1029,7 @@ ReadClient(client_t *con) /* I - Client to read from */ LogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri); SendError(con, HTTP_METHOD_NOT_ALLOWED); - ShutdownClient(con); - return (1); + return (CloseClient(con)); } /* @@ -1013,14 +1062,13 @@ ReadClient(client_t *con) /* I - Client to read from */ { LogMessage(L_ERROR, "Bad operation \"%s\"!", operation); SendError(con, HTTP_BAD_REQUEST); - ShutdownClient(con); - return (1); + return (CloseClient(con)); } con->start = time(NULL); con->operation = con->http.state; - LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd, + LogMessage(L_DEBUG, "ReadClient: %d %s %s HTTP/%d.%d", con->http.fd, operation, con->uri, con->http.version / 100, con->http.version % 100); @@ -1042,8 +1090,7 @@ ReadClient(client_t *con) /* I - Client to read from */ if (status != HTTP_OK && status != HTTP_CONTINUE) { SendError(con, HTTP_BAD_REQUEST); - ShutdownClient(con); - return (1); + return (CloseClient(con)); } break; @@ -1057,7 +1104,40 @@ ReadClient(client_t *con) /* I - Client to read from */ if (status == HTTP_OK) { - con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]); + if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0]) + { + /* + * Figure out the locale from the Accept-Language and Content-Type + * fields... + */ + + if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ',')) != NULL) + *ptr = '\0'; + + if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ';')) != NULL) + *ptr = '\0'; + + if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "charset=")) != NULL) + { + /* + * Combine language and charset, and trim any extra params in the + * content-type. + */ + + snprintf(locale, sizeof(locale), "%s.%s", + con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8); + + if ((ptr = strchr(locale, ',')) != NULL) + *ptr = '\0'; + } + else + snprintf(locale, sizeof(locale), "%s.%s", + con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], DefaultCharset); + + con->language = cupsLangGet(locale); + } + else + con->language = cupsLangGet(DefaultLocale); decode_auth(con); @@ -1073,10 +1153,7 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendError(con, HTTP_BAD_REQUEST)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } else if (con->operation == HTTP_OPTIONS) { @@ -1088,10 +1165,7 @@ ReadClient(client_t *con) /* I - Client to read from */ best->type != AUTH_NONE) { if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 && @@ -1103,10 +1177,7 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); @@ -1116,10 +1187,7 @@ ReadClient(client_t *con) /* I - Client to read from */ EncryptClient(con); #else if (!SendError(con, HTTP_NOT_IMPLEMENTED)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); #endif /* HAVE_SSL */ } @@ -1129,26 +1197,20 @@ ReadClient(client_t *con) /* I - Client to read from */ } if (!SendHeader(con, HTTP_OK, NULL)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n"); httpPrintf(HTTP(con), "Content-Length: 0\r\n"); httpPrintf(HTTP(con), "\r\n"); } - else if (strstr(con->uri, "..") != NULL) + else if (!is_path_absolute(con->uri)) { /* * Protect against malicious users! */ if (!SendError(con, HTTP_FORBIDDEN)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } else { @@ -1161,10 +1223,7 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); @@ -1172,24 +1231,18 @@ ReadClient(client_t *con) /* I - Client to read from */ httpPrintf(HTTP(con), "\r\n"); EncryptClient(con); - - status = IsAuthorized(con); #else if (!SendError(con, HTTP_NOT_IMPLEMENTED)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); #endif /* HAVE_SSL */ } - if (status != HTTP_OK) + if ((status = IsAuthorized(con)) != HTTP_OK) { LogMessage(L_DEBUG2, "ReadClient: Unauthorized request for %s...\n", con->uri); SendError(con, status); - ShutdownClient(con); - return (1); + return (CloseClient(con)); } if (con->http.expect) @@ -1215,10 +1268,7 @@ ReadClient(client_t *con) /* I - Client to read from */ else { if (!SendError(con, HTTP_NOT_FOUND)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } @@ -1237,7 +1287,11 @@ ReadClient(client_t *con) /* I - Client to read from */ if (strncmp(con->uri, "/admin", 6) == 0) { SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin); - SetString(&con->options, con->uri + 6); + + if ((ptr = strchr(con->uri + 6, '?')) != NULL) + SetStringf(&con->options, "admin%s", ptr); + else + SetString(&con->options, "admin"); } else if (strncmp(con->uri, "/printers", 9) == 0) { @@ -1252,19 +1306,16 @@ ReadClient(client_t *con) /* I - Client to read from */ else { SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin); - SetString(&con->options, con->uri + 5); + SetString(&con->options, con->uri + 5); } - if (con->options[0] == '/') - strcpy(con->options, con->options + 1); + if (con->options[0] == '/') + cups_strcpy(con->options, con->options + 1); if (!SendCommand(con, con->command, con->options)) { if (!SendError(con, HTTP_NOT_FOUND)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } else LogRequest(con, HTTP_OK); @@ -1282,10 +1333,7 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendError(con, HTTP_FORBIDDEN)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } @@ -1299,25 +1347,24 @@ ReadClient(client_t *con) /* I - Client to read from */ sizeof(buf))) == NULL) { if (!SendError(con, HTTP_NOT_FOUND)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } - type = mimeFileType(MimeDatabase, filename); + type = mimeFileType(MimeDatabase, filename, NULL); if (IsCGI(con, filename, &filestats, type)) { + /* + * Note: con->command and con->options were set by + * IsCGI()... + */ + if (!SendCommand(con, con->command, con->options)) { if (!SendError(con, HTTP_NOT_FOUND)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } else LogRequest(con, HTTP_OK); @@ -1330,10 +1377,7 @@ ReadClient(client_t *con) /* I - Client to read from */ if (!check_if_modified(con, &filestats)) { if (!SendError(con, HTTP_NOT_MODIFIED)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } else { @@ -1343,10 +1387,7 @@ ReadClient(client_t *con) /* I - Client to read from */ snprintf(line, sizeof(line), "%s/%s", type->super, type->type); if (!SendFile(con, HTTP_OK, filename, line, &filestats)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } } break; @@ -1369,10 +1410,7 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } @@ -1383,10 +1421,7 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendError(con, HTTP_BAD_REQUEST)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } @@ -1411,7 +1446,11 @@ ReadClient(client_t *con) /* I - Client to read from */ if (strncmp(con->uri, "/admin", 6) == 0) { SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin); - SetString(&con->options, con->uri + 6); + + if ((ptr = strchr(con->uri + 6, '?')) != NULL) + SetStringf(&con->options, "admin%s", ptr); + else + SetString(&con->options, "admin"); } else if (strncmp(con->uri, "/printers", 9) == 0) { @@ -1430,9 +1469,9 @@ ReadClient(client_t *con) /* I - Client to read from */ } if (con->options[0] == '/') - strcpy(con->options, con->options + 1); + cups_strcpy(con->options, con->options + 1); - LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"", + LogMessage(L_DEBUG2, "ReadClient: %d command=\"%s\", options = \"%s\"", con->http.fd, con->command, con->options); if (con->http.version <= HTTP_1_0) @@ -1448,15 +1487,12 @@ ReadClient(client_t *con) /* I - Client to read from */ sizeof(buf))) == NULL) { if (!SendError(con, HTTP_NOT_FOUND)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } - type = mimeFileType(MimeDatabase, filename); + type = mimeFileType(MimeDatabase, filename, NULL); if (!IsCGI(con, filename, &filestats, type)) { @@ -1465,10 +1501,7 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendError(con, HTTP_UNAUTHORIZED)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } } break; @@ -1488,10 +1521,7 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendError(con, HTTP_FORBIDDEN)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } @@ -1513,13 +1543,21 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } + else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0) + { + /* + * Negative content lengths are invalid! + */ + + if (!SendError(con, HTTP_BAD_REQUEST)) + return (CloseClient(con)); + + break; + } /* * Open a temporary file to hold the request... @@ -1527,27 +1565,25 @@ ReadClient(client_t *con) /* I - Client to read from */ SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++); con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); - fchmod(con->file, 0640); - fchown(con->file, User, Group); - LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd, + LogMessage(L_DEBUG2, "ReadClient: %d REQUEST %s=%d", con->http.fd, con->filename, con->file); if (con->file < 0) { if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } + + fchmod(con->file, 0640); + fchown(con->file, RunUser, Group); + fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); break; case HTTP_DELETE : case HTTP_TRACE : SendError(con, HTTP_NOT_IMPLEMENTED); - ShutdownClient(con); - return (1); + return (CloseClient(con)); case HTTP_HEAD : if (strncmp(con->uri, "/printers/", 10) == 0 && @@ -1565,10 +1601,7 @@ ReadClient(client_t *con) /* I - Client to read from */ else { if (!SendError(con, HTTP_NOT_FOUND)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } @@ -1585,16 +1618,10 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendHeader(con, HTTP_OK, "text/html")) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); if (httpPrintf(HTTP(con), "\r\n") < 0) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); LogRequest(con, HTTP_OK); } @@ -1608,10 +1635,7 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendError(con, HTTP_FORBIDDEN)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); break; } @@ -1619,20 +1643,14 @@ ReadClient(client_t *con) /* I - Client to read from */ sizeof(buf))) == NULL) { if (!SendHeader(con, HTTP_NOT_FOUND, "text/html")) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); LogRequest(con, HTTP_NOT_FOUND); } else if (!check_if_modified(con, &filestats)) { if (!SendError(con, HTTP_NOT_MODIFIED)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); LogRequest(con, HTTP_NOT_MODIFIED); } @@ -1642,40 +1660,28 @@ ReadClient(client_t *con) /* I - Client to read from */ * Serve a file... */ - type = mimeFileType(MimeDatabase, filename); + type = mimeFileType(MimeDatabase, filename, NULL); if (type == NULL) strcpy(line, "text/plain"); else snprintf(line, sizeof(line), "%s/%s", type->super, type->type); if (!SendHeader(con, HTTP_OK, line)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats.st_mtime)) < 0) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n", (unsigned long)filestats.st_size) < 0) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); LogRequest(con, HTTP_OK); } if (httpPrintf(HTTP(con), "\r\n") < 0) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); con->http.state = HTTP_WAITING; break; @@ -1693,21 +1699,18 @@ ReadClient(client_t *con) /* I - Client to read from */ switch (con->http.state) { case HTTP_PUT_RECV : - LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d", + LogMessage(L_DEBUG2, "ReadClient: %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d", con->http.fd, con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length", con->http.data_remaining, con->file); if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); else if (bytes > 0) { con->bytes += bytes; - LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d", + LogMessage(L_DEBUG2, "ReadClient: %d writing %d bytes to %d", con->http.fd, bytes, con->file); if (write(con->file, line, bytes) < bytes) @@ -1719,15 +1722,12 @@ ReadClient(client_t *con) /* I - Client to read from */ con->file); close(con->file); - con->file = 0; + con->file = -1; unlink(con->filename); ClearString(&con->filename); if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } } @@ -1739,11 +1739,11 @@ ReadClient(client_t *con) /* I - Client to read from */ fstat(con->file, &filestats); - LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.", + LogMessage(L_DEBUG2, "ReadClient: %d Closing data file %d, size = %d.", con->http.fd, con->file, (int)filestats.st_size); close(con->file); - con->file = 0; + con->file = -1; if (filestats.st_size > MaxRequestSize && MaxRequestSize > 0) @@ -1752,16 +1752,13 @@ ReadClient(client_t *con) /* I - Client to read from */ * Request is too big; remove it and send an error... */ - LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s", + LogMessage(L_DEBUG2, "ReadClient: %d Removing temp file %s", con->http.fd, con->filename); unlink(con->filename); ClearString(&con->filename); if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } /* @@ -1775,15 +1772,12 @@ ReadClient(client_t *con) /* I - Client to read from */ */ if (!SendError(con, status)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } break; case HTTP_POST_RECV : - LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d", + LogMessage(L_DEBUG2, "ReadClient: %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d", con->http.fd, con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length", con->http.data_remaining, con->file); @@ -1796,17 +1790,11 @@ ReadClient(client_t *con) /* I - Client to read from */ if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR) { - LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!", + LogMessage(L_ERROR, "ReadClient: %d IPP Read Error!", con->http.fd); - if (!SendError(con, HTTP_BAD_REQUEST)) - { - CloseClient(con); - return (0); - } - - ShutdownClient(con); - return (1); + SendError(con, HTTP_BAD_REQUEST); + return (CloseClient(con)); } else if (ipp_state != IPP_DATA) break; @@ -1814,7 +1802,7 @@ ReadClient(client_t *con) /* I - Client to read from */ con->bytes += ippLength(con->request); } - if (con->file == 0 && con->http.state != HTTP_POST_SEND) + if (con->file < 0 && con->http.state != HTTP_POST_SEND) { /* * Create a file as needed for the request data... @@ -1822,34 +1810,30 @@ ReadClient(client_t *con) /* I - Client to read from */ SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++); con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); - fchmod(con->file, 0640); - fchown(con->file, User, Group); - LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd, + LogMessage(L_DEBUG2, "ReadClient: %d REQUEST %s=%d", con->http.fd, con->filename, con->file); if (con->file < 0) { if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } + + fchmod(con->file, 0640); + fchown(con->file, RunUser, Group); + fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); } if (con->http.state != HTTP_POST_SEND) { if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); else if (bytes > 0) { con->bytes += bytes; - LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d", + LogMessage(L_DEBUG2, "ReadClient: %d writing %d bytes to %d", con->http.fd, bytes, con->file); if (write(con->file, line, bytes) < bytes) @@ -1861,35 +1845,31 @@ ReadClient(client_t *con) /* I - Client to read from */ con->file); close(con->file); - con->file = 0; + con->file = -1; unlink(con->filename); ClearString(&con->filename); if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } } + else if (con->http.state == HTTP_POST_RECV) + return (1); /* ??? */ else if (con->http.state != HTTP_POST_SEND) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } if (con->http.state == HTTP_POST_SEND) { - if (con->file) + if (con->file >= 0) { fstat(con->file, &filestats); - LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.", + LogMessage(L_DEBUG2, "ReadClient: %d Closing data file %d, size = %d.", con->http.fd, con->file, (int)filestats.st_size); close(con->file); - con->file = 0; + con->file = -1; if (filestats.st_size > MaxRequestSize && MaxRequestSize > 0) @@ -1898,7 +1878,7 @@ ReadClient(client_t *con) /* I - Client to read from */ * Request is too big; remove it and send an error... */ - LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s", + LogMessage(L_DEBUG2, "ReadClient: %d Removing temp file %s", con->http.fd, con->filename); unlink(con->filename); ClearString(&con->filename); @@ -1914,10 +1894,7 @@ ReadClient(client_t *con) /* I - Client to read from */ } if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } if (con->command) @@ -1925,10 +1902,7 @@ ReadClient(client_t *con) /* I - Client to read from */ if (!SendCommand(con, con->command, con->options)) { if (!SendError(con, HTTP_NOT_FOUND)) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); } else LogRequest(con, HTTP_OK); @@ -1945,10 +1919,7 @@ ReadClient(client_t *con) /* I - Client to read from */ } if (!con->http.keep_alive && con->http.state == HTTP_WAITING) - { - CloseClient(con); - return (0); - } + return (CloseClient(con)); else return (1); } @@ -1971,13 +1942,23 @@ SendCommand(client_t *con, else fd = open("/dev/null", O_RDONLY); + if (fd < 0) + { + LogMessage(L_ERROR, "SendCommand: %d Unable to open \"%s\" for reading: %s", + con->http.fd, con->filename ? con->filename : "/dev/null", + strerror(errno)); + return (0); + } + + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + con->pipe_pid = pipe_command(con, fd, &(con->file), command, options); close(fd); LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid); - LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file); + LogMessage(L_DEBUG, "SendCommand: %d file=%d", con->http.fd, con->file); if (con->pipe_pid == 0) return (0); @@ -2002,6 +1983,7 @@ SendCommand(client_t *con, return (0); } + con->file_ready = 0; con->got_fields = 0; con->field_col = 0; @@ -2024,10 +2006,10 @@ SendError(client_t *con, /* I - Connection */ * Put the request in the access_log file... */ - if (con->operation > HTTP_WAITING) - LogRequest(con, code); + LogRequest(con, code); - LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code); + LogMessage(L_DEBUG, "SendError: %d code=%d (%s)", con->http.fd, code, + httpStatus(code)); /* * To work around bugs in some proxies, don't use Keep-Alive for some @@ -2106,7 +2088,7 @@ SendFile(client_t *con, { con->file = open(filename, O_RDONLY); - LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file); + LogMessage(L_DEBUG, "SendFile: %d file=%d", con->http.fd, con->file); if (con->file < 0) return (0); @@ -2151,8 +2133,9 @@ SendHeader(client_t *con, /* I - Client to send to */ return (0); if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0) return (0); - if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0) - return (0); + if (ServerHeader) + if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0) + return (0); if (con->http.keep_alive && con->http.version >= HTTP_1_0) { if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0) @@ -2179,7 +2162,7 @@ SendHeader(client_t *con, /* I - Client to send to */ } else { - if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" " + if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\", " "nonce=\"%s\"\r\n", con->http.hostname) < 0) return (0); } @@ -2203,36 +2186,6 @@ SendHeader(client_t *con, /* I - Client to send to */ } -/* - * 'ShutdownClient()' - Shutdown the receiving end of a connection. - */ - -void -ShutdownClient(client_t *con) /* I - Client connection */ -{ - /* - * Shutdown the receiving end of the socket, since the client - * still needs to read the error message... - */ - - shutdown(con->http.fd, 0); - con->http.used = 0; - - /* - * Update the activity time so that we timeout after 30 seconds rather - * then the current Timeout setting (300 by default). This prevents - * some DoS situations... - */ - - con->http.activity = time(NULL) - Timeout + 30; - - LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...", - con->http.fd); - - FD_CLR(con->http.fd, InputSet); -} - - /* * 'UpdateCGI()' - Read status messages from CGI scripts and programs. */ @@ -2240,129 +2193,23 @@ ShutdownClient(client_t *con) /* I - Client connection */ void UpdateCGI(void) { - int bytes; /* Number of bytes read */ - char *lineptr, /* Pointer to end of line in buffer */ - *message; /* Pointer to message text */ - int loglevel; /* Log level for message */ - static int bufused = 0; /* Number of bytes used in buffer */ - static char buffer[1024]; /* Status buffer */ - - - if ((bytes = read(CGIPipes[0], buffer + bufused, - sizeof(buffer) - bufused - 1)) > 0) - { - bufused += bytes; - buffer[bufused] = '\0'; - lineptr = strchr(buffer, '\n'); - } - else if (bytes < 0 && errno == EINTR) - return; - else - { - lineptr = buffer + bufused; - lineptr[1] = 0; - } - - if (bytes == 0 && bufused == 0) - lineptr = NULL; - - while (lineptr != NULL) - { - /* - * Terminate each line and process it... - */ - - *lineptr++ = '\0'; - - /* - * Figure out the logging level... - */ - - if (strncmp(buffer, "EMERG:", 6) == 0) - { - loglevel = L_EMERG; - message = buffer + 6; - } - else if (strncmp(buffer, "ALERT:", 6) == 0) - { - loglevel = L_ALERT; - message = buffer + 6; - } - else if (strncmp(buffer, "CRIT:", 5) == 0) - { - loglevel = L_CRIT; - message = buffer + 5; - } - else if (strncmp(buffer, "ERROR:", 6) == 0) - { - loglevel = L_ERROR; - message = buffer + 6; - } - else if (strncmp(buffer, "WARNING:", 8) == 0) - { - loglevel = L_WARN; - message = buffer + 8; - } - else if (strncmp(buffer, "NOTICE:", 6) == 0) - { - loglevel = L_NOTICE; - message = buffer + 6; - } - else if (strncmp(buffer, "INFO:", 5) == 0) - { - loglevel = L_INFO; - message = buffer + 5; - } - else if (strncmp(buffer, "DEBUG:", 6) == 0) - { - loglevel = L_DEBUG; - message = buffer + 6; - } - else if (strncmp(buffer, "DEBUG2:", 7) == 0) - { - loglevel = L_DEBUG2; - message = buffer + 7; - } - else if (strncmp(buffer, "PAGE:", 5) == 0) - { - loglevel = L_PAGE; - message = buffer + 5; - } - else - { - loglevel = L_DEBUG; - message = buffer; - } - - /* - * Skip leading whitespace in the message... - */ - - while (isspace(*message)) - message ++; - - LogMessage(loglevel, "[CGI] %s", message); + char *ptr, /* Pointer to end of line in buffer */ + message[1024]; /* Pointer to message text */ + int loglevel; /* Log level for message */ - /* - * Copy over the buffer data we've used up... - */ - - strcpy(buffer, lineptr); - bufused -= lineptr - buffer; - - if (bufused < 0) - bufused = 0; - lineptr = strchr(buffer, '\n'); - } + while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel, + message, sizeof(message))) != NULL) + if (!strchr(CGIStatusBuffer->buffer, '\n')) + break; - if (bytes <= 0) + if (ptr == NULL) { /* * Fatal error on pipe - should never happen! */ - LogMessage(L_ERROR, "UpdateCGI: error reading from CGI error pipe - %s", + LogMessage(L_CRIT, "UpdateCGI: error reading from CGI error pipe - %s", strerror(errno)); } } @@ -2381,6 +2228,11 @@ WriteClient(client_t *con) /* I - Client connection */ ipp_state_t ipp_state; /* IPP state value */ +#ifdef DEBUG + LogMessage(L_DEBUG2, "WriteClient(con=%p) %d response=%p, file=%d pipe_pid=%d", + con, con->http.fd, con->response, con->file, con->pipe_pid); +#endif /* DEBUG */ + if (con->http.state != HTTP_GET_SEND && con->http.state != HTTP_POST_SEND) return (1); @@ -2392,6 +2244,11 @@ WriteClient(client_t *con) /* I - Client connection */ } else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0) { +#ifdef DEBUG + LogMessage(L_DEBUG2, "WriteClient: Read %d bytes from file %d...", + bytes, con->file); +#endif /* DEBUG */ + if (con->pipe_pid && !con->got_fields) { /* @@ -2412,14 +2269,14 @@ WriteClient(client_t *con) /* I - Client connection */ *bufptr++ = '\0'; httpPrintf(HTTP(con), "%s\r\n", buf); - LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf); + LogMessage(L_DEBUG2, "WriteClient: %d %s", con->http.fd, buf); /* * Update buffer... */ bytes -= (bufptr - buf); - memcpy(buf, bufptr, bytes + 1); + memmove(buf, bufptr, bytes + 1); bufptr = buf - 1; /* @@ -2476,31 +2333,34 @@ WriteClient(client_t *con) /* I - Client connection */ con->http.state = HTTP_WAITING; - LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from OutputSet...", + LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from OutputSet...", con->http.fd); FD_CLR(con->http.fd, OutputSet); - if (con->file) + if (con->file >= 0) { - LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...", - con->file); - FD_CLR(con->file, InputSet); + if (FD_ISSET(con->file, InputSet)) + { + LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from InputSet...", + con->file); + FD_CLR(con->file, InputSet); + } if (con->pipe_pid) kill(con->pipe_pid, SIGTERM); - LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.", + LogMessage(L_DEBUG2, "WriteClient: %d Closing data file %d.", con->http.fd, con->file); close(con->file); - con->file = 0; + con->file = -1; con->pipe_pid = 0; } if (con->filename) { - LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s", + LogMessage(L_DEBUG2, "WriteClient: %d Removing temp file %s", con->http.fd, con->filename); unlink(con->filename); ClearString(&con->filename); @@ -2527,9 +2387,19 @@ WriteClient(client_t *con) /* I - Client connection */ return (0); } } + else + { + con->file_ready = 0; + + if (con->pipe_pid && !FD_ISSET(con->file, InputSet)) + { + LogMessage(L_DEBUG2, "WriteClient: Adding fd %d to InputSet...", con->file); + FD_SET(con->file, InputSet); + } + } if (bytes >= 1024) - LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes); + LogMessage(L_DEBUG2, "WriteClient: %d %d bytes", con->http.fd, bytes); con->http.activity = time(NULL); @@ -2557,7 +2427,7 @@ check_if_modified(client_t *con, /* I - Client connection */ if (*ptr == '\0') return (1); - LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"", + LogMessage(L_DEBUG2, "check_if_modified: %d If-Modified-Since=\"%s\"", con->http.fd, ptr); while (*ptr != '\0') @@ -2581,7 +2451,7 @@ check_if_modified(client_t *con, /* I - Client connection */ } } - LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d", + LogMessage(L_DEBUG2, "check_if_modified: %d sizes=%d,%d dates=%d,%d", con->http.fd, size, (int)filestats->st_size, (int)date, (int)filestats->st_mtime); @@ -2626,7 +2496,7 @@ decode_auth(client_t *con) /* I - Client to decode to */ if ((s = strchr(value, ':')) == NULL) { - LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"", + LogMessage(L_DEBUG, "decode_auth: %d no colon in auth string \"%s\"", con->http.fd, value); return; } @@ -2651,16 +2521,16 @@ decode_auth(client_t *con) /* I - Client to decode to */ * Get the username and password from the Digest attributes... */ - if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username", + if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "username", value)) strlcpy(con->username, value, sizeof(con->username)); - if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response", + if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "response", value)) - strlcpy(con->password, value, sizeof(con->password) - 1); + strlcpy(con->password, value, sizeof(con->password)); } - LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"", + LogMessage(L_DEBUG2, "decode_auth: %d username=\"%s\"", con->http.fd, con->username); } @@ -2669,14 +2539,15 @@ decode_auth(client_t *con) /* I - Client to decode to */ * 'get_file()' - Get a filename and state info. */ -static char * /* O - Real filename */ -get_file(client_t *con, /* I - Client connection */ - struct stat *filestats,/* O - File information */ - char *filename, /* IO - Filename buffer */ - int len) /* I - Buffer length */ +static char * /* O - Real filename */ +get_file(client_t *con, /* I - Client connection */ + struct stat *filestats, /* O - File information */ + char *filename, /* IO - Filename buffer */ + int len) /* I - Buffer length */ { - int status; /* Status of filesystem calls */ - char *params; /* Pointer to parameters in URI */ + int status; /* Status of filesystem calls */ + char *ptr; /* Pointer info filename */ + int plen; /* Remaining length after pointer */ /* @@ -2693,8 +2564,8 @@ get_file(client_t *con, /* I - Client connection */ else snprintf(filename, len, "%s%s", DocumentRoot, con->uri); - if ((params = strchr(filename, '?')) != NULL) - *params = '\0'; + if ((ptr = strchr(filename, '?')) != NULL) + *ptr = '\0'; /* * Grab the status for this language; if there isn't a language-specific file @@ -2712,6 +2583,9 @@ get_file(client_t *con, /* I - Client connection */ { snprintf(filename, len, "%s%s", DocumentRoot, con->uri); + if ((ptr = strchr(filename, '?')) != NULL) + *ptr = '\0'; + status = stat(filename, filestats); } } @@ -2722,15 +2596,55 @@ get_file(client_t *con, /* I - Client connection */ if (!status && S_ISDIR(filestats->st_mode)) { - if (filename[strlen(filename) - 1] == '/') - strlcat(filename, "index.html", len); - else - strlcat(filename, "/index.html", len); + if (filename[strlen(filename) - 1] != '/') + strlcat(filename, "/", len); + ptr = filename + strlen(filename); + plen = len - (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 */ + +#ifdef HAVE_PHP + if (status) + { + strlcpy(ptr, "index.php", plen); + status = stat(filename, filestats); + } +#endif /* HAVE_PHP */ + +#ifdef HAVE_PYTHON + if (status) + { + strlcpy(ptr, "index.pyc", plen); + status = stat(filename, filestats); + } + + if (status) + { + strlcpy(ptr, "index.py", plen); + status = stat(filename, filestats); + } +#endif /* HAVE_PYTHON */ } - LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d", + LogMessage(L_DEBUG2, "get_file: %d filename=%s size=%d", con->http.fd, filename, status ? -1 : (int)filestats->st_size); if (status) @@ -2747,7 +2661,7 @@ get_file(client_t *con, /* I - Client connection */ static http_status_t /* O - Status */ install_conf_file(client_t *con) /* I - Connection */ { - FILE *in, /* Input file */ + cups_file_t *in, /* Input file */ *out; /* Output file */ char buffer[1024]; /* Copy buffer */ int bytes; /* Number of bytes */ @@ -2777,43 +2691,43 @@ install_conf_file(client_t *con) /* I - Connection */ { confinfo.st_uid = User; confinfo.st_gid = Group; - confinfo.st_mode = 0640; + confinfo.st_mode = ConfigFilePerm; } /* * Open the request file and new config file... */ - if ((in = fopen(con->filename, "rb")) == NULL) + if ((in = cupsFileOpen(con->filename, "rb")) == NULL) { LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s", con->filename, strerror(errno)); return (HTTP_SERVER_ERROR); } - if ((out = fopen(newfile, "wb")) == NULL) + if ((out = cupsFileOpen(newfile, "wb")) == NULL) { - fclose(in); + cupsFileClose(in); LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s", newfile, strerror(errno)); return (HTTP_SERVER_ERROR); } - fchmod(fileno(out), confinfo.st_mode); - fchown(fileno(out), confinfo.st_uid, confinfo.st_gid); + fchmod(cupsFileNumber(out), confinfo.st_mode); + fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid); /* * Copy from the request to the new config file... */ - while ((bytes = fread(buffer, 1, sizeof(buffer), in)) > 0) - if (fwrite(buffer, 1, bytes, out) < bytes) + while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0) + if (cupsFileWrite(out, buffer, bytes) < bytes) { LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s", newfile, strerror(errno)); - fclose(in); - fclose(out); + cupsFileClose(in); + cupsFileClose(out); unlink(newfile); return (HTTP_SERVER_ERROR); @@ -2823,8 +2737,8 @@ install_conf_file(client_t *con) /* I - Connection */ * Close the files... */ - fclose(in); - if (fclose(out)) + cupsFileClose(in); + if (cupsFileClose(out)) { LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s", newfile, strerror(errno)); @@ -2884,7 +2798,11 @@ install_conf_file(client_t *con) /* I - Connection */ */ if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0) - NeedReload = TRUE; + NeedReload = RELOAD_CUPSD; + else + NeedReload = RELOAD_ALL; + + ReloadTime = time(NULL); /* * Return that the file was created successfully... @@ -2894,6 +2812,41 @@ install_conf_file(client_t *con) /* I - Connection */ } +/* + * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. ".."). + */ + +static int /* O - 0 if relative, 1 if absolute */ +is_path_absolute(const char *path) /* I - Input path */ +{ + /* + * Check for a leading slash... + */ + + if (path[0] != '/') + return (0); + + /* + * Check for "/.." in the path... + */ + + while ((path = strstr(path, "/..")) != NULL) + { + if (!path[3] || path[3] == '/') + return (0); + + path ++; + } + + /* + * If we haven't found any relative paths, return 1 indicating an + * absolute path... + */ + + return (1); +} + + /* * 'pipe_command()' - Pipe the output of a command to the remote client. */ @@ -2909,7 +2862,6 @@ pipe_command(client_t *con, /* I - Client connection */ int pid; /* Process ID */ char *commptr; /* Command string pointer */ char *uriptr; /* URI string pointer */ - int fd; /* Looping var */ int fds[2]; /* Pipe FDs */ int argc; /* Number of arguments */ int envc; /* Number of environment variables */ @@ -2925,51 +2877,146 @@ pipe_command(client_t *con, /* I - Client connection */ ipp_port[1024], /* IPP_PORT environment variable */ lang[1024], /* LANG environment variable */ ld_library_path[1024], /* LD_LIBRARY_PATH environment variable */ + ld_preload[1024], /* LD_PRELOAD environment variable */ + dyld_library_path[1024],/* DYLD_LIBRARY_PATH environment variable */ + shlib_path[1024], /* SHLIB_PATH environment variable */ nlspath[1024], /* NLSPATH environment variable */ - query_string[10240], /* QUERY_STRING env variable */ + *query_string, /* QUERY_STRING env variable */ remote_addr[1024], /* REMOTE_ADDR environment variable */ remote_host[1024], /* REMOTE_HOST environment variable */ remote_user[1024], /* REMOTE_USER environment variable */ script_name[1024], /* SCRIPT_NAME environment variable */ server_name[1024], /* SERVER_NAME environment variable */ server_port[1024], /* SERVER_PORT environment variable */ - tmpdir[1024]; /* TMPDIR environment variable */ + tmpdir[1024], /* TMPDIR environment variable */ + vg_args[1024], /* VG_ARGS environment variable */ + ld_assume_kernel[1024]; /* LD_ASSUME_KERNEL environment variable */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* POSIX signal handler */ #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ + static const char * const locale_encodings[] = + { /* Locale charset names */ + "ASCII", "ISO8859-1", "ISO8859-2", "ISO8859-3", + "ISO8859-4", "ISO8859-5", "ISO8859-6", "ISO8859-7", + "ISO8859-8", "ISO8859-9", "ISO8859-10", "UTF-8", + "ISO8859-13", "ISO8859-14", "ISO8859-15", "CP874", + "CP1250", "CP1251", "CP1252", "CP1253", + "CP1254", "CP1255", "CP1256", "CP1257", + "CP1258", "KOI8R", "KOI8U", "ISO8859-11", + "ISO8859-16", "", "", "", + + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + + "CP932", "CP936", "CP949", "CP950", + "CP1361", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + + "EUC-CN", "EUC-JP", "EUC-KR", "EUC-TW" + }; + static const char * const encryptions[] = + { + "CUPS_ENCRYPTION=IfRequested", + "CUPS_ENCRYPTION=Never", + "CUPS_ENCRYPTION=Required", + "CUPS_ENCRYPTION=Always" + }; /* - * Copy the command string... + * Parse a copy of the options string, which is of the form: + * + * name argument+argument+argument + * name?argument+argument+argument + * name param=value¶m=value + * name?param=value¶m=value + * + * If the string contains an "=" character after the initial name, + * then we treat it as a HTTP GET form request and make a copy of + * the remaining string for the environment variable. + * + * The string is always parsed out as command-line arguments, to + * be consistent with Apache... */ - strlcpy(argbuf, options, sizeof(argbuf)); + LogMessage(L_DEBUG2, "pipe_command: command=\"%s\", options=\"%s\"", + command, options); - /* - * Parse the string; arguments can be separated by + and are terminated - * by ?... - */ + strlcpy(argbuf, options, sizeof(argbuf)); - argv[0] = argbuf; + argv[0] = argbuf; + query_string = NULL; for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++) - if (*commptr == ' ' || *commptr == '+') + { + /* + * Break arguments whenever we see a + or space... + */ + + if (*commptr == ' ' || *commptr == '+' || (*commptr == '?' && argc == 1)) { + /* + * Terminate the current string and skip trailing whitespace... + */ + *commptr++ = '\0'; while (*commptr == ' ') commptr ++; - if (*commptr != '\0') + /* + * If we don't have a blank string, save it as another argument... + */ + + if (*commptr) { argv[argc] = commptr; argc ++; } + else + break; + + /* + * If we see an "=" in the remaining string, make a copy of it since + * it will be query data... + */ + + if (argc == 2 && strchr(commptr, '=') && con->operation == HTTP_GET) + SetStringf(&query_string, "QUERY_STRING=%s", commptr); + + /* + * Don't skip the first non-blank character... + */ commptr --; } - else if (*commptr == '%') + else if (*commptr == '%' && isxdigit(commptr[1] & 255) && + isxdigit(commptr[2] & 255)) { + /* + * Convert the %xx notation to the individual character. + */ + if (commptr[1] >= '0' && commptr[1] <= '9') *commptr = (commptr[1] - '0') << 4; else @@ -2980,10 +3027,16 @@ pipe_command(client_t *con, /* I - Client connection */ else *commptr |= tolower(commptr[2]) - 'a' + 10; - strcpy(commptr + 1, commptr + 3); + cups_strcpy(commptr + 1, commptr + 3); + + /* + * Check for a %00 and break if that is the case... + */ + + if (!*commptr) + break; } - else if (*commptr == '?') - break; + } argv[argc] = NULL; @@ -2994,22 +3047,21 @@ pipe_command(client_t *con, /* I - Client connection */ * Setup the environment variables as needed... */ - snprintf(lang, sizeof(lang), "LANG=%s", - con->language ? con->language->language : "C"); + if (con->language) + snprintf(lang, sizeof(lang), "LANG=%s.%s", con->language->language, + locale_encodings[con->language->encoding]); + else + strcpy(lang, "LANG=C"); + + sprintf(ipp_port, "IPP_PORT=%d", LocalPort); #ifdef AF_INET6 if (con->http.hostaddr.addr.sa_family == AF_INET6) - { - sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv6.sin6_port)); sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.ipv6.sin6_port)); - } else #endif /* AF_INET6 */ - { - sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv4.sin_port)); sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.ipv4.sin_port)); - } if (strcmp(con->http.hostname, "localhost") == 0) strlcpy(server_name, "SERVER_NAME=localhost", sizeof(server_name)); @@ -3036,6 +3088,7 @@ pipe_command(client_t *con, /* I - Client connection */ else envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9"; envp[envc ++] = "REDIRECT_STATUS=1"; + envp[envc ++] = "CUPS_SERVER=localhost"; envp[envc ++] = ipp_port; envp[envc ++] = server_name; envp[envc ++] = server_port; @@ -3048,23 +3101,45 @@ pipe_command(client_t *con, /* I - Client connection */ envp[envc ++] = cups_datadir; envp[envc ++] = cups_serverroot; + if (getenv("VG_ARGS") != NULL) + { + snprintf(vg_args, sizeof(vg_args), "VG_ARGS=%s", getenv("VG_ARGS")); + envp[envc ++] = vg_args; + } + + if (getenv("LD_ASSUME_KERNEL") != NULL) + { + snprintf(ld_assume_kernel, sizeof(ld_assume_kernel), "LD_ASSUME_KERNEL=%s", + getenv("LD_ASSUME_KERNEL")); + envp[envc ++] = ld_assume_kernel; + } + if (getenv("LD_LIBRARY_PATH") != NULL) { snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH")); envp[envc ++] = ld_library_path; } - else if (getenv("DYLD_LIBRARY_PATH") != NULL) + + if (getenv("LD_PRELOAD") != NULL) + { + snprintf(ld_preload, sizeof(ld_preload), "LD_PRELOAD=%s", + getenv("LD_PRELOAD")); + envp[envc ++] = ld_preload; + } + + if (getenv("DYLD_LIBRARY_PATH") != NULL) { - snprintf(ld_library_path, sizeof(ld_library_path), "DYLD_LIBRARY_PATH=%s", + snprintf(dyld_library_path, sizeof(dyld_library_path), "DYLD_LIBRARY_PATH=%s", getenv("DYLD_LIBRARY_PATH")); - envp[envc ++] = ld_library_path; + envp[envc ++] = dyld_library_path; } - else if (getenv("SHLIB_PATH") != NULL) + + if (getenv("SHLIB_PATH") != NULL) { - snprintf(ld_library_path, sizeof(ld_library_path), "SHLIB_PATH=%s", + snprintf(shlib_path, sizeof(shlib_path), "SHLIB_PATH=%s", getenv("SHLIB_PATH")); - envp[envc ++] = ld_library_path; + envp[envc ++] = shlib_path; } if (getenv("NLSPATH") != NULL) @@ -3098,15 +3173,12 @@ pipe_command(client_t *con, /* I - Client connection */ LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]); envp[envc ++] = "REQUEST_METHOD=GET"; - if (*commptr) + if (query_string) { /* * Add GET form variables after ?... */ - *commptr++ = '\0'; - - snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr); envp[envc ++] = query_string; } } @@ -3126,27 +3198,32 @@ pipe_command(client_t *con, /* I - Client connection */ */ if (con->http.encryption == HTTP_ENCRYPT_ALWAYS) - { envp[envc ++] = "HTTPS=ON"; - envp[envc ++] = "CUPS_ENCRYPTION=Always"; - } + + envp[envc ++] = (char *)encryptions[LocalEncryption]; + + /* + * Terminate the environment array... + */ envp[envc] = NULL; if (LogLevel == L_DEBUG2) { for (i = 0; i < argc; i ++) - LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]); + LogMessage(L_DEBUG2, "pipe_command: argv[%d] = \"%s\"", i, argv[i]); for (i = 0; i < envc; i ++) - LogMessage(L_DEBUG2, "envp[%d] = \"%s\"", i, envp[i]); + LogMessage(L_DEBUG2, "pipe_command: envp[%d] = \"%s\"", i, envp[i]); } /* * Create a pipe for the output... */ - if (pipe(fds)) + if (cupsdOpenPipe(fds)) { + ClearString(&query_string); + LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s", argv[0], strerror(errno)); return (0); @@ -3168,7 +3245,7 @@ pipe_command(client_t *con, /* I - Client connection */ * Child comes here... Close stdin if necessary and dup the pipe to stdout. */ - if (getuid() == 0) + if (!RunUser) { /* * Running as root, so change to a non-priviledged user... @@ -3177,15 +3254,20 @@ pipe_command(client_t *con, /* I - Client connection */ if (setgid(Group)) exit(errno); + if (setgroups(1, &Group)) + exit(errno); + if (setuid(User)) exit(errno); } + else + { + /* + * Reset group membership to just the main one we belong to. + */ - /* - * Reset group membership to just the main one we belong to. - */ - - setgroups(0, NULL); + setgroups(1, &Group); + } /* * Update stdin/stdout/stderr... @@ -3205,13 +3287,6 @@ pipe_command(client_t *con, /* I - Client connection */ close(2); dup(CGIPipes[1]); - /* - * Close extra file descriptors... - */ - - for (fd = 3; fd < MaxFDs; fd ++) - close(fd); - /* * Change umask to restrict permissions on created files... */ @@ -3257,8 +3332,7 @@ pipe_command(client_t *con, /* I - Client connection */ LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0], strerror(errno)); - close(fds[0]); - close(fds[1]); + cupsdClosePipe(fds); pid = 0; } else @@ -3277,6 +3351,8 @@ pipe_command(client_t *con, /* I - Client connection */ ReleaseSignals(); + ClearString(&query_string); + return (pid); } @@ -3330,5 +3406,5 @@ CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */ /* - * End of "$Id: client.c,v 1.91.2.53 2003/03/14 21:43:30 mike Exp $". + * End of "$Id$". */