|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.IO.Packaging;
using System.Security.RightsManagement;
using MS.Internal.Documents.Application;
using Microsoft.Win32;
using SR = System.Windows.TrustUI.SR;
namespace MS.Internal.Documents
{
/// <summary>
/// RightsManagementProvider is used to connect DRP to RM APIs
/// </summary>
/// <remarks>
/// This class is a facade for the RM APIs. It is the model between the Manager
/// and the EncryptedPackageEnvelope and System.Security.RightsManagement classes.
/// </remarks>
internal class RightsManagementProvider : IRightsManagementProvider, IDisposable
{
#region Constructors
//--------------------------------------------------------------------------
// Constructors
//--------------------------------------------------------------------------
/// <summary>
/// A constructor that takes an encrypted package. This
/// can be null
/// <<param name="package">The encrypted package</param>
/// </summary>
public RightsManagementProvider(EncryptedPackageEnvelope encryptedPackage)
{
_encryptedPackageEnvelope = encryptedPackage;
}
#endregion Constructors
#region IRightsManagementProvider
//--------------------------------------------------------------------------
// IRightsManagementProvider
//--------------------------------------------------------------------------
/// <summary>
/// Is the XPS document RM-protected?
/// </summary>
bool IRightsManagementProvider.IsProtected
{
get
{
InitializeMembers();
return (CurrentPublishLicense != null);
}
}
/// <summary>
/// Gets the rights granted in the current use license.
/// </summary>
RightsManagementLicense IRightsManagementProvider.CurrentUseLicense
{
get { return _rmUseLicense; }
}
/// <summary>
/// Gets or sets the publish license associated with the current package.
/// Setting the current publish license invalidates any saved use licenses.
/// </summary>
PublishLicense IRightsManagementProvider.CurrentPublishLicense
{
get
{
return _currentPublishLicense;
}
set
{
_currentPublishLicense = value;
// Invalidate the saved use license and grants
_useLicense = null;
_rmUseLicense = null;
}
}
/// <summary>
/// Gets the currently active user.
/// </summary>
RightsManagementUser IRightsManagementProvider.CurrentUser
{
get { return _user; }
}
/// <summary>
/// Enrolls a new user and sets up the secure environment.
/// </summary>
/// <param name="accountType">The account type to use to initialize the
/// secure environment</param>
void IRightsManagementProvider.InitializeEnvironment(EnrollmentAccountType accountType)
{
InitializeMembers();
CleanUpSecureEnvironment();
try
{
AuthenticationType authType;
UserActivationMode userActMode;
switch (accountType)
{
case (EnrollmentAccountType.Network):
{
authType = AuthenticationType.Windows;
userActMode = UserActivationMode.Permanent;
break;
}
case (EnrollmentAccountType.Temporary):
{
authType = AuthenticationType.Windows;
userActMode = UserActivationMode.Temporary;
break;
}
case (EnrollmentAccountType.NET):
{
authType = AuthenticationType.Passport;
userActMode = UserActivationMode.Permanent;
break;
}
default:
{
throw new NotSupportedException();
}
}
_secureEnvironment = SecureEnvironment.Create(
GetApplicationManifest(),
authType,
userActMode);
}
// If the secure environment initialization fails, or the AccountType is
// set incorrectly, we need to clean out the SecureEnvironment.
catch
{
CleanUpSecureEnvironment();
throw;
}
Trace.SafeWrite(
Trace.Rights,
"SecureEnvironment was initialized as part of enrollment.");
SetUserFromSecureEnvironment();
}
/// <summary>
/// Sets up the secure environment for a particular user.
/// </summary>
/// <param name="user">The user for whom to set up the environment.</param>
void IRightsManagementProvider.InitializeEnvironment(RightsManagementUser user)
{
InitializeMembers();
CleanUpSecureEnvironment();
_secureEnvironment = SecureEnvironment.Create(
GetApplicationManifest(),
user);
Trace.SafeWriteIf(
(_secureEnvironment != null),
Trace.Rights,
"SecureEnvironment was initialized for a specific user.");
SetUserFromSecureEnvironment();
}
/// <summary>
/// Loads a use license for the user from the package.
/// This requires InitializeEnvironment to have been called.
/// </summary>
/// <returns>Whether or not a use license could be loaded directly from the
/// package</returns>
bool IRightsManagementProvider.LoadUseLicense()
{
if (!IsProtected)
{
throw new InvalidOperationException(
SR.RMProviderExceptionNoPackageToDecrypt);
}
UseLicense useLicense;
useLicense = _encryptedPackageEnvelope
.RightsManagementInformation.LoadUseLicense(_user);
if (useLicense != null)
{
Trace.SafeWrite(Trace.Rights, "Existing use license was found.");
_useLicense = useLicense;
}
return (useLicense != null);
}
/// <summary>
/// Acquires a use license for the package.
/// This requires InitializeEnvironment to have been called.
/// </summary>
/// <returns>Whether or not a use license could be acquired</returns>
bool IRightsManagementProvider.AcquireUseLicense()
{
if (!IsProtected)
{
throw new InvalidOperationException(
SR.RMProviderExceptionNoPackageToDecrypt);
}
UseLicense useLicense = null;
RightsManagementException rmException = null;
try
{
useLicense = CurrentPublishLicense.AcquireUseLicense(_secureEnvironment);
}
catch(RightsManagementException e)
{
rmException = e;
}
if (useLicense != null)
{
Trace.SafeWrite(Trace.Rights, "A new use license was acquired.");
_useLicense = useLicense;
}
else
{
Trace.SafeWrite(
Trace.Rights,
"New use license acquisition failed: {0}",
rmException.Message);
}
return (useLicense != null);
}
/// <summary>
/// Saves the current use license and embeds it in the package.
/// This requires a use license to have been acquired.
/// </summary>
void IRightsManagementProvider.SaveUseLicense(EncryptedPackageEnvelope package)
{
if (!IsProtected)
{
throw new InvalidOperationException(
SR.RMProviderExceptionNoPackageToDecrypt);
}
if (_useLicense == null)
{
throw new InvalidOperationException(
SR.RMProviderExceptionNoUseLicense);
}
if (AllowLicenseCaching)
{
Trace.SafeWrite(Trace.Rights, "Writing Use License to package.");
// Attempt to write the acquired license back to the package
if (package.FileOpenAccess != FileAccess.Read)
{
package.RightsManagementInformation.
SaveUseLicense(_user, _useLicense);
}
}
else
{
Trace.SafeWrite(Trace.Rights, "NOLICCACHE is set, not writing Use License to package.");
}
}
/// <summary>
/// Binds the use license to the secure environment. This sets the
/// CurrentUseLicense property to the appropriate value.
/// This requires the use license to be set beforehand.
/// </summary>
void IRightsManagementProvider.BindUseLicense()
{
if (!IsProtected)
{
throw new InvalidOperationException(
SR.RMProviderExceptionNoPackageToDecrypt);
}
if (_useLicense == null)
{
throw new InvalidOperationException(
SR.RMProviderExceptionNoUseLicense);
}
// If a CryptoProvider is already set in the EncryptedPackageEnvelope,
// we can't set it again. As a result we should use the existing one if
// possible.
CryptoProvider cryptoProvider = null;
ReadOnlyCollection<ContentGrant> grants = null;
cryptoProvider =
_encryptedPackageEnvelope.RightsManagementInformation.CryptoProvider;
if (cryptoProvider != null)
{
grants = cryptoProvider.BoundGrants;
}
// If there is no CryptoProvider set in the envelope, we need to create
// one and associate it with the envelope now
if (cryptoProvider == null)
{
cryptoProvider = GenerateCryptoProvider();
grants = cryptoProvider.BoundGrants;
_encryptedPackageEnvelope.RightsManagementInformation.CryptoProvider =
cryptoProvider;
}
// Create our RM license object using the grants retrieved from the
// CryptoProvider
Invariant.Assert(
grants != null,
"CryptoProvider had no bound grants.");
_rmUseLicense = ConvertGrantList(_user, grants);
// If possible use the CryptoProvider to decrypt the publish license
if (HasPermission(
_rmUseLicense, RightsManagementPermissions.AllowOwner))
{
_unsignedPublishLicense =
CurrentPublishLicense.DecryptUnsignedPublishLicense(
cryptoProvider);
Trace.SafeWrite(
Trace.Rights, "Publish license was decrypted.");
}
}
/// <summary>
/// Gets a list of all credentials available to the current user.
/// </summary>
/// <returns>A list of all available credentials</returns>
ReadOnlyCollection<RightsManagementUser> IRightsManagementProvider.GetAvailableCredentials()
{
ReadOnlyCollection<ContentUser> users;
users = SecureEnvironment.GetActivatedUsers();
List<RightsManagementUser> rmUsers =
new List<RightsManagementUser>(users.Count);
foreach (ContentUser user in users)
{
rmUsers.Add(RightsManagementUser.CreateUser(user));
}
return new ReadOnlyCollection<RightsManagementUser>(rmUsers);
}
/// <summary>
/// Gets the default credentials.
/// </summary>
/// <returns>Default credentials</returns>
RightsManagementUser IRightsManagementProvider.GetDefaultCredentials()
{
RightsManagementUser defaultUser = null;
string defaultUserName = null;
int defaultAccountType = -1; //invalid account
ReadOnlyCollection<RightsManagementUser> users =
((IRightsManagementProvider)this).GetAvailableCredentials();
using (RegistryKey defaultRMKey = Registry.CurrentUser.OpenSubKey(
_registryLocationForDefaultUser))
{
if (defaultRMKey != null)
{
// Get account name and validate it as a string.
object keyValue = defaultRMKey.GetValue(_registryValueNameForAccountName);
if ((keyValue != null) &&
(defaultRMKey.GetValueKind(_registryValueNameForAccountName) == RegistryValueKind.String))
{
defaultUserName = (string)keyValue;
}
// Get account type and validate it as an int.
keyValue = defaultRMKey.GetValue(_registryValueNameForAccountType);
if ((keyValue != null) &&
(defaultRMKey.GetValueKind(_registryValueNameForAccountType) == RegistryValueKind.DWord))
{
defaultAccountType = (int)keyValue;
}
}
}
//lets match up the user/type string with an actual avalable user
if ((users.Count > 0) && (defaultUserName != null))
{
RightsManagementUser user = RightsManagementUser.CreateUser(
defaultUserName,
(AuthenticationType)defaultAccountType);
int index = users.IndexOf(user);
//Did we find the new default user in our Available users?
if (index != -1)
{
defaultUser = users[index];
}
}
if ((defaultUser == null) && (users.Count > 0))
{
//If we get here then default doesn't match any available users.
//Select first user as default.
defaultUser = users[0];
}
return defaultUser;
}
/// <summary>
/// Sets the default credentials.
/// </summary>
void IRightsManagementProvider.SetDefaultCredentials(RightsManagementUser user)
{
ArgumentNullException.ThrowIfNull(user);
//Get AvailableCreds list so we can match new default user.
ReadOnlyCollection<RightsManagementUser> users =
((IRightsManagementProvider)this).GetAvailableCredentials();
int index = users.IndexOf(user);
//Did we find the new default user in our Available users?
//If not, doesn't save to registry.
if (index != -1)
{
string defaultUserName = users[index].Name;
int defaultAccountType = (int)users[index].AuthenticationType;
using (RegistryKey defaultRMKey = Registry.CurrentUser.CreateSubKey(
_registryLocationForDefaultUser))
{
if (defaultRMKey != null)
{
defaultRMKey.SetValue(
_registryValueNameForAccountName, defaultUserName);
defaultRMKey.SetValue(
_registryValueNameForAccountType, defaultAccountType);
}
}
}
}
/// <summary>
/// Removes user from available credentials.
/// </summary>
void IRightsManagementProvider.RemoveCredentials(RightsManagementUser user)
{
ArgumentNullException.ThrowIfNull(user);
SecureEnvironment.RemoveActivatedUser(user);
}
/// <summary>
/// Gets a dictionary of all users and grants stored in the package.
/// </summary>
/// <returns>All the users and licenses in the package, keyed by User.
/// </returns>
IDictionary<RightsManagementUser, RightsManagementLicense>
IRightsManagementProvider.GetAllAccessRights()
{
if (IsProtected &&
_rightsDictionary == null &&
_rmUseLicense != null &&
_rmUseLicense.HasPermission(RightsManagementPermissions.AllowOwner) &&
_unsignedPublishLicense != null)
{
UnsignedPublishLicense unsignedLicense = _unsignedPublishLicense;
IDictionary<RightsManagementUser, IList<ContentGrant>> grantDictionary =
new Dictionary<RightsManagementUser, IList<ContentGrant>>();
// Add all the grants from the unsigned license to the dictionary
foreach (ContentGrant grant in GetGrantsFromUnsignedLicense(unsignedLicense))
{
ContentUser contentUser = null;
contentUser = grant.User;
RightsManagementUser user =
RightsManagementUser.CreateUser(contentUser);
if (!grantDictionary.ContainsKey(user))
{
grantDictionary[user] = new List<ContentGrant>();
}
grantDictionary[user].Add(grant);
}
ContentUser contentUserOwner = null;
contentUserOwner = unsignedLicense.Owner;
RightsManagementUser owner =
RightsManagementUser.CreateUser(contentUserOwner);
// Add a grant for the owner of the document to the dictionary
if (!grantDictionary.ContainsKey(owner))
{
grantDictionary[owner] = new List<ContentGrant>(1);
}
ContentGrant ownerGrant =
CreateGrant(
owner,
ContentRight.Owner,
DateTime.MinValue,
DateTime.MaxValue);
grantDictionary[owner].Add(ownerGrant);
// Convert the grant dictionary to a dictionary of licenses
IDictionary<RightsManagementUser, RightsManagementLicense> dictionary =
new Dictionary<RightsManagementUser, RightsManagementLicense>(
grantDictionary.Count);
foreach (RightsManagementUser user in grantDictionary.Keys)
{
RightsManagementLicense currentLicense =
ConvertGrantList(user, grantDictionary[user]);
dictionary[user] = currentLicense;
}
_rightsDictionary =
(IDictionary<RightsManagementUser, RightsManagementLicense>)dictionary;
}
return _rightsDictionary;
}
/// <summary>
/// Decrypt the encrypted package into a metro stream.
/// </summary>
Stream IRightsManagementProvider.DecryptPackage()
{
if (_encryptedPackageEnvelope == null)
{
throw new InvalidOperationException(
SR.RMProviderExceptionNoPackageToDecrypt);
}
Stream result;
Trace.SafeWrite(Trace.Rights, "Decrypting the document.");
result = _encryptedPackageEnvelope.GetPackageStream();
return result;
}
/// <summary>
/// Create an encrypted package from a stream using the current publish license.
/// </summary>
/// <param name="ciphered">The stream to encrypt</param>
/// <returns>An EncryptedPackageEnvelope around the stream.</returns>
EncryptedPackageEnvelope IRightsManagementProvider.EncryptPackage(Stream ciphered)
{
// If the publish license is to not encrypt, return null
if (CurrentPublishLicense == null)
{
return null;
}
// If the user doesn't have rights to encrypt data, we also should not
// attempt to create an EncryptedPackageEnvelope, since we could never
// write to it
RightsManagementLicense currentUseLicense =
((IRightsManagementProvider)this).CurrentUseLicense;
if (currentUseLicense != null &&
!HasPermission(currentUseLicense, RightsManagementPermissions.AllowEdit))
{
return null;
}
CryptoProvider cp = GenerateCryptoProvider();
EncryptedPackageEnvelope result = null;
result = EncryptedPackageEnvelope.Create(
ciphered,
CurrentPublishLicense,
cp);
// Always save the use license back into any new RM protected document
if (result != null)
{
((IRightsManagementProvider)this).SaveUseLicense(result);
}
return result;
}
/// <summary>
/// Generates an unsigned publish license for the package from a collection
/// of licenses.
/// </summary>
/// <param name="licenses">
/// The list of licenses from which to generate a publish license</param>
/// <param name="validUntil">
/// The optional date until when the publish license will be valid</param>
/// <param name="referralUri">
/// A URI to contact to request additional permissions</param>
void IRightsManagementProvider.GenerateUnsignedPublishLicense(
IList<RightsManagementLicense> licenses)
{
ArgumentNullException.ThrowIfNull(licenses);
// If the document is already protected, only owners can republish it
// with different permissions
if (IsProtected && !HasPermission(
_rmUseLicense, RightsManagementPermissions.AllowOwner))
{
throw new InvalidOperationException(
SR.RMProviderExceptionNotOwnerOfDocument);
}
Trace.SafeWrite(
Trace.Rights, "Creating a publish license for the document.");
//
// Create the unsigned publish license
//
UnsignedPublishLicense unsignedPublishLicense = null;
unsignedPublishLicense = new UnsignedPublishLicense();
ICollection<ContentGrant> licenseGrants =
GetGrantsFromUnsignedLicense(unsignedPublishLicense);
//
// Retrieve the referral information from the first license
//
string referralInfoName = string.Empty;
Uri referralInfoUri = null;
if (licenses.Count > 0)
{
referralInfoName = licenses[0].ReferralInfoName;
referralInfoUri = licenses[0].ReferralInfoUri;
}
//
// Generate grants from the licenses passed in
//
Dictionary<RightsManagementUser, RightsManagementLicense> rights =
new Dictionary<RightsManagementUser, RightsManagementLicense>();
// Look through each license passed in and generate grants from it
foreach (RightsManagementLicense rmLicense in licenses)
{
// If the user has not already been granted rights, add the grants
// given to the user to the grants dictionary and the grant list
if (!rights.ContainsKey(rmLicense.LicensedUser))
{
rights[rmLicense.LicensedUser] = rmLicense;
IList<ContentGrant> grantList = GetGrantsFromLicense(rmLicense);
foreach (ContentGrant grant in grantList)
{
licenseGrants.Add(grant);
}
}
}
// If the current user was not specified as an owner, add the user to
// the rights dictionary and grant the user admin privileges forever
if (!rights.ContainsKey(_user))
{
ContentGrant ownerGrant =
CreateGrant(
_user,
ContentRight.Owner,
DateTime.MinValue,
DateTime.MaxValue);
// Add the grant to the unsigned publish license
licenseGrants.Add(ownerGrant);
// Add the grant to the rights dictionary
IList<ContentGrant> grantList = new List<ContentGrant>();
grantList.Add(ownerGrant);
rights[_user] = ConvertGrantList(_user, grantList);
}
//
// Set up remaining properties of the unsigned publish license
//
unsignedPublishLicense.Owner = _user;
unsignedPublishLicense.ReferralInfoName = referralInfoName;
unsignedPublishLicense.ReferralInfoUri = referralInfoUri;
//
// Save temporary unsigned license and rights dictionary for signing
//
_temporaryRightsDictionary = rights;
_temporaryUnsignedPublishLicense = unsignedPublishLicense;
}
/// <summary>
/// Generates an unsigned publish license for the package from
/// the template.
/// </summary>
/// <param name="template">
/// The template from which to generate a publish license</param>
void IRightsManagementProvider.GenerateUnsignedPublishLicense(string template)
{
if (string.IsNullOrEmpty(template))
{
throw new NullReferenceException("template");
}
UnsignedPublishLicense unsignedPublishLicense = null;
// Create the license from the template.
unsignedPublishLicense = new UnsignedPublishLicense(template);
// Get who is currently listed as the owner of the document in the
// publish license
ContentUser currentOwnerFromLicense = null;
currentOwnerFromLicense = unsignedPublishLicense.Owner;
RightsManagementUser currentOwner = null;
if (currentOwnerFromLicense != null)
{
currentOwner = RightsManagementUser.CreateUser(
currentOwnerFromLicense);
}
// If the listed owner is not the current user, change the listed owner
// and ensure that the old owner still maintains owner rights on the
// document
if (!_user.Equals(currentOwner))
{
ContentGrant currentOwnerGrant = null;
if (currentOwner != null)
{
currentOwnerGrant = CreateGrant(
currentOwner,
ContentRight.Owner,
DateTime.MinValue,
DateTime.MaxValue);
}
unsignedPublishLicense.Owner = _user;
if (currentOwnerGrant != null)
{
unsignedPublishLicense.Grants.Add(currentOwnerGrant);
}
}
// Assign the new publish license.
_temporaryRightsDictionary = null;
_temporaryUnsignedPublishLicense = unsignedPublishLicense;
}
/// <summary>
/// Signs the unsigned publish license and saves a corresponding updated use
/// license.
/// This requires GenerateUnsignedPublishLicense to have been called.
/// </summary>
void IRightsManagementProvider.SignPublishLicense()
{
// If the document is already protected, only owners can republish it
if (IsProtected &&
!HasPermission(_rmUseLicense,
RightsManagementPermissions.AllowOwner))
{
throw new InvalidOperationException(
SR.RMProviderExceptionNotOwnerOfDocument);
}
UseLicense useLicense;
Trace.SafeWrite(
Trace.Rights, "Signing the publish license for the document.");
CurrentPublishLicense =
_temporaryUnsignedPublishLicense.Sign(
_secureEnvironment, out useLicense);
_useLicense = useLicense;
// Copy and clear temporary values
_unsignedPublishLicense = _temporaryUnsignedPublishLicense;
_rightsDictionary = _temporaryRightsDictionary;
_temporaryUnsignedPublishLicense = null;
_temporaryRightsDictionary = null;
// If the RightsDictionary exists then set use license.
if (_rightsDictionary != null)
{
_rmUseLicense = _rightsDictionary[_user];
}
else
{
// Since the RightsDictionary doesn't exist (most likely because we're using
// a template), generate the owner data.
List<ContentGrant> grantList = new List<ContentGrant>();
grantList.Add(CreateGrant(_user, ContentRight.Owner, DateTime.MinValue, DateTime.MaxValue));
_rmUseLicense = ConvertGrantList(_user, grantList);
}
}
/// <summary>
/// Saves the current set of licenses.
/// </summary>
void IRightsManagementProvider.SaveCurrentLicenses()
{
// Save the current publish license and use licenses for rollback
_lastSavedPublishLicense = _currentPublishLicense;
_lastSavedRMUseLicense = _rmUseLicense;
_lastSavedUseLicense = _useLicense;
_lastSavedRightsDictionary = _rightsDictionary;
}
/// <summary>
/// Reverts to the last saved set of licenses.
/// </summary>
void IRightsManagementProvider.RevertToSavedLicenses()
{
CurrentPublishLicense = _lastSavedPublishLicense;
_useLicense = _lastSavedUseLicense;
_rmUseLicense = _lastSavedRMUseLicense;
_rightsDictionary = _lastSavedRightsDictionary;
_lastSavedPublishLicense = null;
_lastSavedUseLicense = null;
_lastSavedRMUseLicense = null;
_lastSavedRightsDictionary = null;
}
/// <summary>
/// Sets the encapsulated EncryptedPackageEnvelope to a new value and loads
/// a new publish license from it. If the new publish license is the same
/// as the old one, the function restores the old use license.
/// </summary>
/// <param name="newPackage">The new encrypted package</param>
/// <param name="publishLicenseChanged">Whether or not the new encrypted
/// package has a different publish license</param>
void IRightsManagementProvider.SetEncryptedPackage(EncryptedPackageEnvelope newPackage, out bool publishLicenseChanged)
{
PublishLicense savedPublishLicense = null;
UseLicense savedUseLicense = null;
RightsManagementLicense savedRMLicense = null;
// Save the current set of licenses. In case the publish license in the
// new encrypted package envelope is the same, these will be restored.
if (_publishLicenseFromEnvelope != null)
{
savedPublishLicense = _publishLicenseFromEnvelope;
savedUseLicense = _useLicense;
savedRMLicense = _rmUseLicense;
}
_encryptedPackageEnvelope = newPackage;
_publishLicenseFromEnvelope = null;
CurrentPublishLicense = null;
InitializeMembers();
publishLicenseChanged = true;
// If both publish licenses are non-null, compare them
if (savedPublishLicense != null &&
_publishLicenseFromEnvelope != null)
{
string serializedSavedPublishLicense = string.Empty;
string serializedNewPublishLicense = string.Empty;
serializedSavedPublishLicense = savedPublishLicense.ToString();
serializedNewPublishLicense = _publishLicenseFromEnvelope.ToString();
publishLicenseChanged = !string.Equals(
serializedSavedPublishLicense,
serializedNewPublishLicense,
StringComparison.Ordinal);
}
// If both publish licenses are null, that means the document wasn't
// protected before and still isn't protected, so the publish license
// hasn't changed.
else if (savedPublishLicense == null &&
_publishLicenseFromEnvelope == null)
{
publishLicenseChanged = false;
}
if (IsProtected && !publishLicenseChanged)
{
// If the publish license hasn't changed, restore the saved use
// license and generate a new CryptoProvider from it.
_useLicense = savedUseLicense;
_rmUseLicense = savedRMLicense;
CryptoProvider cryptoProvider = GenerateCryptoProvider();
_encryptedPackageEnvelope.RightsManagementInformation.CryptoProvider =
cryptoProvider;
}
// Since the encrypted package envelope has been changed, the last saved
// licenses aren't applicable any more.
_lastSavedPublishLicense = null;
_lastSavedUseLicense = null;
_lastSavedRMUseLicense = null;
_lastSavedRightsDictionary = null;
Trace.SafeWrite(
Trace.Rights,
"SetEncryptedPackage called. publishLicenseChanged: {0}",
publishLicenseChanged);
}
#endregion IRightsManagementProvider
#region IDisposable Members
//--------------------------------------------------------------------------
// IDisposable Members
//--------------------------------------------------------------------------
/// <summary>
/// Disposes everything that needs to be disposed.
/// </summary>
public void Dispose()
{
if (_cryptoProviders != null)
{
foreach (CryptoProvider cryptoProvider in _cryptoProviders)
{
cryptoProvider.Dispose();
}
_cryptoProviders = null;
}
CleanUpSecureEnvironment();
GC.SuppressFinalize(this);
}
#endregion IDisposable Members
#region Private Methods
//--------------------------------------------------------------------------
// Private Methods
//--------------------------------------------------------------------------
/// <summary>
/// Destroys the RightsManagementProvider.
/// </summary>
~RightsManagementProvider()
{
Dispose();
}
/// <summary>
/// Binds the current use license to the current secure environment to get
/// a new CryptoProvider.
/// </summary>
private CryptoProvider GenerateCryptoProvider()
{
CryptoProvider cryptoProvider = null;
cryptoProvider = _useLicense.Bind(_secureEnvironment);
Trace.SafeWrite(
Trace.Rights, "The CryptoProvider was initialized.");
// Add a reference to the CryptoProvider to the list of CryptoProviders
// for disposal
if (cryptoProvider != null)
{
if (_cryptoProviders == null)
{
IList<CryptoProvider> cryptoProviders = new List<CryptoProvider>();
_cryptoProviders = cryptoProviders;
}
_cryptoProviders.Add(cryptoProvider);
}
return cryptoProvider;
}
/// <summary>
/// Checks whether the specified license has the specified permission.
/// </summary>
/// <param name="license">The license to check</param>
/// <param name="permission">The permission for which to check</param>
/// <returns>Whether or not the license has the permission</returns>
private static bool HasPermission(
RightsManagementLicense license, RightsManagementPermissions permission)
{
return license.HasPermission(permission);
}
/// <summary>
/// Adds referral information read from the current publish license to a
/// use license.
/// </summary>
/// <param name="rmLicense">The use license (grant list) to which to add
/// referral information</param>
private void AddReferralInfo(RightsManagementLicense rmLicense)
{
// If there is no publish license yet there is no information to add
if (CurrentPublishLicense == null)
{
return;
}
rmLicense.ReferralInfoName = CurrentPublishLicense.ReferralInfoName;
rmLicense.ReferralInfoUri = CurrentPublishLicense.ReferralInfoUri;
}
/// <summary>
/// Converts a collection of grants into a "license" that is usable
/// internally by this component.
/// </summary>
/// <param name="useLicense">The UseLicense to convert</param>
private RightsManagementLicense ConvertGrantList(
RightsManagementUser user,
IList<ContentGrant> grantList)
{
RightsManagementLicense rmLicense = new RightsManagementLicense();
rmLicense.LicensedUser = user;
rmLicense.LicensePermissions = RightsManagementPermissions.AllowNothing;
rmLicense.ValidFrom = DateTime.MinValue;
rmLicense.ValidUntil = DateTime.MaxValue;
AddReferralInfo(rmLicense);
if (grantList != null)
{
bool canSign = false;
DateTime validFrom = DateTime.MinValue;
DateTime validUntil = DateTime.MaxValue;
foreach (ContentGrant grant in grantList)
{
ContentRight right;
right = grant.Right;
if (grant.ValidFrom > validFrom)
{
validFrom = grant.ValidFrom;
}
if (grant.ValidUntil < validUntil)
{
validUntil = grant.ValidUntil;
}
switch (right)
{
// Translate each grant into a Mongoose permission
case ContentRight.View:
rmLicense.AddPermission(
RightsManagementPermissions.AllowView);
break;
case ContentRight.Print:
rmLicense.AddPermission(
RightsManagementPermissions.AllowPrint);
break;
case ContentRight.Extract:
rmLicense.AddPermission(
RightsManagementPermissions.AllowCopy);
break;
case ContentRight.Owner:
rmLicense.AddPermission(
RightsManagementPermissions.AllowOwner);
break;
// The Edit grant can mean two things:
//
// 1) Without any other grants, it means basically nothing
// since Mongoose doesn't support editing documents.
// 2) In conjunction with the Sign grant, it means that
// the user can sign the document.
//
// As a result we have to keep track of the edit and sign
// grants separately and determine later whether both were
// granted before we can allow the user to sign.
case ContentRight.Edit:
rmLicense.AddPermission(
RightsManagementPermissions.AllowEdit);
break;
case ContentRight.Sign:
canSign = true;
break;
case ContentRight.DocumentEdit:
// DocumentEdit is a custom right, that when applied with
// Edit, we want to treat as our custom right, Sign.
canSign = true;
break;
}
}
if (rmLicense.HasPermission(RightsManagementPermissions.AllowEdit) &&
canSign)
{
rmLicense.AddPermission(
RightsManagementPermissions.AllowSign);
}
rmLicense.ValidFrom = validFrom;
rmLicense.ValidUntil = validUntil;
}
return rmLicense;
}
/// <summary>
/// Generates a list of Grant objects corresponding to the data contained
/// in a RightsManagementLicense object.
/// </summary>
/// <param name="rmLicense">The RightsManagementLicense to transform</param>
/// <returns>A list of Grant objects</returns>
private IList<ContentGrant> GetGrantsFromLicense(
RightsManagementLicense rmLicense)
{
RightsManagementUser user = rmLicense.LicensedUser;
List<ContentGrant> grants = new List<ContentGrant>();
// Translate each Mongoose permission into a grant
if (HasPermission(rmLicense, RightsManagementPermissions.AllowView))
{
grants.Add(
CreateGrant(
user,
ContentRight.View,
rmLicense.ValidFrom,
rmLicense.ValidUntil));
}
if (HasPermission(rmLicense, RightsManagementPermissions.AllowPrint))
{
grants.Add(
CreateGrant(
user,
ContentRight.Print,
rmLicense.ValidFrom,
rmLicense.ValidUntil));
}
if (HasPermission(rmLicense, RightsManagementPermissions.AllowCopy))
{
grants.Add(
CreateGrant(
user,
ContentRight.Extract,
rmLicense.ValidFrom,
rmLicense.ValidUntil));
}
bool editRightGranted = false;
if (HasPermission(rmLicense, RightsManagementPermissions.AllowEdit))
{
grants.Add(
CreateGrant(
user,
ContentRight.Edit,
rmLicense.ValidFrom,
rmLicense.ValidUntil));
editRightGranted = true;
}
if (HasPermission(rmLicense, RightsManagementPermissions.AllowSign))
{
// The sign permission translates to the combination of the Edit
// and Sign grants. If the user was already granted Edit, there is
// no need to grant it again.
if (!editRightGranted)
{
grants.Add(
CreateGrant(
user,
ContentRight.Edit,
rmLicense.ValidFrom,
rmLicense.ValidUntil));
}
grants.Add(
CreateGrant(
user,
ContentRight.Sign,
rmLicense.ValidFrom,
rmLicense.ValidUntil));
}
if (HasPermission(rmLicense, RightsManagementPermissions.AllowOwner))
{
grants.Add(
CreateGrant(
user,
ContentRight.Owner,
rmLicense.ValidFrom,
rmLicense.ValidUntil));
}
return grants;
}
/// <summary>
/// Initializes the necessary member variables. This can be called more
/// than once without ill effect.
/// </summary>
private void InitializeMembers()
{
if (_encryptedPackageEnvelope != null &&
_publishLicenseFromEnvelope == null)
{
RightsManagementInformation rmInfo =
_encryptedPackageEnvelope.RightsManagementInformation;
PublishLicense publishLicense = null;
publishLicense = rmInfo.LoadPublishLicense();
if (publishLicense == null)
{
throw new FileFormatException(
SR.RMProviderExceptionNoPublishLicense);
}
_publishLicenseFromEnvelope = publishLicense;
CurrentPublishLicense = publishLicense;
}
}
/// <summary>
/// Cleans up the SecureEnvironment member.
/// </summary>
private void CleanUpSecureEnvironment()
{
if (_secureEnvironment != null)
{
_secureEnvironment.Dispose();
_secureEnvironment = null;
}
}
/// <summary>
/// Sets the currently active user from the value stored in the saved
/// secure environment.
/// </summary>
private void SetUserFromSecureEnvironment()
{
_user =
RightsManagementUser.CreateUser(_secureEnvironment.User);
}
/// <summary>
/// Creates a ContentGrant object with the given user and rights.
/// </summary>
/// <param name="user">The user for whom the grant applies</param>
/// <param name="right">The right to grant</param>
/// <returns>A new ContentGrant object</returns>
private ContentGrant CreateGrant(RightsManagementUser user, ContentRight right, DateTime validFrom, DateTime validUntil)
{
return new ContentGrant(user, right, validFrom, validUntil);
}
/// <summary>
/// Gets a collection of grants from an unsigned publish license.
/// </summary>
/// <param name="unsignedLicense">An unsigned publish license</param>
/// <returns>A collection of grants</returns>
private ICollection<ContentGrant> GetGrantsFromUnsignedLicense(
UnsignedPublishLicense unsignedLicense)
{
ICollection<ContentGrant> grants = null;
if (unsignedLicense != null)
{
grants = unsignedLicense.Grants;
}
return grants;
}
/// <summary>
/// Cleans up the SecureEnvironment member.
/// </summary>
private string GetApplicationManifest()
{
//Get the current Process and MainModule.
System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
//Content of the RM application Manifest
System.Diagnostics.ProcessModule processModule = currentProcess.MainModule;
Invariant.Assert(
processModule != null,
"Failed to get Process Module");
string fileName = processModule.FileName;
string applicationManifest = null;
//Using exe module path, create path to application manifest file (XPSViewerManifest.xml)
string applicationManifestFileLocation = Path.Combine( Path.GetDirectoryName(fileName),
_applicationManifestFileName);
// Create an instance of StreamReader to read from a file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(applicationManifestFileLocation))
{
applicationManifest = sr.ReadToEnd();
}
return applicationManifest;
}
#endregion Private Methods
#region Private Properties
//--------------------------------------------------------------------------
// Private Properties
//--------------------------------------------------------------------------
/// <summary>
/// Gets whether the XPS document is RM protected. This is a convenient way
/// to use the IsProtected property without having to cast to an instance
/// of IRightsManagementProvider each time.
/// </summary>
private bool IsProtected
{
get
{
return ((IRightsManagementProvider)this).IsProtected;
}
}
/// <summary>
/// Gets or sets the current publish license that will be used for saving
/// an encrypted document. This is a convenient way to use the
/// CurrentPublishLicense property without having to cast to an instance of
/// IRightsManagementProvider each time.
/// </summary>
private PublishLicense CurrentPublishLicense
{
get
{
return ((IRightsManagementProvider)this).CurrentPublishLicense;
}
set
{
((IRightsManagementProvider)this).CurrentPublishLicense = value;
}
}
/// <summary>
/// Indicates whether the current Use License allows caching of the license
/// back to the container.
/// </summary>
private bool AllowLicenseCaching
{
get
{
bool result = false;
// If the key pair "NOLICCACHE","1" (in _noLicCacheKeyValuePair) is present
// in the Use License's ApplicationData, this signifies that license
// caching should be disabled (and thus we will return false here)
result =
!(_useLicense != null &&
_useLicense.ApplicationData != null &&
_useLicense.ApplicationData.Contains(_noLicCacheKeyValuePair));
return result;
}
}
#endregion Private Properties
#region Private Fields
//--------------------------------------------------------------------------
// Private Fields
//--------------------------------------------------------------------------
/// <summary>
/// The underlying EncryptedPackageEnvelope class from the RM APIs.
/// </summary>
EncryptedPackageEnvelope _encryptedPackageEnvelope;
/// <summary>
/// The currently active secure environment.
/// </summary>
SecureEnvironment _secureEnvironment;
/// <summary>
/// The use license the user has for the currently open package.
/// </summary>
UseLicense _useLicense;
/// <summary>
/// The last saved use license.
/// </summary>
UseLicense _lastSavedUseLicense;
/// <summary>
/// A copy of the unsigned publish license.
/// </summary>
UnsignedPublishLicense _unsignedPublishLicense;
/// <summary>
/// A generated unsigned publish license that has not yet been signed. Once
/// it is signed, it will replace the _unsignedPublishLicense above.
/// </summary>
UnsignedPublishLicense _temporaryUnsignedPublishLicense;
/// <summary>
/// The publish license saved in the current _encryptedPackage.
/// </summary>
PublishLicense _publishLicenseFromEnvelope;
/// <summary>
/// The current publish license, which may be different from
/// _publishLicenseFromEnvelope above if the user has committed a publishing
/// operation.
/// </summary>
PublishLicense _currentPublishLicense;
/// <summary>
/// The last saved publish license.
/// </summary>
PublishLicense _lastSavedPublishLicense;
/// <summary>
/// The specially formatted version of the use license describing what
/// rights the current user has on the document.
/// </summary>
RightsManagementLicense _rmUseLicense;
/// <summary>
/// The last saved RM use license.
/// </summary>
RightsManagementLicense _lastSavedRMUseLicense;
/// <summary>
/// The user for whom this document has been opened.
/// </summary>
RightsManagementUser _user;
/// <summary>
/// A dictionary of rights granted to users on this document.
/// </summary>
IDictionary<RightsManagementUser, RightsManagementLicense> _rightsDictionary;
/// <summary>
/// The last saved version of the dictionary of rights granted to users.
/// </summary>
IDictionary<RightsManagementUser, RightsManagementLicense> _lastSavedRightsDictionary;
/// <summary>
/// A dictionary of rights corresponding to the rights granted in a
/// temporary unsigned publish license.
/// </summary>
IDictionary<RightsManagementUser, RightsManagementLicense> _temporaryRightsDictionary;
/// <summary>
/// A list of all the CryptoProviders generated
/// </summary>
IList<CryptoProvider> _cryptoProviders;
//Name of the RM application manifest.
private const string _applicationManifestFileName = "XPSViewerManifest.xml";
private const string _registryLocationForDefaultUser =
@"Software\Microsoft\XPSViewer\";
private const string _registryBaseForXpsViewer =
@"HKEY_CURRENT_USER\Software\Microsoft\XPSViewer\";
private const string _registryValueNameForAccountName = "AccountName";
private const string _registryValueNameForAccountType = "AccountType";
/// <summary>
/// The Key/Value pair for the NOLICCACHE publishing option. This disables
/// caching of the Use License in the container if present.
/// </summary>
private readonly KeyValuePair<string, string> _noLicCacheKeyValuePair =
new KeyValuePair<string, string>("NOLICCACHE", "1");
#endregion Private Fields
}
}
|