/* cygserver.cc Copyright 2001 Red Hat Inc. Written by Egor Duda This file is part of Cygwin. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include #include #include #include "cygwin/cygserver.h" SECURITY_ATTRIBUTES sec_none_nih, sec_all_nih; GENERIC_MAPPING pipe_mapping; DWORD request_count = 0; void init_security () { static SECURITY_DESCRIPTOR sd; InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl (&sd, TRUE, 0, FALSE); sec_none_nih.nLength = sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES); sec_none_nih.bInheritHandle = sec_all_nih.bInheritHandle = FALSE; sec_none_nih.lpSecurityDescriptor = NULL; sec_all_nih.lpSecurityDescriptor = &sd; pipe_mapping.GenericRead = FILE_READ_DATA; pipe_mapping.GenericWrite = FILE_WRITE_DATA; pipe_mapping.GenericExecute = 0; pipe_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA; } BOOL setup_privileges () { BOOL rc, ret_val; HANDLE hToken = NULL; TOKEN_PRIVILEGES sPrivileges; rc = OpenProcessToken ( GetCurrentProcess() , TOKEN_ALL_ACCESS , &hToken ) ; if ( !rc ) { printf ( "error opening process token (%lu)\n", GetLastError () ); ret_val = FALSE; goto out; } rc = LookupPrivilegeValue ( NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid ); if ( !rc ) { printf ( "error getting prigilege luid (%lu)\n", GetLastError () ); ret_val = FALSE; goto out; } sPrivileges.PrivilegeCount = 1 ; sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ; rc = AdjustTokenPrivileges ( hToken, FALSE, &sPrivileges, 0, NULL, NULL ) ; if ( !rc ) { printf ( "error adjusting prigilege level. (%lu)\n", GetLastError () ); ret_val = FALSE; goto out; } ret_val = TRUE; out: CloseHandle ( hToken ); return ret_val; } void get_version (struct request_header* req) { struct request_get_version* request = (struct request_get_version*) req; if (req->cb != sizeof (struct request_get_version)) { req->error_code = EINVAL; return; } req->error_code = 0; request->major = CYGWIN_SERVER_VERSION_MAJOR; request->api = CYGWIN_SERVER_VERSION_API; request->minor = CYGWIN_SERVER_VERSION_MINOR; request->patch = CYGWIN_SERVER_VERSION_PATCH; } int check_and_dup_handle (HANDLE from_process, HANDLE to_process, HANDLE from_process_token, DWORD access, HANDLE from_handle, HANDLE* to_handle_ptr) { HANDLE local_handle = NULL; int ret_val = EACCES; char sd_buf [1024]; PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf; DWORD bytes_needed; PRIVILEGE_SET ps; DWORD ps_len = sizeof (ps); BOOL status; if (!DuplicateHandle (from_process, from_handle, GetCurrentProcess (), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { printf ( "error getting handle(%d) to server (%lu)\n", from_handle, GetLastError ()); goto out; } if (!GetKernelObjectSecurity (local_handle, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, sd, sizeof (sd_buf), &bytes_needed)) { printf ( "error getting handle SD (%lu)\n", GetLastError ()); goto out; } MapGenericMask (&access, &pipe_mapping); if (!AccessCheck (sd, from_process_token, access, &pipe_mapping, &ps, &ps_len, &access, &status)) { printf ( "error checking access rights (%lu)\n", GetLastError ()); goto out; } if (!status) { printf ( "access to object denied\n"); goto out; } if (!DuplicateHandle (from_process, from_handle, to_process, to_handle_ptr, access, FALSE, 0)) { printf ( "error getting handle to client (%lu)\n", GetLastError ()); goto out; } ret_val = 0; out: if (local_handle) CloseHandle (local_handle); return (ret_val); } void attach_tty (HANDLE pipe, struct request_header* req) { HANDLE from_process_handle = NULL; HANDLE to_process_handle = NULL; HANDLE token_handle = NULL; DWORD rc; struct request_attach_tty* request = (struct request_attach_tty*) req; if (req->cb != sizeof (struct request_attach_tty)) { req->error_code = EINVAL; return; } #ifdef DEBUG printf ("%d:(%d,%d) -> %d\n", request->master_pid, request->from_master, request->to_master, request->pid); #endif from_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, request->master_pid); to_process_handle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, request->pid); if (!from_process_handle || !to_process_handle) { printf ("error opening process (%lu)\n", GetLastError ()); req->error_code = EACCES; goto out; } ImpersonateNamedPipeClient (pipe); rc = OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, TRUE, &token_handle); RevertToSelf (); if (!rc) { printf ("error opening thread token (%lu)\n", GetLastError ()); req->error_code = EACCES; goto out; } if (check_and_dup_handle (from_process_handle, to_process_handle, token_handle, GENERIC_READ, request->from_master, &request->from_master) != 0) { printf ("error duplicating from_master handle (%lu)\n", GetLastError ()); req->error_code = EACCES; goto out; } if (request->to_master) { if (check_and_dup_handle (from_process_handle, to_process_handle, token_handle, GENERIC_WRITE, request->to_master, &request->to_master) != 0) { printf ("error duplicating to_master handle (%lu)\n", GetLastError ()); req->error_code = EACCES; goto out; } } #ifdef DEBUG printf ("%d -> %d(%d,%d)\n", request->master_pid, request->pid, request->from_master, request->to_master); #endif req->error_code = 0; out: if (from_process_handle) CloseHandle (from_process_handle); if (to_process_handle) CloseHandle (to_process_handle); if (token_handle) CloseHandle (token_handle); } int process_request (HANDLE pipe, struct request_header* req) { if (!req) return -1; switch (req->req_id) { case CYGSERVER_REQUEST_GET_VERSION: get_version (req); break; case CYGSERVER_REQUEST_ATTACH_TTY: attach_tty (pipe, req); break; default: req->error_code = ENOSYS; break; } } DWORD process_client (HANDLE pipe) { DWORD bytes_read, bytes_written, rc; char request_buffer [MAX_REQUEST_SIZE]; struct request_header* req_ptr = (struct request_header*) &request_buffer; rc = ReadFile (pipe, &request_buffer, sizeof (struct request_header), &bytes_read, NULL); if (!rc || bytes_read != sizeof (struct request_header)) { printf ("error reading from pipe (%lu)\n", GetLastError ()); goto out; } if (req_ptr->cb > MAX_REQUEST_SIZE || req_ptr->cb < sizeof (struct request_header)) { printf ("broken request, size (%lu)\n", req_ptr->cb); goto out; } rc = ReadFile (pipe, ((char*) &request_buffer) + sizeof (struct request_header), req_ptr->cb - sizeof (struct request_header), &bytes_read, NULL); if (!rc || bytes_read != req_ptr->cb - sizeof (struct request_header)) { printf ("error reading from pipe (%lu)\n", GetLastError ()); goto out; } process_request (pipe, req_ptr); rc = WriteFile (pipe, &request_buffer, req_ptr->cb, &bytes_written, NULL); if (!rc || bytes_written != req_ptr->cb) { printf ("error writing to pipe (%lu)\n", GetLastError ()); goto out; } out: FlushFileBuffers (pipe); DisconnectNamedPipe (pipe); CloseHandle ( pipe ); return 0; } int serve_requests () { char pipe_name [MAX_PATH]; init_security (); sprintf (pipe_name, "\\\\.\\pipe\\cygwin_lpc"); while (TRUE) { HANDLE pipe = NULL; HANDLE thread = NULL; DWORD tid; pipe = CreateNamedPipe (pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, 1000, &sec_all_nih ); if (pipe == INVALID_HANDLE_VALUE) { printf ("error creating pipe (%lu)\n.", GetLastError ()); goto err; } if ( !ConnectNamedPipe ( pipe, NULL ) && GetLastError () != ERROR_PIPE_CONNECTED) { printf ("error connecting to pipe (%lu)\n.", GetLastError ()); goto err; } #ifdef DEBUG printf ( "request received. total:%8d\n", ++request_count); #endif thread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) process_client, pipe, 0, &tid); if (!thread) { printf ("error creating thread (%lu)\n.", GetLastError ()); goto err; } CloseHandle (thread); continue; err: if (pipe && pipe != INVALID_HANDLE_VALUE) CloseHandle (pipe); Sleep (100); } return 0; } int main (int argc, char** argv) { init_security (); setup_privileges (); serve_requests (); }