Sample: Creating Custom IRM Protectors
This documentation is archived and is not being maintained.

Sample: Creating Custom IRM Protectors

Windows SharePoint Services 3

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

You can create custom rights-management protectors that convert files to rights-management formats when the user downloads them by using the extensible Information Rights Management (IRM) architecture in Windows SharePoint Services. You can convert those files back to nonencrypted file formats by using the same custom protectors when the user uploads the files for storage in the SharePoint document library.

Developers can control the location of the protected content, issuance license (IL), and end-user license (EUL) within the protected version of the file, and vary the format of the protected document based on file type by using a custom IRM protector. Developers can also remove and replace the protection type with a different rights management platform.

This code sample, the SampleDocProtector project, shows how to use IRM protectors for text files. The IRM protector sample protects the text files by using Windows Rights Management Services (RMS) infrastructure in Windows SharePoint Services. It can also be set to encrypt the text files itself, using an encryption method of your choice.

NoteNote

This topic assumes that you are familiar with IRM in Windows SharePoint Services, RMS, the IRM interfaces, and IRM classes. For more information, see Information Rights Management in Windows SharePoint Services Overview, I_IrmProtector Interface, and Custom IRM Protectors.

You can create two types of IRM protectors that use the IRM framework in Windows SharePoint Services: integrated protectors and autonomous protectors.

NoteNote

For more information about integrated and autonomous protectors, see Custom IRM Protectors.

The SampleDocProtector code sample shows how you can use both integrated protectors and autonomous protectors. Integrated protectors rely on Windows SharePoint Services for access to the Windows RMS platform for generating protected versions of files and for removing protection from rights-managed files. Autonomous protectors, however, must configure and execute the entire rights-management process by themselves. Autonomous protectors may access Windows RMS platform directly, or employ some other rights-management platform.

NoteNote

In an actual implementation, you use either the integrated or autonomous protectors, but not both at the same time. This sample is just a demonstration of how to use the IRM protectors.

Important noteImportant

Do not use the sample as is. This sample is a simple demonstration on how to use custom IRM protectors. No client-side application, like Notepad, can consume the encrypted file format specified in the sample. Notepad cannot open and decrypt the text file.

You can find the SampleDocProtector project in the SampleDocProtector code sample folder in the ECM Starter Kit folder in the Windows SharePoint Services 3.0 SDK download.

For more information about the various files that compose the sample project and their purposes, see the ReadMe.txt file included in the SampleDocProtector project folder.

Each IRM protector must be a COM component that implements the I_IrmProtector interface. Although both integrated and autonomous protectors must implement the I_IrmProtector interface, each type of protector implements the interface differently, because Windows SharePoint Services calls the two types using different methods of the interface. Windows SharePoint Services invokes integrated protectors by using the HrProtectRMS and HrUnprotectRMS methods; it invokes autonomous protectors by using the HrProtect and HrUnprotect methods.

The HrProtectRMS function in the SampleDocProtector project has a parameter named piid. The piid parameter is a link to functions and data that assist the encryption and decryption process. The piid argument, which is an I_IrmPolicyInfoRMS object, contains multiple helper functions.

Note that the sample HrProtectRMS function does not call the Windows RMS APIs. This is because all calls to the RMS API, including calls to the RMS server, are handled automatically by Windows SharePoint Services. Instead, the HrProtectRMS function accesses the RMS objects, such as the document IL and EULs, through the piid argument, and then saves them to a stream.

The StgIsStorageILockBytes method is a simple way of storing binary data. The code snippet for the HrProtectRMS function is shown in the following code.

NoteNote

You can find the following code snippet in the SampleProtector.cpp file in the SampleDocProtector project.

