
#include "stdafx.h"
#include <dbt.h>
#include <tchar.h>
#include < tlhelp32.h>
#include "..\Ext2Srv\Ext2Pipe.h"


/*
 * global defintions
 */

#define CL_ASSERT(cond) do {switch('x') {case (cond): case 0: break;}} while (0)


/*
 * glboal variables
 */

/* pipe handles */

HANDLE g_hPipe = NULL;


/*
 * function body
 */

int Ext2StartSrv();

HANDLE Ext2OpenPipe(CHAR *PipeName)
{
    DWORD   rc;

    if (g_hPipe != NULL && g_hPipe != INVALID_HANDLE_VALUE) {
        return g_hPipe;
    }

retry:

    // open pipe (created by Ext2Srv)
    g_hPipe = CreateFile(PipeName,
                         GENERIC_READ | GENERIC_WRITE, 
                         0, NULL, OPEN_EXISTING, 0, NULL);
 
    // exit if the pipe handle is valid. 
    if (g_hPipe == INVALID_HANDLE_VALUE) {

         // exit if an error other than ERROR_PIPE_BUSY occurs. 
        rc = GetLastError(); 
        if (rc != ERROR_PIPE_BUSY) {
            return NULL;
        }
 
        // all pipe instances are busy, so wait for 20 seconds. 
        if (!WaitNamedPipe(PipeName, 20000)) { 
            return NULL;
        }

        Ext2StartSrv();

        goto retry;
    }

    return g_hPipe;
}

void Ext2ClosePipe()
{
    if (g_hPipe != NULL && g_hPipe != INVALID_HANDLE_VALUE) {
        CloseHandle(g_hPipe);
        g_hPipe = NULL;
    }
}

BOOL Ext2ReadPipe(HANDLE p, PVOID b, DWORD c, PDWORD r)
{
    DWORD bytes = 0, total = 0;
    BOOL  rc = FALSE;

    while (total < c) {
        rc = ReadFile(p,(PCHAR)b + total, c - total, &bytes, NULL);
        if (rc) {
            total += bytes;
        } else {
            break;
        }
    }

    if (r)
        *r = total;
    return rc;
}


BOOL Ext2WritePipe(HANDLE p, PVOID b, DWORD c, PDWORD w)
{
    DWORD bytes = 0, total = 0;
    BOOL  rc = FALSE;

    while (total < c) {
        rc = WriteFile(p, (PCHAR)b + total, c - total, &bytes, NULL);
        if (rc) {
            total += bytes;
        } else {
            break;
        }
    }

    if (w)
        *w = total;
    return rc;
}


DWORD Ext2PipeControl(PPIPE_REQ *pr, ULONG *len)
{
    PPIPE_REQ       p;
    PREQ_QUERY_DRV  q;
    PIPE_REQ        ac;
    DWORD           le;
    DWORD           bytes = 0;
    DWORD           rc = 0;
    int             tries = 0;

    p = *pr;
    q = (PREQ_QUERY_DRV)p->data;

Reopen_try:

    /* just try to connect pipe anyway */
    if (NULL == Ext2OpenPipe(EXT2_MGR_SRV)) {
        printf("failed to connect to server.\n");
        if (Ext2StartExt2Srv()) {
			goto Reopen_try;
        }
        goto errorout;
    }
    
    // send a message to srv
    rc = Ext2WritePipe(g_hPipe, p, p->len, &bytes);
    if (!rc || bytes != p->len) {
        le = GetLastError();
		if (le == ERROR_NO_DATA && tries++ < 10) {
			Ext2ClosePipe();
			Sleep(500 * tries);
			goto Reopen_try;
		}
        goto errorout;
    }

    bytes = 0;
    memset(&ac, 0, sizeof(ac));
    rc = Ext2ReadPipe(g_hPipe, &ac, sizeof(ac), &bytes);
    if (!rc || bytes != sizeof(ac)) {
        le = GetLastError();
        goto errorout;
    }
    if (*len < ac.len) {
        if (p) {
            delete [] p;
        }
        *len = ac.len;
        p = (PIPE_REQ *) new CHAR[*len];
        *pr = p;
        if (!p) {
            goto errorout;
        }
    }

    memset(p, 0, *len);
    memcpy(p, &ac, sizeof(ac));
    rc = Ext2ReadPipe(g_hPipe, &p->data[0],
                      ac.len - bytes, &bytes);
    if (!rc || bytes != (ac.len - sizeof(ac))) {
        le = GetLastError();
        goto errorout;
    }

errorout:

    return rc;
}


