view src/os/win32/ngx_service.c @ 7119:fef61d26da39

Fixed buffer overread with unix sockets after accept(). Some OSes (notably macOS, NetBSD, and Solaris) allow unix socket addresses larger than struct sockaddr_un. Moreover, some of them (macOS, Solaris) return socklen of the socket address before it was truncated to fit the buffer provided. As such, on these systems socklen must not be used without additional check that it is within the buffer provided. Appropriate checks added to ngx_event_accept() (after accept()), ngx_event_recvmsg() (after recvmsg()), and ngx_set_inherited_sockets() (after getsockname()). We also obtain socket addresses via getsockname() in ngx_connection_local_sockaddr(), but it does not need any checks as it is only used for INET and INET6 sockets (as there can be no wildcard unix sockets).
author Maxim Dounin <mdounin@mdounin.ru>
date Wed, 04 Oct 2017 21:19:33 +0300
parents a1caf3989b49
children
line wrap: on
line source


/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */



#define NGX_SERVICE_CONTROL_SHUTDOWN   128
#define NGX_SERVICE_CONTROL_REOPEN     129


SERVICE_TABLE_ENTRY st[] = {
    { "nginx", service_main },
    { NULL, NULL }
};


ngx_int_t
ngx_service(ngx_log_t *log)
{
    /* primary thread */

    /* StartServiceCtrlDispatcher() should be called within 30 seconds */

    if (StartServiceCtrlDispatcher(st) == 0) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "StartServiceCtrlDispatcher() failed");
        return NGX_ERROR;
    }

    return NGX_OK;
}


void
service_main(u_int argc, char **argv)
{
    SERVICE_STATUS         status;
    SERVICE_STATUS_HANDLE  service;

    /* thread spawned by SCM */

    service = RegisterServiceCtrlHandlerEx("nginx", service_handler, ctx);
    if (service == INVALID_HANDLE_VALUE) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "RegisterServiceCtrlHandlerEx() failed");
        return;
    }

    status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    status.dwCurrentState = SERVICE_START_PENDING;
    status.dwControlsAccepted = SERVICE_ACCEPT_STOP
                                |SERVICE_ACCEPT_PARAMCHANGE;
    status.dwWin32ExitCode = NO_ERROR;
    status.dwServiceSpecificExitCode = 0;
    status.dwCheckPoint = 1;
    status.dwWaitHint = 2000;

    /* SetServiceStatus() should be called within 80 seconds */

    if (SetServiceStatus(service, &status) == 0) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "SetServiceStatus() failed");
        return;
    }

    /* init */

    status.dwCurrentState = SERVICE_RUNNING;
    status.dwCheckPoint = 0;
    status.dwWaitHint = 0;

    if (SetServiceStatus(service, &status) == 0) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "SetServiceStatus() failed");
        return;
    }

    /* call master or worker loop */

    /*
     * master should use event notification and look status
     * single should use iocp to get notifications from service handler
     */

}


u_int
service_handler(u_int control, u_int type, void *data, void *ctx)
{
    /* primary thread */

    switch (control) {

    case SERVICE_CONTROL_INTERROGATE:
        status = NGX_IOCP_INTERROGATE;
        break;

    case SERVICE_CONTROL_STOP:
        status = NGX_IOCP_STOP;
        break;

    case SERVICE_CONTROL_PARAMCHANGE:
        status = NGX_IOCP_RECONFIGURE;
        break;

    case NGX_SERVICE_CONTROL_SHUTDOWN:
        status = NGX_IOCP_REOPEN;
        break;

    case NGX_SERVICE_CONTROL_REOPEN:
        status = NGX_IOCP_REOPEN;
        break;

    default:
        return ERROR_CALL_NOT_IMPLEMENTED;
    }

    if (ngx_single) {
        if (PostQueuedCompletionStatus(iocp, ... status, ...) == 0) {
            err = ngx_errno;
            ngx_log_error(NGX_LOG_ALERT, log, err,
                          "PostQueuedCompletionStatus() failed");
            return err;
        }

    } else {
        Event
    }

    return NO_ERROR;
}