HRESULT CSampleProtector::HrProtectRMS(ILockBytes *pilbInput, ILockBytes *pilbOutput, I_IrmPolicyInfoRMS *piid, DWORD *pdwStatus)
{
    HRESULT hr = S_OK;
    IStorage *pistgOutput = NULL;
        IStorage *pistgLicenses = NULL;
        IStream *pistmT = NULL;
        BSTR bstr = NULL;
    UINT cEULs = 0;
    BSTR *rgbstrEUL = NULL;
    BSTR *rgbstrId = NULL;
    I_IrmCrypt *piic = NULL;
    BYTE rgbBuffer[STACK_RW_BUF_SIZE];
          = 16;
    ULARGE_INTEGER ulOffset = { 0 };

    if (pilbInput == NULL || pilbOutput == NULL || piid == NULL || pdwStatus == NULL)
    {
        hr = E_INVALIDARG;
        goto LExit;
    }

    *pdwStatus = MSOIPI_STATUS_UNKNOWN;

    hr = StgIsStorageILockBytes(pilbOutput);
    if (FAILED(hr))
        goto LExit;

    if (hr != S_FALSE)
    {
        hr = StgOpenStorageOnILockBytes(pilbOutput, NULL, 
        STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, NULL, &pistgOutput);
        if (hr == STG_E_FILEALREADYEXISTS)
            goto LCreateStorage;
    }
    else
    {
LCreateStorage:
        hr = StgCreateDocfileOnILockBytes(pilbOutput, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, &pistgOutput);
    }
    if (FAILED(hr))
        goto LExit;

        // Write out the key.
        BSTR key = SysAllocString(wzKey);
        hr = HrWriteSubStream(pistgOutput, wzKey, &key);

        // Write out the signed IL.
    hr = piid->HrGetSignedIL(&bstr);
    if (FAILED(hr) || bstr == NULL)
        goto LExit;
        hr = HrWriteSubStream(pistgOutput, wzSignedIL, &bstr);

        // Write out the server Id.
    hr = piid->HrGetServerId(&bstr);
    if (FAILED(hr) || bstr == NULL)
        goto LExit;
        hr = HrWriteSubStream(pistgOutput, wzServerID, &bstr);
        if (FAILED(hr))
            goto LExit;

        // Write out the licenses.
        hr = HrEnsureStg(pistgOutput, wzLicenses, true /*fReadWrite*/, &pistgLicenses);
        if (FAILED(hr))
            goto LExit;
    hr = piid->HrGetEULs(NULL, NULL, &cEULs);
        // printf("Got %d EULS", cEULs);
    if (FAILED(hr))
        goto LExit;
    rgbstrEUL = (BSTR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cEULs * sizeof(BSTR));
    if (rgbstrEUL == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto LExit;
    }
    rgbstrId = (BSTR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cEULs * sizeof(BSTR));
    if (rgbstrId == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto LExit;
    }
    hr = piid->HrGetEULs(rgbstrEUL, rgbstrId, &cEULs);

    if (FAILED(hr))
        goto LExit;
        WCHAR    streamId[STREAM_MAX];
    for (UINT iEUL = 0; iEUL < cEULs; iEUL++)
    {
        if (rgbstrEUL[iEUL] != NULL && rgbstrId[iEUL] != NULL)
            {
            hr = StringCchCopy(streamId, cElements(streamId), wzEULPrefix);
            WCHAR   *pwz         = streamId + wcslen(streamId);
            *pwz++ = (WCHAR) iEUL;
            *pwz = NULL;
            hr = HrWriteSubStream(pistgLicenses, streamId, &rgbstrEUL[iEUL]);
            if (FAILED(hr))
                goto LExit;
        }
        if (rgbstrEUL[iEUL] != NULL)
            SysFreeString(rgbstrEUL[iEUL]);
        if (rgbstrId[iEUL] != NULL)
            SysFreeString(rgbstrId[iEUL]);
    }
    HeapFree(GetProcessHeap(), 0, rgbstrId);
    rgbstrId = NULL;
    HeapFree(GetProcessHeap(), 0, rgbstrEUL);
    rgbstrEUL = NULL;
    pistgLicenses->Release();
    pistgLicenses = NULL;

    // Write out the rights template.
    hr = piid->HrGetRightsTemplate(&bstr);
    if (FAILED(hr) || bstr == NULL)
        goto LExit;
    hr = HrWriteSubStream(pistgOutput, wzRightsTemplate, &bstr);
    if (FAILED(hr))
        goto LExit;

    // Write out the list GUID.
    hr = piid->HrGetListGuid(&bstr);
    if (FAILED(hr) || bstr == NULL)
        goto LExit;
    hr = HrWriteSubStream(pistgOutput, wzListGuid, &bstr);
    if (FAILED(hr))
        goto LExit;

    // Write out the content.
    hr = HrEnsureStm(pistgOutput, wzContent, true /*fReadWrite*/, &pistmT);
    if (FAILED(hr))
        goto LExit;
    hr = piid->HrGetICrypt(&piic);
    if (FAILED(hr) || piic == NULL)
        goto LExit;
    ulOffset.QuadPart = 0;
    while (true)
    {
        ULONG cbRead = cElements(rgbBuffer);
        ULONG cbWritten = 0;
        hr = pilbInput->ReadAt(ulOffset, rgbBuffer, cbRead, &cbRead);
        if (FAILED(hr))
            goto LExit;
        if (cbRead == 0)
            goto LFinished;
        // cbRead needs to be a multiple of 16 for RMS.
        // If it is not, pad it for encryption.
        if(cbRead % BLOCK_SIZE)
        {
            ULONG nCbRead = ((cbRead /BLOCK_SIZE)+1)*BLOCK_SIZE;
            for(ULONG pad = 0; pad < (nCbRead - cbRead);pad++)
            {
                rgbBuffer[cbRead + pad] = '0';
            }
            cbRead = nCbRead;
        }
        hr = piic->HrEncrypt(0, rgbBuffer, cbRead, 0);
        if (FAILED(hr))
            goto LExit;
        hr = pistmT->Write(rgbBuffer, cbRead, &cbWritten);
        if (FAILED(hr))
            goto LExit;
        if (cbRead != cbWritten)
        {
            hr = STG_E_CANTSAVE;
            goto LExit;
        }

        ulOffset.QuadPart += cbRead;
    }

LFinished:
    piic->Release();
    piic = NULL;
    hr = pistmT->Commit(STGC_DEFAULT);

LExit:
    *pdwStatus = SUCCEEDED(hr) ? MSOIPI_STATUS_PROTECT_SUCCESS : MSOIPI_STATUS_CANT_PROTECT;
    if (piic != NULL)
        piic->Release();
    if (rgbstrId != NULL)
        HeapFree(GetProcessHeap(), 0, rgbstrId);
    if (rgbstrEUL != NULL)
        HeapFree(GetProcessHeap(), 0, rgbstrEUL);
    if (bstr != NULL)
        SysFreeString(bstr);
    if (pistmT != NULL)
    {
        pistmT->Release();
        pistmT = NULL;
    }
    if (pistgLicenses != NULL)
        pistgLicenses->Release();
    if (pistgOutput != NULL)
        pistgOutput->Release();

    return hr;
}

