The .NET Framework provides many classes that help us to develop secure code within our applications. Many of these classes offer role-based security and cryptography. The .NET Framework also brings code access permission objects, which are the building blocks for protecting against unauthorized code access. They are fundamental for enforcing security on managed code-only code that has permission to run in the current context can be executed.
Each code access permission demonstrates one of the following rights:
The right to access protected resources such as files
The right to perform a protected operation such as accessing managed code
For the Internet world, particularly for network applications, the System.Net classes provide built-in support for authentication and code access permissions. The .NET Framework provides the SocketPermission class for enforcing code access permissions.
The SocketPermission class is used to control rights to make or accept connections, controlling access to a network via sockets. A SocketPermission consists of a host specification and a set of 'actions' specifying ways to connect to that host. It enforces secure code by monitoring the values of the hostname, IP address, and transport protocol.
There are two ways to enforce security permission in C# sockets
Imperatively, using the SocketPermission class
Declaratively, using the SocketPermissionAttribute
Imperative security syntax implements permissions by creating a new instance of the SocketPermission class to demand a particular permission when the code is executed, such as the right to make a TCP connection. It is generally used when the security settings are changed at runtime. Declarative syntax uses attributes to place security information into the metadata of our code, so that the client that calls our code can use reflection to see what permissions are required by the code.
Imperative Security
This type of security syntax creates a new instance of the SocketPermission class to enforce security. You can use imperative security syntax to perform demands and overrides, but not requests. Before calling the corresponding security measure, it is necessary to initialize the state of the SocketPermission class through the constructor so that it represents the particular form of permission that you are looking for. This kind of security syntax is useful only when you have some information needed for security that is only available at runtime, for example, if you want to secure some host over a port but don't know the host name and port number until the program executes.
The following application demonstrates the basic use of the SocketPermission class. As this code behaves as a client, the SocketServer.cs application created earlier in the chapter must be running before executing this program. Otherwise, it will throw a SocketException when the connect method is called. The program takes an optional command-line parameter that can be either assert (the default if no option is specified), or deny. We use this option to either grant or deny permission to the program to make a connection to the server:
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Security;
using System.Security.Permissions;
public class ImpSecurity
{
public static void Main(String [] arg)
{
// option could be either assert or deny passed on the command line
// if option is assert then the program executes successfully
// otherwise it triggers a securityexception
String option = null;
if (arg.Length > 0)
{
option = arg[0];
}
else
{
option = "assert";
}
Console.WriteLine("option:" + option);
MethodA(option);
}
public static void MethodA(String option)
{
Console.WriteLine("MethodA");
IPHostEntry ipHost = Dns.Resolve("127.0.0.1");
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);
Socket sender = new Socket(AddressFamily.Internetwork, SocketType.Stream,
ProtocolType.Tcp);
SocketPermission permitSocket = new SocketPermission(NetworkAccess.Connect,
TransportType.Tcp, "127.0.0.1", SocketPermission.AllPorts);
// Select Assert or Deny on the basis of parameter passed
if (option.Equals("deny"))
{
permitSocket.Deny();
}
else
{
permitSocket.Assert();
}
try
{
// Connect the socket to the remote endPoint. Catch any errors
sender.Connect(ipEndPoint);
Console.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString());
byte[] bytes = new byte[1024];
byte[] msg = Encoding.ASCII.GetBytes("This is a test
// Send the data through the socket
int bytesSent = sender.Send(msg);
// Receive the response from the remote device
int bytesRec = sender.Receive(bytes);
Console.WriteLine("Echoed Test = {0}", Encoding.ASCII.Getstring(bytes,
0, bytesRec));
}
catch(SocketException se)
{
Console.WriteLine("Socket Exception:" + se.ToString());
}
catch(SecurityException sece)
{
Console.WriteLine("Socket Exception:" + sece.ToString());
}
finally
{
if (sender.Connected)
{
// Release the socket
sender.Shutdown(SocketShutdown.Both);
sender.Close();
}
}
Console.WriteLine("Closing MethodA");
}
}
The above code shows how to implement code access security using the imperative syntax. The important part of the code is a single method call, to either the Assert() or Deny() method of the SocketPermission. The program basically follows two different paths of execution, depending on the command-line argument passed in.
One path demonstrates a successful connection, and the other illustrates a failed connection request due to the security implications of the code. In the program just shown, we restrict the code from making a TCP connection to a server. This restriction is performed by calling the Deny() method of the SocketPermission class. But before calling any methods of that class, we first have to initialize the SocketPermission object. This initialization is performed in the constructor:
SocketPermission permitSocket = new SocketPermission(NetworkAccess.Connect,
TransportType.Tcp, "127.0.0.1", SocketPermission.AllPorts);
The constructor creates a new instance of the SocketPermission class and initializes it for the given transport address with the specified permission. The parameters denote the network access for which we want to grant/deny permissions (here, connecting to a server), the transport type (TCP), the hostname, and the port number. The NetworkAccess value can be one of the following:
Member name
Description
Accept
Indicates that the application is allowed to accept connections from the Internet on a local resource.
Connect
Indicates that the application is allowed to connect to specific Internet resources.
The TranportType parameter represents the transport protocol, it can be All, Tcp, Udp, etc. The third parameter is the hostname, with the port number as the last parameter.
After the above line is executed, a new SocketPermission object is created that controls access to the specified hostname and port using the specified TransportType.
Once we have initialized the SocketPermission in this way, we can call Assert() or Deny() to allow or prevent the code from performing the specified NetworkAccess.
Passing "deny" as a parameter to this application (that is, running the application by entering ImpSecurity deny at the command line) produces the following output:
C:\Networking\Sockets>ImpPermissions deny
option:deny
MethodA
Socket Exception:System.Security.SecurityException: Request for the permission
of type System.Net.SocketPermission, System, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089 failed.
at System.Security.SecurityRuntime.FrameDescHelper(FrameSecurityDescriptor
secDesc, IPermission demand, PermissionToken permToken)
at System.Security.CodeAccessSecurityEngine.Check(PermissionToken permToken,
CodeAccessPermission demand, StackCrawlMark& stackMark, Int32 checkFrames, Int32
unrestrictedOverride)
at System.Security.CodeAccessSecurityEngine.Check(CodeAccessPermission cap,
StackCrawlMark& stackMark)
at System.Security.CodeAccessPermission.Demand()
at System.Net.Sockets.Socket.CheckCacheRemote(SocketAddress socketAddress,
EndPoint remoteEP, Boolean isOverwrite)
at System.Net.Sockets.Socket.Connect(EndPoint remoteEP)
at ImpSecurity.MethodA(String option)
The state of the failed permission was:
port="11000"/>
Closing MethodA
Declarative Security
Declarative security uses .NET attributes to place security information inside the metadata of the code. Attributes can be placed at the assembly, class, or member level to indicate the type of request, demand, or override that is needed. In order to use this security syntax, the state data must be initialized first for the SocketPermissionAttribute object through the declarative syntax so that it represents the form of permission that is being enforced on the code.
The following example demonstrates how to enforce permissions using SocketPermissionAttribute. We define two methods, called LegalMethod() and IllegalMethod(), which both attempt to connect to a server via TCP. On the first method we place a SocketPermissionAttribute that grants the necessary permission to the method; on the second, we place an attribute to deny connect permission:
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Security;
using System.Security.Permissions;
public class DecSecurity
{
public static void Main(String [] arg)
{
LegalMethod();
IllegalMethod();
}
[SocketPermission(SecurityAction.Assert, Access = "Connect",
Host = "127.0.0.1", Port="All", Transport="Tcp")]
public static void LegalMethod()
{
Console.WriteLine("LegalMethod");
IPHostEntry ipHost = Dns.Resolve("127.0. 0.1");
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);
Socket sender = new Socket(AddressFamily.Internetwork, SoeketType.Stream,
ProtocolType.Tcp);
try
{
// Connect the socket to the remote endPoint. Catch any errors
sender.Connect(ipEndPoint);
Console.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString ());
}
catch(SecurityException se)
{
Console.WriteLine("Security Exception:" + se);
}
catch(SocketException se)
{
Console.WriteLine("Socket Exception:" + se);
}
finally
{
if (sender.Connected)
{
// Release the socket
sender.Shutdown(SocketShutdown.Both);
sender.Close();
}
}
}
[SocketPermission(SecurityAction.Deny, Access = "Connect",
Host = "127.0,0.1", Port = "All", Transport = "Tcp")]
public static void IllegalMethod()
{
Console.WriteLine("IllegalMethod");
IPHostEntry ipHost = Dns.Resolve("127.0.0.1");
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);
Socket sender = new Socket(AddressFamily.Internetwork,
SocketType.Stream, ProtocolType.Tcp);
try
{
// Connect the socket to the remote endPoint. Catch any errors
sender.Connect(ipEndPoint);
Console.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString ());
}
catch(SocketException se)
{
Console.WriteLine("Socket Exception:" + se);
}
catch(SecurityException se)
{
Console.WriteLine("Security Exception:" + se);
}
finally
{
if (sender.Connected)
{
// Release the socket
sender.Shutdown(SocketShutdown.Both);
sender.Close();
}
}
}
}
The above program is similar in functionality to the previous one. This program, however, uses the declarative security syntax instead of the imperative security demonstrated earlier. The code contains two different methods for connecting to the server, because the attributes couldn't be dynamically manipulated. The SocketPermissionAttribute declaration is placed on each method that we need to secure:
[SocketPermission(SecurityAction.Deny, Access = "Connect",
Host = "127.0.0.1", Port = "All", Transport = "Tcp")]
The above code declares and initializes the SocketPermissionAttribute, very much as we did in the SocketPermission's constructor. The properties of the SocketPermissionAttribute must have values that are not a null reference or invalid. Also, once set, these properties cannot be changed. The properties' values are the same as in the SocketPermission class. The declarative code, once compiled, is stored inside the metadata of the application code, so calling code can use reflection to see what permissions the code requires to run.