VirtualBox

Changeset 59542 in vbox


Ignore:
Timestamp:
Feb 1, 2016 1:27:36 PM (9 years ago)
Author:
vboxsync
Message:

IPRT: r3/win/process-win.cpp: Try to use the LSA cache for the domain authentication tokens to also allow impersonation if the Active Directory server is not around or the VM is offline.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/win/process-win.cpp

    r59519 r59542  
    55
    66/*
    7  * Copyright (C) 2006-2015 Oracle Corporation
     7 * Copyright (C) 2006-2016 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3838#include <errno.h>
    3939#include <Strsafe.h>
     40#include <LsaLookup.h>
    4041#include <Lmcons.h>
     42
     43#define _NTDEF_ /* Prevents redefining (P)UNICODE_STRING. */
     44#include <Ntsecapi.h>
    4145
    4246#include <iprt/process.h>
     
    106110    HANDLE          hProcess;
    107111}                  *g_paProcesses;
     112
     113/** Strucutre for storing a user's account info.
     114 *  Must be free'd with rtProcWinFreeAccountInfo(). */
     115typedef struct RTPROCWINACCOUNTINFO
     116{
     117    /** User name. */
     118    PRTUTF16        pwszUserName;
     119    /** Domain this account is tied to. Can be NULL if no domain is being used. */
     120    PRTUTF16        pwszDomain;
     121} RTPROCWINACCOUNTINFO, *PRTPROCWINACCOUNTINFO;
    108122
    109123/** @name userenv.dll imports (we don't unload it).
     
    12701284
    12711285/**
     1286 * Extracts the user name + domain from a given UPN (User Principal Name, [email protected]) or
     1287 * Down-Level Logon Name format (example.com\joedoe) string.
     1288 *
     1289 * @return  IPRT status code.
     1290 * @param   pwszString      Pointer to string to extract the account info from.
     1291 * @param   pAccountInfo    Where to store the parsed account info.
     1292 *                          Must be free'd with rtProcWinFreeAccountInfo().
     1293 */
     1294static int rtProcWinParseAccountInfo(PRTUTF16 pwszString, PRTPROCWINACCOUNTINFO pAccountInfo)
     1295{
     1296    AssertPtrReturn(pwszString,   VERR_INVALID_POINTER);
     1297    AssertPtrReturn(pAccountInfo, VERR_INVALID_POINTER);
     1298
     1299    /*
     1300     * Note: UPN handling is defined in RFC 822. We only implement very rudimentary parsing for the user
     1301     *       name and domain fields though.
     1302     */
     1303    char *pszString;
     1304    int rc = RTUtf16ToUtf8(pwszString, &pszString);
     1305    if (RT_SUCCESS(rc))
     1306    {
     1307        do
     1308        {
     1309            /* UPN or FQDN handling needed? */
     1310            /** @todo Add more validation here as needed. Regular expressions would be nice. */
     1311            char *pszDelim = strchr(pszString, '@');
     1312            if (pszDelim) /* UPN name? */
     1313            {
     1314                rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszUserName, 0, NULL);
     1315                if (RT_FAILURE(rc))
     1316                    break;
     1317
     1318                rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszDomain, 0, NULL);
     1319                if (RT_FAILURE(rc))
     1320                    break;
     1321            }
     1322            else if (pszDelim = strchr(pszString, '\\')) /* FQDN name? */
     1323            {
     1324                rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszDomain, 0, NULL);
     1325                if (RT_FAILURE(rc))
     1326                    break;
     1327
     1328                rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszUserName, 0, NULL);
     1329                if (RT_FAILURE(rc))
     1330                    break;
     1331            }
     1332            else
     1333                rc = VERR_INVALID_PARAMETER;
     1334
     1335        } while (0);
     1336
     1337        RTStrFree(pszString);
     1338    }
     1339
     1340#ifdef DEBUG
     1341    LogRelFunc(("Name  : %ls\n", pAccountInfo->pwszUserName));
     1342    LogRelFunc(("Domain: %ls\n", pAccountInfo->pwszDomain));
     1343#endif
     1344
     1345    if (RT_FAILURE(rc))
     1346        LogRelFunc(("Parsing \"%ls\" failed with rc=%Rrc\n", pwszString, rc));
     1347    return rc;
     1348}
     1349
     1350
     1351static void rtProcWinFreeAccountInfo(PRTPROCWINACCOUNTINFO pAccountInfo)
     1352{
     1353    if (!pAccountInfo)
     1354        return;
     1355
     1356    if (pAccountInfo->pwszUserName)
     1357    {
     1358        RTUtf16Free(pAccountInfo->pwszUserName);
     1359        pAccountInfo->pwszUserName = NULL;
     1360    }
     1361
     1362    if (pAccountInfo->pwszDomain)
     1363    {
     1364        RTUtf16Free(pAccountInfo->pwszDomain);
     1365        pAccountInfo->pwszDomain = NULL;
     1366    }
     1367}
     1368
     1369
     1370/**
    12721371 * Method \#2.
    12731372 */
     
    13201419        if (fFlags & RTPROC_FLAGS_SERVICE)
    13211420        {
     1421            PSID pSid = NULL;
     1422
    13221423            /* Try query the SID and domain sizes first. */
    1323             DWORD           cbSid      = 0; /* Must be zero to query size! */
    1324             DWORD           cwcDomain  = 0;
    1325             SID_NAME_USE    SidNameUse = SidTypeUser;
     1424            DWORD        cbSid      = 0; /* Must be zero to query size! */
     1425            DWORD        cwcDomain  = 0;
     1426            SID_NAME_USE SidNameUse = SidTypeUser;
    13261427            fRc = LookupAccountNameW(NULL, pwszUser, NULL, &cbSid, NULL, &cwcDomain, &SidNameUse);
    1327 
    1328             /* Allocate memory for the LookupAccountNameW output buffers and do it for real. */
    1329             cbSid = fRc && cbSid != 0 ? cbSid + 16 : _1K;
    1330             PSID pSid = (PSID)RTMemAllocZ(cbSid);
    1331             if (pSid)
    1332             {
    1333                 cwcDomain = fRc ? cwcDomain + 2 : _4K;
    1334                 PRTUTF16 pwszDomain = (PRTUTF16)RTMemAllocZ(cwcDomain * sizeof(RTUTF16));
    1335                 if (pwszDomain)
     1428            if (!fRc)
     1429            {
     1430                dwErr = GetLastError();
     1431
     1432                /*
     1433                 * The errors ERROR_TRUSTED_DOMAIN_FAILURE and ERROR_TRUSTED_RELATIONSHIP_FAILURE
     1434                 * can happen if an ADC (Active Domain Controller) is offline or not reachable.
     1435                 *
     1436                 * Try to handle these errors gracefully by asking the local LSA cache of the
     1437                 * client OS instead then. For this to work, the desired user must have at
     1438                 * least logged in once at that client -- otherwise there will be no cached
     1439                 * authentication available and this fallback will fail.
     1440                 */
     1441                if (   dwErr == ERROR_TRUSTED_DOMAIN_FAILURE
     1442                    || dwErr == ERROR_TRUSTED_RELATIONSHIP_FAILURE)
    13361443                {
    1337                     /* Note: Just pass in the UPN (User Principal Name), e.g. [email protected] */
    1338                     if (LookupAccountNameW(NULL /*lpSystemName*/, pwszUser, pSid, &cbSid, pwszDomain, &cwcDomain, &SidNameUse))
     1444                    LSA_OBJECT_ATTRIBUTES objAttr;
     1445                    RT_ZERO(objAttr);
     1446                    objAttr.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
     1447
     1448                    LSA_HANDLE lsahPolicy;
     1449                    NTSTATUS ntSts = LsaOpenPolicy(NULL, &objAttr, POLICY_LOOKUP_NAMES, &lsahPolicy);
     1450                    if (ntSts == STATUS_SUCCESS)
    13391451                    {
    1340                         if (IsValidSid(pSid))
     1452                        RTPROCWINACCOUNTINFO accountInfo;
     1453                        RT_ZERO(accountInfo);
     1454                        rc = rtProcWinParseAccountInfo(pwszUser, &accountInfo);
     1455                        AssertRC(rc);
     1456                        AssertPtr(accountInfo.pwszUserName);
     1457
     1458                        LSA_UNICODE_STRING lsaUser;
     1459                        lsaUser.Buffer        = accountInfo.pwszUserName;
     1460                        lsaUser.Length        = RTUtf16Len(accountInfo.pwszUserName) * sizeof(WCHAR);
     1461                        lsaUser.MaximumLength = lsaUser.Length;
     1462
     1463                        PLSA_REFERENCED_DOMAIN_LIST pDomainList     = NULL;
     1464                        PLSA_TRANSLATED_SID2        pTranslatedSids = NULL;
     1465                        ntSts = LsaLookupNames2(lsahPolicy, 0 /* Flags */,
     1466                                                1 /* Number of users to lookup */, &lsaUser, &pDomainList, &pTranslatedSids);
     1467                        if (ntSts == STATUS_SUCCESS)
    13411468                        {
    1342                             /* Array of process names we want to look for. */
    1343                             static const char * const s_papszProcNames[] =
     1469                            AssertPtr(pDomainList);
     1470                            AssertPtr(pTranslatedSids);
     1471                            LogRelFunc(("LsaLookupNames2: cDomains=%u, DomainIndex=%ld, SidUse=%ld\n",
     1472                                        pDomainList->Entries, pTranslatedSids[0].DomainIndex, pTranslatedSids[0].Use));
     1473                            Assert(pTranslatedSids[0].Use == SidTypeUser);
     1474
     1475                            if (pDomainList->Entries)
    13441476                            {
    1345 #ifdef VBOX                     /* The explorer entry is a fallback in case GA aren't installed. */
    1346                                 { "VBoxTray.exe" },
    1347 #endif
    1348                                 { "explorer.exe" },
    1349                                 NULL
    1350                             };
    1351                             fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, &hTokenUserDesktop);
     1477                                AssertPtr(pDomainList->Domains);
     1478                                LogRelFunc(("LsaLookupNames2: Domain=%ls\n",
     1479                                            pDomainList->Domains[pTranslatedSids[0].DomainIndex].Name.Buffer));
     1480                            }
     1481
     1482                            cbSid = GetLengthSid(pTranslatedSids->Sid) + 16;
     1483                            Assert(cbSid);
     1484                            pSid = (PSID)RTMemAllocZ(cbSid);
     1485                            if (!CopySid(cbSid, pSid, pTranslatedSids->Sid))
     1486                            {
     1487                                dwErr = GetLastError();
     1488                                LogRelFunc(("CopySid failed with: %ld\n", dwErr));
     1489                                rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_2;
     1490                            }
    13521491                        }
    13531492                        else
    13541493                        {
    1355                             dwErr = GetLastError();
    1356                             rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_4;
     1494                            dwErr = LsaNtStatusToWinError(ntSts);
     1495                            LogRelFunc(("LsaLookupNames2 failed with: %ld\n", dwErr));
     1496                            rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_2;
    13571497                        }
     1498
     1499                        if (pDomainList)
     1500                        {
     1501                            LsaFreeMemory(pDomainList);
     1502                            pDomainList = NULL;
     1503                        }
     1504                        if (pTranslatedSids)
     1505                        {
     1506                            LsaFreeMemory(pTranslatedSids);
     1507                            pTranslatedSids = NULL;
     1508                        }
     1509#ifndef DEBUG
     1510 # error "FOOOO!!!"
     1511#endif
     1512                        rtProcWinFreeAccountInfo(&accountInfo);
     1513                        LsaClose(lsahPolicy);
    13581514                    }
    13591515                    else
    13601516                    {
    1361                         dwErr = GetLastError();
     1517                        dwErr = LsaNtStatusToWinError(ntSts);
     1518                        LogRelFunc(("LsaOpenPolicy failed with: %ld\n", dwErr));
    13621519                        rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_3;
    13631520                    }
    1364                     RTMemFree(pwszDomain);
     1521
     1522                    /* Note: pSid will be free'd down below. */
     1523                }
     1524                else if (dwErr == ERROR_INSUFFICIENT_BUFFER)
     1525                {
     1526                    /* Allocate memory for the LookupAccountNameW output buffers and do it for real. */
     1527                    cbSid = fRc && cbSid != 0 ? cbSid + 16 : _1K;
     1528                    pSid = (PSID)RTMemAllocZ(cbSid);
     1529                    if (pSid)
     1530                    {
     1531                        cwcDomain = fRc ? cwcDomain + 2 : _4K;
     1532                        PRTUTF16 pwszDomain = (PRTUTF16)RTMemAllocZ(cwcDomain * sizeof(RTUTF16));
     1533                        if (pwszDomain)
     1534                        {
     1535                            /* Note: Just pass in the UPN (User Principal Name), e.g. [email protected] */
     1536                            if (!LookupAccountNameW(NULL /*lpSystemName*/, pwszUser, pSid, &cbSid, pwszDomain, &cwcDomain,
     1537                                                    &SidNameUse))
     1538                            {
     1539                                dwErr = GetLastError();
     1540                                LogRelFunc(("LookupAccountNameW(2) failed with: %ld\n", dwErr));
     1541                                rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_4;
     1542                            }
     1543
     1544                            RTMemFree(pwszDomain);
     1545                        }
     1546                        else
     1547                            rc = VERR_NO_MEMORY;
     1548
     1549                        /* Note: pSid will be free'd down below. */
     1550                    }
     1551                    else
     1552                        rc = VERR_NO_MEMORY;
    13651553                }
    13661554                else
    1367                     rc = VERR_NO_MEMORY;
     1555                {
     1556                    LogRelFunc(("LookupAccountNameW(1) failed with: %ld\n", dwErr));
     1557                    rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_2;
     1558                }
     1559            }
     1560
     1561            if (pSid)
     1562            {
     1563                if (IsValidSid(pSid))
     1564                {
     1565                    /* Array of process names we want to look for. */
     1566                    static const char * const s_papszProcNames[] =
     1567                    {
     1568#ifdef VBOX             /* The explorer entry is a fallback in case GA aren't installed. */
     1569                        { "VBoxTray.exe" },
     1570#endif
     1571                        { "explorer.exe" },
     1572                        NULL
     1573                    };
     1574                    fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, &hTokenUserDesktop);
     1575                    dwErr  = 0;
     1576                }
     1577                else
     1578                {
     1579                    dwErr = GetLastError();
     1580                    LogRelFunc(("SID is invalid: %ld\n", dwErr));
     1581                    rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_3;
     1582                }
    13681583
    13691584                RTMemFree(pSid);
    13701585            }
    1371             else
    1372                 rc = VERR_NO_MEMORY;
    13731586        }
    13741587        /* else: !RTPROC_FLAGS_SERVICE: Nothing to do here right now. */
     
    15051718            LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
    15061719    }
     1720
     1721    LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
    15071722    return rc;
    15081723}
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette