X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scheduler%2Fclient.c;h=3e3845e058094ce5b7e92966350db076a36ec320;hb=503b54c9302c8de6207e079a80a89a787eb612ea;hp=25be7a491b0ff4f52f6b1871b9d83b145c01872b;hpb=1f0275e3cc0bc5fbe11e3bffd54581aa05c9c764;p=thirdparty%2Fcups.git diff --git a/scheduler/client.c b/scheduler/client.c index 25be7a491..02e5fafb8 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -1,80 +1,33 @@ /* - * "$Id: client.c 7673 2008-06-18 22:31:26Z mike $" + * Client routines for the CUPS scheduler. * - * Client routines for the Common UNIX Printing System (CUPS) scheduler. + * Copyright 2007-2015 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. * - * Copyright 2007-2008 by Apple Inc. - * Copyright 1997-2007 by Easy Software Products, all rights reserved. + * This file contains Kerberos support code, copyright 2006 by + * Jelmer Vernooij. * - * This file contains Kerberos support code, copyright 2006 by - * Jelmer Vernooij. - * - * These coded instructions, statements, and computer programs are the - * property of Apple Inc. and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "LICENSE.txt" - * which should have been included with this file. If this file is - * file is missing or damaged, see the license at "http://www.cups.org/". - * - * Contents: - * - * cupsdAcceptClient() - Accept a new client. - * cupsdCloseAllClients() - Close all remote clients immediately. - * cupsdCloseClient() - Close a remote client. - * cupsdFlushHeader() - Flush the header fields to the client. - * cupsdReadClient() - Read data from a client. - * cupsdSendCommand() - Send output from a command via HTTP. - * cupsdSendError() - Send an error message via HTTP. - * cupsdSendHeader() - Send an HTTP request. - * cupsdUpdateCGI() - Read status messages from CGI scripts and programs. - * cupsdWriteClient() - Write data to a client as needed. - * check_if_modified() - Decode an "If-Modified-Since" line. - * compare_clients() - Compare two client connections. - * encrypt_client() - Enable encryption for the client... - * get_cdsa_certificate() - Convert a keychain name into the CFArrayRef - * required by SSLSetCertificate. - * get_file() - Get a filename and state info. - * install_conf_file() - Install a configuration file. - * is_cgi() - Is the resource a CGI script/program? - * is_path_absolute() - Is a path absolute and free of relative elements. - * make_certificate() - Make a self-signed SSL/TLS certificate. - * pipe_command() - Pipe the output of a command to the remote client. - * write_file() - Send a file via HTTP. - * write_pipe() - Flag that data is available on the CGI pipe. + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". */ /* * Include necessary headers... */ +#define _CUPS_NO_DEPRECATED +#define _HTTP_NO_PRIVATE #include "cupsd.h" -#ifdef HAVE_CDSASSL -# include -# ifdef HAVE_SECIDENTITYSEARCHPRIV_H -# include -# else /* Declare prototype for function in that header... */ -extern OSStatus SecIdentitySearchCreateWithPolicy(SecPolicyRef policy, - CFStringRef idString, CSSM_KEYUSE keyUsage, - CFTypeRef keychainOrArray, - Boolean returnOnlyValidIdentities, - SecIdentitySearchRef* searchRef); -# endif /* HAVE_SECIDENTITYSEARCHPRIV_H */ -# ifdef HAVE_SECPOLICYPRIV_H -# include -# else /* Declare prototype for function in that header... */ -extern OSStatus SecPolicySetValue(SecPolicyRef policyRef, - const CSSM_DATA *value); -# endif /* HAVE_SECPOLICYPRIV_H */ -# ifdef HAVE_SECBASEPRIV_H -# include -# else /* Declare prototype for function in that header... */ -extern const char *cssmErrorString(int error); -# endif /* HAVE_SECBASEPRIV_H */ -#endif /* HAVE_CDSASSL */ - -#ifdef HAVE_GNUTLS -# include -#endif /* HAVE_GNUTLS */ +#ifdef __APPLE__ +# include +#endif /* __APPLE__ */ +#ifdef HAVE_TCPD_H +# include +#endif /* HAVE_TCPD_H */ /* @@ -86,22 +39,17 @@ static int check_if_modified(cupsd_client_t *con, static int compare_clients(cupsd_client_t *a, cupsd_client_t *b, void *data); #ifdef HAVE_SSL -static int encrypt_client(cupsd_client_t *con); +static int cupsd_start_tls(cupsd_client_t *con, http_encryption_t e); #endif /* HAVE_SSL */ -#ifdef HAVE_CDSASSL -static CFArrayRef get_cdsa_certificate(cupsd_client_t *con); -#endif /* HAVE_CDSASSL */ static char *get_file(cupsd_client_t *con, struct stat *filestats, - char *filename, int len); -static http_status_t install_conf_file(cupsd_client_t *con); + char *filename, size_t len); +static http_status_t install_cupsd_conf(cupsd_client_t *con); static int is_cgi(cupsd_client_t *con, const char *filename, struct stat *filestats, mime_type_t *type); static int is_path_absolute(const char *path); -#ifdef HAVE_SSL -static int make_certificate(cupsd_client_t *con); -#endif /* HAVE_SSL */ static int pipe_command(cupsd_client_t *con, int infile, int *outfile, char *command, char *options, int root); +static int valid_host(cupsd_client_t *con); static int write_file(cupsd_client_t *con, http_status_t code, char *filename, char *type, struct stat *filestats); @@ -115,21 +63,20 @@ static void write_pipe(cupsd_client_t *con); void cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ { + const char *hostname; /* Hostname of client */ + char name[256]; /* Hostname of client */ int count; /* Count of connections on a host */ - int val; /* Parameter value */ cupsd_client_t *con, /* New client pointer */ *tempcon; /* Temporary client pointer */ - http_addrlist_t *addrlist, /* List of adddresses for host */ - *addr; /* Current address */ socklen_t addrlen; /* Length of address */ - char *hostname; /* Hostname for address */ http_addr_t temp; /* Temporary address variable */ static time_t last_dos = 0; /* Time of last DoS attack */ +#ifdef HAVE_TCPD_H + struct request_info wrap_req; /* TCP wrappers request information */ +#endif /* HAVE_TCPD_H */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdAcceptClient(lis=%p) %d Clients = %d", - lis, lis->fd, cupsArrayCount(Clients)); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAcceptClient(lis=%p(%d)) Clients=%d", lis, lis->fd, cupsArrayCount(Clients)); /* * Make sure we don't have a full set of clients already... @@ -171,18 +118,14 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ return; } - con->http.activity = time(NULL); - con->file = -1; - con->http.hostaddr = &(con->clientaddr); - /* * Accept the client and get the remote address... */ - addrlen = sizeof(http_addr_t); + con->number = ++ LastClientNumber; + con->file = -1; - if ((con->http.fd = accept(lis->fd, (struct sockaddr *)con->http.hostaddr, - &addrlen)) < 0) + if ((con->http = httpAcceptConnection(lis->fd, 0)) == NULL) { if (errno == ENFILE || errno == EMFILE) cupsdPauseListening(); @@ -194,29 +137,16 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ return; } -#ifdef AF_INET6 - if (lis->address.addr.sa_family == AF_INET6) - { - /* - * Save the connected port number... - */ + /* + * Save the connected address and port number... + */ - con->http.hostaddr->ipv6.sin6_port = lis->address.ipv6.sin6_port; + addrlen = sizeof(con->clientaddr); - /* - * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we - * can more easily use... - */ + if (getsockname(httpGetFd(con->http), (struct sockaddr *)&con->clientaddr, &addrlen) || addrlen == 0) + con->clientaddr = lis->address; - if (con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0] == 0 && - con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1] == 0 && - ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]) == 0xffff) - con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2] = 0; - } - else -#endif /* AF_INET6 */ - if (lis->address.addr.sa_family == AF_INET) - con->http.hostaddr->ipv4.sin_port = lis->address.ipv4.sin_port; + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Server address is \"%s\".", httpAddrString(&con->clientaddr, name, sizeof(name))); /* * Check the number of clients on the same address... @@ -225,7 +155,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ for (count = 0, tempcon = (cupsd_client_t *)cupsArrayFirst(Clients); tempcon; tempcon = (cupsd_client_t *)cupsArrayNext(Clients)) - if (httpAddrEqual(tempcon->http.hostaddr, con->http.hostaddr)) + if (httpAddrEqual(httpGetAddress(tempcon->http), httpGetAddress(con->http))) { count ++; if (count >= MaxClientsPerHost) @@ -239,16 +169,12 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ last_dos = time(NULL); cupsdLogMessage(CUPSD_LOG_WARN, "Possible DoS attack - more than %d clients connecting " - "from %s!", - MaxClientsPerHost, con->http.hostname); + "from %s.", + MaxClientsPerHost, + httpGetHostname(con->http, name, sizeof(name))); } -#ifdef WIN32 - closesocket(con->http.fd); -#else - close(con->http.fd); -#endif /* WIN32 */ - + httpClose(con->http); free(con); return; } @@ -257,40 +183,10 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * Get the hostname or format the IP address as needed... */ - if (httpAddrLocalhost(con->http.hostaddr)) - { - /* - * Map accesses from the loopback interface to "localhost"... - */ - - strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname)); - hostname = con->http.hostname; - } + if (HostNameLookups) + hostname = httpResolveHostname(con->http, NULL, 0); else - { - /* - * Map accesses from the same host to the server name. - */ - - for (addr = ServerAddrs; addr; addr = addr->next) - if (httpAddrEqual(con->http.hostaddr, &(addr->addr))) - break; - - if (addr) - { - strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname)); - hostname = con->http.hostname; - } - else if (HostNameLookups) - hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname, - sizeof(con->http.hostname)); - else - { - hostname = NULL; - httpAddrString(con->http.hostaddr, con->http.hostname, - sizeof(con->http.hostname)); - } - } + hostname = httpGetHostname(con->http, NULL, 0); if (hostname == NULL && HostNameLookups == 2) { @@ -298,19 +194,11 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * Can't have an unresolved IP address with double-lookups enabled... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdAcceptClient: Closing connection %d...", - con->http.fd); + httpClose(con->http); -#ifdef WIN32 - closesocket(con->http.fd); -#else - close(con->http.fd); -#endif /* WIN32 */ - - cupsdLogMessage(CUPSD_LOG_WARN, + cupsdLogClient(con, CUPSD_LOG_WARN, "Name lookup failed - connection from %s closed!", - con->http.hostname); + httpGetHostname(con->http, NULL, 0)); free(con); return; @@ -322,14 +210,17 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * Do double lookups as needed... */ - if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL)) != NULL) + http_addrlist_t *addrlist, /* List of addresses */ + *addr; /* Current address */ + + if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, NULL)) != NULL) { /* * See if the hostname maps to the same IP address... */ for (addr = addrlist; addr; addr = addr->next) - if (httpAddrEqual(con->http.hostaddr, &(addr->addr))) + if (httpAddrEqual(httpGetAddress(con->http), &(addr->addr))) break; } else @@ -344,103 +235,117 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * with double-lookups enabled... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdAcceptClient: Closing connection %d...", - con->http.fd); + httpClose(con->http); -#ifdef WIN32 - closesocket(con->http.fd); -#else - close(con->http.fd); -#endif /* WIN32 */ - - cupsdLogMessage(CUPSD_LOG_WARN, + cupsdLogClient(con, CUPSD_LOG_WARN, "IP lookup failed - connection from %s closed!", - con->http.hostname); + httpGetHostname(con->http, NULL, 0)); free(con); return; } } -#ifdef AF_INET6 - if (con->http.hostaddr->addr.sa_family == AF_INET6) - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv6)", - con->http.fd, con->http.hostname, - ntohs(con->http.hostaddr->ipv6.sin6_port)); - else -#endif /* AF_INET6 */ +#ifdef HAVE_TCPD_H + /* + * See if the connection is denied by TCP wrappers... + */ + + request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, httpGetFd(con->http), + NULL); + fromhost(&wrap_req); + + if (!hosts_access(&wrap_req)) + { + httpClose(con->http); + + cupsdLogClient(con, CUPSD_LOG_WARN, + "Connection from %s refused by /etc/hosts.allow and " + "/etc/hosts.deny rules.", httpGetHostname(con->http, NULL, 0)); + free(con); + return; + } +#endif /* HAVE_TCPD_H */ + #ifdef AF_LOCAL - if (con->http.hostaddr->addr.sa_family == AF_LOCAL) - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s (Domain)", - con->http.fd, con->http.hostname); + if (httpAddrFamily(httpGetAddress(con->http)) == AF_LOCAL) + { +# ifdef __APPLE__ + socklen_t peersize; /* Size of peer credentials */ + pid_t peerpid; /* Peer process ID */ + char peername[256]; /* Name of process */ + + peersize = sizeof(peerpid); + if (!getsockopt(httpGetFd(con->http), SOL_LOCAL, LOCAL_PEERPID, &peerpid, + &peersize)) + { + if (!proc_name((int)peerpid, peername, sizeof(peername))) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Accepted from %s (Domain ???[%d])", + httpGetHostname(con->http, NULL, 0), (int)peerpid); + else + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Accepted from %s (Domain %s[%d])", + httpGetHostname(con->http, NULL, 0), peername, (int)peerpid); + } + else +# endif /* __APPLE__ */ + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Accepted from %s (Domain)", + httpGetHostname(con->http, NULL, 0)); + } else #endif /* AF_LOCAL */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv4)", - con->http.fd, con->http.hostname, - ntohs(con->http.hostaddr->ipv4.sin_port)); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Accepted from %s:%d (IPv%d)", + httpGetHostname(con->http, NULL, 0), + httpAddrPort(httpGetAddress(con->http)), + httpAddrFamily(httpGetAddress(con->http)) == AF_INET ? 4 : 6); /* * Get the local address the client connected to... */ addrlen = sizeof(temp); - if (getsockname(con->http.fd, (struct sockaddr *)&temp, &addrlen)) + if (getsockname(httpGetFd(con->http), (struct sockaddr *)&temp, &addrlen)) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get local address - %s", - strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to get local address - %s", + strerror(errno)); - strcpy(con->servername, "localhost"); + strlcpy(con->servername, "localhost", sizeof(con->servername)); + con->serverport = LocalPort; + } +#ifdef AF_LOCAL + else if (httpAddrFamily(&temp) == AF_LOCAL) + { + strlcpy(con->servername, "localhost", sizeof(con->servername)); con->serverport = LocalPort; } +#endif /* AF_LOCAL */ else { -#ifdef AF_INET6 - if (temp.addr.sa_family == AF_INET6) - { - httpAddrLookup(&temp, con->servername, sizeof(con->servername)); - con->serverport = ntohs(lis->address.ipv6.sin6_port); - } - else -#endif /* AF_INET6 */ - if (temp.addr.sa_family == AF_INET) - { + if (httpAddrLocalhost(&temp)) + strlcpy(con->servername, "localhost", sizeof(con->servername)); + else if (HostNameLookups) httpAddrLookup(&temp, con->servername, sizeof(con->servername)); - con->serverport = ntohs(lis->address.ipv4.sin_port); - } else - { - strcpy(con->servername, "localhost"); - con->serverport = LocalPort; - } - } - - cupsArrayAdd(Clients, con); + httpAddrString(&temp, con->servername, sizeof(con->servername)); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdAcceptClient: %d connected to server on %s:%d", - con->http.fd, con->servername, con->serverport); + con->serverport = httpAddrPort(&(lis->address)); + } /* - * Using TCP_NODELAY improves responsiveness, especially on systems - * with a slow loopback interface... Since we write large buffers - * when sending print files and requests, there shouldn't be any - * performance penalty for this... + * Add the connection to the array of active clients... */ - val = 1; - setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); + cupsArrayAdd(Clients, con); /* - * Close this file on all execs... + * Add the socket to the server select. */ - fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC); - - /* - * Add the socket to the select() input mask. - */ + cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, NULL, + con); - cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for request."); /* * Temporarily suspend accept()'s until we lose a client... @@ -454,15 +359,13 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * See if we are connecting on a secure port... */ - if (lis->encryption == HTTP_ENCRYPT_ALWAYS) + if (lis->encryption == HTTP_ENCRYPTION_ALWAYS) { /* * https connection; go secure... */ - con->http.encryption = HTTP_ENCRYPT_ALWAYS; - - if (!encrypt_client(con)) + if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS)) cupsdCloseClient(con); } else @@ -481,10 +384,13 @@ cupsdCloseAllClients(void) cupsd_client_t *con; /* Current client */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d", cupsArrayCount(Clients)); + for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; con = (cupsd_client_t *)cupsArrayNext(Clients)) - cupsdCloseClient(con); + if (cupsdCloseClient(con)) + cupsdCloseClient(con); } @@ -496,101 +402,17 @@ int /* O - 1 if partial close, 0 if fully closed */ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ { int partial; /* Do partial close for SSL? */ -#ifdef HAVE_LIBSSL - SSL_CTX *context; /* Context for encryption */ - SSL *conn; /* Connection for encryption */ - unsigned long error; /* Error code */ -#elif defined(HAVE_GNUTLS) - http_tls_t *conn; /* TLS connection information */ - int error; /* Error code */ - gnutls_certificate_server_credentials *credentials; - /* TLS credentials */ -# elif defined(HAVE_CDSASSL) - http_tls_t *conn; /* CDSA connection information */ -#endif /* HAVE_LIBSSL */ - - - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCloseClient: %d", con->http.fd); - - /* - * Flush pending writes before closing... - */ - httpFlushWrite(HTTP(con)); - partial = 0; + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing connection."); -#ifdef HAVE_SSL /* - * Shutdown encryption as needed... + * Flush pending writes before closing... */ - if (con->http.tls) - { - partial = 1; - -# ifdef HAVE_LIBSSL - conn = (SSL *)(con->http.tls); - context = SSL_get_SSL_CTX(conn); - - switch (SSL_shutdown(conn)) - { - case 1 : - cupsdLogMessage(CUPSD_LOG_DEBUG, - "SSL shutdown successful!"); - break; - case -1 : - cupsdLogMessage(CUPSD_LOG_ERROR, - "Fatal error during SSL shutdown!"); - default : - while ((error = ERR_get_error()) != 0) - cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s", - ERR_error_string(error, NULL)); - break; - } - - SSL_CTX_free(context); - SSL_free(conn); - -# elif defined(HAVE_GNUTLS) - conn = (http_tls_t *)(con->http.tls); - credentials = (gnutls_certificate_server_credentials *)(conn->credentials); - - error = gnutls_bye(conn->session, GNUTLS_SHUT_WR); - switch (error) - { - case GNUTLS_E_SUCCESS: - cupsdLogMessage(CUPSD_LOG_DEBUG, - "SSL shutdown successful!"); - break; - default: - cupsdLogMessage(CUPSD_LOG_ERROR, - "SSL shutdown failed: %s", gnutls_strerror(error)); - break; - } - - gnutls_deinit(conn->session); - gnutls_certificate_free_credentials(*credentials); - free(credentials); - free(conn); + httpFlushWrite(con->http); -# elif defined(HAVE_CDSASSL) - conn = (http_tls_t *)(con->http.tls); - - while (SSLClose(conn->session) == errSSLWouldBlock) - usleep(1000); - - SSLDisposeContext(conn->session); - - if (conn->certsArray) - CFRelease(conn->certsArray); - - free(conn); -# endif /* HAVE_LIBSSL */ - - con->http.tls = NULL; - } -#endif /* HAVE_SSL */ + partial = 0; if (con->pipe_pid != 0) { @@ -598,9 +420,6 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ * Stop any CGI process... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdCloseClient: %d Killing process ID %d...", - con->http.fd, con->pipe_pid); cupsdEndProcess(con->pipe_pid, 1); con->pipe_pid = 0; } @@ -609,10 +428,6 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ { cupsdRemoveSelect(con->file); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdCloseClient: %d Closing data file %d.", - con->http.fd, con->file); - close(con->file); con->file = -1; } @@ -621,19 +436,31 @@ cupsdCloseClient(cupsd_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 (httpGetFd(con->http) >= 0) { cupsArrayRemove(ActiveClients, con); cupsdSetBusyState(); +#ifdef HAVE_SSL + /* + * Shutdown encryption as needed... + */ + + if (httpIsEncrypted(con->http)) + partial = 1; +#endif /* HAVE_SSL */ + if (partial) { /* * Only do a partial close so that the encrypted client gets everything. */ - shutdown(con->http.fd, 0); - cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con); + httpShutdown(con->http); + cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, + NULL, con); + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for socket close."); } else { @@ -641,9 +468,9 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ * Shut the socket down fully... */ - cupsdRemoveSelect(con->http.fd); - close(con->http.fd); - con->http.fd = -1; + cupsdRemoveSelect(httpGetFd(con->http)); + httpClose(con->http); + con->http = NULL; } } @@ -653,12 +480,16 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ * Free memory... */ - if (con->http.input_set) - free(con->http.input_set); + cupsdRemoveSelect(httpGetFd(con->http)); - httpClearCookie(HTTP(con)); + httpClose(con->http); + + if (con->filename) + { + unlink(con->filename); + cupsdClearString(&con->filename); + } - cupsdClearString(&con->filename); cupsdClearString(&con->command); cupsdClearString(&con->options); cupsdClearString(&con->query_string); @@ -710,21 +541,6 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ } -/* - * 'cupsdFlushHeader()' - Flush the header fields to the client. - */ - -int /* I - Bytes written or -1 on error */ -cupsdFlushHeader(cupsd_client_t *con) /* I - Client to flush to */ -{ - int bytes = httpFlushWrite(HTTP(con)); - - con->http.data_encoding = HTTP_ENCODE_LENGTH; - - return (bytes); -} - - /* * 'cupsdReadClient()' - Read data from a client. */ @@ -733,11 +549,8 @@ void cupsdReadClient(cupsd_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 */ 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 */ @@ -749,15 +562,31 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ static unsigned request_id = 0; /* Request ID for temp files */ - status = HTTP_CONTINUE; + status = HTTP_STATUS_CONTINUE; - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d, used=%d, file=%d state=%d", - con->http.fd, con->http.used, con->file, con->http.state); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdReadClient: error=%d, used=%d, state=%s, data_encoding=HTTP_ENCODING_%s, data_remaining=" CUPS_LLFMT ", request=%p(%s), file=%d", httpError(con->http), (int)httpGetReady(con->http), httpStateString(httpGetState(con->http)), httpIsChunked(con->http) ? "CHUNKED" : "LENGTH", CUPS_LLCAST httpGetRemaining(con->http), con->request, con->request ? ippStateString(ippGetState(con->request)) : "", con->file); - if (con->http.error) + if (httpGetState(con->http) == HTTP_STATE_GET_SEND || + httpGetState(con->http) == HTTP_STATE_POST_SEND || + httpGetState(con->http) == HTTP_STATE_STATUS) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: http error seen..."); + /* + * If we get called in the wrong state, then something went wrong with the + * connection and we need to shut it down... + */ + + if (!httpGetReady(con->http) && recv(httpGetFd(con->http), buf, 1, MSG_PEEK) < 1) + { + /* + * Connection closed... + */ + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on EOF."); + cupsdCloseClient(con); + return; + } + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP read state %s.", httpStateString(httpGetState(con->http))); cupsdCloseClient(con); return; } @@ -771,18 +600,16 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ con->auto_ssl = 0; - if (recv(con->http.fd, buf, 1, MSG_PEEK) == 1 && + if (recv(httpGetFd(con->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0]))) { /* * Encrypt this connection... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: Saw first byte %02X, auto-negotiating SSL/TLS session...", - buf[0] & 255); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "Saw first byte %02X, auto-negotiating SSL/TLS session.", buf[0] & 255); - if (!encrypt_client(con)) + if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS)) cupsdCloseClient(con); return; @@ -790,17 +617,27 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } #endif /* HAVE_SSL */ - switch (con->http.state) + switch (httpGetState(con->http)) { - case HTTP_WAITING : + case HTTP_STATE_WAITING : /* * See if we've received a request line... */ - if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL) + con->operation = httpReadRequest(con->http, con->uri, sizeof(con->uri)); + if (con->operation == HTTP_STATE_ERROR || + con->operation == HTTP_STATE_UNKNOWN_METHOD || + con->operation == HTTP_STATE_UNKNOWN_VERSION) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: httpGets returned EOF..."); + if (httpError(con->http)) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_WAITING Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); + else + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_WAITING Closing on error: %s", + cupsLastErrorString()); + cupsdCloseClient(con); return; } @@ -809,29 +646,19 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Ignore blank request lines... */ - if (line[0] == '\0') + if (con->operation == HTTP_STATE_WAITING) break; /* * Clear other state variables... */ - httpClearFields(HTTP(con)); - - con->http.activity = time(NULL); - con->http.version = HTTP_1_0; - con->http.keep_alive = HTTP_KEEPALIVE_OFF; - con->http.data_encoding = HTTP_ENCODE_LENGTH; - con->http.data_remaining = 0; - con->http._data_remaining = 0; - con->operation = HTTP_WAITING; - con->bytes = 0; - con->file = -1; - con->file_ready = 0; - con->pipe_pid = 0; - con->username[0] = '\0'; - con->password[0] = '\0'; - con->uri[0] = '\0'; + con->bytes = 0; + con->file = -1; + con->file_ready = 0; + con->pipe_pid = 0; + con->username[0] = '\0'; + con->password[0] = '\0'; cupsdClearString(&con->command); cupsdClearString(&con->options); @@ -856,96 +683,57 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } #ifdef HAVE_GSSAPI - con->gss_have_creds = 0; + con->have_gss = 0; + con->gss_uid = 0; #endif /* HAVE_GSSAPI */ - /* - * Grab the request line... - */ - - switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version)) - { - case 1 : - if (line[0]) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Bad request line \"%s\" from %s!", line, - con->http.hostname); - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); - cupsdCloseClient(con); - } - return; - case 2 : - con->http.version = HTTP_0_9; - break; - case 3 : - if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Bad request line \"%s\" from %s!", line, - con->http.hostname); - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); - cupsdCloseClient(con); - return; - } - - if (major < 2) - { - con->http.version = (http_version_t)(major * 100 + minor); - if (con->http.version == HTTP_1_1 && KeepAlive) - con->http.keep_alive = HTTP_KEEPALIVE_ON; - else - con->http.keep_alive = HTTP_KEEPALIVE_OFF; - } - else - { - cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE); - cupsdCloseClient(con); - return; - } - break; - } - /* * Handle full URLs in the request line... */ if (strcmp(con->uri, "*")) { - char method[HTTP_MAX_URI], /* Method/scheme */ + char scheme[HTTP_MAX_URI], /* Method/scheme */ userpass[HTTP_MAX_URI], /* Username:password */ hostname[HTTP_MAX_URI], /* Hostname */ resource[HTTP_MAX_URI]; /* Resource path */ int port; /* Port number */ - /* * Separate the URI into its components... */ - httpSeparateURI(HTTP_URI_CODING_MOST, con->uri, - method, sizeof(method), - userpass, sizeof(userpass), - hostname, sizeof(hostname), &port, - resource, sizeof(resource)); + if (httpSeparateURI(HTTP_URI_CODING_MOST, con->uri, + scheme, sizeof(scheme), + userpass, sizeof(userpass), + hostname, sizeof(hostname), &port, + resource, sizeof(resource)) < HTTP_URI_STATUS_OK) + { + cupsdLogClient(con, CUPSD_LOG_ERROR, "Bad URI \"%s\" in request.", + con->uri); + cupsdSendError(con, HTTP_STATUS_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE); + cupsdCloseClient(con); + return; + } - /* + /* * Only allow URIs with the servername, localhost, or an IP * address... */ - if (strcmp(method, "file") && - strcasecmp(hostname, ServerName) && - strcasecmp(hostname, "localhost") && - !isdigit(hostname[0])) + if (strcmp(scheme, "file") && + _cups_strcasecmp(hostname, ServerName) && + _cups_strcasecmp(hostname, "localhost") && + !cupsArrayFind(ServerAlias, hostname) && + !isdigit(hostname[0]) && hostname[0] != '[') { /* * Nope, we don't do proxies... */ - cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!", - con->uri); - cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE); + cupsdLogClient(con, CUPSD_LOG_ERROR, "Bad URI \"%s\" in request.", + con->uri); + cupsdSendError(con, HTTP_STATUS_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } @@ -955,72 +743,68 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * con->uri are HTTP_MAX_URI bytes in size... */ - strcpy(con->uri, resource); + strlcpy(con->uri, resource, sizeof(con->uri)); } /* * Process the request... */ - if (!strcmp(operation, "GET")) - con->http.state = HTTP_GET; - else if (!strcmp(operation, "PUT")) - con->http.state = HTTP_PUT; - else if (!strcmp(operation, "POST")) - con->http.state = HTTP_POST; - else if (!strcmp(operation, "DELETE")) - con->http.state = HTTP_DELETE; - else if (!strcmp(operation, "TRACE")) - con->http.state = HTTP_TRACE; - else if (!strcmp(operation, "OPTIONS")) - con->http.state = HTTP_OPTIONS; - else if (!strcmp(operation, "HEAD")) - con->http.state = HTTP_HEAD; - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation); - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); - cupsdCloseClient(con); - return; - } - - con->start = time(NULL); - con->operation = con->http.state; + gettimeofday(&(con->start), NULL); - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %s %s HTTP/%d.%d", - con->http.fd, operation, con->uri, - con->http.version / 100, con->http.version % 100); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "%s %s HTTP/%d.%d", + httpStateString(con->operation) + 11, con->uri, + httpGetVersion(con->http) / 100, + httpGetVersion(con->http) % 100); - con->http.status = HTTP_OK; - - cupsArrayAdd(ActiveClients, con); - cupsdSetBusyState(); + if (!cupsArrayFind(ActiveClients, con)) + { + cupsArrayAdd(ActiveClients, con); + cupsdSetBusyState(); + } - case HTTP_OPTIONS : - case HTTP_DELETE : - case HTTP_GET : - case HTTP_HEAD : - case HTTP_POST : - case HTTP_PUT : - case HTTP_TRACE : + case HTTP_STATE_OPTIONS : + case HTTP_STATE_DELETE : + case HTTP_STATE_GET : + case HTTP_STATE_HEAD : + case HTTP_STATE_POST : + case HTTP_STATE_PUT : + case HTTP_STATE_TRACE : /* * Parse incoming parameters until the status changes... */ - while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE) - if (con->http.used == 0 || - !memchr(con->http.buffer, '\n', con->http.used)) + while ((status = httpUpdate(con->http)) == HTTP_STATUS_CONTINUE) + if (!httpGetReady(con->http)) break; - if (status != HTTP_OK && status != HTTP_CONTINUE) + if (status != HTTP_STATUS_OK && status != HTTP_STATUS_CONTINUE) { - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); + if (httpError(con->http) && httpError(con->http) != EPIPE) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing for error %d (%s) while reading headers.", + httpError(con->http), strerror(httpError(con->http))); + else + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing on EOF while reading headers."); + + cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } break; default : + if (!httpGetReady(con->http) && recv(httpGetFd(con->http), buf, 1, MSG_PEEK) < 1) + { + /* + * Connection closed... + */ + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on EOF."); + cupsdCloseClient(con); + return; + } break; /* Anti-compiler-warning-code */ } @@ -1028,22 +812,27 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Handle new transfers... */ - if (status == HTTP_OK) + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Read: status=%d", status); + + if (status == HTTP_STATUS_OK) { - if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0]) + if (httpGetField(con->http, 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) + if ((ptr = strchr(httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE), + ',')) != NULL) *ptr = '\0'; - if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ';')) != NULL) + if ((ptr = strchr(httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE), + ';')) != NULL) *ptr = '\0'; - if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "charset=")) != NULL) + if ((ptr = strstr(httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE), + "charset=")) != NULL) { /* * Combine language and charset, and trim any extra params in the @@ -1051,18 +840,14 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ */ snprintf(locale, sizeof(locale), "%s.%s", - con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8); + httpGetField(con->http, 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); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d Browser asked for language \"%s\"...", - con->http.fd, locale); + snprintf(locale, sizeof(locale), "%s.UTF-8", + httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE)); con->language = cupsLangGet(locale); } @@ -1071,24 +856,45 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ cupsdAuthorize(con); - if (!strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) && - KeepAlive) - con->http.keep_alive = HTTP_KEEPALIVE_ON; + if (!_cups_strncasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), + "Keep-Alive", 10) && KeepAlive) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_ON); + else if (!_cups_strncasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), + "close", 5)) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); - if (!con->http.fields[HTTP_FIELD_HOST][0] && - con->http.version >= HTTP_1_1) + if (!httpGetField(con->http, HTTP_FIELD_HOST)[0] && + httpGetVersion(con->http) >= HTTP_VERSION_1_1) { /* * HTTP/1.1 and higher require the "Host:" field... */ - if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) { + cupsdLogClient(con, CUPSD_LOG_ERROR, "Missing Host: field in request."); cupsdCloseClient(con); return; } } - else if (con->operation == HTTP_OPTIONS) + else if (!valid_host(con)) + { + /* + * Access to localhost must use "localhost" or the corresponding IPv4 + * or IPv6 values in the Host: field. + */ + + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Request from \"%s\" using invalid Host: field \"%s\".", + httpGetHostname(con->http, NULL, 0), httpGetField(con->http, HTTP_FIELD_HOST)); + + if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + } + else if (con->operation == HTTP_STATE_OPTIONS) { /* * Do OPTIONS command... @@ -1096,45 +902,37 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (con->best && con->best->type != CUPSD_AUTH_NONE) { - if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE)) + httpClearFields(con->http); + + if (!cupsdSendHeader(con, HTTP_STATUS_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } - if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") && - con->http.tls == NULL) + if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), "Upgrade") && strstr(httpGetField(con->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(con->http)) { #ifdef HAVE_SSL /* * Do encryption stuff... */ - if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); - httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); - httpPrintf(HTTP(con), "Content-Length: 0\r\n"); - httpPrintf(HTTP(con), "\r\n"); + httpClearFields(con->http); - if (cupsdFlushHeader(con) < 0) - { + if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE)) + { cupsdCloseClient(con); return; } - if (!encrypt_client(con)) + if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED)) { cupsdCloseClient(con); return; } #else - if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1142,17 +940,12 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ #endif /* HAVE_SSL */ } - if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n"); - httpPrintf(HTTP(con), "Content-Length: 0\r\n"); - httpPrintf(HTTP(con), "\r\n"); + httpClearFields(con->http); + httpSetField(con->http, HTTP_FIELD_ALLOW, + "GET, HEAD, OPTIONS, POST, PUT"); + httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); - if (cupsdFlushHeader(con) < 0) + if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1164,7 +957,10 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Protect against malicious users! */ - if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE)) + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Request for non-absolute resource \"%s\".", con->uri); + + if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1172,38 +968,30 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else { - if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") && - con->http.tls == NULL) + if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), + "Upgrade") && !httpIsEncrypted(con->http)) { #ifdef HAVE_SSL /* * Do encryption stuff... */ - if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } + httpClearFields(con->http); - httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); - httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); - httpPrintf(HTTP(con), "Content-Length: 0\r\n"); - httpPrintf(HTTP(con), "\r\n"); - - if (cupsdFlushHeader(con) < 0) - { + if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, + CUPSD_AUTH_NONE)) + { cupsdCloseClient(con); return; } - if (!encrypt_client(con)) + if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED)) { cupsdCloseClient(con); return; } #else - if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1211,26 +999,23 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ #endif /* HAVE_SSL */ } - if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK) + if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_STATUS_OK) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: Unauthorized request for %s...", - con->uri); cupsdSendError(con, status, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } - if (con->http.expect && - (con->operation == HTTP_POST || con->operation == HTTP_PUT)) + if (httpGetExpect(con->http) && + (con->operation == HTTP_STATE_POST || con->operation == HTTP_STATE_PUT)) { - if (con->http.expect == HTTP_CONTINUE) + if (httpGetExpect(con->http) == HTTP_STATUS_CONTINUE) { /* * Send 100-continue header... */ - if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE)) + if (httpWriteResponse(con->http, HTTP_STATUS_CONTINUE)) { cupsdCloseClient(con); return; @@ -1242,27 +1027,23 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Send 417-expectation-failed header... */ - if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - httpPrintf(HTTP(con), "Content-Length: 0\r\n"); - httpPrintf(HTTP(con), "\r\n"); + httpClearFields(con->http); + httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); - if (cupsdFlushHeader(con) < 0) - { - cupsdCloseClient(con); - return; - } + cupsdSendError(con, HTTP_STATUS_EXPECTATION_FAILED, CUPSD_AUTH_NONE); + cupsdCloseClient(con); + return; } } - switch (con->http.state) + switch (httpGetState(con->http)) { - case HTTP_GET_SEND : - if (!strncmp(con->uri, "/printers/", 10) && + case HTTP_STATE_GET_SEND : + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Processing GET %s", con->uri); + + if ((!strncmp(con->uri, "/ppd/", 5) || + !strncmp(con->uri, "/printers/", 10) || + !strncmp(con->uri, "/classes/", 9)) && !strcmp(con->uri + strlen(con->uri) - 4, ".ppd")) { /* @@ -1272,28 +1053,134 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ - if ((p = cupsdFindPrinter(con->uri + 10)) != NULL) - snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); + if (!strncmp(con->uri, "/ppd/", 5)) + p = cupsdFindPrinter(con->uri + 5); + else if (!strncmp(con->uri, "/printers/", 10)) + p = cupsdFindPrinter(con->uri + 10); else { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + p = cupsdFindClass(con->uri + 9); + + if (p) { - cupsdCloseClient(con); - return; - } + int i; /* Looping var */ + + for (i = 0; i < p->num_printers; i ++) + { + if (!(p->printers[i]->type & CUPS_PRINTER_CLASS)) + { + char ppdname[1024];/* PPD filename */ + + snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", + ServerRoot, p->printers[i]->name); + if (!access(ppdname, 0)) + { + p = p->printers[i]; + break; + } + } + } + + if (i >= p->num_printers) + p = NULL; + } + } + + if (p) + { + snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); + } + else + { + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } break; } } + else if ((!strncmp(con->uri, "/icons/", 7) || + !strncmp(con->uri, "/printers/", 10) || + !strncmp(con->uri, "/classes/", 9)) && + !strcmp(con->uri + strlen(con->uri) - 4, ".png")) + { + /* + * Send icon file - get the real queue name since queue names are + * not case sensitive but filenames can be... + */ - if ((!strncmp(con->uri, "/admin", 6) && - strncmp(con->uri, "/admin/conf/", 12) && - strncmp(con->uri, "/admin/log/", 11)) || - !strncmp(con->uri, "/printers", 9) || - !strncmp(con->uri, "/classes", 8) || - !strncmp(con->uri, "/help", 5) || - !strncmp(con->uri, "/jobs", 5)) + con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".png" */ + + if (!strncmp(con->uri, "/icons/", 7)) + p = cupsdFindPrinter(con->uri + 7); + else if (!strncmp(con->uri, "/printers/", 10)) + p = cupsdFindPrinter(con->uri + 10); + else + { + p = cupsdFindClass(con->uri + 9); + + if (p) + { + int i; /* Looping var */ + + for (i = 0; i < p->num_printers; i ++) + { + if (!(p->printers[i]->type & CUPS_PRINTER_CLASS)) + { + char ppdname[1024];/* PPD filename */ + + snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", + ServerRoot, p->printers[i]->name); + if (!access(ppdname, 0)) + { + p = p->printers[i]; + break; + } + } + } + + if (i >= p->num_printers) + p = NULL; + } + } + + if (p) + snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name); + else + { + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + + break; + } + } + + if ((!strncmp(con->uri, "/admin", 6) && strcmp(con->uri, "/admin/conf/cupsd.conf") && strncmp(con->uri, "/admin/log/", 11)) || + !strncmp(con->uri, "/printers", 9) || + !strncmp(con->uri, "/classes", 8) || + !strncmp(con->uri, "/help", 5) || + !strncmp(con->uri, "/jobs", 5)) { + if (!WebInterface) + { + /* + * Web interface is disabled. Show an appropriate message... + */ + + if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + + break; + } + /* * Send CGI output... */ @@ -1348,31 +1235,28 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (!cupsdSendCommand(con, con->command, con->options, 0)) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } else - cupsdLogRequest(con, HTTP_OK); + cupsdLogRequest(con, HTTP_STATUS_OK); - if (con->http.version <= HTTP_1_0) - con->http.keep_alive = HTTP_KEEPALIVE_OFF; + if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); } - else if ((!strncmp(con->uri, "/admin/conf/", 12) && - (strchr(con->uri + 12, '/') || - strlen(con->uri) == 12)) || - (!strncmp(con->uri, "/admin/log/", 11) && - (strchr(con->uri + 11, '/') || - strlen(con->uri) == 11))) + else if (!strncmp(con->uri, "/admin/log/", 11) && (strchr(con->uri + 11, '/') || strlen(con->uri) == 11)) { /* - * GET can only be done to configuration files under + * GET can only be done to configuration files directly under * /admin/conf... */ - if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE)) + cupsdLogClient(con, CUPSD_LOG_ERROR, "Request for subdirectory \"%s\".", con->uri); + + if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1389,7 +1273,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if ((filename = get_file(con, &filestats, buf, sizeof(buf))) == NULL) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1400,6 +1284,8 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ type = mimeFileType(MimeDatabase, filename, NULL, NULL); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "filename=\"%s\", type=%s/%s", filename, type ? type->super : "", type ? type->type : ""); + if (is_cgi(con, filename, &filestats, type)) { /* @@ -1409,23 +1295,23 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (!cupsdSendCommand(con, con->command, con->options, 0)) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } else - cupsdLogRequest(con, HTTP_OK); + cupsdLogRequest(con, HTTP_STATUS_OK); - if (con->http.version <= HTTP_1_0) - con->http.keep_alive = HTTP_KEEPALIVE_OFF; + if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); break; } if (!check_if_modified(con, &filestats)) { - if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1434,11 +1320,11 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ else { if (type == NULL) - strcpy(line, "text/plain"); + strlcpy(line, "text/plain", sizeof(line)); else snprintf(line, sizeof(line), "%s/%s", type->super, type->type); - if (!write_file(con, HTTP_OK, filename, line, &filestats)) + if (!write_file(con, HTTP_STATUS_OK, filename, line, &filestats)) { cupsdCloseClient(con); return; @@ -1447,25 +1333,21 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } break; - case HTTP_POST_RECV : + case HTTP_STATE_POST_RECV : /* * See if the POST request includes a Content-Length field, and if * so check the length against any limits that are set... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "POST %s", con->uri); - cupsdLogMessage(CUPSD_LOG_DEBUG2, "CONTENT_TYPE = %s", - con->http.fields[HTTP_FIELD_CONTENT_TYPE]); - - if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && + if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && MaxRequestSize > 0 && - con->http.data_remaining > MaxRequestSize) + httpGetLength2(con->http) > MaxRequestSize) { /* * Request too large... */ - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1473,15 +1355,13 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ break; } - else if (con->http.data_remaining < 0 || - (!con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && - con->http.data_encoding == HTTP_ENCODE_LENGTH)) + else if (httpGetLength2(con->http) < 0) { /* * Negative content lengths are invalid! */ - if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1495,12 +1375,24 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * content-type field will be "application/ipp"... */ - if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], + if (!strcmp(httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE), "application/ipp")) con->request = ippNew(); - else if ((!strncmp(con->uri, "/admin", 6) && - strncmp(con->uri, "/admin/conf/", 12) && - strncmp(con->uri, "/admin/log/", 11)) || + else if (!WebInterface) + { + /* + * Web interface is disabled. Show an appropriate message... + */ + + if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + + break; + } + else if ((!strncmp(con->uri, "/admin", 6) && strncmp(con->uri, "/admin/log/", 11)) || !strncmp(con->uri, "/printers", 9) || !strncmp(con->uri, "/classes", 8) || !strncmp(con->uri, "/help", 5) || @@ -1558,14 +1450,8 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ cupsdSetString(&con->options, NULL); } - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d command=\"%s\", " - "options = \"%s\"", - con->http.fd, con->command, - con->options ? con->options : "(null)"); - - if (con->http.version <= HTTP_1_0) - con->http.keep_alive = HTTP_KEEPALIVE_OFF; + if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); } else { @@ -1576,7 +1462,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if ((filename = get_file(con, &filestats, buf, sizeof(buf))) == NULL) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1593,7 +1479,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Only POST to CGI's... */ - if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_UNAUTHORIZED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1602,21 +1488,21 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } break; - case HTTP_PUT_RECV : + case HTTP_STATE_PUT_RECV : /* * Validate the resource name... */ - if (strncmp(con->uri, "/admin/conf/", 12) || - strchr(con->uri + 12, '/') || - strlen(con->uri) == 12) + if (strcmp(con->uri, "/admin/conf/cupsd.conf")) { /* - * PUT can only be done to configuration files under - * /admin/conf... + * PUT can only be done to the cupsd.conf file... */ - if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE)) + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Disallowed PUT request for \"%s\".", con->uri); + + if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1630,19 +1516,15 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * so check the length against any limits that are set... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "PUT %s", con->uri); - cupsdLogMessage(CUPSD_LOG_DEBUG2, "CONTENT_TYPE = %s", - con->http.fields[HTTP_FIELD_CONTENT_TYPE]); - - if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && + if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && MaxRequestSize > 0 && - con->http.data_remaining > MaxRequestSize) + httpGetLength2(con->http) > MaxRequestSize) { /* * Request too large... */ - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1650,13 +1532,13 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ break; } - else if (con->http.data_remaining < 0) + else if (httpGetLength2(con->http) < 0) { /* * Negative content lengths are invalid! */ - if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1675,33 +1557,29 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (con->file < 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create request file %s: %s", - con->filename, strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to create request file \"%s\": %s", + con->filename, strerror(errno)); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d REQUEST %s=%d", con->http.fd, - con->filename, con->file); - 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 : - cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE); + case HTTP_STATE_DELETE : + case HTTP_STATE_TRACE : + cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; - case HTTP_HEAD : + case HTTP_STATE_HEAD : if (!strncmp(con->uri, "/printers/", 10) && !strcmp(con->uri + strlen(con->uri) - 4, ".ppd")) { @@ -1716,88 +1594,116 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); else { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } + cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); break; } } - - if ((!strncmp(con->uri, "/admin", 6) && - strncmp(con->uri, "/admin/conf/", 12) && - strncmp(con->uri, "/admin/log/", 11)) || - !strncmp(con->uri, "/printers", 9) || - !strncmp(con->uri, "/classes", 8) || - !strncmp(con->uri, "/help", 5) || - !strncmp(con->uri, "/jobs", 5)) + else if (!strncmp(con->uri, "/printers/", 10) && + !strcmp(con->uri + strlen(con->uri) - 4, ".png")) { /* - * CGI output... + * Send PNG file - get the real printer name since printer + * names are not case sensitive but filenames can be... */ - if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE)) + con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ + + if ((p = cupsdFindPrinter(con->uri + 10)) != NULL) + snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name); + else { - cupsdCloseClient(con); - return; + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + + cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); + break; } + } + else if (!WebInterface) + { + httpClearFields(con->http); - if (httpPrintf(HTTP(con), "\r\n") < 0) + if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - if (cupsdFlushHeader(con) < 0) + cupsdLogRequest(con, HTTP_STATUS_OK); + break; + } + + if ((!strncmp(con->uri, "/admin", 6) && strcmp(con->uri, "/admin/conf/cupsd.conf") && strncmp(con->uri, "/admin/log/", 11)) || + !strncmp(con->uri, "/printers", 9) || + !strncmp(con->uri, "/classes", 8) || + !strncmp(con->uri, "/help", 5) || + !strncmp(con->uri, "/jobs", 5)) + { + /* + * CGI output... + */ + + httpClearFields(con->http); + + if (!cupsdSendHeader(con, HTTP_STATUS_OK, "text/html", CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_OK); + cupsdLogRequest(con, HTTP_STATUS_OK); } - else if ((!strncmp(con->uri, "/admin/conf/", 12) && - (strchr(con->uri + 12, '/') || - strlen(con->uri) == 12)) || - (!strncmp(con->uri, "/admin/log/", 11) && - (strchr(con->uri + 11, '/') || - strlen(con->uri) == 11))) + else if (!strncmp(con->uri, "/admin/log/", 11) && (strchr(con->uri + 11, '/') || strlen(con->uri) == 11)) { /* * HEAD can only be done to configuration files under * /admin/conf... */ - if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE)) + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Request for subdirectory \"%s\".", con->uri); + + if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } + cupsdLogRequest(con, HTTP_STATUS_FORBIDDEN); break; } else if ((filename = get_file(con, &filestats, buf, sizeof(buf))) == NULL) { - if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html", CUPSD_AUTH_NONE)) + httpClearFields(con->http); + + if (!cupsdSendHeader(con, HTTP_STATUS_NOT_FOUND, "text/html", + CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_NOT_FOUND); + cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); } else if (!check_if_modified(con, &filestats)) { - if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_NOT_MODIFIED); + cupsdLogRequest(con, HTTP_STATUS_NOT_MODIFIED); } else { @@ -1807,46 +1713,24 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ type = mimeFileType(MimeDatabase, filename, NULL, NULL); if (type == NULL) - strcpy(line, "text/plain"); + strlcpy(line, "text/plain", sizeof(line)); else snprintf(line, sizeof(line), "%s/%s", type->super, type->type); - if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } + httpClearFields(con->http); - if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", - httpGetDateString(filestats.st_mtime)) < 0) - { - cupsdCloseClient(con); - return; - } + httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, + httpGetDateString(filestats.st_mtime)); + httpSetLength(con->http, (size_t)filestats.st_size); - if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n", - (unsigned long)filestats.st_size) < 0) + if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_OK); - } - - if (httpPrintf(HTTP(con), "\r\n") < 0) - { - cupsdCloseClient(con); - return; - } - - if (cupsdFlushHeader(con) < 0) - { - cupsdCloseClient(con); - return; + cupsdLogRequest(con, HTTP_STATUS_OK); } - - con->http.state = HTTP_WAITING; break; default : @@ -1859,21 +1743,21 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Handle any incoming data... */ - switch (con->http.state) + switch (httpGetState(con->http)) { - case HTTP_PUT_RECV : - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d con->data_encoding=HTTP_ENCODE_%s, " - "con->data_remaining=" CUPS_LLFMT ", con->file=%d", - con->http.fd, - con->http.data_encoding == HTTP_ENCODE_CHUNKED ? - "CHUNKED" : "LENGTH", - CUPS_LLCAST con->http.data_remaining, con->file); - + case HTTP_STATE_PUT_RECV : do { - if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0) + if ((bytes = httpRead2(con->http, line, sizeof(line))) < 0) { + if (httpError(con->http) && httpError(con->http) != EPIPE) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_PUT_RECV Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); + else + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_PUT_RECV Closing on EOF."); + cupsdCloseClient(con); return; } @@ -1881,36 +1765,47 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ { con->bytes += bytes; - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d writing %d bytes to %d", - con->http.fd, bytes, con->file); + if (MaxRequestSize > 0 && con->bytes > MaxRequestSize) + { + close(con->file); + con->file = -1; + unlink(con->filename); + cupsdClearString(&con->filename); - if (write(con->file, line, bytes) < bytes) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdReadClient: Unable to write %d bytes to %s: %s", - bytes, con->filename, strerror(errno)); + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + } - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: Closing data file %d...", - con->file); + if (write(con->file, line, (size_t)bytes) < bytes) + { + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to write %d bytes to \"%s\": %s", bytes, + con->filename, strerror(errno)); close(con->file); con->file = -1; unlink(con->filename); cupsdClearString(&con->filename); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } } + else if (httpGetState(con->http) == HTTP_STATE_PUT_RECV) + { + cupsdCloseClient(con); + return; + } } - while (con->http.state == HTTP_PUT_RECV && con->http.used > 0); + while (httpGetState(con->http) == HTTP_STATE_PUT_RECV && httpGetReady(con->http)); - if (con->http.state == HTTP_WAITING) + if (httpGetState(con->http) == HTTP_STATE_STATUS) { /* * End of file, see how big it is... @@ -1918,12 +1813,6 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ fstat(con->file, &filestats); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d Closing data file %d, size=" - CUPS_LLFMT ".", - con->http.fd, con->file, - CUPS_LLCAST filestats.st_size); - close(con->file); con->file = -1; @@ -1934,13 +1823,10 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Request is too big; remove it and send an error... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d Removing temp file %s", - con->http.fd, con->filename); unlink(con->filename); cupsdClearString(&con->filename); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1951,7 +1837,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Install the configuration file... */ - status = install_conf_file(con); + status = install_cupsd_conf(con); /* * Return the status to the client... @@ -1965,82 +1851,93 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } break; - case HTTP_POST_RECV : - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d con->data_encoding=HTTP_ENCODE_" - "%s, con->data_remaining=" CUPS_LLFMT ", con->file=%d", - con->http.fd, - con->http.data_encoding == HTTP_ENCODE_CHUNKED ? - "CHUNKED" : "LENGTH", - CUPS_LLCAST con->http.data_remaining, con->file); - + case HTTP_STATE_POST_RECV : do { - if (con->request) + if (con->request && con->file < 0) { /* * Grab any request data from the connection... */ - if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR) + if (!httpWait(con->http, 0)) + return; + + if ((ipp_state = ippRead(con->http, con->request)) == IPP_STATE_ERROR) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdReadClient: %d IPP Read Error!", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_ERROR, "IPP read error: %s", + cupsLastErrorString()); - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); + cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } - else if (ipp_state != IPP_DATA) + else if (ipp_state != IPP_STATE_DATA) { - if (con->http.state == HTTP_POST_SEND) + if (httpGetState(con->http) == HTTP_STATE_POST_SEND) { - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); + cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } + if (httpGetReady(con->http)) + continue; break; } else - con->bytes += ippLength(con->request); + { + cupsdLogClient(con, CUPSD_LOG_DEBUG, "%d.%d %s %d", + con->request->request.op.version[0], + con->request->request.op.version[1], + ippOpString(con->request->request.op.operation_id), + con->request->request.op.request_id); + con->bytes += (off_t)ippLength(con->request); + } } - if (con->file < 0 && con->http.state != HTTP_POST_SEND) + if (con->file < 0 && httpGetState(con->http) != HTTP_STATE_POST_SEND) { /* * Create a file as needed for the request data... */ - cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++); + cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, + request_id ++); con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); if (con->file < 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create request file %s: %s", - con->filename, strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to create request file \"%s\": %s", + con->filename, strerror(errno)); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd, - con->filename, con->file); - 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 (httpGetState(con->http) != HTTP_STATE_POST_SEND) { - if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0) + if (!httpWait(con->http, 0)) + return; + else if ((bytes = httpRead2(con->http, line, sizeof(line))) < 0) { + if (httpError(con->http) && httpError(con->http) != EPIPE) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_POST_SEND Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); + else + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_POST_SEND Closing on EOF."); + cupsdCloseClient(con); return; } @@ -2048,55 +1945,59 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ { con->bytes += bytes; - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d writing %d bytes to %d", - con->http.fd, bytes, con->file); + if (MaxRequestSize > 0 && con->bytes > MaxRequestSize) + { + close(con->file); + con->file = -1; + unlink(con->filename); + cupsdClearString(&con->filename); + + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + } - if (write(con->file, line, bytes) < bytes) + if (write(con->file, line, (size_t)bytes) < bytes) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdReadClient: Unable to write %d bytes to %s: %s", - bytes, con->filename, strerror(errno)); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: Closing file %d...", - con->file); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to write %d bytes to \"%s\": %s", + bytes, con->filename, strerror(errno)); close(con->file); con->file = -1; unlink(con->filename); cupsdClearString(&con->filename); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, + CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } } - else if (con->http.state == HTTP_POST_RECV) + else if (httpGetState(con->http) == HTTP_STATE_POST_RECV) return; - else if (con->http.state != HTTP_POST_SEND) + else if (httpGetState(con->http) != HTTP_STATE_POST_SEND) { + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing on unexpected state %s.", + httpStateString(httpGetState(con->http))); cupsdCloseClient(con); return; } } } - while (con->http.state == HTTP_POST_RECV && con->http.used > 0); + while (httpGetState(con->http) == HTTP_STATE_POST_RECV && httpGetReady(con->http)); - if (con->http.state == HTTP_POST_SEND) + if (httpGetState(con->http) == HTTP_STATE_POST_SEND) { if (con->file >= 0) { fstat(con->file, &filestats); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d Closing data file %d, " - "size=" CUPS_LLFMT ".", - con->http.fd, con->file, - CUPS_LLCAST filestats.st_size); - close(con->file); con->file = -1; @@ -2107,9 +2008,6 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Request is too big; remove it and send an error... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d Removing temp file %s", - con->http.fd, con->filename); unlink(con->filename); cupsdClearString(&con->filename); @@ -2123,25 +2021,34 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ con->request = NULL; } - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } + else if (filestats.st_size == 0) + { + /* + * Don't allow empty file... + */ + + unlink(con->filename); + cupsdClearString(&con->filename); + } if (con->command) { if (!cupsdSendCommand(con, con->command, con->options, 0)) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } else - cupsdLogRequest(con, HTTP_OK); + cupsdLogRequest(con, HTTP_STATUS_OK); } } @@ -2151,9 +2058,6 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (con->filename) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdReadClient: %d Removing temp file %s", - con->http.fd, con->filename); unlink(con->filename); cupsdClearString(&con->filename); } @@ -2167,10 +2071,14 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ break; /* Anti-compiler-warning-code */ } - if (con->http.state == HTTP_WAITING) + if (httpGetState(con->http) == HTTP_STATE_WAITING) { - if (!con->http.keep_alive) + if (!httpGetKeepAlive(con->http)) + { + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing because Keep-Alive is disabled."); cupsdCloseClient(con); + } else { cupsArrayRemove(ActiveClients, con); @@ -2200,10 +2108,10 @@ cupsdSendCommand( if (fd < 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdSendCommand: %d Unable to open \"%s\" for reading: %s", - con->http.fd, con->filename ? con->filename : "/dev/null", - strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to open \"%s\" for reading: %s", + con->filename ? con->filename : "/dev/null", + strerror(errno)); return (0); } @@ -2212,16 +2120,16 @@ cupsdSendCommand( else fd = -1; - con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root); + con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root); + con->pipe_status = HTTP_STATUS_OK; + + httpClearFields(con->http); if (fd >= 0) close(fd); - cupsdLogMessage(CUPSD_LOG_INFO, "Started \"%s\" (pid=%d)", command, - con->pipe_pid); - - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendCommand: %d file=%d", - con->http.fd, con->file); + cupsdLogClient(con, CUPSD_LOG_INFO, "Started \"%s\" (pid=%d, file=%d)", + command, con->pipe_pid, con->file); if (con->pipe_pid == 0) return (0); @@ -2230,10 +2138,12 @@ cupsdSendCommand( cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for CGI data."); + con->sent_header = 0; con->file_ready = 0; con->got_fields = 0; - con->field_col = 0; + con->header_used = 0; return (1); } @@ -2248,20 +2158,23 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ http_status_t code, /* I - Error code */ int auth_type)/* I - Authentication type */ { + char location[HTTP_MAX_VALUE]; /* Location field */ + + + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdSendError code=%d, auth_type=%d", code, auth_type); + #ifdef HAVE_SSL /* * Force client to upgrade for authentication if that is how the * server is configured... */ - if (code == HTTP_UNAUTHORIZED && - DefaultEncryption == HTTP_ENCRYPT_REQUIRED && - strcasecmp(con->http.hostname, "localhost") && - !con->http.tls) + if (code == HTTP_STATUS_UNAUTHORIZED && + DefaultEncryption == HTTP_ENCRYPTION_REQUIRED && + _cups_strcasecmp(httpGetHostname(con->http, NULL, 0), "localhost") && + !httpIsEncrypted(con->http)) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdSendError: Encryption before authentication!"); - code = HTTP_UPGRADE_REQUIRED; + code = HTTP_STATUS_UPGRADE_REQUIRED; } #endif /* HAVE_SSL */ @@ -2271,9 +2184,6 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ cupsdLogRequest(con, code); - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendError: %d code=%d (%s)", - con->http.fd, code, httpStatus(code)); - /* * To work around bugs in some proxies, don't use Keep-Alive for some * error messages... @@ -2282,34 +2192,20 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ * never disable it in that case. */ - if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE) - con->http.keep_alive = HTTP_KEEPALIVE_OFF; - - /* - * Send an error message back to the client. If the error code is a - * 400 or 500 series, make sure the message contains some text, too! - */ + strlcpy(location, httpGetField(con->http, HTTP_FIELD_LOCATION), sizeof(location)); - if (!cupsdSendHeader(con, code, NULL, auth_type)) - return (0); + httpClearFields(con->http); -#ifdef HAVE_SSL - if (code == HTTP_UPGRADE_REQUIRED) - if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0) - return (0); + httpSetField(con->http, HTTP_FIELD_LOCATION, location); - if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0) - return (0); -#endif /* HAVE_SSL */ + if (code >= HTTP_STATUS_BAD_REQUEST && con->type != CUPSD_AUTH_NEGOTIATE) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); - if (con->http.version >= HTTP_1_1 && - con->http.keep_alive == HTTP_KEEPALIVE_OFF) - { - if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0) - return (0); - } + if (httpGetVersion(con->http) >= HTTP_VERSION_1_1 && + httpGetKeepAlive(con->http) == HTTP_KEEPALIVE_OFF) + httpSetField(con->http, HTTP_FIELD_CONNECTION, "close"); - if (code >= HTTP_BAD_REQUEST) + if (code >= HTTP_STATUS_BAD_REQUEST) { /* * Send a human-readable error message. @@ -2323,13 +2219,13 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ redirect[0] = '\0'; - if (code == HTTP_UNAUTHORIZED) + if (code == HTTP_STATUS_UNAUTHORIZED) text = _cupsLangString(con->language, _("Enter your username and password or the " "root username and password to access this " "page. If you are using Kerberos authentication, " "make sure you have a valid Kerberos ticket.")); - else if (code == HTTP_UPGRADE_REQUIRED) + else if (code == HTTP_STATUS_UPGRADE_REQUIRED) { text = urltext; @@ -2346,45 +2242,58 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ "CONTENT=\"3;URL=https://%s:%d%s\">\n", con->servername, con->serverport, con->uri); } + else if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED) + text = _cupsLangString(con->language, + _("The web interface is currently disabled. Run " + "\"cupsctl WebInterface=yes\" to enable it.")); else text = ""; snprintf(message, sizeof(message), - "\n" + "\n" "\n" "\n" "\t\n" - "\t%d %s\n" + "\t%s - " CUPS_SVERSION "\n" "\t\n" "%s" "\n" "\n" - "