BOOL Ext2DefineDosDevicePipe(DWORD flags,  CHAR *dos,  CHAR *symlink)
{
    PPIPE_REQ       p = NULL;
    ULONG           len = REQ_BODY_SIZE;
    BOOL            rc = 0;
 
    p = (PPIPE_REQ) new CHAR[len];
    if (NULL == p)
        goto errorout;

    if (flags & DDD_REMOVE_DEFINITION) {

        /* do dismount */
        PREQ_REMOVE_DRV  q;
        memset(p, 0, len);
        p->magic = PIPE_REQ_MAGIC;
        p->len = sizeof(PIPE_REQ) + sizeof(REQ_REMOVE_DRV);
        p->cmd = CMD_REMOVE_DRV;
        q = (PREQ_REMOVE_DRV)&p->data[0];
        q->pid = GetCurrentProcessId();
        q->drive = (UCHAR)toupper(dos[0]);
        q->flags = flags;
        strcpy(&q->name[0], symlink);
        p->len += (int)strlen(symlink) + 1;

        rc = Ext2PipeControl(&p, &len);
        if (!rc) {
            printf("pipe communication failed.\n");
            goto errorout;
        }

        if (q->result) {
            printf("%C: removed.\n", q->drive);
            rc = TRUE;
        } else {
            printf("failed to remove %C:.\n", q->drive);
            goto errorout;
        }

    } else {

        /* do mount */
        PREQ_DEFINE_DRV q;
        memset(p, 0, len);
        p->magic = PIPE_REQ_MAGIC;
        p->len = sizeof(PIPE_REQ) + sizeof(REQ_DEFINE_DRV);
        p->cmd = CMD_DEFINE_DRV;
        q = (PREQ_DEFINE_DRV)&p->data[0];
        q->pid = GetCurrentProcessId();
        q->drive = (UCHAR)toupper(dos[0]);
        q->flags = flags;
        strcpy(&q->name[0], symlink);
        p->len += (int)strlen(symlink) + 1;

        rc = Ext2PipeControl(&p, &len);
        if ( !rc) {
            printf("pipe communication failed.\n");
            goto errorout;
        }

        if (q->result) {
            rc = TRUE;
            printf("%C: assigned to %s\n", q->drive, &q->name[0]);
        } else {
            printf("failed to assign %C: to %s.\n", q->drive, &q->name[0]);
            goto errorout;
        }
    }

errorout:

    if (p)
        delete []p;

    return rc;
}

BOOL Ext2DefineDosDeviceLocal(DWORD flags,  CHAR *dos,  CHAR *symlink)
{
    CHAR dosPath[] = "A:\0";
    BOOL rc;

    dosPath[0] = (CHAR)toupper(dos[0]);
    rc = DefineDosDevice(flags, dosPath, symlink);
    if (rc) {
        if (flags & DDD_REMOVE_DEFINITION) {
            Ext2DrvNotify(dosPath[0], FALSE);
        } else {
            Ext2DrvNotify(dosPath[0], TRUE);
        }
    }

    return rc;
}

BOOL Ext2DefineDosDevice(DWORD flags,  CHAR *dos,  CHAR *symlink)
{
    if (CanDoLocalMount()) {
        return Ext2DefineDosDeviceLocal(flags, dos, symlink);
    }

    return Ext2DefineDosDevicePipe(flags, dos, symlink);
}


DWORD Ext2QueryDrivePipe(CHAR drive, CHAR *symlink)
{
    PPIPE_REQ       p = NULL;
    PREQ_QUERY_DRV  q;
    ULONG           len = REQ_BODY_SIZE;
    DWORD           rc = 0;
 
    p = (PPIPE_REQ) new CHAR[len];
    if (NULL == p)
        goto errorout;

     /* do query */
    memset(p, 0, len);
    p->magic = PIPE_REQ_MAGIC;
    p->len = sizeof(PIPE_REQ) + sizeof(REQ_QUERY_DRV);
    p->cmd = CMD_QUERY_DRV;
    q = (PREQ_QUERY_DRV)&p->data[0];
    q->drive = (UCHAR)toupper(drive);
    rc = Ext2PipeControl(&p, &len);
    if ( !rc) {
        printf("pipe communication failed.\n");
        goto errorout;
    }

    rc = q->type;
    if (q->result) {
        printf("%C: -> %s\n", q->drive, &q->name[0]);
        strcpy(symlink, &q->name[0]);
    } else {
        printf("failed to queyr %C:\n", q->drive);
        goto errorout;
    }

errorout:

    if (p)
        delete []p;

    return rc;
}

