X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scheduler%2Fclient.c;h=de825609becaf707cdd59df3c2d563d34c4f766e;hb=b2e108950fe59ee63a1f6d2381d67d3f06999cab;hp=9bfa9c679d54b7ac08160894813f4197039e603c;hpb=43ed090d134f5d67d0f4b43d741856fb5b05d87b;p=thirdparty%2Fcups.git diff --git a/scheduler/client.c b/scheduler/client.c index 9bfa9c679..de825609b 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -1,9 +1,9 @@ /* - * "$Id: client.c,v 1.91.2.49 2003/03/13 04:19:41 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 * @@ -27,17 +27,19 @@ * CloseAllClients() - Close all remote clients immediately. * CloseClient() - Close a remote client. * EncryptClient() - Enable encryption for the client... + * IsCGI() - Is the resource a CGI script/program? * ReadClient() - Read data from a client. * SendCommand() - Send output from a command via HTTP. * 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. @@ -47,9 +49,9 @@ * Include necessary headers... */ +#include #include "cupsd.h" #include -#include /* @@ -62,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); @@ -91,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); /* @@ -109,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... @@ -161,7 +165,7 @@ AcceptClient(listener_t *lis) /* I - Listener socket */ return; } - + /* * Get the hostname or format the IP address as needed... */ @@ -351,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 */ @@ -367,14 +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); + LogMessage(L_DEBUG, "CloseClient: %d", con->http.fd); - ClearString(&con->filename); - ClearString(&con->command); - ClearString(&con->options); + partial = 0; #ifdef HAVE_SSL /* @@ -383,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); @@ -436,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... + */ + + if (NumClients == MaxClients) + ResumeListening(); + + /* + * Compact the list of clients as necessary... + */ - NumClients --; + NumClients --; - if (con < (Clients + NumClients)) - memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t)); + if (con < (Clients + NumClients)) + memmove(con, con + 1, (Clients + NumClients - con) * sizeof(client_t)); + } + + return (partial); } @@ -539,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)); @@ -547,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); @@ -569,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); } @@ -586,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); @@ -594,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; @@ -655,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; @@ -666,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); @@ -677,38 +729,182 @@ EncryptClient(client_t *con) /* I - Client to encrypt */ } +/* + * 'IsCGI()' - Is the resource a CGI script/program? + */ + +int /* O - 1 = CGI, 0 = file */ +IsCGI(client_t *con, /* I - Client connection */ + const char *filename, /* I - Real filename */ + struct stat *filestats, /* I - File information */ + mime_type_t *type) /* I - MIME type */ +{ + const char *options; /* Options on URL */ + + + LogMessage(L_DEBUG2, "IsCGI(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)\n", + con, filename, filestats, type ? type->super : "unknown", + type ? type->type : "unknown"); + + /* + * Get the options, if any... + */ + + if ((options = strchr(con->uri, '?')) != NULL) + options ++; + + /* + * Check for known types... + */ + + if (!type || strcasecmp(type->super, "application")) + { + LogMessage(L_DEBUG2, "IsCGI: Returning 0..."); + return (0); + } + + if (!strcasecmp(type->type, "x-httpd-cgi") && + (filestats->st_mode & 0111)) + { + /* + * "application/x-httpd-cgi" is a CGI script. + */ + + SetString(&con->command, filename); + + filename = strrchr(filename, '/') + 1; /* Filename always absolute */ + + if (options) + SetStringf(&con->options, "%s %s", filename, options); + else + SetStringf(&con->options, "%s", filename); + + LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", + con->command, con->options); + + return (1); + } +#ifdef HAVE_JAVA + else if (!strcasecmp(type->type, "x-httpd-java")) + { + /* + * "application/x-httpd-java" is a Java servlet. + */ + + SetString(&con->command, CUPS_JAVA); + + if (options) + SetStringf(&con->options, "java %s %s", filename, options); + else + SetStringf(&con->options, "java %s", filename); + + LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", + con->command, con->options); + + return (1); + } +#endif /* HAVE_JAVA */ +#ifdef HAVE_PERL + else if (!strcasecmp(type->type, "x-httpd-perl")) + { + /* + * "application/x-httpd-perl" is a Perl page. + */ + + SetString(&con->command, CUPS_PERL); + + if (options) + SetStringf(&con->options, "perl %s %s", filename, options); + else + SetStringf(&con->options, "perl %s", filename); + + LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", + con->command, con->options); + + return (1); + } +#endif /* HAVE_PERL */ +#ifdef HAVE_PHP + else if (!strcasecmp(type->type, "x-httpd-php")) + { + /* + * "application/x-httpd-php" is a PHP page. + */ + + SetString(&con->command, CUPS_PHP); + + if (options) + SetStringf(&con->options, "php %s %s", filename, options); + else + SetStringf(&con->options, "php %s", filename); + + LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", + con->command, con->options); + + return (1); + } +#endif /* HAVE_PHP */ +#ifdef HAVE_PYTHON + else if (!strcasecmp(type->type, "x-httpd-python")) + { + /* + * "application/x-httpd-python" is a Python page. + */ + + SetString(&con->command, CUPS_PYTHON); + + if (options) + SetStringf(&con->options, "python %s %s", filename, options); + else + SetStringf(&con->options, "python %s", filename); + + LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", + con->command, con->options); + + return (1); + } +#endif /* HAVE_PYTHON */ + + LogMessage(L_DEBUG2, "IsCGI: Returning 0..."); + + return (0); +} + + /* * '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) @@ -720,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)); } /* @@ -744,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'; @@ -766,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) @@ -793,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; } @@ -833,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)); } /* @@ -867,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); @@ -896,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; @@ -911,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); @@ -927,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) { @@ -942,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 && @@ -957,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"); @@ -970,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 */ } @@ -983,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 { @@ -1015,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"); @@ -1026,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) @@ -1069,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; } @@ -1091,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) { @@ -1106,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); @@ -1136,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; } @@ -1153,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); @@ -1184,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 { @@ -1197,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; @@ -1223,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; } @@ -1237,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; } @@ -1265,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) { @@ -1284,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) @@ -1302,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)) { @@ -1319,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; @@ -1342,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; } @@ -1367,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... @@ -1381,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 && @@ -1419,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; } @@ -1439,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); } @@ -1462,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; } @@ -1473,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); } @@ -1496,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; @@ -1547,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) @@ -1573,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)); } } @@ -1593,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) @@ -1606,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)); } /* @@ -1629,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); @@ -1650,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; @@ -1668,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... @@ -1676,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) @@ -1715,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) @@ -1752,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); @@ -1768,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) @@ -1779,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); @@ -1799,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); } @@ -1825,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); @@ -1856,6 +1983,7 @@ SendCommand(client_t *con, return (0); } + con->file_ready = 0; con->got_fields = 0; con->field_col = 0; @@ -1878,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 @@ -1960,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); @@ -2005,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) @@ -2033,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); } @@ -2058,32 +2187,31 @@ SendHeader(client_t *con, /* I - Client to send to */ /* - * 'ShutdownClient()' - Shutdown the receiving end of a connection. + * 'UpdateCGI()' - Read status messages from CGI scripts and programs. */ void -ShutdownClient(client_t *con) /* I - Client connection */ +UpdateCGI(void) { - /* - * Shutdown the receiving end of the socket, since the client - * still needs to read the error message... - */ + char *ptr, /* Pointer to end of line in buffer */ + message[1024]; /* Pointer to message text */ + int loglevel; /* Log level for 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; + while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel, + message, sizeof(message))) != NULL) + if (!strchr(CGIStatusBuffer->buffer, '\n')) + break; - LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...", - con->http.fd); + if (ptr == NULL) + { + /* + * Fatal error on pipe - should never happen! + */ - FD_CLR(con->http.fd, InputSet); + LogMessage(L_CRIT, "UpdateCGI: error reading from CGI error pipe - %s", + strerror(errno)); + } } @@ -2100,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); @@ -2111,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) { /* @@ -2131,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; /* @@ -2195,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); @@ -2246,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); @@ -2276,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') @@ -2300,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); @@ -2345,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; } @@ -2370,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); } @@ -2388,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 */ /* @@ -2412,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 @@ -2431,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); } } @@ -2441,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) @@ -2466,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 */ @@ -2496,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); @@ -2542,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)); @@ -2603,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... @@ -2613,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. */ @@ -2624,65 +2858,165 @@ pipe_command(client_t *con, /* I - Client connection */ char *command, /* I - Command to run */ char *options) /* I - Options for command */ { + int i; /* Looping var */ int pid; /* Process ID */ char *commptr; /* Command string pointer */ - int fd; /* Looping var */ + char *uriptr; /* URI string pointer */ int fds[2]; /* Pipe FDs */ int argc; /* Number of arguments */ int envc; /* Number of environment variables */ char argbuf[10240], /* Argument buffer */ *argv[100], /* Argument strings */ *envp[100]; /* Environment variables */ - char lang[1024], /* LANG env variable */ - content_length[1024], /* CONTENT_LENGTH env variable */ - content_type[1024], /* CONTENT_TYPE env variable */ - ipp_port[1024], /* Default listen port */ - server_port[1024], /* Default server port */ - server_name[1024], /* Default listen hostname */ - remote_host[1024], /* REMOTE_HOST env variable */ - remote_user[1024], /* REMOTE_USER env variable */ - tmpdir[1024], /* TMPDIR environment variable */ - ldpath[1024], /* LD_LIBRARY_PATH environment variable */ + char content_length[1024], /* CONTENT_LENGTH environment variable */ + content_type[1024], /* CONTENT_TYPE environment variable */ + cups_datadir[1024], /* CUPS_DATADIR environment variable */ + cups_serverroot[1024], /* CUPS_SERVERROOT environment variable */ + http_cookie[1024], /* HTTP_COOKIE environment variable */ + http_user_agent[1024], /* HTTP_USER_AGENT environment variable */ + 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 */ - datadir[1024], /* CUPS_DATADIR environment variable */ - root[1024], /* CUPS_SERVERROOT 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 */ + 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 @@ -2693,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; @@ -2707,72 +3047,125 @@ 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)); else snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName); snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname); + strcpy(remote_addr, "REMOTE_ADDR="); + httpAddrString(&(con->http.hostaddr), remote_addr + 12, + sizeof(remote_addr) - 12); snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username); snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir); - snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir); - snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot); + snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir); + snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s", ServerRoot); + + envc = 0; + + envp[envc ++] = "PATH=/bin:/usr/bin"; + envp[envc ++] = "SERVER_SOFTWARE=CUPS/1.1"; + envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1"; + if (con->http.version == HTTP_1_1) + envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1"; + else if (con->http.version == HTTP_1_0) + envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0"; + 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; + envp[envc ++] = remote_addr; + envp[envc ++] = remote_host; + envp[envc ++] = remote_user; + envp[envc ++] = lang; + envp[envc ++] = TZ; + envp[envc ++] = tmpdir; + 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(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s", + { + snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH")); - else if (getenv("DYLD_LIBRARY_PATH") != NULL) - snprintf(ldpath, sizeof(ldpath), "DYLD_LIBRARY_PATH=%s", + envp[envc ++] = ld_library_path; + } + + 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(dyld_library_path, sizeof(dyld_library_path), "DYLD_LIBRARY_PATH=%s", getenv("DYLD_LIBRARY_PATH")); - else if (getenv("SHLIB_PATH") != NULL) - snprintf(ldpath, sizeof(ldpath), "SHLIB_PATH=%s", + envp[envc ++] = dyld_library_path; + } + + if (getenv("SHLIB_PATH") != NULL) + { + snprintf(shlib_path, sizeof(shlib_path), "SHLIB_PATH=%s", getenv("SHLIB_PATH")); - else - ldpath[0] = '\0'; + envp[envc ++] = shlib_path; + } if (getenv("NLSPATH") != NULL) + { snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH")); - else - nlspath[0] = '\0'; - - envp[0] = "PATH=/bin:/usr/bin"; - envp[1] = "SERVER_SOFTWARE=CUPS/1.2"; - envp[2] = "GATEWAY_INTERFACE=CGI/1.1"; - envp[3] = "SERVER_PROTOCOL=HTTP/1.1"; - envp[4] = ipp_port; - envp[5] = server_name; - envp[6] = server_port; - envp[7] = remote_host; - envp[8] = remote_user; - envp[9] = lang; - envp[10] = TZ; - envp[11] = tmpdir; - envp[12] = datadir; - envp[13] = root; - - envc = 14; - - if (ldpath[0]) - envp[envc ++] = ldpath; - - if (nlspath[0]) envp[envc ++] = nlspath; + } + + if (con->http.cookie) + { + snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s", + con->http.cookie); + envp[envc ++] = http_cookie; + } + + if (con->http.fields[HTTP_FIELD_USER_AGENT][0]) + { + snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s", + con->http.fields[HTTP_FIELD_USER_AGENT]); + envp[envc ++] = http_user_agent; + } + + snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri); + if ((uriptr = strchr(script_name, '?')) != NULL) + *uriptr = '\0'; + envp[envc ++] = script_name; if (con->operation == HTTP_GET) { @@ -2780,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; } } @@ -2808,19 +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, "pipe_command: argv[%d] = \"%s\"", i, argv[i]); + for (i = 0; i < envc; 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); @@ -2842,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... @@ -2851,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... @@ -2877,14 +3285,7 @@ pipe_command(client_t *con, /* I - Client connection */ exit(errno); close(2); - open("/dev/null", O_WRONLY); - - /* - * Close extra file descriptors... - */ - - for (fd = 3; fd < MaxFDs; fd ++) - close(fd); + dup(CGIPipes[1]); /* * Change umask to restrict permissions on created files... @@ -2931,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 @@ -2951,6 +3351,8 @@ pipe_command(client_t *con, /* I - Client connection */ ReleaseSignals(); + ClearString(&query_string); + return (pid); } @@ -3004,5 +3406,5 @@ CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */ /* - * End of "$Id: client.c,v 1.91.2.49 2003/03/13 04:19:41 mike Exp $". + * End of "$Id$". */