%d %s

\n" + "

%s

\n" "

%s

\n" "\n" "\n", - code, httpStatus(code), redirect, code, httpStatus(code), text); + _httpStatus(con->language, code), redirect, + _httpStatus(con->language, code), text); - if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0) - return (0); - if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", - (int)strlen(message)) < 0) + /* + * Send an error message back to the client. If the error code is a + * 400 or 500 series, make sure the message contains some text, too! + */ + + size_t length = strlen(message); /* Length of message */ + + httpSetLength(con->http, length); + + if (!cupsdSendHeader(con, code, "text/html", auth_type)) return (0); - if (httpPrintf(HTTP(con), "\r\n") < 0) + + if (httpWrite2(con->http, message, length) < 0) return (0); - if (httpPrintf(HTTP(con), "%s", message) < 0) + + if (httpFlushWrite(con->http) < 0) return (0); } - else if (httpPrintf(HTTP(con), "\r\n") < 0) - return (0); - - if (cupsdFlushHeader(con) < 0) - return (0); + else + { + httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); - con->http.state = HTTP_WAITING; + if (!cupsdSendHeader(con, code, NULL, auth_type)) + return (0); + } return (1); } @@ -2402,197 +2311,130 @@ cupsdSendHeader( int auth_type) /* I - Type of authentication */ { char auth_str[1024]; /* Authorization string */ -#ifdef HAVE_GSSAPI - static char *gss_buf = NULL; /* Kerberos auth data buffer */ - static int gss_bufsize = 0; /* Size of Kerberos auth data buffer */ -#endif /* HAVE_GSSAPI */ + cupsdLogClient(con, CUPSD_LOG_DEBUG, "cupsdSendHeader: code=%d, type=\"%s\", auth_type=%d", code, type, auth_type); + /* * Send the HTTP status header... */ - if (code == HTTP_CONTINUE) + if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED) { /* - * 100-continue doesn't send any headers... + * Treat our special "web interface is disabled" status as "200 OK" for web + * browsers. */ - return (httpPrintf(HTTP(con), "HTTP/%d.%d 100 Continue\r\n\r\n", - con->http.version / 100, con->http.version % 100) > 0); + code = HTTP_STATUS_OK; } - httpFlushWrite(HTTP(con)); - - con->http.data_encoding = HTTP_ENCODE_FIELDS; - - if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100, - con->http.version % 100, code, httpStatus(code)) < 0) - return (0); - if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 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) - return (0); - if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", - KeepAliveTimeout) < 0) - return (0); - } - if (code == HTTP_METHOD_NOT_ALLOWED) - if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0) - return (0); + httpSetField(con->http, HTTP_FIELD_SERVER, ServerHeader); + + if (code == HTTP_STATUS_METHOD_NOT_ALLOWED) + httpSetField(con->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST, PUT"); - if (code == HTTP_UNAUTHORIZED) + if (code == HTTP_STATUS_UNAUTHORIZED) { if (auth_type == CUPSD_AUTH_NONE) { if (!con->best || con->best->type <= CUPSD_AUTH_NONE) - auth_type = DefaultAuthType; + auth_type = cupsdDefaultAuthType(); else auth_type = con->best->type; } auth_str[0] = '\0'; - if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST) + if (auth_type == CUPSD_AUTH_BASIC) strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str)); - else if (auth_type == CUPSD_AUTH_DIGEST) - snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"", - con->http.hostname); #ifdef HAVE_GSSAPI - else if (auth_type == CUPSD_AUTH_NEGOTIATE && con->gss_output_token.length == 0) + else if (auth_type == CUPSD_AUTH_NEGOTIATE) + { +# ifdef AF_LOCAL + if (httpAddrFamily(httpGetAddress(con->http)) == AF_LOCAL) + strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str)); + else +# endif /* AF_LOCAL */ strlcpy(auth_str, "Negotiate", sizeof(auth_str)); + } #endif /* HAVE_GSSAPI */ -#ifdef HAVE_AUTHORIZATION_H - if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE) + if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE && + !_cups_strcasecmp(httpGetHostname(con->http, NULL, 0), "localhost")) { - int i; /* Looping var */ - char *auth_key; /* Auth key buffer */ - size_t auth_size; /* Size of remaining buffer */ + /* + * Add a "trc" (try root certification) parameter for local non-Kerberos + * requests when the request requires system group membership - then the + * client knows the root certificate can/should be used. + * + * Also, for OS X we also look for @AUTHKEY and add an "authkey" + * parameter as needed... + */ + char *name, /* Current user name */ + *auth_key; /* Auth key buffer */ + size_t auth_size; /* Size of remaining buffer */ auth_key = auth_str + strlen(auth_str); - auth_size = sizeof(auth_str) - (auth_key - auth_str); + auth_size = sizeof(auth_str) - (size_t)(auth_key - auth_str); - for (i = 0; i < con->best->num_names; i ++) + for (name = (char *)cupsArrayFirst(con->best->names); + name; + name = (char *)cupsArrayNext(con->best->names)) { - if (!strncasecmp(con->best->names[i], "@AUTHKEY(", 9)) +#ifdef HAVE_AUTHORIZATION_H + if (!_cups_strncasecmp(name, "@AUTHKEY(", 9)) { - snprintf(auth_key, auth_size, ", authkey=\"%s\"", - con->best->names[i] + 9); + snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9); /* end parenthesis is stripped in conf.c */ break; } - else if (!strcasecmp(con->best->names[i], "@SYSTEM") && - SystemGroupAuthKey) + else +#endif /* HAVE_AUTHORIZATION_H */ + if (!_cups_strcasecmp(name, "@SYSTEM")) { - snprintf(auth_key, auth_size, ", authkey=\"%s\"", SystemGroupAuthKey); +#ifdef HAVE_AUTHORIZATION_H + if (SystemGroupAuthKey) + snprintf(auth_key, auth_size, + ", authkey=\"%s\"", + SystemGroupAuthKey); + else +#else + strlcpy(auth_key, ", trc=\"y\"", auth_size); +#endif /* HAVE_AUTHORIZATION_H */ break; } } } -#endif /* HAVE_AUTHORIZATION_H */ if (auth_str[0]) { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendHeader: WWW-Authenticate: %s", - auth_str); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "WWW-Authenticate: %s", auth_str); - if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0) - return (0); + httpSetField(con->http, HTTP_FIELD_WWW_AUTHENTICATE, auth_str); } } -#ifdef HAVE_GSSAPI - /* - * WWW-Authenticate: Negotiate can be included even for - * non-401 replies... - */ + if (con->language && strcmp(con->language->language, "C")) + httpSetField(con->http, HTTP_FIELD_CONTENT_LANGUAGE, con->language->language); - if (con->gss_output_token.length > 0 && con->gss_output_token.length <= 65536) + if (type) { - OM_uint32 minor_status; /* Minor status code */ - int bufsize; /* Size of output token buffer */ - - - bufsize = con->gss_output_token.length * 4 / 3 + 2; - - if (bufsize > gss_bufsize) - { - char *buf; /* New buffer */ + if (!strcmp(type, "text/html")) + httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, "text/html; charset=utf-8"); + else + httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, type); + } + return (!httpWriteResponse(con->http, code)); +} - bufsize = (bufsize + 1023) & 1023;/* Round up */ - if (gss_buf) - buf = realloc(gss_buf, bufsize); - else - buf = malloc(bufsize); - - if (!buf) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to allocate %d bytes for Kerberos credentials!", - bufsize); - return (0); - } - - gss_buf = buf; - gss_bufsize = bufsize; - } - - httpEncode64_2(gss_buf, gss_bufsize, - con->gss_output_token.value, - con->gss_output_token.length); - gss_release_buffer(&minor_status, &con->gss_output_token); - - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdSendHeader: WWW-Authenticate: Negotiate %s", gss_buf); - - if (httpPrintf(HTTP(con), "WWW-Authenticate: Negotiate %s\r\n", - gss_buf) < 0) - return (0); - } - else if (con->gss_output_token.length > 65536) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Kerberos credentials larger than 64k (%d)!", - (int)con->gss_output_token.length); - return (0); - } -#endif /* HAVE_GSSAPI */ - - if (con->language && strcmp(con->language->language, "C")) - { - if (httpPrintf(HTTP(con), "Content-Language: %s\r\n", - con->language->language) < 0) - return (0); - } - - if (type) - { - if (!strcmp(type, "text/html")) - { - if (httpPrintf(HTTP(con), - "Content-Type: text/html; charset=utf-8\r\n") < 0) - return (0); - } - else if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0) - return (0); - } - - return (1); -} - - -/* - * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs. - */ +/* + * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs. + */ void cupsdUpdateCGI(void) @@ -2604,8 +2446,13 @@ cupsdUpdateCGI(void) while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel, message, sizeof(message))) != NULL) + { + if (loglevel == CUPSD_LOG_INFO) + cupsdLogMessage(CUPSD_LOG_INFO, "%s", message); + if (!strchr(CGIStatusBuffer->buffer, '\n')) break; + } if (ptr == NULL && !CGIStatusBuffer->bufused) { @@ -2627,24 +2474,45 @@ cupsdUpdateCGI(void) void cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ { - int bytes; /* Number of bytes written */ - char buf[16385]; /* Data buffer */ - char *bufptr; /* Pointer into buffer */ + int bytes, /* Number of bytes written */ + field_col; /* Current column */ + char *bufptr, /* Pointer into buffer */ + *bufend; /* Pointer to end of buffer */ ipp_state_t ipp_state; /* IPP state value */ -#ifdef DEBUG - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdWriteClient(con=%p) %d response=%p(%d), file=%d " - "pipe_pid=%d state=%d", - con, con->http.fd, con->response, - con->response ? con->response->state : -1, - con->file, con->pipe_pid, con->http.state); -#endif /* DEBUG */ + cupsdLogClient(con, CUPSD_LOG_DEBUG, "con->http=%p", con->http); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "cupsdWriteClient " + "error=%d, " + "used=%d, " + "state=%s, " + "data_encoding=HTTP_ENCODING_%s, " + "data_remaining=" CUPS_LLFMT ", " + "response=%p(%s), " + "pipe_pid=%d, " + "file=%d", + httpError(con->http), (int)httpGetReady(con->http), + httpStateString(httpGetState(con->http)), + httpIsChunked(con->http) ? "CHUNKED" : "LENGTH", + CUPS_LLCAST httpGetLength2(con->http), + con->response, + con->response ? ippStateString(ippGetState(con->request)) : "", + con->pipe_pid, con->file); + + if (httpGetState(con->http) != HTTP_STATE_GET_SEND && + httpGetState(con->http) != HTTP_STATE_POST_SEND) + { + /* + * If we get called in the wrong state, then something went wrong with the + * connection and we need to shut it down... + */ - if (con->http.state != HTTP_GET_SEND && - con->http.state != HTTP_POST_SEND) + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP write state %s.", + httpStateString(httpGetState(con->http))); + cupsdCloseClient(con); return; + } if (con->pipe_pid) { @@ -2654,30 +2522,73 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for CGI data."); + if (!con->file_ready) { /* * Try again later when there is CGI output available... */ - cupsdRemoveSelect(con->http.fd); + cupsdRemoveSelect(httpGetFd(con->http)); return; } con->file_ready = 0; } - if (con->response && con->response->state != IPP_DATA) + bytes = (ssize_t)(sizeof(con->header) - (size_t)con->header_used); + + if (!con->pipe_pid && bytes > (ssize_t)httpGetRemaining(con->http)) + { + /* + * Limit GET bytes to original size of file (STR #3265)... + */ + + bytes = (ssize_t)httpGetRemaining(con->http); + } + + if (con->response && con->response->state != IPP_STATE_DATA) { - ipp_state = ippWrite(HTTP(con), con->response); - bytes = ipp_state != IPP_ERROR && - (con->file >= 0 || ipp_state != IPP_DATA); + size_t wused = httpGetPending(con->http); /* Previous write buffer use */ + + do + { + /* + * Write a single attribute or the IPP message header... + */ + + ipp_state = ippWrite(con->http, con->response); + + /* + * If the write buffer has been flushed, stop buffering up attributes... + */ + + if (httpGetPending(con->http) <= wused) + break; + } + while (ipp_state != IPP_STATE_DATA && ipp_state != IPP_STATE_ERROR); + + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Writing IPP response, ipp_state=%s, old " + "wused=" CUPS_LLFMT ", new wused=" CUPS_LLFMT, + ippStateString(ipp_state), + CUPS_LLCAST wused, CUPS_LLCAST httpGetPending(con->http)); + + if (httpGetPending(con->http) > 0) + httpFlushWrite(con->http); + + bytes = ipp_state != IPP_STATE_ERROR && + (con->file >= 0 || ipp_state != IPP_STATE_DATA); + + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "bytes=%d, http_state=%d, data_remaining=" CUPS_LLFMT, + (int)bytes, httpGetState(con->http), + CUPS_LLCAST httpGetLength2(con->http)); } - else if ((bytes = read(con->file, buf, sizeof(buf) - 1)) > 0) + else if ((bytes = read(con->file, con->header + con->header_used, (size_t)bytes)) > 0) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdWriteClient: Read %d bytes from file %d...", - bytes, con->file); + con->header_used += bytes; if (con->pipe_pid && !con->got_fields) { @@ -2685,20 +2596,22 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ * Inspect the data for Content-Type and other fields. */ - buf[bytes] = '\0'; - - for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++) + for (bufptr = con->header, bufend = con->header + con->header_used, + field_col = 0; + !con->got_fields && bufptr < bufend; + bufptr ++) + { if (*bufptr == '\n') { /* * Send line to client... */ - if (bufptr > buf && bufptr[-1] == '\r') + if (bufptr > con->header && bufptr[-1] == '\r') bufptr[-1] = '\0'; *bufptr++ = '\0'; - cupsdLogMessage(CUPSD_LOG_DEBUG2, "Script header: %s", buf); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Script header: %s", con->header); if (!con->sent_header) { @@ -2706,134 +2619,151 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ * Handle redirection and CGI status codes... */ - if (!strncasecmp(buf, "Location:", 9)) + http_field_t field; /* HTTP field */ + char *value = strchr(con->header, ':'); + /* Value of field */ + + if (value) { - cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE); - con->sent_header = 2; + *value++ = '\0'; + while (isspace(*value & 255)) + value ++; + } - if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0) - return; + field = httpFieldValue(con->header); + + if (field != HTTP_FIELD_UNKNOWN && value) + { + httpSetField(con->http, field, value); + + if (field == HTTP_FIELD_LOCATION) + { + con->pipe_status = HTTP_STATUS_SEE_OTHER; + con->sent_header = 2; + } + else + con->sent_header = 1; } - else if (!strncasecmp(buf, "Status:", 7)) + else if (!_cups_strcasecmp(con->header, "Status") && value) { - cupsdSendError(con, (http_status_t)atoi(buf + 7), CUPSD_AUTH_NONE); + con->pipe_status = (http_status_t)atoi(value); con->sent_header = 2; } - else + else if (!_cups_strcasecmp(con->header, "Set-Cookie") && value) { - cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE); + httpSetCookie(con->http, value); con->sent_header = 1; - - if (con->http.version == HTTP_1_1) - { - if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0) - return; - } - } + } } - if (strncasecmp(buf, "Status:", 7)) - httpPrintf(HTTP(con), "%s\r\n", buf); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWriteClient: %d %s", - con->http.fd, buf); - /* * Update buffer... */ - bytes -= (bufptr - buf); - memmove(buf, bufptr, bytes + 1); - bufptr = buf - 1; + con->header_used -= bufptr - con->header; + + if (con->header_used > 0) + memmove(con->header, bufptr, (size_t)con->header_used); + + bufptr = con->header - 1; /* * See if the line was empty... */ - if (con->field_col == 0) + if (field_col == 0) { con->got_fields = 1; - if (cupsdFlushHeader(con) < 0) + if (httpGetVersion(con->http) == HTTP_VERSION_1_1 && + !httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0]) + httpSetLength(con->http, 0); + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending status %d for CGI.", con->pipe_status); + + if (con->pipe_status == HTTP_STATUS_OK) { - cupsdCloseClient(con); - return; + if (!cupsdSendHeader(con, con->pipe_status, NULL, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + } + else + { + if (!cupsdSendError(con, con->pipe_status, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } } - - if (con->http.version == HTTP_1_1) - con->http.data_encoding = HTTP_ENCODE_CHUNKED; } else - con->field_col = 0; + field_col = 0; } else if (*bufptr != '\r') - con->field_col ++; - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdWriteClient: %d bytes=%d, got_fields=%d", - con->http.fd, bytes, con->got_fields); - - if (bytes > 0 && !con->got_fields) - { - /* - * Remaining text needs to go out... - */ - - httpPrintf(HTTP(con), "%s", buf); - - con->http.activity = time(NULL); - return; + field_col ++; } - else if (bytes == 0) - { - con->http.activity = time(NULL); + + if (!con->got_fields) return; - } } - if (bytes > 0) + if (con->header_used > 0) { - if (httpWrite2(HTTP(con), buf, bytes) < 0) + if (httpWrite2(con->http, con->header, (size_t)con->header_used) < 0) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdWriteClient: %d Write of %d bytes failed!", - con->http.fd, bytes); - + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); cupsdCloseClient(con); return; } - if (con->http.data_encoding == HTTP_ENCODE_CHUNKED) - httpFlushWrite(HTTP(con)); + if (httpIsChunked(con->http)) + httpFlushWrite(con->http); - con->bytes += bytes; + con->bytes += con->header_used; - if (con->http.state == HTTP_WAITING) + if (httpGetState(con->http) == HTTP_STATE_WAITING) bytes = 0; + else + bytes = con->header_used; + + con->header_used = 0; } } - if (bytes <= 0) + if (bytes <= 0 || + (httpGetState(con->http) != HTTP_STATE_GET_SEND && + httpGetState(con->http) != HTTP_STATE_POST_SEND)) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWriteClient: %d bytes < 0", - con->http.fd); - - cupsdLogRequest(con, HTTP_OK); - - httpFlushWrite(HTTP(con)); - - if (con->http.data_encoding == HTTP_ENCODE_CHUNKED && con->sent_header == 1) + if (!con->sent_header && con->pipe_pid) + cupsdSendError(con, HTTP_STATUS_SERVER_ERROR, CUPSD_AUTH_NONE); + else { - if (httpWrite2(HTTP(con), "", 0) < 0) + cupsdLogRequest(con, HTTP_STATUS_OK); + + if (httpIsChunked(con->http) && (!con->pipe_pid || con->sent_header > 0)) { - cupsdCloseClient(con); - return; + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending 0-length chunk."); + + if (httpWrite2(con->http, "", 0) < 0) + { + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); + cupsdCloseClient(con); + return; + } } + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Flushing write buffer."); + httpFlushWrite(con->http); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "New state is %s", httpStateString(httpGetState(con->http))); } - con->http.state = HTTP_WAITING; + cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, NULL, con); - cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for request."); if (con->file >= 0) { @@ -2842,10 +2772,6 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ if (con->pipe_pid) cupsdEndProcess(con->pipe_pid, 0); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdWriteClient: %d Closing data file %d.", - con->http.fd, con->file); - close(con->file); con->file = -1; con->pipe_pid = 0; @@ -2853,9 +2779,6 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ if (con->filename) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdWriteClient: %d Removing temp file %s", - con->http.fd, con->filename); unlink(con->filename); cupsdClearString(&con->filename); } @@ -2876,14 +2799,19 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ cupsdClearString(&con->options); cupsdClearString(&con->query_string); - if (!con->http.keep_alive) + if (!httpGetKeepAlive(con->http)) { + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing because Keep-Alive is disabled."); cupsdCloseClient(con); return; } + else + { + cupsArrayRemove(ActiveClients, con); + cupsdSetBusyState(); + } } - - con->http.activity = time(NULL); } @@ -2896,28 +2824,26 @@ check_if_modified( cupsd_client_t *con, /* I - Client connection */ struct stat *filestats) /* I - File information */ { - char *ptr; /* Pointer into field */ + const char *ptr; /* Pointer into field */ time_t date; /* Time/date value */ off_t size; /* Size/length value */ size = 0; date = 0; - ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE]; + ptr = httpGetField(con->http, HTTP_FIELD_IF_MODIFIED_SINCE); if (*ptr == '\0') return (1); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "check_if_modified: %d If-Modified-Since=\"%s\"", - con->http.fd, ptr); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "check_if_modified: filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"", filestats, CUPS_LLCAST filestats->st_size, (int)filestats->st_mtime, ptr); while (*ptr != '\0') { while (isspace(*ptr) || *ptr == ';') ptr ++; - if (strncasecmp(ptr, "length=", 7) == 0) + if (_cups_strncasecmp(ptr, "length=", 7) == 0) { ptr += 7; size = strtoll(ptr, NULL, 10); @@ -2935,13 +2861,6 @@ check_if_modified( ptr ++; } - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "check_if_modified: %d sizes=" CUPS_LLFMT "," - CUPS_LLFMT " dates=%d,%d", - con->http.fd, CUPS_LLCAST size, - CUPS_LLCAST filestats->st_size, (int)date, - (int)filestats->st_mtime); - return ((size != filestats->st_size && size != 0) || (date < filestats->st_mtime && date != 0) || (size == 0 && date == 0)); @@ -2970,455 +2889,190 @@ compare_clients(cupsd_client_t *a, /* I - First client */ #ifdef HAVE_SSL /* - * 'encrypt_client()' - Enable encryption for the client... + * 'cupsd_start_tls()' - Start encryption on a connection. */ -static int /* O - 1 on success, 0 on error */ -encrypt_client(cupsd_client_t *con) /* I - Client to encrypt */ +static int /* O - 0 on success, -1 on error */ +cupsd_start_tls(cupsd_client_t *con, /* I - Client connection */ + http_encryption_t e) /* I - Encryption mode */ { -# ifdef HAVE_LIBSSL - SSL_CTX *context; /* Context for encryption */ - SSL *conn; /* Connection for encryption */ - BIO *bio; /* BIO data */ - unsigned long error; /* Error code */ + if (httpEncryption(con->http, e)) + { + cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to encrypt connection: %s", + cupsLastErrorString()); + return (-1); + } + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Connection now encrypted."); + return (0); +} +#endif /* HAVE_SSL */ - /* - * Verify that we have a certificate... - */ - if (access(ServerKey, 0) || access(ServerCertificate, 0)) - { - /* - * Nope, make a self-signed certificate... - */ +/* + * 'get_file()' - Get a filename and state info. + */ + +static char * /* O - Real filename */ +get_file(cupsd_client_t *con, /* I - Client connection */ + struct stat *filestats, /* O - File information */ + char *filename, /* IO - Filename buffer */ + size_t len) /* I - Buffer length */ +{ + int status; /* Status of filesystem calls */ + char *ptr; /* Pointer info filename */ + size_t plen; /* Remaining length after pointer */ + char language[7], /* Language subdirectory, if any */ + dest[1024]; /* Destination name */ + int perm_check = 1; /* Do permissions check? */ - if (!make_certificate(con)) - return (0); - } /* - * Create the SSL context and accept the connection... + * Figure out the real filename... */ - context = SSL_CTX_new(SSLv23_server_method()); + language[0] = '\0'; + + if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/')) + { + strlcpy(dest, con->uri + 5, sizeof(dest)); + ptr = dest + strlen(dest) - 4; - SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */ - SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM); - SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM); + if (ptr <= dest || strcmp(ptr, ".ppd")) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Disallowed path \"%s\".", con->uri); + return (NULL); + } - bio = BIO_new(_httpBIOMethods()); - BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con)); + *ptr = '\0'; + if (!cupsdFindPrinter(dest)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "No printer \"%s\" found.", dest); + return (NULL); + } - conn = SSL_new(context); - SSL_set_bio(conn, bio, bio); + snprintf(filename, len, "%s%s", ServerRoot, con->uri); - if (SSL_accept(conn) != 1) + perm_check = 0; + } + else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/')) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "encrypt_client: Unable to encrypt connection from %s!", - con->http.hostname); + strlcpy(dest, con->uri + 7, sizeof(dest)); + ptr = dest + strlen(dest) - 4; - while ((error = ERR_get_error()) != 0) - cupsdLogMessage(CUPSD_LOG_ERROR, "encrypt_client: %s", - ERR_error_string(error, NULL)); + if (ptr <= dest || strcmp(ptr, ".png")) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Disallowed path \"%s\".", con->uri); + return (NULL); + } - SSL_CTX_free(context); - SSL_free(conn); - return (0); - } + *ptr = '\0'; + if (!cupsdFindDest(dest)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "No printer \"%s\" found.", dest); + return (NULL); + } - cupsdLogMessage(CUPSD_LOG_DEBUG, - "encrypt_client: %d Connection from %s now encrypted.", - con->http.fd, con->http.hostname); + snprintf(filename, len, "%s/%s.png", CacheDir, dest); + if (access(filename, F_OK) < 0) + snprintf(filename, len, "%s/images/generic.png", DocumentRoot); - con->http.tls = conn; - return (1); + perm_check = 0; + } + else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/')) + snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5); + else if (!strcmp(con->uri, "/admin/conf/cupsd.conf")) + { + strlcpy(filename, ConfigurationFile, len); + + perm_check = 0; + } + else if (!strncmp(con->uri, "/admin/log/", 11)) + { + if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/') + strlcpy(filename, AccessLog, len); + else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/') + strlcpy(filename, ErrorLog, len); + else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/') + strlcpy(filename, PageLog, len); + else + return (NULL); -# elif defined(HAVE_GNUTLS) - http_tls_t *conn; /* TLS session object */ - int error; /* Error code */ - gnutls_certificate_server_credentials *credentials; - /* TLS credentials */ + perm_check = 0; + } + else if (con->language) + { + snprintf(language, sizeof(language), "/%s", con->language->language); + snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri); + } + else + snprintf(filename, len, "%s%s", DocumentRoot, con->uri); + if ((ptr = strchr(filename, '?')) != NULL) + *ptr = '\0'; /* - * Verify that we have a certificate... + * Grab the status for this language; if there isn't a language-specific file + * then fallback to the default one... */ - if (access(ServerKey, 0) || access(ServerCertificate, 0)) + if ((status = lstat(filename, filestats)) != 0 && language[0] && + strncmp(con->uri, "/icons/", 7) && + strncmp(con->uri, "/ppd/", 5) && + strncmp(con->uri, "/rss/", 5) && + strncmp(con->uri, "/admin/conf/", 12) && + strncmp(con->uri, "/admin/log/", 11)) { /* - * Nope, make a self-signed certificate... + * Drop the country code... */ - if (!make_certificate(con)) - return (0); - } + language[3] = '\0'; + snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri); - /* - * Create the SSL object and perform the SSL handshake... - */ + if ((ptr = strchr(filename, '?')) != NULL) + *ptr = '\0'; - conn = (http_tls_t *)malloc(sizeof(http_tls_t)); + if ((status = lstat(filename, filestats)) != 0) + { + /* + * Drop the language prefix and try the root directory... + */ - if (conn == NULL) - return (0); + language[0] = '\0'; + snprintf(filename, len, "%s%s", DocumentRoot, con->uri); - credentials = (gnutls_certificate_server_credentials *) - malloc(sizeof(gnutls_certificate_server_credentials)); - if (credentials == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "encrypt_client: Unable to encrypt connection from %s!", - con->http.hostname); - cupsdLogMessage(CUPSD_LOG_ERROR, "encrypt_client: %s", strerror(errno)); + if ((ptr = strchr(filename, '?')) != NULL) + *ptr = '\0'; - free(conn); - return (0); + status = lstat(filename, filestats); + } } - gnutls_certificate_allocate_credentials(credentials); - gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate, - ServerKey, GNUTLS_X509_FMT_PEM); + /* + * If we've found a symlink, 404 the sucker to avoid disclosing information. + */ - gnutls_init(&(conn->session), GNUTLS_SERVER); - gnutls_set_default_priority(conn->session); - gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials); - gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr)HTTP(con)); - gnutls_transport_set_pull_function(conn->session, _httpReadGNUTLS); - gnutls_transport_set_push_function(conn->session, _httpWriteGNUTLS); + if (!status && S_ISLNK(filestats->st_mode)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename); + return (NULL); + } - error = gnutls_handshake(conn->session); + /* + * Similarly, if the file/directory does not have world read permissions, do + * not allow access... + */ - if (error != GNUTLS_E_SUCCESS) + if (!status && perm_check && !(filestats->st_mode & S_IROTH)) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "encrypt_client: Unable to encrypt connection from %s!", - con->http.hostname); - cupsdLogMessage(CUPSD_LOG_ERROR, "encrypt_client: %s", - gnutls_strerror(error)); - - gnutls_deinit(conn->session); - gnutls_certificate_free_credentials(*credentials); - free(conn); - free(credentials); - return (0); + cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename); + return (NULL); } - cupsdLogMessage(CUPSD_LOG_DEBUG, - "encrypt_client: %d Connection from %s now encrypted.", - con->http.fd, con->http.hostname); - - conn->credentials = credentials; - con->http.tls = conn; - return (1); - -# elif defined(HAVE_CDSASSL) - OSStatus error; /* Error code */ - http_tls_t *conn; /* CDSA connection information */ - - - if ((conn = (http_tls_t *)malloc(sizeof(http_tls_t))) == NULL) - return (0); - - error = 0; - conn->session = NULL; - conn->certsArray = get_cdsa_certificate(con); - - if (!conn->certsArray) - { - /* - * No keychain (yet), make a self-signed certificate... - */ - - if (make_certificate(con)) - conn->certsArray = get_cdsa_certificate(con); - } - - if (!conn->certsArray) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "encrypt_client: Could not find signing key in keychain " - "\"%s\"", ServerCertificate); - error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */ - } - - if (!error) - error = SSLNewContext(true, &conn->session); - - if (!error) - error = SSLSetIOFuncs(conn->session, _httpReadCDSA, _httpWriteCDSA); - - if (!error) - error = SSLSetProtocolVersionEnabled(conn->session, kSSLProtocol2, false); - - if (!error) - error = SSLSetConnection(conn->session, HTTP(con)); - - if (!error) - error = SSLSetAllowsExpiredCerts(conn->session, true); - - if (!error) - error = SSLSetAllowsAnyRoot(conn->session, true); - - if (!error) - error = SSLSetCertificate(conn->session, conn->certsArray); - - if (!error) - { - /* - * Perform SSL/TLS handshake - */ - - while ((error = SSLHandshake(conn->session)) == errSSLWouldBlock) - usleep(1000); - } - - if (error) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "encrypt_client: Unable to encrypt connection from %s!", - con->http.hostname); - - cupsdLogMessage(CUPSD_LOG_ERROR, "encrypt_client: %s (%d)", - cssmErrorString(error), (int)error); - - con->http.error = error; - con->http.status = HTTP_ERROR; - - if (conn->session) - SSLDisposeContext(conn->session); - - if (conn->certsArray) - CFRelease(conn->certsArray); - - free(conn); - - return (0); - } - - cupsdLogMessage(CUPSD_LOG_DEBUG, - "encrypt_client: %d Connection from %s now encrypted.", - con->http.fd, con->http.hostname); - - con->http.tls = conn; - return (1); - -# endif /* HAVE_LIBSSL */ -} -#endif /* HAVE_SSL */ - - -#ifdef HAVE_CDSASSL -/* - * 'get_cdsa_certificate()' - Get a SSL/TLS certificate from the System keychain. - */ - -static CFArrayRef /* O - Array of certificates */ -get_cdsa_certificate(cupsd_client_t *con) /* I - Client connection */ -{ - OSStatus err; /* Error info */ - SecKeychainRef keychain; /* Keychain reference */ - SecIdentitySearchRef search; /* Search reference */ - SecIdentityRef identity; /* Identity */ - CFArrayRef certificates = NULL; - /* Certificate array */ - - - if ((err = SecKeychainOpen(ServerCertificate, &keychain))) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\", %s", - ServerCertificate, cssmErrorString(err)); - return (NULL); - } - -# if HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY - /* - * Use a policy to search for valid certificates who's common name matches the - * servername... - */ - - SecPolicySearchRef policy_search; /* Policy search ref */ - SecPolicyRef policy; /* Policy ref */ - CSSM_DATA options; /* Policy options */ - CSSM_APPLE_TP_SSL_OPTIONS - ssl_options; /* SSL Option for hostname */ - - - if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL, - NULL, &policy_search)) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference"); - CFRelease(keychain); - return (NULL); - } - - if (SecPolicySearchCopyNext(policy_search, &policy)) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Cannot find a policy to use for searching"); - CFRelease(keychain); - CFRelease(policy_search); - return (NULL); - } - - memset(&ssl_options, 0, sizeof(ssl_options)); - ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION; - ssl_options.ServerName = con->servername; - ssl_options.ServerNameLen = strlen(con->servername); - - options.Data = (uint8 *)&ssl_options; - options.Length = sizeof(ssl_options); - - if (SecPolicySetValue(policy, &options)) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Cannot set policy value to use for searching"); - CFRelease(keychain); - CFRelease(policy_search); - return (NULL); - } - - err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN, - keychain, FALSE, &search); -# else - /* - * Assume there is exactly one SecIdentity in the keychain... - */ - - err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search); -# endif /* HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */ - - if (err) - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Cannot create keychain search reference: %s", - cssmErrorString(err)); - else - { - if ((err = SecIdentitySearchCopyNext(search, &identity))) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Cannot find signing key in keychain \"%s\", error %d", - ServerCertificate, (int)err); - } - else - { - if (CFGetTypeID(identity) != SecIdentityGetTypeID()) - cupsdLogMessage(CUPSD_LOG_ERROR, - "SecIdentitySearchCopyNext CFTypeID failure!"); - else - { - if ((certificates = CFArrayCreate(NULL, (const void **)&identity, - 1, &kCFTypeArrayCallBacks)) == NULL) - cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array"); - } - - CFRelease(identity); - } - - CFRelease(search); - } - -# if HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY - CFRelease(policy); - CFRelease(policy_search); -# endif /* HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */ - - return (certificates); -} -#endif /* HAVE_CDSASSL */ - - -/* - * 'get_file()' - Get a filename and state info. - */ - -static char * /* O - Real filename */ -get_file(cupsd_client_t *con, /* I - Client connection */ - struct stat *filestats, /* O - File information */ - char *filename, /* IO - Filename buffer */ - int len) /* I - Buffer length */ -{ - int status; /* Status of filesystem calls */ - char *ptr; /* Pointer info filename */ - int plen; /* Remaining length after pointer */ - char language[7]; /* Language subdirectory, if any */ - - - /* - * Figure out the real filename... - */ - - language[0] = '\0'; - - if (!strncmp(con->uri, "/ppd/", 5)) - snprintf(filename, len, "%s%s", ServerRoot, con->uri); - else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/')) - snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5); - else if (!strncmp(con->uri, "/admin/conf/", 12)) - snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11); - else if (!strncmp(con->uri, "/admin/log/", 11)) - { - if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/') - strlcpy(filename, AccessLog, len); - else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/') - strlcpy(filename, ErrorLog, len); - else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/') - strlcpy(filename, PageLog, len); - else - return (NULL); - } - else if (con->language) - { - snprintf(language, sizeof(language), "/%s", con->language->language); - snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri); - } - else - snprintf(filename, len, "%s%s", DocumentRoot, con->uri); - - if ((ptr = strchr(filename, '?')) != NULL) - *ptr = '\0'; - - /* - * Grab the status for this language; if there isn't a language-specific file - * then fallback to the default one... - */ - - if ((status = stat(filename, filestats)) != 0 && language[0] && - strncmp(con->uri, "/ppd/", 5) && - strncmp(con->uri, "/admin/conf/", 12) && - strncmp(con->uri, "/admin/log/", 11)) - { - /* - * Drop the country code... - */ - - language[3] = '\0'; - snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri); - - if ((ptr = strchr(filename, '?')) != NULL) - *ptr = '\0'; - - if ((status = stat(filename, filestats)) != 0) - { - /* - * Drop the language prefix and try the root directory... - */ - - language[0] = '\0'; - snprintf(filename, len, "%s%s", DocumentRoot, con->uri); - - if ((ptr = strchr(filename, '?')) != NULL) - *ptr = '\0'; - - status = stat(filename, filestats); - } - } - - /* - * If we're found a directory, get the index.html file instead... - */ + /* + * If we've found a directory, get the index.html file instead... + */ if (!status && S_ISDIR(filestats->st_mode)) { @@ -3457,16 +3111,16 @@ get_file(cupsd_client_t *con, /* I - Client connection */ *ptr = '\0'; ptr = filename + strlen(filename); - plen = len - (ptr - filename); + plen = len - (size_t)(ptr - filename); strlcpy(ptr, "index.html", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); #ifdef HAVE_JAVA if (status) { strlcpy(ptr, "index.class", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_JAVA */ @@ -3474,7 +3128,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if (status) { strlcpy(ptr, "index.pl", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_PERL */ @@ -3482,7 +3136,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if (status) { strlcpy(ptr, "index.php", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_PHP */ @@ -3490,26 +3144,42 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if (status) { strlcpy(ptr, "index.pyc", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } if (status) { strlcpy(ptr, "index.py", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_PYTHON */ } while (status && language[0]); - } - cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_file: %d filename=%s size=%d", - con->http.fd, filename, - status ? -1 : (int)filestats->st_size); + /* + * If we've found a symlink, 404 the sucker to avoid disclosing information. + */ + + if (!status && S_ISLNK(filestats->st_mode)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename); + return (NULL); + } + + /* + * Similarly, if the file/directory does not have world read permissions, do + * not allow access... + */ + + if (!status && perm_check && !(filestats->st_mode & S_IROTH)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename); + return (NULL); + } + } - if (!status) - con->http.data_remaining = (int)filestats->st_size; + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "get_file: filestats=%p, filename=%p, len=" CUPS_LLFMT ", returning \"%s\".", filestats, filename, CUPS_LLCAST len, status ? "(null)" : filename); if (status) return (NULL); @@ -3519,83 +3189,61 @@ get_file(cupsd_client_t *con, /* I - Client connection */ /* - * 'install_conf_file()' - Install a configuration file. + * 'install_cupsd_conf()' - Install a configuration file. */ static http_status_t /* O - Status */ -install_conf_file(cupsd_client_t *con) /* I - Connection */ +install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ { + char filename[1024]; /* Configuration filename */ cups_file_t *in, /* Input file */ *out; /* Output file */ - char buffer[1024]; /* Copy buffer */ - int bytes; /* Number of bytes */ - char conffile[1024], /* Configuration filename */ - newfile[1024], /* New config filename */ - oldfile[1024]; /* Old config filename */ - struct stat confinfo; /* Config file info */ + char buffer[16384]; /* Copy buffer */ + ssize_t bytes; /* Number of bytes */ /* - * First construct the filenames... + * Open the request file... */ - snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11); - snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11); - snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11); - - cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", conffile); - - /* - * Get the owner, group, and permissions of the configuration file. - * If it doesn't exist, assign it to the User and Group in the - * cupsd.conf file with mode 0640 permissions. - */ - - if (stat(conffile, &confinfo)) + if ((in = cupsFileOpen(con->filename, "rb")) == NULL) { - confinfo.st_uid = User; - confinfo.st_gid = Group; - confinfo.st_mode = ConfigFilePerm; + cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s", + con->filename, strerror(errno)); + goto server_error; } /* - * Open the request file and new config file... + * Open the new config file... */ - if ((in = cupsFileOpen(con->filename, "rb")) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\" - %s", - con->filename, strerror(errno)); - return (HTTP_SERVER_ERROR); - } - - if ((out = cupsFileOpen(newfile, "wb")) == NULL) + if ((out = cupsdCreateConfFile(ConfigurationFile, ConfigFilePerm)) == NULL) { cupsFileClose(in); - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open config file \"%s\" - %s", - newfile, strerror(errno)); - return (HTTP_SERVER_ERROR); + goto server_error; } - fchmod(cupsFileNumber(out), confinfo.st_mode); - fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid); + cupsdLogClient(con, CUPSD_LOG_INFO, "Installing config file \"%s\"...", + ConfigurationFile); /* * Copy from the request to the new config file... */ while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0) - if (cupsFileWrite(out, buffer, bytes) < bytes) + if (cupsFileWrite(out, buffer, (size_t)bytes) < bytes) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to copy to config file \"%s\" - %s", - newfile, strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to copy to config file \"%s\": %s", + ConfigurationFile, strerror(errno)); cupsFileClose(in); cupsFileClose(out); - unlink(newfile); - return (HTTP_SERVER_ERROR); + snprintf(filename, sizeof(filename), "%s.N", ConfigurationFile); + cupsdUnlinkOrRemoveFile(filename); + + goto server_error; } /* @@ -3603,81 +3251,40 @@ install_conf_file(cupsd_client_t *con) /* I - Connection */ */ cupsFileClose(in); - if (cupsFileClose(out)) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Error file closing config file \"%s\" - %s", - newfile, strerror(errno)); - - unlink(newfile); - return (HTTP_SERVER_ERROR); - } + if (cupsdCloseCreatedConfFile(out, ConfigurationFile)) + goto server_error; /* * Remove the request file... */ - unlink(con->filename); + cupsdUnlinkOrRemoveFile(con->filename); cupsdClearString(&con->filename); /* - * Unlink the old backup, rename the current config file to the backup - * filename, and rename the new config file to the config file name... + * Set the NeedReload flag... */ - if (unlink(oldfile)) - if (errno != ENOENT) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to remove backup config file \"%s\" - %s", - oldfile, strerror(errno)); - - unlink(newfile); - - return (HTTP_SERVER_ERROR); - } - - if (rename(conffile, oldfile)) - if (errno != ENOENT) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to rename old config file \"%s\" - %s", - conffile, strerror(errno)); - - unlink(newfile); - - return (HTTP_SERVER_ERROR); - } - - if (rename(newfile, conffile)) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to rename new config file \"%s\" - %s", - newfile, strerror(errno)); - - rename(oldfile, conffile); - unlink(newfile); - - return (HTTP_SERVER_ERROR); - } + NeedReload = RELOAD_CUPSD; + ReloadTime = time(NULL); /* - * If the cupsd.conf file was updated, set the NeedReload flag... + * Return that the file was created successfully... */ - if (!strcmp(con->uri, "/admin/conf/cupsd.conf")) - NeedReload = RELOAD_CUPSD; - else - NeedReload = RELOAD_ALL; - - ReloadTime = time(NULL); + return (HTTP_STATUS_CREATED); /* - * Return that the file was created successfully... + * Common exit for errors... */ - return (HTTP_CREATED); + server_error: + + cupsdUnlinkOrRemoveFile(con->filename); + cupsdClearString(&con->filename); + + return (HTTP_STATUS_SERVER_ERROR); } @@ -3694,11 +3301,6 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ const char *options; /* Options on URL */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "is_cgi(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)", - con, filename, filestats, type ? type->super : "unknown", - type ? type->type : "unknown"); - /* * Get the options, if any... */ @@ -3713,13 +3315,13 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ * Check for known types... */ - if (!type || strcasecmp(type->super, "application")) + if (!type || _cups_strcasecmp(type->super, "application")) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, "is_cgi: Returning 0..."); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 0.", filename, filestats, type ? type->super : "unknown", type ? type->type : "unknown"); return (0); } - if (!strcasecmp(type->type, "x-httpd-cgi") && + if (!_cups_strcasecmp(type->type, "x-httpd-cgi") && (filestats->st_mode & 0111)) { /* @@ -3731,14 +3333,11 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ if (options) cupsdSetStringf(&con->options, " %s", options); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"", - con->command, con->options); - + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); return (1); } #ifdef HAVE_JAVA - else if (!strcasecmp(type->type, "x-httpd-java")) + else if (!_cups_strcasecmp(type->type, "x-httpd-java")) { /* * "application/x-httpd-java" is a Java servlet. @@ -3751,565 +3350,111 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ else cupsdSetStringf(&con->options, " %s", filename); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"", - con->command, con->options); - + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); return (1); } #endif /* HAVE_JAVA */ #ifdef HAVE_PERL - else if (!strcasecmp(type->type, "x-httpd-perl")) + else if (!_cups_strcasecmp(type->type, "x-httpd-perl")) { /* * "application/x-httpd-perl" is a Perl page. */ - cupsdSetString(&con->command, CUPS_PERL); - - if (options) - cupsdSetStringf(&con->options, " %s %s", filename, options); - else - cupsdSetStringf(&con->options, " %s", filename); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "is_cgi: 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. - */ - - cupsdSetString(&con->command, CUPS_PHP); - - if (options) - cupsdSetStringf(&con->options, " %s %s", filename, options); - else - cupsdSetStringf(&con->options, " %s", filename); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "is_cgi: 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. - */ - - cupsdSetString(&con->command, CUPS_PYTHON); - - if (options) - cupsdSetStringf(&con->options, " %s %s", filename, options); - else - cupsdSetStringf(&con->options, " %s", filename); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"", - con->command, con->options); - - return (1); - } -#endif /* HAVE_PYTHON */ - - cupsdLogMessage(CUPSD_LOG_DEBUG2, "is_cgi: Returning 0..."); - - return (0); -} - - -/* - * '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); -} - - -#ifdef HAVE_SSL -/* - * 'make_certificate()' - Make a self-signed SSL/TLS certificate. - */ - -static int /* O - 1 on success, 0 on failure */ -make_certificate(cupsd_client_t *con) /* I - Client connection */ -{ -#if defined(HAVE_LIBSSL) && defined(HAVE_WAITPID) - int pid, /* Process ID of command */ - status; /* Status of command */ - char command[1024], /* Command */ - *argv[12], /* Command-line arguments */ - *envp[MAX_ENV + 1], /* Environment variables */ - home[1024], /* HOME environment variable */ - infofile[1024], /* Type-in information for cert */ - seedfile[1024]; /* Random number seed file */ - int envc, /* Number of environment variables */ - bytes; /* Bytes written */ - cups_file_t *fp; /* Seed/info file */ - int infofd; /* Info file descriptor */ - - - /* - * Run the "openssl" command to seed the random number generator and - * generate a self-signed certificate that is good for 10 years: - * - * openssl rand -rand seedfile 1 - * - * openssl req -new -x509 -keyout ServerKey \ - * -out ServerCertificate -days 3650 -nodes - * - * The seeding step is crucial in ensuring that the openssl command - * does not block on systems without sufficient entropy... - */ - - if (!cupsFileFind("openssl", getenv("PATH"), 1, command, sizeof(command))) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "No SSL certificate and openssl command not found!"); - return (0); - } - - if (access("/dev/urandom", 0)) - { - /* - * If the system doesn't provide /dev/urandom, then any random source - * will probably be blocking-style, so generate some random data to - * use as a seed for the certificate. Note that we have already - * seeded the random number generator in cupsdInitCerts()... - */ - - cupsdLogMessage(CUPSD_LOG_INFO, - "Seeding the random number generator..."); - - snprintf(home, sizeof(home), "HOME=%s", TempDir); - - /* - * Write the seed file... - */ - - if ((fp = cupsTempFile2(seedfile, sizeof(seedfile))) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create seed file %s - %s", - seedfile, strerror(errno)); - return (0); - } - - for (bytes = 0; bytes < 262144; bytes ++) - cupsFilePutChar(fp, random()); - - cupsFileClose(fp); - - /* - * Run the openssl command to seed its random number generator... - */ - - argv[0] = "openssl"; - argv[1] = "rand"; - argv[2] = "-rand"; - argv[3] = seedfile; - argv[4] = "1"; - argv[5] = NULL; - - envc = cupsdLoadEnv(envp, MAX_ENV); - envp[envc++] = home; - envp[envc] = NULL; - - if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL, - &pid)) - { - unlink(seedfile); - return (0); - } - - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) - { - status = 1; - break; - } - - cupsdFinishProcess(pid, command, sizeof(command)); - - /* - * Remove the seed file, as it is no longer needed... - */ - - unlink(seedfile); - - if (status) - { - if (WIFEXITED(status)) - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to seed random number generator - " - "the openssl command stopped with status %d!", - WEXITSTATUS(status)); - else - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to seed random number generator - " - "the openssl command crashed on signal %d!", - WTERMSIG(status)); - - return (0); - } - } - - /* - * Create a file with the certificate information fields... - * - * Note: This assumes that the default questions are asked by the openssl - * command... - */ - - if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create certificate information file %s - %s", - infofile, strerror(errno)); - return (0); - } - - cupsFilePrintf(fp, ".\n.\n.\n%s\n.\n%s\n%s\n", - ServerName, ServerName, ServerAdmin); - cupsFileClose(fp); - - cupsdLogMessage(CUPSD_LOG_INFO, - "Generating SSL server key and certificate..."); - - argv[0] = "openssl"; - argv[1] = "req"; - argv[2] = "-new"; - argv[3] = "-x509"; - argv[4] = "-keyout"; - argv[5] = ServerKey; - argv[6] = "-out"; - argv[7] = ServerCertificate; - argv[8] = "-days"; - argv[9] = "3650"; - argv[10] = "-nodes"; - argv[11] = NULL; - - cupsdLoadEnv(envp, MAX_ENV); - - infofd = open(infofile, O_RDONLY); - - if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL, - &pid)) - { - close(infofd); - unlink(infofile); - return (0); - } - - close(infofd); - unlink(infofile); - - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) - { - status = 1; - break; - } - - cupsdFinishProcess(pid, command, sizeof(command)); - - if (status) - { - if (WIFEXITED(status)) - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create SSL server key and certificate - " - "the openssl command stopped with status %d!", - WEXITSTATUS(status)); - else - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create SSL server key and certificate - " - "the openssl command crashed on signal %d!", - WTERMSIG(status)); - } - else - { - cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...", - ServerKey); - cupsdLogMessage(CUPSD_LOG_INFO, - "Created SSL server certificate file \"%s\"...", - ServerCertificate); - } - - return (!status); - -#elif defined(HAVE_GNUTLS) - gnutls_x509_crt crt; /* Self-signed certificate */ - gnutls_x509_privkey key; /* Encryption key */ - cups_lang_t *language; /* Default language info */ - cups_file_t *fp; /* Key/cert file */ - unsigned char buffer[8192]; /* Buffer for x509 data */ - size_t bytes; /* Number of bytes of data */ - unsigned char serial[4]; /* Serial number buffer */ - time_t curtime; /* Current time */ - int result; /* Result of GNU TLS calls */ - - - /* - * Create the encryption key... - */ - - cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key..."); - - gnutls_x509_privkey_init(&key); - gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0); - - /* - * Save it... - */ - - bytes = sizeof(buffer); - - if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, - buffer, &bytes)) < 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s", - gnutls_strerror(result)); - gnutls_x509_privkey_deinit(key); - return (0); - } - else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL) - { - cupsFileWrite(fp, (char *)buffer, bytes); - cupsFileClose(fp); - - cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...", - ServerKey); - } - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create SSL server key file \"%s\" - %s", - ServerKey, strerror(errno)); - gnutls_x509_privkey_deinit(key); - return (0); - } - - /* - * Create the self-signed certificate... - */ + cupsdSetString(&con->command, CUPS_PERL); - cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate..."); + if (options) + cupsdSetStringf(&con->options, " %s %s", filename, options); + else + cupsdSetStringf(&con->options, " %s", filename); - language = cupsLangDefault(); - curtime = time(NULL); - serial[0] = curtime >> 24; - serial[1] = curtime >> 16; - serial[2] = curtime >> 8; - serial[3] = curtime; + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); + return (1); + } +#endif /* HAVE_PERL */ +#ifdef HAVE_PHP + else if (!_cups_strcasecmp(type->type, "x-httpd-php")) + { + /* + * "application/x-httpd-php" is a PHP page. + */ - gnutls_x509_crt_init(&crt); - if (strlen(language->language) == 5) - gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, - language->language + 3, 2); - else - gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, - "US", 2); - gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, - ServerName, strlen(ServerName)); - gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, - ServerName, strlen(ServerName)); - gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, - 0, "Unknown", 7); - gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, - "Unknown", 7); - gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0, - "Unknown", 7); - gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0, - ServerAdmin, strlen(ServerAdmin)); - gnutls_x509_crt_set_key(crt, key); - gnutls_x509_crt_set_serial(crt, serial, sizeof(serial)); - gnutls_x509_crt_set_activation_time(crt, curtime); - gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400); - gnutls_x509_crt_set_ca_status(crt, 0); - gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME, - ServerName); - gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0); - gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT); - gnutls_x509_crt_set_version(crt, 3); - - bytes = sizeof(buffer); - if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0) - gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes); - - gnutls_x509_crt_sign(crt, crt, key); + cupsdSetString(&con->command, CUPS_PHP); - /* - * Save it... - */ + if (options) + cupsdSetStringf(&con->options, " %s %s", filename, options); + else + cupsdSetStringf(&con->options, " %s", filename); - bytes = sizeof(buffer); - if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, - buffer, &bytes)) < 0) - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to export SSL server certificate - %s", - gnutls_strerror(result)); - else if ((fp = cupsFileOpen(ServerCertificate, "w")) != NULL) + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); + return (1); + } +#endif /* HAVE_PHP */ +#ifdef HAVE_PYTHON + else if (!_cups_strcasecmp(type->type, "x-httpd-python")) { - cupsFileWrite(fp, (char *)buffer, bytes); - cupsFileClose(fp); + /* + * "application/x-httpd-python" is a Python page. + */ - cupsdLogMessage(CUPSD_LOG_INFO, - "Created SSL server certificate file \"%s\"...", - ServerCertificate); - } - else - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create SSL server certificate file \"%s\" - %s", - ServerCertificate, strerror(errno)); + cupsdSetString(&con->command, CUPS_PYTHON); - /* - * Cleanup... - */ + if (options) + cupsdSetStringf(&con->options, " %s %s", filename, options); + else + cupsdSetStringf(&con->options, " %s", filename); - gnutls_x509_crt_deinit(crt); - gnutls_x509_privkey_deinit(key); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); + return (1); + } +#endif /* HAVE_PYTHON */ - return (1); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 0.", filename, filestats, type->super, type->type); + return (0); +} -#elif defined(HAVE_CDSASSL) && defined(HAVE_WAITPID) - int pid, /* Process ID of command */ - status; /* Status of command */ - char command[1024], /* Command */ - *argv[4], /* Command-line arguments */ - *envp[MAX_ENV + 1], /* Environment variables */ - keychain[1024], /* Keychain argument */ - infofile[1024]; /* Type-in information for cert */ - cups_file_t *fp; /* Seed/info file */ - int infofd; /* Info file descriptor */ +/* + * '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 */ +{ /* - * Run the "certtool" command to generate a self-signed certificate... + * Check for a leading slash... */ - if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command))) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "No SSL certificate and certtool command not found!"); + if (path[0] != '/') return (0); - } /* - * Create a file with the certificate information fields... - * - * Note: This assumes that the default questions are asked by the certtool - * command... + * Check for "<" or quotes in the path and reject since this is probably + * someone trying to inject HTML... */ - if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create certificate information file %s - %s", - infofile, strerror(errno)); + if (strchr(path, '<') != NULL || strchr(path, '\"') != NULL || strchr(path, '\'') != NULL) return (0); - } - - cupsFilePrintf(fp, "%s\nr\n\ny\nb\ns\ny\n%s\n\n\n\n\n%s\ny\n", - con->servername, con->servername, ServerAdmin); - cupsFileClose(fp); - - cupsdLogMessage(CUPSD_LOG_INFO, - "Generating SSL server key and certificate..."); - - snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate); - - argv[0] = "certtool"; - argv[1] = "c"; - argv[2] = keychain; - argv[3] = NULL; - cupsdLoadEnv(envp, MAX_ENV); - - infofd = open(infofile, O_RDONLY); + /* + * Check for "/.." in the path... + */ - if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL, - &pid)) + while ((path = strstr(path, "/..")) != NULL) { - close(infofd); - unlink(infofile); - return (0); - } - - close(infofd); - unlink(infofile); - - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) - { - status = 1; - break; - } - - cupsdFinishProcess(pid, command, sizeof(command)); + if (!path[3] || path[3] == '/') + return (0); - if (status) - { - if (WIFEXITED(status)) - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create SSL server key and certificate - " - "the certtool command stopped with status %d!", - WEXITSTATUS(status)); - else - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create SSL server key and certificate - " - "the certtool command crashed on signal %d!", - WTERMSIG(status)); - } - else - { - cupsdLogMessage(CUPSD_LOG_INFO, - "Created SSL server certificate file \"%s\"...", - ServerCertificate); + path ++; } - return (!status); + /* + * If we haven't found any relative paths, return 1 indicating an + * absolute path... + */ -#else - return (0); -#endif /* HAVE_LIBSSL && HAVE_WAITPID */ + return (1); } -#endif /* HAVE_SSL */ /* @@ -4335,7 +3480,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ char argbuf[10240], /* Argument buffer */ *argv[100], /* Argument strings */ *envp[MAX_ENV + 20]; /* Environment variables */ - char auth_type[256], /* CUPSD_AUTH_TYPE environment variable */ + char auth_type[256], /* AUTH_TYPE environment variable */ content_length[1024], /* CONTENT_LENGTH environment variable */ content_type[1024], /* CONTENT_TYPE environment variable */ http_cookie[32768], /* HTTP_COOKIE environment variable */ @@ -4351,10 +3496,6 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ server_name[1024], /* SERVER_NAME environment variable */ server_port[1024]; /* SERVER_PORT environment variable */ ipp_attribute_t *attr; /* attributes-natural-language attribute */ -#ifdef HAVE_GSSAPI - krb5_ccache ccache = NULL; /* Kerberos credentials */ - char krb5ccname[1024]; /* KRB5CCNAME environment variable */ -#endif /* HAVE_GSSAPI */ /* @@ -4375,9 +3516,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ * be consistent with Apache... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "pipe_command: command=\"%s\", options=\"%s\"", - command, options ? options : "(null)"); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "pipe_command: infile=%d, outfile=%p, command=\"%s\", options=\"%s\", root=%d", infile, outfile, command, options ? options : "(null)", root); argv[0] = command; @@ -4409,7 +3548,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ commptr ++; } - if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string) + if (*commptr == '?' && con->operation == HTTP_STATE_GET && !con->query_string) { commptr ++; cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr); @@ -4452,9 +3591,9 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ */ if (commptr[1] >= '0' && commptr[1] <= '9') - *commptr = (commptr[1] - '0') << 4; + *commptr = (char)((commptr[1] - '0') << 4); else - *commptr = (tolower(commptr[1]) - 'a' + 10) << 4; + *commptr = (char)((tolower(commptr[1]) - 'a' + 10) << 4); if (commptr[2] >= '0' && commptr[2] <= '9') *commptr |= commptr[2] - '0'; @@ -4481,8 +3620,8 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ if (con->username[0]) { - snprintf(auth_type, sizeof(auth_type), "CUPSD_AUTH_TYPE=%s", - httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION)); + snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s", + httpGetField(con->http, HTTP_FIELD_AUTHORIZATION)); if ((uriptr = strchr(auth_type + 10, ' ')) != NULL) *uriptr = '\0'; @@ -4502,7 +3641,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ * the POSIX locale... */ - strcpy(lang, "LANG=C"); + strlcpy(lang, "LANG=C", sizeof(lang)); break; case 2 : @@ -4530,14 +3669,14 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ else if (con->language) snprintf(lang, sizeof(lang), "LANG=%s.UTF8", con->language->language); else - strcpy(lang, "LANG=C"); + strlcpy(lang, "LANG=C", sizeof(lang)); - strcpy(remote_addr, "REMOTE_ADDR="); - httpAddrString(con->http.hostaddr, remote_addr + 12, + strlcpy(remote_addr, "REMOTE_ADDR=", sizeof(remote_addr)); + httpAddrString(httpGetAddress(con->http), remote_addr + 12, sizeof(remote_addr) - 12); snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", - con->http.hostname); + httpGetHostname(con->http, NULL, 0)); snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri); if ((uriptr = strchr(script_name, '?')) != NULL) @@ -4546,10 +3685,20 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s", DocumentRoot, script_name + 12); - sprintf(server_port, "SERVER_PORT=%d", con->serverport); + snprintf(server_port, sizeof(server_port), "SERVER_PORT=%d", con->serverport); - snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", - con->servername); + if (httpGetField(con->http, HTTP_FIELD_HOST)[0]) + { + char *nameptr; /* Pointer to ":port" */ + + snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", + httpGetField(con->http, HTTP_FIELD_HOST)); + if ((nameptr = strrchr(server_name, ':')) != NULL && !strchr(nameptr, ']')) + *nameptr = '\0'; /* Strip trailing ":port" */ + } + else + snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", + con->servername); envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0]))); @@ -4574,151 +3723,37 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username); envp[envc ++] = remote_user; - - /* - * Save Kerberos credentials, if any... - */ - -#ifdef HAVE_GSSAPI - if (con->gss_have_creds) - { -# if !defined(HAVE_KRB5_CC_NEW_UNIQUE) && !defined(HAVE_HEIMDAL) - cupsdLogMessage(CUPSD_LOG_INFO, - "Sorry, your version of Kerberos does not support " - "delegated credentials!"); - -# else - krb5_error_code error; /* Kerberos error code */ - OM_uint32 major_status, /* Major status code */ - minor_status; /* Minor status code */ - krb5_principal principal; /* Kerberos principal */ - - -# ifdef __APPLE__ - /* - * If the weak-linked GSSAPI/Kerberos library is not present, don't try - * to use it... - */ - - if (krb5_init_context != NULL) - { -# endif /* __APPLE__ */ - - /* - * We MUST create a file-based cache because memory-based caches are - * only valid for the current process/address space. - * - * Due to various bugs/features in different versions of Kerberos, we - * need either the krb5_cc_new_unique() function or Heimdal's version - * of krb5_cc_gen_new() to create a new FILE: credential cache that - * can be passed to the backend. These functions create a temporary - * file (typically in /tmp) containing the cached credentials, which - * are removed when we have successfully printed a job. - */ - -# ifdef HAVE_KRB5_CC_NEW_UNIQUE - if ((error = krb5_cc_new_unique(KerberosContext, "FILE", NULL, - &ccache)) != 0) -# else /* HAVE_HEIMDAL */ - if ((error = krb5_cc_gen_new(KerberosContext, &krb5_fcc_ops, - &ccache)) != 0) -# endif /* HAVE_KRB5_CC_NEW_UNIQUE */ - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create new credentials cache (%d/%s)", - error, strerror(errno)); - ccache = NULL; - } - else if ((error = krb5_parse_name(KerberosContext, con->username, - &principal)) != 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to parse kerberos username (%d/%s)", error, - strerror(errno)); - krb5_cc_destroy(KerberosContext, ccache); - ccache = NULL; - } - else if ((error = krb5_cc_initialize(KerberosContext, ccache, - principal))) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to initialize credentials cache (%d/%s)", error, - strerror(errno)); - krb5_cc_destroy(KerberosContext, ccache); - krb5_free_principal(KerberosContext, principal); - ccache = NULL; - } - else - { - krb5_free_principal(KerberosContext, principal); - - /* - * Copy the user's credentials to the new cache file... - */ - - major_status = gss_krb5_copy_ccache(&minor_status, - con->gss_delegated_cred, ccache); - - if (GSS_ERROR(major_status)) - { - cupsdLogGSSMessage(CUPSD_LOG_ERROR, major_status, minor_status, - "Unable to import client credentials cache"); - krb5_cc_destroy(KerberosContext, ccache); - ccache = NULL; - } - else - { - /* - * Add the KRB5CCNAME environment variable to the job so that the - * backend can use the credentials when printing. - */ - - snprintf(krb5ccname, sizeof(krb5ccname), "KRB5CCNAME=FILE:%s", - krb5_cc_get_name(KerberosContext, ccache)); - envp[envc++] = krb5ccname; - - if (!RunUser) - chown(krb5_cc_get_name(KerberosContext, ccache), User, Group); - } - } -# ifdef __APPLE__ - } -# endif /* __APPLE__ */ -# endif /* HAVE_KRB5_CC_NEW_UNIQUE || HAVE_HEIMDAL */ - } -#endif /* HAVE_GSSAPI */ - } - if (con->http.version == HTTP_1_1) + if (httpGetVersion(con->http) == HTTP_VERSION_1_1) envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1"; - else if (con->http.version == HTTP_1_0) + else if (httpGetVersion(con->http) == HTTP_VERSION_1_0) envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0"; else envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9"; - if (con->http.cookie) + if (httpGetCookie(con->http)) { snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s", - con->http.cookie); + httpGetCookie(con->http)); envp[envc ++] = http_cookie; } - if (con->http.fields[HTTP_FIELD_USER_AGENT][0]) + if (httpGetField(con->http, HTTP_FIELD_USER_AGENT)[0]) { snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s", - con->http.fields[HTTP_FIELD_USER_AGENT]); + httpGetField(con->http, HTTP_FIELD_USER_AGENT)); envp[envc ++] = http_user_agent; } - if (con->http.fields[HTTP_FIELD_REFERER][0]) + if (httpGetField(con->http, HTTP_FIELD_REFERER)[0]) { snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s", - con->http.fields[HTTP_FIELD_REFERER]); + httpGetField(con->http, HTTP_FIELD_REFERER)); envp[envc ++] = http_referer; } - if (con->operation == HTTP_GET) + if (con->operation == HTTP_STATE_GET) { envp[envc ++] = "REQUEST_METHOD=GET"; @@ -4738,7 +3773,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT, CUPS_LLCAST con->bytes); snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s", - con->http.fields[HTTP_FIELD_CONTENT_TYPE]); + httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE)); envp[envc ++] = "REQUEST_METHOD=POST"; envp[envc ++] = content_length; @@ -4749,7 +3784,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ * Tell the CGI if we are using encryption... */ - if (con->http.tls) + if (httpIsEncrypted(con->http)) envp[envc ++] = "HTTPS=ON"; /* @@ -4758,14 +3793,14 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ envp[envc] = NULL; - if (LogLevel == CUPSD_LOG_DEBUG2) + if (LogLevel >= CUPSD_LOG_DEBUG) { for (i = 0; i < argc; i ++) - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "pipe_command: argv[%d] = \"%s\"", i, argv[i]); + cupsdLogMessage(CUPSD_LOG_DEBUG, + "[CGI] argv[%d] = \"%s\"", i, argv[i]); for (i = 0; i < envc; i ++) - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "pipe_command: envp[%d] = \"%s\"", i, envp[i]); + cupsdLogMessage(CUPSD_LOG_DEBUG, + "[CGI] envp[%d] = \"%s\"", i, envp[i]); } /* @@ -4774,7 +3809,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ if (cupsdOpenPipe(fds)) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create pipes for CGI %s - %s", + cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s", argv[0], strerror(errno)); return (0); } @@ -4784,13 +3819,13 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ */ if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1], - -1, -1, root, DefaultProfile, &pid) < 0) + -1, -1, root, DefaultProfile, NULL, &pid) < 0) { /* * Error - can't fork! */ - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for CGI %s - %s", argv[0], + cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0], strerror(errno)); cupsdClosePipe(fds); @@ -4803,14 +3838,9 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ */ if (con->username[0]) -#ifdef HAVE_GSSAPI - cupsdAddCert(pid, con->username, ccache); -#else - cupsdAddCert(pid, con->username, NULL); -#endif /* HAVE_GSSAPI */ + cupsdAddCert(pid, con->username, con->type); - cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] %s started - PID = %d", - command, pid); + cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] Started %s (PID %d)", command, pid); *outfile = fds[0]; close(fds[1]); @@ -4820,6 +3850,176 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ } +/* + * 'valid_host()' - Is the Host: field valid? + */ + +static int /* O - 1 if valid, 0 if not */ +valid_host(cupsd_client_t *con) /* I - Client connection */ +{ + cupsd_alias_t *a; /* Current alias */ + cupsd_netif_t *netif; /* Current network interface */ + const char *end; /* End character */ + char *ptr; /* Pointer into host value */ + + + /* + * Copy the Host: header for later use... + */ + + strlcpy(con->clientname, httpGetField(con->http, HTTP_FIELD_HOST), + sizeof(con->clientname)); + if ((ptr = strrchr(con->clientname, ':')) != NULL && !strchr(ptr, ']')) + { + *ptr++ = '\0'; + con->clientport = atoi(ptr); + } + else + con->clientport = con->serverport; + + /* + * Then validate... + */ + + if (httpAddrLocalhost(httpGetAddress(con->http))) + { + /* + * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical + * addresses when accessing CUPS via the loopback interface... + */ + + return (!_cups_strcasecmp(con->clientname, "localhost") || + !_cups_strcasecmp(con->clientname, "localhost.") || +#ifdef __linux + !_cups_strcasecmp(con->clientname, "localhost.localdomain") || +#endif /* __linux */ + !strcmp(con->clientname, "127.0.0.1") || + !strcmp(con->clientname, "[::1]")); + } + +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) + /* + * Check if the hostname is something.local (Bonjour); if so, allow it. + */ + + if ((end = strrchr(con->clientname, '.')) != NULL && end > con->clientname && + !end[1]) + { + /* + * "." on end, work back to second-to-last "."... + */ + + for (end --; end > con->clientname && *end != '.'; end --); + } + + if (end && (!_cups_strcasecmp(end, ".local") || + !_cups_strcasecmp(end, ".local."))) + return (1); +#endif /* HAVE_DNSSD || HAVE_AVAHI */ + + /* + * Check if the hostname is an IP address... + */ + + if (isdigit(con->clientname[0] & 255) || con->clientname[0] == '[') + { + /* + * Possible IPv4/IPv6 address... + */ + + http_addrlist_t *addrlist; /* List of addresses */ + + + if ((addrlist = httpAddrGetList(con->clientname, AF_UNSPEC, NULL)) != NULL) + { + /* + * Good IPv4/IPv6 address... + */ + + httpAddrFreeList(addrlist); + return (1); + } + } + + /* + * Check for (alias) name matches... + */ + + for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias); + a; + a = (cupsd_alias_t *)cupsArrayNext(ServerAlias)) + { + /* + * "ServerAlias *" allows all host values through... + */ + + if (!strcmp(a->name, "*")) + return (1); + + if (!_cups_strncasecmp(con->clientname, a->name, a->namelen)) + { + /* + * Prefix matches; check the character at the end - it must be "." or nul. + */ + + end = con->clientname + a->namelen; + + if (!*end || (*end == '.' && !end[1])) + return (1); + } + } + +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) + for (a = (cupsd_alias_t *)cupsArrayFirst(DNSSDAlias); + a; + a = (cupsd_alias_t *)cupsArrayNext(DNSSDAlias)) + { + /* + * "ServerAlias *" allows all host values through... + */ + + if (!strcmp(a->name, "*")) + return (1); + + if (!_cups_strncasecmp(con->clientname, a->name, a->namelen)) + { + /* + * Prefix matches; check the character at the end - it must be "." or nul. + */ + + end = con->clientname + a->namelen; + + if (!*end || (*end == '.' && !end[1])) + return (1); + } + } +#endif /* HAVE_DNSSD || HAVE_AVAHI */ + + /* + * Check for interface hostname matches... + */ + + for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList); + netif; + netif = (cupsd_netif_t *)cupsArrayNext(NetIFList)) + { + if (!_cups_strncasecmp(con->clientname, netif->hostname, netif->hostlen)) + { + /* + * Prefix matches; check the character at the end - it must be "." or nul. + */ + + end = con->clientname + netif->hostlen; + + if (!*end || (*end == '.' && !end[1])) + return (1); + } + } + + return (0); +} + + /* * 'write_file()' - Send a file via HTTP. */ @@ -4833,41 +4033,29 @@ write_file(cupsd_client_t *con, /* I - Client connection */ { con->file = open(filename, O_RDONLY); - cupsdLogMessage(CUPSD_LOG_DEBUG2, "write_file: %d file=%d", con->http.fd, - con->file); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_file: code=%d, filename=\"%s\" (%d), type=\"%s\", filestats=%p.", code, filename, con->file, type ? type : "(null)", filestats); if (con->file < 0) return (0); fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); - con->pipe_pid = 0; + con->pipe_pid = 0; + con->sent_header = 1; - if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE)) - return (0); + httpClearFields(con->http); - if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", - httpGetDateString(filestats->st_mtime)) < 0) - return (0); - if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n", - CUPS_LLCAST filestats->st_size) < 0) - return (0); - if (httpPrintf(HTTP(con), "\r\n") < 0) - return (0); + httpSetLength(con->http, (size_t)filestats->st_size); - if (cupsdFlushHeader(con) < 0) - return (0); + httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, + httpGetDateString(filestats->st_mtime)); - con->http.data_encoding = HTTP_ENCODE_LENGTH; - con->http.data_remaining = filestats->st_size; + if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE)) + return (0); - if (con->http.data_remaining <= INT_MAX) - con->http._data_remaining = con->http.data_remaining; - else - con->http._data_remaining = INT_MAX; + cupsdAddSelect(httpGetFd(con->http), NULL, (cupsd_selfunc_t)cupsdWriteClient, con); - cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, - (cupsd_selfunc_t)cupsdWriteClient, con); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending file."); return (1); } @@ -4880,16 +4068,12 @@ write_file(cupsd_client_t *con, /* I - Client connection */ static void write_pipe(cupsd_client_t *con) /* I - Client connection */ { - cupsdLogMessage(CUPSD_LOG_DEBUG2, "write_pipe: CGI output on fd %d...", - con->file); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_pipe: CGI output on fd %d.", con->file); con->file_ready = 1; cupsdRemoveSelect(con->file); - cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con); -} + cupsdAddSelect(httpGetFd(con->http), NULL, (cupsd_selfunc_t)cupsdWriteClient, con); - -/* - * End of "$Id: client.c 7673 2008-06-18 22:31:26Z mike $". - */ + cupsdLogClient(con, CUPSD_LOG_DEBUG, "CGI data ready to be sent."); +}