DWORD Ext2QueryDriveLocal(CHAR drive, CHAR *symlink)
{
    DWORD           type;
    CHAR            devPath[] = "A:";

    devPath[0] = drive;
    type = GetDriveType(devPath);
    if (type != DRIVE_NO_ROOT_DIR) {
        QueryDosDevice(devPath, symlink, MAX_PATH);
    }

    return type;
}

DWORD Ext2QueryDrive(CHAR drive, CHAR *symlink)
{
    if (CanDoLocalMount()) {
        return Ext2QueryDriveLocal(drive, symlink);
    }

    return Ext2QueryDrivePipe(drive, symlink);
}

int Ext2CreateToken(DWORD pid, DWORD *sid, HANDLE *token)
{
    HANDLE  h = NULL, token_user = NULL;
    DWORD dwTokenRights;
    PSID IntegrityLevelSid = NULL;
    CString SIDvalue;
    TOKEN_MANDATORY_LABEL IntegrityLevelToken = {0};

    int     rc = -1;

    rc = ProcessIdToSessionId(pid, sid);
    if (!rc) {
        rc = -1 * GetLastError();
        goto errorout;
    }

    h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (h == NULL || h == INVALID_HANDLE_VALUE) {
        rc = -1 * GetLastError();
        goto errorout;
    }

    rc = OpenProcessToken(h, TOKEN_DUPLICATE, &token_user);
    if(!rc) {
        rc = -1 * GetLastError();
        goto errorout;
    }

    dwTokenRights = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY |
                    TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT |
                    TOKEN_ADJUST_SESSIONID;
    rc = DuplicateTokenEx(token_user, dwTokenRights, NULL,
                          SecurityImpersonation /*SecurityIdentification*/,
                          TokenPrimary, token);
    if (!rc) {
        rc = -1 * GetLastError();
        goto errorout;
    }

    //  S-revision-authority-rid
    //  S-1-16-?
    //  16 represents
    //     SECURITY_MANDATORY_LABEL_AUTHORITY

    SIDvalue.Format("S-1-16-%d", SECURITY_MANDATORY_MEDIUM_RID);
    if(!ConvertStringSidToSidA(SIDvalue, &IntegrityLevelSid)) {
        /* not critical, ignore this error */
        goto errorout;
    }

    IntegrityLevelToken.Label.Attributes = SE_GROUP_INTEGRITY;
    IntegrityLevelToken.Label.Sid = IntegrityLevelSid;

    SetTokenInformation(*token,
                        TokenIntegrityLevel,
                        &IntegrityLevelToken,
                        sizeof(TOKEN_MANDATORY_LABEL) +
                        GetLengthSid(IntegrityLevelSid));
    LocalFree(IntegrityLevelSid);

errorout:

    if (token_user && token_user != INVALID_HANDLE_VALUE)
        CloseHandle(token_user);

    if (h && h != INVALID_HANDLE_VALUE)
        CloseHandle(h);

    return rc;
}


DWORD Ext2QuerySrv(TCHAR *srv, DWORD *pids, DWORD as)
{
    DWORD  total = 0;
    HANDLE p = NULL;
    PROCESSENTRY32 r = {0};

    p = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == p)
        return 0;

    r.dwSize=sizeof(PROCESSENTRY32);
    if (!Process32First(p, &r)) {
        goto errorout;
    }

    do {
        TCHAR *n = _tcsrchr(&r.szExeFile[0], _T('\\'));
        if (!n)
            n = &r.szExeFile[0];
        if (_tcsicmp(n, srv) == 0) {
            pids[total++] = r.th32ProcessID;
            if (total >= as)
                break;
        }
        
    } while(Process32Next(p, &r));

errorout:

    CloseHandle(p);

    return total;
}

TCHAR *Ext2StrLastA(TCHAR *t, TCHAR *s)
{
    int lt = (int)strlen(t), ls = (int)strlen(s), i;

    for (i = lt - ls; i >= 0; i--) {
        if (0 == _strnicmp(&t[i], s, ls))
            return &t[i];
    }

    return NULL;
}

TCHAR * Ext2BuildCMDA()
{
    TCHAR  cmd[512] = {0}, *p,  *refresh = NULL;
    int    len = 0;

    if (GetModuleFileName(NULL, cmd, 510)) {
    } else { 
        strcpy(cmd, GetCommandLine());
    }

    len = (int)_tcslen(cmd) + 2;
    refresh = new TCHAR[len];
    if (!refresh)
        goto errorout;
    memset(refresh, 0, sizeof(TCHAR)*len);
    _tcscpy(refresh, cmd);
    p = _tcsstr(refresh, _T("/"));
    if (p)
        *p = 0;

    p = Ext2StrLastA(refresh, "Ext2Mgr");
    if (p) {
        p[4] = 'S';
        p[5] = 'r';
        p[6] = 'v';
    } else {
        delete [] refresh;
        refresh = NULL;
    }

errorout:
    return refresh;
}

WCHAR *Ext2StrLastW(WCHAR *t, WCHAR *s)
{
    int lt = (int)wcslen(t), ls = (int)wcslen(s), i;

    for (i = lt - ls; i >= 0; i--) {
        if (0 == _wcsnicmp(&t[i], s, ls))
            return &t[i];
    }

    return NULL;
}

WCHAR *Ext2BuildCMDW()
{
    WCHAR  cmd[512] = {0}, *p, *refresh = NULL;
    int    len = 0;

    if (GetModuleFileNameW(NULL, cmd, 510)) {
    } else { 
        wcscpy(cmd, GetCommandLineW());
    }

    len = (int)wcslen(cmd) + 2;
    refresh = new WCHAR[len];
    if (!refresh)
        goto errorout;
    memset(refresh, 0, sizeof(WCHAR)*len);
    wcscpy(refresh, cmd);
    p = wcsstr(refresh, L"/");
    if (p)
        *p = 0;

    p = Ext2StrLastW(refresh, L"Ext2Mgr");
    if (p) {
        p[4] = 'S';
        p[5] = 'r';
        p[6] = 'v';
    } else {
        delete [] refresh;
        refresh = NULL;
    }

errorout:
    return refresh;
}

PROCESS_INFORMATION g_Ext2Srv;


typedef BOOL
(WINAPI *lpfnCreateProcessWithTokenW)(
    HANDLE hToken,
    DWORD dwLogonFlags,
    LPCWSTR lpApplicationName,
    LPWSTR lpCommandLine,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCWSTR lpCurrentDirectory,
    LPSTARTUPINFOW lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
    );

lpfnCreateProcessWithTokenW g_CreateProcessWithTokenW;

int Ext2StartSrvAsUser(HANDLE token)
{
    LPWSTR  cmd = NULL;
    STARTUPINFOW si = {0};
    PROCESS_INFORMATION pi = {0};
    int     rc = -1;

    cmd = Ext2BuildCMDW();
    if (!cmd) {
        rc = -1;
        goto errorout;
    }

    si.cb = sizeof( STARTUPINFO );
    if (token && g_CreateProcessWithTokenW) {
        rc = g_CreateProcessWithTokenW(
                             token, 0, NULL, cmd,
                             NORMAL_PRIORITY_CLASS |
                             CREATE_NO_WINDOW, 
                             NULL, NULL,
                             &si, &pi );
    } else {
        rc = CreateProcessW(  NULL, cmd, NULL, NULL,
                             FALSE, NORMAL_PRIORITY_CLASS |
                             CREATE_NO_WINDOW, NULL, NULL,
                             &si, &pi );
    }

    if (!rc) {
        rc = -1 * GetLastError();
        goto errorout;
    }

    g_Ext2Srv = pi;

errorout:

    if (cmd)
        delete []cmd;

    return rc;
}

int Ext2StartSrvAsElevated()
{
    LPTSTR  cmd = NULL;
    STARTUPINFO si = {0};
    PROCESS_INFORMATION pi = {0};
    int     rc = -1;

    cmd = Ext2BuildCMDA();
    if (!cmd) {
        rc = -1;
        goto errorout;
    }

    si.cb = sizeof( STARTUPINFO );
    rc = CreateProcess(  NULL, cmd, NULL, NULL,
                         FALSE, NORMAL_PRIORITY_CLASS |
                         CREATE_NO_WINDOW, NULL, NULL,
                         &si, &pi );

    if (!rc) {
        rc = -1 * GetLastError();
        goto errorout;
    }

    g_Ext2Srv = pi;

errorout:

    if (cmd)
        delete []cmd;

    return rc;
}