The HrInit function initializes the IRM protector. In the HrInit function, the pfUseRMS parameter is set to true to use an integrated protector. To use an autonomous protector, set it to false. It then contacts the RMS server directly to request a EUL for the document. Do not implement it this way in a real application. You implement either an integrated or autonomous protector, but not both in your code.

The code snippet for the HrInit function is as follows:

/*-----------------------------------------------------------------------------
    CSampleProtector::HrInit
------------------------------------------------------------------------------*/
STDMETHODIMP CSampleProtector::HrInit(BSTR *pbstrProduct, DWORD *pdwVersion, BSTR *pbstrExtensions, BOOL *pfUseRMS)
{
    if ( pbstrExtensions == NULL || pfUseRMS == NULL)
        return E_INVALIDARG;
    *pfUseRMS        = true; //set to "false" to use the non-RMS infrastructure
    *pbstrProduct    = SysAllocString(L"SampleIrmProtector");
    *pdwVersion      = 1;
    *pbstrExtensions = SysAllocString(L"txt");
    return S_OK;

The HrProtect function handles the encryption. This example uses a simple encryption method. For your implementation for use in your organization, you want to use your organization's encryption method of choice.

The code snippet for the HrProtect function is as follows:

/*---------------------------------------------------------------------
    CSampleProtector::HrProtect
---------------------------------------------------------------------*/
HRESULT CSampleProtector::HrProtect(ILockBytes *pilbInput, ILockBytes *pilbOutput, I_IrmPolicyInfo *piid, DWORD *pdwStatus)
{
    HRESULT hr = S_OK;
    IStorage *pistgOutput = NULL;
    IStream *pistmT = NULL;
    BSTR bstr = NULL;
    DWORD dw = 0;
    BOOL bRequestingUserIsSystem = false;
    BYTE rgbBuffer[STACK_RW_BUF_SIZE];
    ULARGE_INTEGER ulOffset = { 0 };
    if (pilbInput == NULL || pilbOutput == NULL || piid == NULL || pdwStatus == NULL)
    {
        hr = E_INVALIDARG;
        goto LExit;
    }
    *pdwStatus = MSOIPI_STATUS_UNKNOWN;
    hr = StgIsStorageILockBytes(pilbOutput);
    if (FAILED(hr))
        goto LExit;
    if (hr != S_FALSE)
    {
        hr = StgOpenStorageOnILockBytes(pilbOutput, NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, NULL, &pistgOutput);
        if (hr == STG_E_FILEALREADYEXISTS)
            goto LCreateStorage;
    }
    else
    {
LCreateStorage:
        hr = StgCreateDocfileOnILockBytes(pilbOutput, STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, &pistgOutput);
    }
    if (FAILED(hr))
        goto LExit;
    // Write out the key.
    BSTR key = SysAllocString(wzKey);
    hr = HrWriteSubStream(pistgOutput, wzKey, &key);
    // Write out the list GUID.
    hr = piid->HrGetListGuid(&bstr);
    if (FAILED(hr) || bstr == NULL)
        goto LExit;
    hr = HrWriteSubStream(pistgOutput, wzListGuid, &bstr);
    if (FAILED(hr))
        goto LExit;
    // Write out the rights mask.
    hr = piid->HrGetRightsMask(&dw);
    if (FAILED(hr))
        goto LExit;
    hr = HrWriteSubStream(pistgOutput, wzRightsMask, &bstr);
    if (FAILED(hr))
        goto LExit;
    // Write out the requesting user.
    hr = piid->HrGetRequestingUser(&bstr, &bRequestingUserIsSystem);
    if (FAILED(hr) || bstr == NULL)
        goto LExit;
    hr = HrWriteSubStream(pistgOutput, wzRequestingUser, &bstr);
    if (FAILED(hr))
        goto LExit;
    // Write out the URL.
    hr = piid->HrGetURL(&bstr);
    if (FAILED(hr) || bstr == NULL)
        goto LExit;
    hr = HrWriteSubStream(pistgOutput, wzURL, &bstr);
    if (FAILED(hr))
        goto LExit;
    // Write out the title.
    hr = piid->HrGetPolicyTitle(&bstr);
    if (FAILED(hr) || bstr == NULL)
        goto LExit;
    hr = HrWriteSubStream(pistgOutput, wzPolicyTitle, &bstr);
    if (FAILED(hr))
        goto LExit;
    // Write out the description.
    hr = piid->HrGetPolicyTitle(&bstr);
    if (FAILED(hr) || bstr == NULL)
        goto LExit;
    hr = HrWriteSubStream(pistgOutput, wzPolicyDescription, &bstr);
    if (FAILED(hr))
        goto LExit;
    // Write out the offline days.
    hr = piid->HrGetOfflineDays(&dw);
    if (FAILED(hr))
        goto LExit;
    hr = HrWriteSubStream(pistgOutput, wzOfflineDays, &bstr);
    if (FAILED(hr))
        goto LExit;
    // Write out the content.
    hr = HrEnsureStm(pistgOutput, wzContent, true /*fReadWrite*/, &pistmT);
    if (FAILED(hr))
        goto LExit;
    ulOffset.QuadPart = 0;
    while (true)
    {
        ULONG cbRead = cElements(rgbBuffer);
        ULONG cbWritten = 0;
        hr = pilbInput->ReadAt(ulOffset, rgbBuffer, cbRead, &cbRead);
        if (FAILED(hr))
            goto LExit;
        if (cbRead == 0)
            goto LFinished;
        for (ULONG i = 0; i < cbRead; i++)
            //Simple encryption: flip bits
            rgbBuffer[i] ^= 1;
        hr = pistmT->Write(rgbBuffer, cbRead, &cbWritten);
        if (FAILED(hr))
            goto LExit;
        if (cbRead != cbWritten)
        {
            hr = STG_E_CANTSAVE;
            goto LExit;
        }
        ulOffset.QuadPart += cbRead;
    }
LFinished:
    hr = pistmT->Commit(STGC_DEFAULT);
LExit:
    *pdwStatus = SUCCEEDED(hr) ? MSOIPI_STATUS_PROTECT_SUCCESS : MSOIPI_STATUS_CANT_PROTECT;
    if (bstr != NULL)
        SysFreeString(bstr);
    if (pistmT != NULL)
        pistmT->Release();
    if (pistgOutput != NULL)
        pistgOutput->Release();
    return hr;
}

After you compile your custom IRM protector, you must register that protector with Windows SharePoint Services to make it available for document libraries. You register the IRM protector, which is a COM component, the way you would any COM component.

For this sample, the registration syntax is as follows:

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0\IrmProtectors]
"{6EC4BB1F-3F73-4799-BC98-A3DF9AE23A0B}"="SampleDocProtector"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\SampleDocProtector]
"Extensions"="doc"
"Product"="SampleIrmProtector"
"Version"="1"

You can find the registration information in the IrmProtectors.reg file in the SampleDocProtector project.

To use an integrated protector or IRM protectors included in Windows SharePoint Services, you enable the IRM by specifying a location to your Windows RMS server using the settings in the IRM Settings page in Central Administration.

However, if you use autonomous protectors, you must enable the IRM add-in using stsadm.exe. You do this by setting the irmaddinsenabled property to true. If the IRM add-in is not enabled, the autonomous protectors will not encrypt the downloaded content.

The following command shows how you can enable the IRM add-in property using stdsadm.exe:

stsadm -o setproperty -propertyname irmaddinsenabled -propertyvalue TRUE
Show:
© 2016 Microsoft