(file) Return to preexec.c CVS log (file) (dir) Up to [OMI] / omi / disp

File: [OMI] / omi / disp / preexec.c (download)
Revision: 1.1, Mon Apr 20 17:19:51 2015 UTC (9 years ago) by krisbash
Branch: MAIN
CVS Tags: OMI_1_0_8_2, OMI_1_0_8_1, HEAD
OMI 1.0.8-1

/*
**==============================================================================
**
** Open Management Infrastructure (OMI)
**
** Copyright (c) Microsoft Corporation
** 
** Licensed under the Apache License, Version 2.0 (the "License"); you may not 
** use this file except in compliance with the License. You may obtain a copy 
** of the License at 
**
**     http://www.apache.org/licenses/LICENSE-2.0 
**
** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
** MERCHANTABLITY OR NON-INFRINGEMENT. 
**
** See the Apache 2 License for the specific language governing permissions 
** and limitations under the License.
**
**==============================================================================
*/

#include "preexec.h"
#include <common.h>
#include <base/base.h>
#include <pal/strings.h>
#include <base/paths.h>
#include <base/strarr.h>

#if defined(CONFIG_POSIX)
# include <pthread.h>
# include <errno.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <signal.h>
#endif

/*
**==============================================================================
**
** Local Definitions:
**
**==============================================================================
*/

static pid_t _Exec(
    const char* path,
    const char* uidStr,
    const char* gidStr)
{
    pid_t pid = fork();

    /* Return if failure */
    if (pid < 0)
        return -1;

    /* Return if parent */
    if (pid > 0)
        return pid;

    /* In child process here... */

    /* Close any open file descriptors */
    {
        int fd;
        int n = getdtablesize();

        if (n > 2500 || n < 0)
            n = 2500;

        /* Leave stdin(0), stdout(1), stderr(2) open (for debugging) */
        for (fd = 3; fd < n; ++fd)
            close(fd);
    }

    /* Execute the program */
    execl(
        path,   /* path of program */
        path,   /* argv[0] */
        uidStr, /* argv[1] */
        gidStr, /* argv[2] */
        NULL);

    _exit(1); 
    /* Unreachable */
    return -1;
}

static void _BlockSIGCHLD()
{
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigprocmask(SIG_BLOCK, &set, NULL);
}

static void _UnblockSIGCHLD()
{
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigprocmask(SIG_UNBLOCK, &set, NULL);
}

typedef struct _Bucket /* derives from HashBucket */
{
    struct _Bucket* next;
    char* key;
}
Bucket;

static size_t _Hash(
    const HashBucket* bucket_)
{
    Bucket* bucket = (Bucket*)bucket_;
    size_t h = 0;
    char* key = bucket->key;

    while (*key)
        h += 5 * *key++;

    return h;
}

static int _Equal(
    const HashBucket* bucket1_,
    const HashBucket* bucket2_)
{
    Bucket* bucket1 = (Bucket*)bucket1_;
    Bucket* bucket2 = (Bucket*)bucket2_;
    return strcmp(bucket1->key, bucket2->key) == 0;
}

static void _Release(
    HashBucket* bucket_)
{
    Bucket* bucket = (Bucket*)bucket_;
    PAL_Free(bucket->key);
    PAL_Free(bucket);
}

static MI_Boolean _Contains(
    HashMap* self,
    const char* key)
{
    Bucket bucket;
    bucket.key = (char*)key;
    return HashMap_Find(self, (const HashBucket*)&bucket) ? MI_TRUE : MI_FALSE;
}

static int _Insert(
    HashMap* self,
    const char* key)
{
    Bucket* bucket = (Bucket*)PAL_Calloc(1, sizeof(Bucket));

    if (!bucket)
        return -1;

    bucket->key = PAL_Strdup(key);

    if (!bucket->key)
    {
        PAL_Free(bucket);
        return -1;
    }

    if (HashMap_Insert(self, (HashBucket*)bucket) != 0)
    {
        PAL_Free(bucket);
        PAL_Free(bucket->key);
        return -1;
    }

    return 0;
}

/*
**==============================================================================
**
** Public Definitions:
**
**==============================================================================
*/

int PreExec_Construct(
    PreExec* self)
{
    const size_t NUMLISTS = 32;
    memset(self, 0, sizeof(*self));

    if (HashMap_Init(&self->cache, NUMLISTS, _Hash, _Equal, _Release) != 0)
        return -1;

    return 0;
}

void PreExec_Destruct(
    PreExec* self)
{
    HashMap_Destroy(&self->cache);
}

int PreExec_Exec(
    PreExec* self,
    const char* programPath,
    uid_t uid,
    uid_t gid)
{
    char path[PAL_MAX_PATH_SIZE];
    char key[PAL_MAX_PATH_SIZE];
    char uidBuf[11];
    const char* uidStr;
    char gidBuf[11];
    const char* gidStr;

    /* If no pre-exec program, nothing to do */
    if (programPath == NULL)
        return 0;

    /* Form the UID string */
    {
        size_t dummy;
        uidStr = Uint32ToStr(uidBuf, (PAL_Uint32)uid, &dummy);
    }

    /* Form the GID string */
    {
        size_t dummy;
        gidStr = Uint32ToStr(gidBuf, (PAL_Uint32)gid, &dummy);
    }

    /* Form a hash key from PREEXEC+UID+GID */
    {
        key[0] = '\0';
        Strlcat(key, programPath, PAL_MAX_PATH_SIZE);
        Strlcat(key, "+", PAL_MAX_PATH_SIZE);
        Strlcat(key, uidStr, PAL_MAX_PATH_SIZE);
        Strlcat(key, "+", PAL_MAX_PATH_SIZE);
        Strlcat(key, gidStr, PAL_MAX_PATH_SIZE);
    }

    /* If key already in cache, then return without doing anything */
    {
        static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER;

        pthread_mutex_lock(&s_mutex);

        if (_Contains(&self->cache, key))
        {
            pthread_mutex_unlock(&s_mutex);
            return 0;
        }

        /* Add key to cache */
        _Insert(&self->cache, key);
        pthread_mutex_unlock(&s_mutex);
    }

    /* If programPath is relative, form the full path of the pre-exec program */
    {
        path[0] = '\0';
        if (*programPath != '/')
        {
            const char* bindir = OMI_GetPath(ID_BINDIR);

            if (bindir != NULL)
            {
                Strlcpy(path, bindir, PAL_MAX_PATH_SIZE);
                Strlcat(path, "/", PAL_MAX_PATH_SIZE);
            }
        }

        Strlcat(path, programPath, PAL_MAX_PATH_SIZE);
    }

    /* Execute and wait on the pre-exec program to exit */
    _BlockSIGCHLD();
    {
        pid_t pid = _Exec(path, uidStr, gidStr);

        if (pid == -1)
        {
            _UnblockSIGCHLD();
            trace_PreExecFailed(path);
            return -1;
        }

        {
            pid_t r;
            int status;

            r = waitpid(pid, &status, 0);

            if (r != pid || WEXITSTATUS(status) != 0)
            {
                _UnblockSIGCHLD();
                trace_PreExecFailed(path);
                return -1;
            }
        }
    }
    _UnblockSIGCHLD();

    trace_PreExecOk(path);
    return 0;
}

ViewCVS 0.9.2