int Ext2StartSrv()
{
    DWORD   pid[12] = {0}, num;
    int     rc = -1;

    num = Ext2QuerySrv(_T("Ext2Srv.exe"), pid, 10);
    if (num) {
        /* already started */
        rc = num;
        goto errorout;
    }

    if (IsVistaOrAbove()) {

        DWORD   sid = 0, mysid = 0;
        HANDLE  token = 0;

        num = Ext2QuerySrv(_T("Explorer.exe"), &pid[0], 10);
        if (!num) {
            goto errorout;
        }

        pid[num] = GetCurrentProcessId();
        num++;

        rc = ProcessIdToSessionId(GetCurrentProcessId(), &mysid);
        if (!rc) {
            rc = -1 * GetLastError();
            goto errorout;
        }

        while (num > 0 && pid[num - 1]) {

            rc = Ext2CreateToken(pid[--num], &sid, &token);
            if (rc > 0) {
                if (sid == mysid)  {
                    rc = Ext2StartSrvAsUser(token);
                } else {
                    rc = 0;
                }
                if (token && token != INVALID_HANDLE_VALUE) {
                    CloseHandle(token);
                    token = NULL;
                }
                if (rc == 1) {
                    break;
                }
            }
        }

    } else {

        Ext2StartSrvAsElevated();
    }

errorout:

    return rc;
}

void Ext2StopSrv()
{
    PROCESS_INFORMATION pi = g_Ext2Srv;

    memset(&g_Ext2Srv, 0, sizeof(pi));

    /* wait until process exits or timeouts */

    if (pi.hProcess != INVALID_HANDLE_VALUE) { 
        TerminateProcess(pi.hProcess, -1);
        CloseHandle(pi.hProcess); 
    } 
    if (pi.hThread != INVALID_HANDLE_VALUE) {
        CloseHandle(pi.hThread); 
    }
}

/* Ext2EnablePrivilege(SE_TCB_NAME) */

BOOL Ext2EnablePrivilege(LPCTSTR lpszPrivilegeName)
{
    TOKEN_PRIVILEGES tp = {0};
    HANDLE  token;
    LUID    luid;
    BOOL    rc;

    rc = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES |
                          TOKEN_QUERY | TOKEN_READ, &token);
    if(!rc)
        goto errorout;

    rc = LookupPrivilegeValue(NULL, lpszPrivilegeName, &luid);
    if(!rc)
        goto errorout;

    /* initialize token privilege */
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    rc = AdjustTokenPrivileges(token, FALSE, &tp, NULL, NULL, NULL);
    CloseHandle(token);

errorout:

    return rc;
}

#define KEY_WOW64_64KEY         (0x0100)

BOOL KdpQueryRegDWORD(CHAR *service, CHAR *value, DWORD *data)
{
    int     rc = TRUE;
    HKEY    key;
    CHAR   keyPath[MAX_PATH] = "SYSTEM\\CurrentControlSet\\Services\\";
    LONG    status, type, len = 0;

    strcat(keyPath, service);
    status = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                            keyPath,
                            0,
                            KEY_ALL_ACCESS | KEY_WOW64_64KEY,
                            &key) ;
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
        goto errorout;
    }

    len = sizeof(DWORD);
    status = RegQueryValueEx( key,
                              value,
                              0,
                              (LPDWORD)&type,
                              (BYTE *)data,
                              (LPDWORD)&len);
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
    }
    RegCloseKey(key);

errorout:

    return rc;
}

BOOL KdpSetRegDWORD(CHAR *service, CHAR *value, DWORD data)
{
    int     rc = TRUE;
    HKEY    key;
    CHAR   keyPath[MAX_PATH] = "SYSTEM\\CurrentControlSet\\Services\\";
    LONG    status;

    strcat(keyPath, service);
    status = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                            keyPath,
                            0,
                            KEY_ALL_ACCESS | KEY_WOW64_64KEY,
                            &key) ;
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
        goto errorout;
    }

    status = RegSetValueEx( key,
                              value,
                              0,
                              REG_DWORD,
                              (BYTE *)&data,
                              sizeof(DWORD));
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
    }
    RegCloseKey(key);

errorout:

    return rc;
}

BOOL Ext2StartPipeSrv()
{
    HMODULE hAdvapi32;
    BOOL rc = FALSE;

    if (CanDoLocalMount()) {
        return TRUE;
    }

    /* query advapi32!CreateProcessWithToken */
    hAdvapi32 = GetModuleHandle("Advapi32.DLL");
    g_CreateProcessWithTokenW = (lpfnCreateProcessWithTokenW)
        GetProcAddress(hAdvapi32, "CreateProcessWithTokenW");

    if (!g_CreateProcessWithTokenW) {
        rc = FALSE;
        goto errorout;
    }

    /* add more privilege for toke duplication */
    Ext2EnablePrivilege(SE_INCREASE_QUOTA_NAME);
    Ext2EnablePrivilege(SE_TCB_NAME);

    /* start Ext2Srv.exe */
    rc = Ext2StartSrv();

errorout:

    return rc;
}

VOID Ext2StopPipeSrv()
{
    /* close pipe handles */
    Ext2ClosePipe();

    /* terminate process: Ext2Srv */
    Ext2StopSrv();
}