The underlying functionality and API to set and use a pre-shared key is present and is simple to use, although the API documentation is rather brief. The main routine is MprAdminInterfaceSetCredentialsEx and this need only be preceded by a call to MprAdminServerConnect and (strictly speaking) eventually be followed by a call to MprAdminServerDisconnect.
The documentation on docs.microsoft.com for MprAdminInterfaceSetCredentialsEx only mentions a pre-shared key in the description of its third parameter (“dwLevel”): “A value of 1 indicates the information is a pre-shared key for the interface”. The Microsoft Open Specification document “[MS-RRASM]: Routing and Remote Access Server (RRAS) Management Protocol” contains additional (and useful) information. For example, in section “3.1.4.41 RRouterInterfaceSetCredentialsEx”, it mentions that: “If dwLevel is 0x0000002 and hInterface is NULL, the preshared key is used for L2TP”.
The documentation of the companion routine MprAdminInterfaceGetCredentialsEx mentions that: “A value of 1 indicates the information is a pre-shared key for the interface, which is in an encrypted format”. Empirically, the routine always seems to return the string “****************” (16 asterisks) – which corresponds with how a pre-shared key is displayed in the Routing and Remote Access Properties page Security tab.
The L2TP pre-shared key set by MprAdminInterfaceSetCredentialsEx is persisted in the registry as an LSA secret at HKLM\Security\Policy\Secrets\L$_RasServerCredentials#0.
A C# code extract demonstrating the process is shown below.
if (MprAdminServerConnect(null, out IntPtr mpr) == 0)
{
string key = args[0];
MPR_CREDENTIALSEX_1 creds = new MPR_CREDENTIALSEX_1 { Size = key.Length, CredentialsInfo = Marshal.StringToHGlobalAnsi(key) };
MprAdminInterfaceSetCredentialsEx(mpr, IntPtr.Zero, 2, creds);
MprAdminServerDisconnect(mpr);
}
In a “live” system, the L2TP pre-shared key is an element in a providerContext in the Windows Filtering Platform (WFP) Base Filtering Engine (BFE) configuration. More precisely, it is a presharedKeyAuthentication sub-element of the FWPM_IPSEC_IKE_MM_CONTEXT providerContext named "L2TP Main Mode Policy". The complete BFE configuration can be viewed with the command “netsh wfp show state”.
Setting a pre-shared key via the WFP API
A recipe for doing this is to search for the FWPM_IPSEC_IKE_MM_CONTEXT providerContext named "L2TP Main Mode Policy" and then add a slightly modified copy, changing the providerContextKey to a new (unique) value, optionally changing the displayData to something appropriate and changing/extending the ikeMmPolicy to contain a presharedKeyAuthentication method (with the desired pre-shared key value). A new FWPM_LAYER_IKEEXT_V4 filter must also be added to utilise the new providerContext. A C# code extract demonstrating the process is shown below.
FWPM_SESSION0 session = new FWPM_SESSION0();
session.DisplayData.Name = "Gary";
session.Flags = FWPM_SESSION_FLAG_DYNAMIC;
if (FwpmEngineOpen0(null, RPC_C_AUTHN_DEFAULT, null, session, out IntPtr engine) == 0)
{
var x = new FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE0 { ProviderContextType = FWPM_PROVIDER_CONTEXT_TYPE.FWPM_IPSEC_IKE_MM_CONTEXT };
if (FwpmProviderContextCreateEnumHandle0(engine, x, out IntPtr h) == 0)
{
if (FwpmProviderContextEnum2(engine, h, 2, out IntPtr entries, out uint m) == 0)
{
for (uint i = 0; i < m; i++)
{
FWPM_PROVIDER_CONTEXT2 ctx = Marshal.PtrToStructure<FWPM_PROVIDER_CONTEXT2>(Marshal.ReadIntPtr(entries, (int)i * IntPtr.Size));
Console.WriteLine(ctx.DisplayData.Name);
if (ctx.DisplayData.Name == "L2TP Main Mode Policy")
{
uint n = ctx.Policy.IkeMmPolicy->NumAuthenticationMethods;
IKEEXT_AUTHENTICATION_METHOD2* methods = stackalloc IKEEXT_AUTHENTICATION_METHOD2[(int)n + 1];
for (uint j = 0; j < n; j++) methods[j] = ctx.Policy.IkeMmPolicy->AuthenticationMethods[j];
string preshared = args[0];
methods[n].AuthenticationMethodType = IKEEXT_AUTHENTICATION_METHOD_TYPE.IKEEXT_PRESHARED_KEY;
methods[n].PresharedKeyAuthentication.PresharedKey.Size = (uint)preshared.Length;
methods[n].PresharedKeyAuthentication.PresharedKey.Data = (byte*)Marshal.StringToHGlobalAnsi(preshared);
ctx.ProviderContextKey = Guid.NewGuid();
ctx.DisplayData.Name = "Gary Context";
ctx.Policy.IkeMmPolicy->NumAuthenticationMethods = n + 1;
ctx.Policy.IkeMmPolicy->AuthenticationMethods = methods;
if (FwpmProviderContextAdd2(engine, ctx, null, out ulong _) == 0)
{
ulong weight = 1;
FWPM_FILTER0 filter = new FWPM_FILTER0();
filter.DisplayData.Name = "Gary Filter";
filter.Flags = FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
filter.LayerKey = FWPM_LAYER_IKEEXT_V4;
filter.SubLayerKey = FWPM_SUBLAYER_UNIVERSAL;
filter.Weight.Type = FWP_DATA_TYPE.FWP_UINT64;
filter.Weight.uint64 = &weight;
filter.Action.Type = FWP_ACTION_TYPE.FWP_ACTION_PERMIT;
filter.U.ProviderContextKey = ctx.ProviderContextKey;
if (FwpmFilterAdd0(engine, filter, null, out ulong _) == 0)
{
Console.Write("Waiting..."); Console.ReadKey();
}
}
}
}
FwpmFreeMemory0(&entries);
}
FwpmProviderContextDestroyEnumHandle0(engine, h);
}
FwpmEngineClose0(engine);
}
The only constraint on the filter subLayerKey and weight is that the filter should have a higher effective weight than the existing FWPM_LAYER_IKEEXT_V4 filter.
Simple trick to set a pre-shared key
Here are examples of creating suitable “dummy” IPsec tunnels (the endpoint value can be any address that does not apply to any potential traffic) with a shared secret of “XXX”:
netsh advfirewall consec add rule name="Dummy" endpoint1=192.168.3.1/32 endpoint2=any mode=tunnel action=requireinrequireout auth1=computerpsk auth1psk="XXX"
New-NetIPsecRule -DisplayName "Dummy" -Phase1AuthSet (New-NetIPsecPhase1AuthSet -DisplayName "Dummy" -Proposal (New-NetIPsecAuthProposal -Machine -PreSharedKey "XXX")).InstanceID -InboundSecurity Require -OutboundSecurity Require -KeyModule IKEv1 -Mode Tunnel -LocalAddress 192.168.3.1/32 -RemoteAddress Any
The pre-shared key created by this method is persisted in the registry under HKLM\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\Phase1AuthenticationSets.
Similar to the L2TP pre-shared key, in a “live” system the pre-shared key created by this method is a presharedKeyAuthentication sub-element of an FWPM_IPSEC_IKE_MM_CONTEXT providerContext named after the Phase1AuthenticationSet ("Dummy", in the examples above). The filter that references this providerContext has a subLayerKey of FWPM_SUBLAYER_IPSEC_TUNNEL which gives the filter a higher effective weight than the L2TP filter.
On an “out-of-the-box” system, the ikeProposals in the L2TP providerContext and the new providerContext are usually identical; if testing (or a review of the “netsh wfp show state” output) indicates discrepancies then the commands to create the “Dummy” tunnel can be extended to compensate for the differences.
No comments:
Post a Comment