Jaa


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

  • Anonymous
    July 14, 2006
    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?
  • Anonymous
    July 17, 2006
    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
  • Anonymous
    July 26, 2006
    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?
  • Anonymous
    July 26, 2006
    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
  • Anonymous
    August 04, 2006
    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;
    }
    }
    }

  • Anonymous
    April 21, 2007
    Recently I visited Toronto for Beta release of software I designed. As always with Beta versions, we