Sandboxed Applications Can’t Elevate Their Own Permissions


Every once in a while someone will ask how they can do something similar to these caspol commands from within their application. Generally, they want their application to be deployed from the Internet or a file share and don’t want users to have to deal with setting up CAS policy properly to get the application to run.

The answer of course is that you can’t do this … if an application were allowed to add code groups to policy without user interaction in order to elevate their privileges then every malicious application out there would go ahead and grant themselves full access to everybody’s machine; effectively rendering CAS useless as a protection mechanism.

Instead, you’ll need to have the end user make a trust decision for you. In v1.x this was difficult, you generally had to deploy a policy MSI for the user to run or give them a set of caspol commands. With v2.0 of the CLR, we’ve made things a lot easier via ClickOnce applications. You can use ClickOnce to request any permissions that your application needs to run effectively – if these permissions would elevate the application above what it would normally get, then the user is prompted to make a trust decision.

This way your app can elevate to whatever permission level it needs, and you don’t have to worry about pushing out confusing CAS policy changes to everyone who wants to run it.

Comments (6)

  1. Kevin Westhead says:

    Isn’t this somewhat of a step backwards though? I thought the advantage of CAS was that it took users out of the trust loop. Doesn’t the ClickOnce solution suffer from the same problem as ActiveX, i.e. users can make bad trust decisions?

  2. shawnfa says:

    Hi Kevin,

    The ClickOnce prompt is setup to be much more user friendly than the ActiveX prompt was.  It provides visual feedback for dangerous applications, and helps users to make better trust decisions rather than the ActiveX "dump the certificate and let the user click yes because they don’t understand the prompt" model.

    -Shawn

  3. collin says:

    It makes sense that sandboxed applications can’t elevate their own permissions.  What about applications that have full trust?  Is it possible for them to pull off caspol-like commands?

  4. shawnfa says:

    Sure.  The obvious way is to just spawn off caspol, but there’s also an object model available — which is what caspol uses under the hood.  Check out the System.Security.SecurityManager class to get started … come to think of it, this might make a good blog entry 🙂

    -Shawn

  5. collin says:

    I chose to write a custom policy setup application leveraging the SecurityManager class instead of using the auto-generated policy msi.  (My application requires .NET Framework 1.1 so I can’t use ClickOnce.)  I like this solution much better.  It doesn’t overwrite the user’s security policy and it doesn’t suffer the weird install, uninstall, install, uninstall problem described in [url=http://blogs.msdn.com/shawnfa/archive/2004/09/07/226530.aspx#462111]this post[/url].

    Here is the small library I made:

    using System;

    using System.Security;

    using System.Security.Policy;

    using System.Security.Permissions;

    using System.Collections;

    using System.Windows.Forms;

    namespace Utility

    {

    /// <summary>

    /// Performs basic modifications to the security policy, such as adding or removing custom code groups.

    /// Methods in this class require ControlPolicy permission.

    /// </summary>

    public abstract class Security

    {

    /// <summary>

    /// Adds a fully-trusted, strong-named code group to the machine policy level.

    /// </summary>

    public static void AddCodeGroup(string publicKey, string name, string description)

    {

    // create an unrestricted policy

    PolicyStatement policy = new PolicyStatement(new NamedPermissionSet("FullTrust"));

    // get the public key

    byte[] bytes = Utility.HexEncoding.GetBytes(publicKey);

    StrongNamePublicKeyBlob blob = new StrongNamePublicKeyBlob(bytes);

    // creat the new code group

    CodeGroup codeGroup = new UnionCodeGroup(new StrongNameMembershipCondition(blob, null, null), policy);

    codeGroup.Name = name;

    codeGroup.Description = description;

    // move through the policy levels looking for the Machine policy level

    IEnumerator policyEnumerator = SecurityManager.PolicyHierarchy();

    while(policyEnumerator.MoveNext())

    {

    // check if this is the Machine policy level

    PolicyLevel currentLevel = (PolicyLevel)policyEnumerator.Current;

    if(currentLevel.Label == "Machine")

    {

    // check if the code group already exists and ask to overwrite

    if(ExistsCodeGroup(name, currentLevel))

    {

    DialogResult result = MessageBox.Show(

    String.Format("There is already a code group named ‘{0}’.  Do you want to overwrite it?", name),

    "Code Group Exists",

    MessageBoxButtons.YesNo);

    // if the user does not want to overwrite, abandon ship.

    // otherwise, remove the code group and continue as normal

    if(result == DialogResult.No)

    break;

    else

    RemoveCodeGroup(name, currentLevel);

    }

    // add the code group

    currentLevel.RootCodeGroup.AddChild(codeGroup);

    SecurityManager.SavePolicy();

    break;

    }

    }

    }

    /// <summary>

    /// Removes the code group from the given policy level.

    /// </summary>

    /// <param name="name">The name of the code group.</param>

    public static void RemoveCodeGroup(string name, PolicyLevel level)

    {

    CodeGroup root = level.RootCodeGroup;

    foreach(CodeGroup codeGroup in root.Children)

    {

    if(codeGroup.Name == name)

    {

    root.RemoveChild(codeGroup);

    break;

    }

    }

    }

    /// <summary>

    /// Returns true if the code group exists in the given policy level.

    /// </summary>

    /// <param name="name">The name of the code group.</param>

    /// <param name="level">The name of the policy level to search in.</param>

    public static bool ExistsCodeGroup(string name, PolicyLevel level)

    {

    CodeGroup root = level.RootCodeGroup;

    foreach(CodeGroup codeGroup in root.Children)

    {

    if(codeGroup.Name == name)

    return true;

    }

    return false;

    }

    }

    }

  6. Guy kolbis says:

    Recently I visited Toronto for Beta release of software I designed. As always with Beta versions, we