diff options
author | Dmitry Stogov <dmitry@php.net> | 2007-03-28 15:39:22 +0000 |
---|---|---|
committer | Dmitry Stogov <dmitry@php.net> | 2007-03-28 15:39:22 +0000 |
commit | 4a119f9a79b5db25ba41da8da8e4292c46aafa1f (patch) | |
tree | acb47a56b9d402f26d337b2aaaf72ad369e7d514 /sapi/cgi/fastcgi.c | |
parent | 1c36d38891253b556998db18cca6ac01781c9ba0 (diff) | |
download | php-git-4a119f9a79b5db25ba41da8da8e4292c46aafa1f.tar.gz |
Improved FastCGI SAPI to support external pipe and socket servers on win32
Diffstat (limited to 'sapi/cgi/fastcgi.c')
-rw-r--r-- | sapi/cgi/fastcgi.c | 223 |
1 files changed, 190 insertions, 33 deletions
diff --git a/sapi/cgi/fastcgi.c b/sapi/cgi/fastcgi.c index 6068887f56..ec63526a6a 100644 --- a/sapi/cgi/fastcgi.c +++ b/sapi/cgi/fastcgi.c @@ -32,6 +32,7 @@ #include <windows.h> typedef unsigned int size_t; + typedef unsigned int in_addr_t; struct sockaddr_un { short sun_family; @@ -71,6 +72,8 @@ # include <netdb.h> # include <signal.h> +# define closesocket(s) close(s) + # if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL) # include <sys/poll.h> # endif @@ -147,6 +150,7 @@ static const fcgi_mgmt_rec fcgi_mgmt_vars[] = { static int is_initialized = 0; static int is_fastcgi = 0; static int in_shutdown = 0; +static in_addr_t *allowed_clients = NULL; #ifdef _WIN32 @@ -160,8 +164,6 @@ static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg) #else -static in_addr_t *allowed_clients = NULL; - static void fcgi_signal_handler(int signo) { if (signo == SIGUSR1 || signo == SIGTERM) { @@ -254,12 +256,89 @@ int fcgi_is_fastcgi(void) } } +#ifdef _WIN32 +/* Do some black magic with the NT security API. + * We prepare a DACL (Discretionary Access Control List) so that + * we, the creator, are allowed all access, while "Everyone Else" + * is only allowed to read and write to the pipe. + * This avoids security issues on shared hosts where a luser messes + * with the lower-level pipe settings and screws up the FastCGI service. + */ +static PACL prepare_named_pipe_acl(PSECURITY_DESCRIPTOR sd, LPSECURITY_ATTRIBUTES sa) +{ + DWORD req_acl_size; + char everyone_buf[32], owner_buf[32]; + PSID sid_everyone, sid_owner; + SID_IDENTIFIER_AUTHORITY + siaWorld = SECURITY_WORLD_SID_AUTHORITY, + siaCreator = SECURITY_CREATOR_SID_AUTHORITY; + PACL acl; + + sid_everyone = (PSID)&everyone_buf; + sid_owner = (PSID)&owner_buf; + + req_acl_size = sizeof(ACL) + + (2 * ((sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetSidLengthRequired(1))); + + acl = malloc(req_acl_size); + + if (acl == NULL) { + return NULL; + } + + if (!InitializeSid(sid_everyone, &siaWorld, 1)) { + goto out_fail; + } + *GetSidSubAuthority(sid_everyone, 0) = SECURITY_WORLD_RID; + + if (!InitializeSid(sid_owner, &siaCreator, 1)) { + goto out_fail; + } + *GetSidSubAuthority(sid_owner, 0) = SECURITY_CREATOR_OWNER_RID; + + if (!InitializeAcl(acl, req_acl_size, ACL_REVISION)) { + goto out_fail; + } + + if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_GENERIC_READ | FILE_GENERIC_WRITE, sid_everyone)) { + goto out_fail; + } + + if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, sid_owner)) { + goto out_fail; + } + + if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) { + goto out_fail; + } + + if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE)) { + goto out_fail; + } + + sa->lpSecurityDescriptor = sd; + + return acl; + +out_fail: + free(acl); + return NULL; +} +#endif + +static int is_port_number(const char *bindpath) +{ + while (*bindpath) { + if (*bindpath < '0' || *bindpath > '9') { + return 0; + } + bindpath++; + } + return 1; +} + int fcgi_listen(const char *path, int backlog) { -#ifdef _WIN32 - /* TODO: Support for manual binding on TCP sockets (php -b <port>) */ - return -1; -#else char *s; int tcp = 0; char host[MAXPATHLEN]; @@ -275,6 +354,12 @@ int fcgi_listen(const char *path, int backlog) host[s-path] = '\0'; tcp = 1; } + } else if (is_port_number(path)) { + port = atoi(path); + if (port != 0) { + host[0] = '\0'; + tcp = 1; + } } /* Prepare socket address */ @@ -303,6 +388,33 @@ int fcgi_listen(const char *path, int backlog) } } } else { +#ifdef _WIN32 + SECURITY_DESCRIPTOR sd; + SECURITY_ATTRIBUTES sa; + PACL acl; + HANDLE namedPipe; + + memset(&sa, 0, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.bInheritHandle = FALSE; + acl = prepare_named_pipe_acl(&sd, &sa); + + namedPipe = CreateNamedPipe(path, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE, + PIPE_UNLIMITED_INSTANCES, + 8192, 8192, 0, &sa); + if (namedPipe == INVALID_HANDLE_VALUE) { + return -1; + } + listen_socket = _open_osfhandle((long)namedPipe, 0); + if (!is_initialized) { + fcgi_init(); + } + is_fastcgi = 1; + return listen_socket; + +#else int path_len = strlen(path); if (path_len >= sizeof(sa.sa_unix.sun_path)) { @@ -318,6 +430,7 @@ int fcgi_listen(const char *path, int backlog) sa.sa_unix.sun_len = sock_len; #endif unlink(path); +#endif } /* Create, bind socket and start listen on it */ @@ -369,8 +482,13 @@ int fcgi_listen(const char *path, int backlog) fcgi_init(); } is_fastcgi = 1; - return listen_socket; + +#ifdef _WIN32 + if (tcp) { + listen_socket = _open_osfhandle((long)listen_socket, 0); + } #endif + return listen_socket; } void fcgi_init_request(fcgi_request *req, int listen_socket) @@ -385,6 +503,10 @@ void fcgi_init_request(fcgi_request *req, int listen_socket) req->out_hdr = NULL; req->out_pos = req->out_buf; + +#ifdef _WIN32 + req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL); +#endif } static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count) @@ -394,7 +516,18 @@ static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t coun do { errno = 0; +#ifdef _WIN32 + if (!req->tcp) { + ret = write(req->fd, ((char*)buf)+n, count-n); + } else { + ret = send(req->fd, ((char*)buf)+n, count-n, 0); + if (ret <= 0) { + errno = WSAGetLastError(); + } + } +#else ret = write(req->fd, ((char*)buf)+n, count-n); +#endif if (ret > 0) { n += ret; } else if (ret <= 0 && errno != 0 && errno != EINTR) { @@ -411,7 +544,18 @@ static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count do { errno = 0; +#ifdef _WIN32 + if (!req->tcp) { + ret = read(req->fd, ((char*)buf)+n, count-n); + } else { + ret = recv(req->fd, ((char*)buf)+n, count-n, 0); + if (ret <= 0) { + errno = WSAGetLastError(); + } + } +#else ret = read(req->fd, ((char*)buf)+n, count-n); +#endif if (ret > 0) { n += ret; } else if (ret == 0 && errno == 0) { @@ -666,19 +810,29 @@ static inline void fcgi_close(fcgi_request *req, int force, int destroy) } #ifdef _WIN32 - if (is_impersonate) { + if (is_impersonate && !req->tcp) { RevertToSelf(); } #endif if ((force || !req->keep) && req->fd >= 0) { #ifdef _WIN32 - HANDLE pipe = (HANDLE)_get_osfhandle(req->fd); + if (!req->tcp) { + HANDLE pipe = (HANDLE)_get_osfhandle(req->fd); - if (!force) { - FlushFileBuffers(pipe); + if (!force) { + FlushFileBuffers(pipe); + } + DisconnectNamedPipe(pipe); + } else { + if (!force) { + char buf[8]; + + shutdown(req->fd, 1); + while (recv(req->fd, buf, sizeof(buf), 0) > 0) {} + } + closesocket(req->fd); } - DisconnectNamedPipe(pipe); #else if (!force) { char buf[8]; @@ -707,33 +861,37 @@ int fcgi_accept_request(fcgi_request *req) return -1; } #ifdef _WIN32 - pipe = (HANDLE)_get_osfhandle(req->listen_socket); - - FCGI_LOCK(req->listen_socket); - ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if (!ConnectNamedPipe(pipe, &ov)) { - errno = GetLastError(); - if (errno == ERROR_IO_PENDING) { - while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) { - if (in_shutdown) { - CloseHandle(ov.hEvent); - FCGI_UNLOCK(req->listen_socket); - return -1; + if (!req->tcp) { + pipe = (HANDLE)_get_osfhandle(req->listen_socket); + FCGI_LOCK(req->listen_socket); + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!ConnectNamedPipe(pipe, &ov)) { + errno = GetLastError(); + if (errno == ERROR_IO_PENDING) { + while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) { + if (in_shutdown) { + CloseHandle(ov.hEvent); + FCGI_UNLOCK(req->listen_socket); + return -1; + } } + } else if (errno != ERROR_PIPE_CONNECTED) { } - } else if (errno != ERROR_PIPE_CONNECTED) { } - } - CloseHandle(ov.hEvent); - req->fd = req->listen_socket; - FCGI_UNLOCK(req->listen_socket); + CloseHandle(ov.hEvent); + req->fd = req->listen_socket; + FCGI_UNLOCK(req->listen_socket); + } else { + SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket); #else { + int listen_socket = req->listen_socket; +#endif sa_t sa; socklen_t len = sizeof(sa); FCGI_LOCK(req->listen_socket); - req->fd = accept(req->listen_socket, (struct sockaddr *)&sa, &len); + req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len); FCGI_UNLOCK(req->listen_socket); if (req->fd >= 0 && allowed_clients) { int n = 0; @@ -748,13 +906,12 @@ int fcgi_accept_request(fcgi_request *req) } if (!allowed) { fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr)); - close(req->fd); + closesocket(req->fd); req->fd = -1; continue; } } } -#endif if (req->fd < 0 && (in_shutdown || errno != EINTR)) { return -1; @@ -808,7 +965,7 @@ int fcgi_accept_request(fcgi_request *req) } if (fcgi_read_request(req)) { #ifdef _WIN32 - if (is_impersonate) { + if (is_impersonate && !req->tcp) { pipe = (HANDLE)_get_osfhandle(req->fd); if (!ImpersonateNamedPipeClient(pipe)) { fcgi_close(req, 1, 1); |