Secure RPC Best Practices

Secure RPC Best Practices

This section outlines a series of general security best practices, which are based on experience and are highly encouraged. The potential security threats to RPC include the following:

  • DoS threats when an attacker sends malformed data to an RPC endpoint and the RPC server does not properly handle the bad data and fails.

  • Information disclosure threats as data travels from the client to the server and back unprotected and an attacker uses a packet sniffer to view the data.

  • Data-tampering threats as an attacker intercepts unprotected on-the-wire data and modifies it.

Using the practices covered in this section will help mitigate these threats.

Use the /robust MIDL Switch

The /robust Microsoft Interface Definition Language (MIDL) compiler switch was added to Windows 2000 to add more run-time checking to data as it arrives at the RPC server marshaler. This improves the stability of the server by rejecting more malformed packets than in previous versions of RPC. This is important: any malformed packet is rejected by the RPC marshaling engine.

If your application runs on Windows 2000 and later, you should definitely enable this compiler option. There's no need to change the client or server code. The only downside is that this new feature works only on Windows 2000 and later. If you also target Windows NT 4 as an RPC server, you'll need to create two versions of the server: one for Windows NT 4 and one for Windows 2000 and later. Using the option is simple: just add /robust to the MIDL command line.

NOTE
The gains from using the /robust switch are so great that you should go through the effort of creating two server binaries if you support down-level server platforms, such as Windows NT 4. It truly is worth the work.

Use the [range] Attribute

You can use the [range] attribute in an IDL file to modify the meaning of sensitive parameters or fields, such as those used for size or length. For example, IDL allows the developer to describe the size of a data blob:

void Message([in] long lo, [in] long hi, [size_is(lo, hi)] char **ppData);

In theory, an attacker could set lo and hi to define an out-of-bounds range that could cause the server or client to fail. You can help reduce the probability of such an attack by using the [range] attribute. In the following example, lo and hi are restricted to be between the values 0 and 1023, which means that the size of the data pointed to by ppData can be no larger than 1023 bytes:

void Message([in, range(0,1023)] long lo, [in, range(0,1023)] long hi, [size_is(lo, hi)] char **ppData);

Note that you must use the /robust compiler option when you compile your IDL file to generate the stub code that will perform these checks. Without the /robust switch, the MIDL compiler ignores this attribute. It's also worth noting in this example that it's up to the server software to determine that hi is greater than or equal to lo.

Require Authenticated Connections

You can mitigate many DoS attacks simply by requiring clients to authenticate themselves. Imagine the following scenario: A server exists that accepts data from clients. The server can operate in one of two modes. It can accept data from anyone without authentication, in which case all data transmitted by the client is anonymous. Or it can require that all users authenticate themselves before the server accepts any data; any nonauthenticated data is rejected. In the second mode, the server not only requires client authentication, it also logs the information in an audit log. Which scenario is more prone to denial of service attacks? That's right the anonymous scenario because there's no recourse against the attacker, who is anonymous. Obviously, if an attacker knows that his identity must be divulged, he will be less likely to attempt attacks! So require authenticated connections in your RPC server-based applications.

You need to make changes to both the client and the server to support such authentication. The client sets up the security configuration, and the server can check the settings to determine whether the configuration is good enough, which will depend on the threats to the system. For example, you might require more security options if your application provides access to highly sensitive data. I'll discuss the options momentarily.

A strategy often used by RPC clients who want to add security rather than build it in from the outset is to allow the server to accept both types of connections for a grace period while the clients get upgraded. After that, the plug is pulled on the unauthenticated connections. Of course, the more secure route is to add security capabilities from the outset.

Client-Side Settings

At the client, your application should call RpcBindingSetAuthInfo to set the authentication, privacy, and tamper detection policy. The following is an example from our earlier phone application:

status = RpcBindingSetAuthInfo( phone_Handle, szSPN, // For Kerberos support, use the server's SPN. RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE, NULL, 0);

The second argument, szSPN, specifies the service principal name (SPN), which I'll discuss in detail later. The third argument, AuthnLevel, is set to RPC_C_ AUTHN_LEVEL_PKT_PRIVACY, which means that the data sent between the client and the server is authenticated, encrypted, and integrity-checked. Table 16-2 outlines the possible RPC-supported security setting levels.

Table 16-2. RPC Security Setting Levels

Setting

Value

Comments

RPC_C_AUTHN_LEVEL_DEFAULT

0

Uses the default setting for the authentication service. Personally, I don't use this because you don't always know what the setting may be. Perhaps I've been doing this security stuff for too long, but I'd rather know what I'm getting!

Currently, the default for RPC applications is RPC_C_AUTHN_LEVEL_CONNECT.

RPC_C_AUTHN_LEVEL_NONE

1

No authentication. Not recommended.

RPC_C_AUTHN_LEVEL_CONNECT

2

Authentication is performed when the client first connects to the server.

RPC_C_AUTHN_LEVEL_CALL

3

Authentication is performed at the start of each RPC call. Note that this setting is automatically upgraded to RPC_C_AUTHN_LEVEL_PKT if the protocol sequence is connection-based. Connection-based protocols start with ncacn.

RPC_C_AUTHN_LEVEL_PKT

4

Authentication is performed to make sure that all data is from the expected sender.

RPC_C_AUTHN_LEVEL_PKT_INTEGRITY

5

Same as RPC_C_AUTHN_LEVEL_PKT and also determines whether the data has been tampered with.

RPC_C_AUTHN_LEVEL_PKT_PRIVACY

6

Same as RPC_C_AUTHN_LEVEL _PKT_INTEGRITY, and the data is encrypted.

NOTE
Some would argue that the argument name AuthnLevel is somewhat misleading because the argument controls not only authentication but also integrity and privacy.

To summarize what happens at the client, the client calls RpcBindingSetAuthInfo, which places the client identity information in the binding handle that's passed to the server as the first parameter in remote procedure calls.

Server-Side Settings

To determine an appropriate level of security for the server, you set an authentication handler for the server and, when the client connects, analyze the client connection settings to determine whether the client meets the security quality bar for your application.

You set the authentication mechanism by using RpcServerRegisterAuth Info:

status = RpcServerRegisterAuthInfo( szSPN, RPC_C_AUTHN_GSS_NEGOTIATE, NULL, NULL);

From a Windows authentication perspective, the second argument, AuthnSvc, is critical because it determines how the client is to be authenticated. The most common setting is RPC_C_AUTHN_GSS_WINNT, which will use NTLM authentication to authenticate the client. However, in a Windows 2000 environment and later, it is highly recommended that you instead use RPC_C_AUTHN_GSS_NEGOTIATE, which will use either NTLM or Kerberos automatically.

There is another option, RPC_C_AUTHN_GSS_KERBEROS, but RPC_C_AUTHN_GSS_NEGOTIATE gives your application a little more leeway in that it will still work on down-level platforms such as Windows NT 4. Of course, that means that an attacker also has more leeway because she can force the use of the less secure NTLM authentication protocol.

Servers extract the client authentication information from the client binding handle by calling RpcBindingInqAuthClient in the remote procedure. This will identify the authentication service used NTLM or Kerberos, for example and the authentication level desired, such as none, packet authentication, privacy, and so on.

Here's an example of the code:

//RPC server function with security checks inline. void Message(handle_t hPhone, unsigned char *szMsg) { RPC_AUTHZ_HANDLE hPrivs; DWORD dwAuthn; RPC_STATUS status = RpcBindingInqAuthClient( hPhone, &hPrivs, NULL, &dwAuthn, NULL, NULL); if (status != RPC_S_OK) { printf("RpcBindingInqAuthClient returned: 0x%x\n", status); RpcRaiseException(ERROR_ACCESS_DENIED); } //Now check the authentication level. //We require at least packet-level authentication. if (dwAuthn < RPC_C_AUTHN_LEVEL_PKT) { printf("Client attempted weak authentication.\n"); RpcRaiseException(ERROR_ACCESS_DENIED); } if (RpcImpersonateClient(hIfPhone) != RPC_S_OK) { printf("Impersonation failed.\n"); RpcRaiseException(ERROR_ACCESS_DENIED); } char szName[128+1]; DWORD dwNameLen = 128; if (!GetUserName(szName, &dwNameLen)) lstrcpy(szName, "Unknown user"); printf("The message is: %s\n"  "%s is using authentication level %d\n", szMsg, szName, dwAuthn); RpcRevertToSelf(); }

A number of things are going on here. The Message function is the remote function call from the sample phone application. First the code determines what authentication level is used by calling RpcBindingInqAuthClient and querying the AuthnLevel value. If the function fails or AuthnLevel is less than our security minimum, the call fails and the server raises an access denied exception, which will be caught by the client. Next the code impersonates the caller and determines the username. Finally, after displaying the appropriate message, the call reverts to the process identity.

Note also that the return values from all impersonation functions are checked in this book. In versions of Windows prior to Microsoft Windows .NET Server 2003, it was uncommon for these functions to fail; usually they failed only when the system was low on memory or because of a setting on the application that prevented impersonation. However, Windows .NET Server 2003 introduces a new privilege Impersonate A Client After Authentication that might make it more common for such failures to occur if the process account does not have this privilege.

A Note Regarding Kerberos Support

The szSPN parameter used in the RpcBindingSetAuthInfo call specifies the principal name of the server, which allows Kerberos to work. Remember that Kerberos authenticates the client and the server referred to as mutual authentication and NLTM authenticates the client only. Server authentication provides protection from server spoofing. The szSPN parameter can be NULL if you do not want Kerberos support.

You configure this parameter by calling DsMakeSPN at the client. The function is defined in Ntdsapi.h, and you need to link with Ntdsapi.dll. The following code fragment shows how to use this function:

DWORD cbSPN = MAX_PATH; char szSPN[MAX_PATH + 1]; status = DsMakeSpn("ldap",  "blake-laptop.northwindtraders.com", NULL, 0, NULL, &cbSPN, szSPN);

The server application must also make sure it is using the same name:

LPBYTE szSPN = NULL; status = RpcServerInqDefaultPrincName( RPC_C_AUTHN_GSS_NEGOTIATE, &szSPN); if (status != RPC_S_OK) ErrorHandler(status); //Register server authentication information. status = RpcServerRegisterAuthInfo( szSPN, RPC_C_AUTHN_GSS_NEGOTIATE, 0, 0); if (status != RPC_S_OK) ErrorHandler(status);  if (szSPN) RpcStringFree(&szSPN);

Performance of Different Security Settings

Generally, the first question that comes to everyone's mind relates to performance. What are the performance implications of running RPC servers that require authentication? A sample RPC application named RPCSvc ships with the Microsoft Platform SDK; it was designed specifically to test the performance characteristics of various RPC settings. I ran this application on two computers. The client was running Windows XP Professional, and the server had a 550-MHz CPU and 256 MB of RAM and was running Windows .NET Server 2003. The test consisted of calling a single remote function that passed a 100-byte buffer to the server 1000 times. Table 16-3 shows the results of averaging three test runs using named pipes and TCP/IP.

Table 16-3. Performance Characteristics of Various RPC Settings

AuthnLevel

Using ncacn_np

Using ncacn_ip_tcp

RPC_C_AUTHN_LEVEL_NONE

1926 milliseconds (ms)

1051 ms

RPC_C_AUTHN_LEVEL_CONNECT

2023 ms

1146 ms

RPC_C_AUTHN_LEVEL_PKT_ PRIVACY

2044 ms

1160 ms

As you can see, the performance impact of requiring authentication is not large. It's on the order of 10 percent degradation. However, you get a great deal of security benefit for such little trade-off. Notice that the performance impact of going from RPC_C_AUTHN_LEVEL _CONNECT to RPC_C_AUTHN_LEVEL_PKT_ PRIVACY is minimal. If your application is using RPC_C_AUTHN_LEVEL_ CONNECT, you really ought to use RPC_C_AUTHN_LEVEL _PKT_PRIVACY, which is our next topic.

Use Packet Privacy and Integrity

If you perform authenticated RPC calls, why not go to the next level and opt for packet privacy and integrity also? It's almost free! In January 2000, I performed a security review early in the design phase of a major new Microsoft application, and I suggested that the team use packet privacy and integrity for all their administration communications using RPC. At first the team was wary of the performance impact, but after evaluating the setting it's just a flag change in RpcBindingSetAuthInfo, after all they decided to go with the more secure configuration. About six months before the product shipped, a well-respected security consulting company performed an audit of the application and its source code. In the findings they made a note that made me smile: We spent a great deal of time attempting to break the administration communications channel, with no success. When so many companies fail to protect such sensitive data adequately, we applaud the team for using secured RPC and DCOM.

Figure 16-2 shows the effect of using RPC with the RPC_C_AUTHN_LEVEL_ NONE option, and Figure 16-3 shows the effect of using RPC with the RPC_C_AUTHN_LEVEL_PKT_PRIVACY option.

figure 16-2 rpc traffic using the rpc_c_authn_level_none option. note that the passphrase is exposed.

Figure 16-2. RPC traffic using the RPC_C_AUTHN_LEVEL_NONE option. Note that the passphrase is exposed.

figure 16-3 rpc traffic using the rpc_c_authn_level_pkt_pri vacy option. note that the payload, in the secret message, is encrypted.

Figure 16-3. RPC traffic using the RPC_C_AUTHN_LEVEL_PKT_PRI VACY option. Note that the payload, in the secret message, is encrypted.

Use Strict Context Handles

Use strict context handles if you don't need to share context handles between interfaces. Not using them opens the door for some easy DoS attacks, which I will explain shortly. Normally, when a call to an interface method generates a context handle, that handle becomes freely available to any other interface. When you use the [strict_context_handle] attribute in the ACF file, you guarantee that the methods in that interface will accept only context handles that were created by a method from the same interface.

Here's an example of some dangerous code that does not enforce strict context handles. The first code is from the IDL file, which defines one RPC application using two interfaces, one to manage printers and the other to manage files.

interface PrinterOperations { typedef context_handle void *PRINTER_CONTEXT; void OpenPrinter([in, out] PRINTER_CONTEXT *ctx); void UsePrinter([in] PRINTER_CONTEXT ctx); void ClosePrinter([in, out] PRINTER_CONTEXT *ctx); } interface FileOperations { typedef context_handle void *FILE_CONTEXT; void OpenFile([in, out] FILE_CONTEXT *ctx); void UseFile([in] FILE_CONTEXT ctx); void CloseFile([in, out] FILE_CONTEXT *ctx) }

And here's a portion of the associated RPC server C++ code:

void OpenPrinter(PRINTER_CONTEXT *ctx) { //Create an instance of the printer manipulation object. *ctx = new CPrinterManipulator(); if (*ctx == NULL) RpcRaiseException(ERROR_NOT_ENOUGH_MEMORY); //Perform printer open operations.  } void UseFile(FILE_CONTEXT ctx) { //Get the user's file manipulator instance. CFileManipulator cFile = (CFileManipulator*)ctx; //Perform file operations.  }

This is perfectly valid RPC server code, but it does include a subtle security vulnerability. If an attacker can send a printer context to the file interface, he will probably crash the RPC server process because the call to CFileManipulator cFile = (CFileManipulator*)ctx will cause an access violation. The following malicious client code achieves this:

void *ctxAttacker; OpenPrinter(&ctxAttacker); UseFile(ctxAttacker);

The last function call, UseFile(ctxAttacker), is not sending a FILE_CONTEXT to UseFile it's really a PRINTER_CONTEXT.

To mitigate this, change the ACF file to include strict_context_handle:

[explicit_handle, strict_context_handle] interface PrinterOperations{} interface FileOperations{}

This will force the RPC runtime to verify that any context handle passed to PrinterOperations was created by PrinterOperations and that any context handle passed to FileOperations was created by FileOperations.

Don't Rely on Context Handles for Access Checks

Don't use context handles as a substitute for access checks. It's possible for an attacker to steal a context handle in rare situations and reuse the handle while posing as a different user, even if the attacker doesn't understand the contents of the handle or of the RPC data. This is especially true if the data is unencrypted. The probability of successful attack goes down substantially when you use encrypted messages, but it is still not negligible.

Some products check access when they open a context handle, and they assume all calls on the same context handle come under the same identity. Depending on what your server does with context handles, this might or might not be a security problem, but it's generally a Very Bad Thing to do. If your code performs access checks, you should always check access just prior to the secured operation, regardless of the value of the information held in the context handle.

RPC tries to guarantee that the context handle comes from the same network session, which depends on whether the network transport can guarantee the identity of sessions, but it doesn't guarantee that the context handle comes from the same security session. Therefore, RPC is susceptible to hijacking.

NOTE
In essence, the vulnerability that RPC doesn't guarantee that context handles come from the same security session is an example of a time-of-check, time-of-use problem, in which a developer checks that a situation is valid and later assumes the condition is still true, when in fact the condition might have changed. In this example, the user is validated when the context handle is set up, and then no more checks are performed in other functions that use the handle because you assume the handle is valid and not being used by a malicious user.

Be Wary of NULL Context Handles

Technically, dealing with NULL context handles is a robustness issue, but it could be a DoS threat to your application if you do not plan for this scenario. It is possible for a context handle to point to NULL, like so:

void MyFunc(..., /* [in] [out] */ CONTEXT_HANDLE_TYPE *hCtx) {}

Although hCtx will not be NULL, *hCtx might be NULL, so if your code attempts to use *hCtx, the application might fail. RPC checks that any context handle passed in to your functions was previously allocated by the server, but NULL is a special case and it will always be let through.

Take a look at the following sample code fragment:

short OpenFileByID(handle_t hBinding, PPCONTEXT_HANDLE_TYPE pphCtx, short sDeviceID) { short sErr = 0; HANDLE hFile = NULL; *pphCtx = NULL; if (RpcImpersonateClient(hBinding) == RPC_S_OK) { hFile = OpenIDFile(sDeviceID); if (hFile == INVALID_HANDLE_VALUE) { sErr = -1; } else { //Allocate server- based context memory for the client. FILE_ID *pFid = midl_user_allocate(sizeof ( FILE_ID)); if (pFid) { pFid->hFile = hFile; *pphCtx = (PCONTEXT_HANDLE_TYPE)pFid; } else { sErr = ERROR_NOT_ENOUGH_MEMORY; } } RpcRevertToSelf(); } return sErr; } short ReadFileByID(handle_t hBinding, PCONTEXT_HANDLE_TYPE phCtx) { FILE_ID *pFid; short sErr = 0; if (RpcImpersonateClient(hBinding) == RPC_S_OK) { pFid = (FILE_ID *)phCtx; ReadFileFromID(phCtx->hFile,...); RpcRevertToSelf(); } else { sErr = -1; } return sErr; } short CloseFileByID(handle_t hBinding, PPCONTEXT_HANDLE_TYPE pphCtx) { FILE_ID *pFid = (FILE_ID *)*pphCtx; pFid->hFile = NULL; midl_user_free(pFid); *pphCtx = NULL; return 0; }

This code allows a user to open a file by using the file's identifier by calling the remote OpenFileByID function. If the file access is successful, the function allocates some dynamic memory and stores data about the file in the allocated memory. The context handle then points to the allocated memory. However, if the call to RpcImpersonateClient or OpenIDFile fails, *pphCtx is NULL. If the user later calls CloseFileByID or ReadFileByID, the service will fail as it attempts to dereference the NULL data.

Your RPC server code should always check that the context handle is pointing to a memory location other than NULL before attempting to use it:

if (*pphCtx == NULL) { // Attempting to use a NULL context handle. }

Don't Trust Your Peer

Apply this rule to all networking technologies, not just to RPC. Making RPC calls from a highly privileged process to a less privileged process is dangerous because the caller might be able to impersonate you, the highly privileged caller, which can lead to an elevation of privilege attack if the client is a malicious client. If your RPC server must run with elevated privileges and you must call a peer, opt for an anonymous connection or support only Identify security semantics. This is achieved using the RpcBindingSetAuthInfoEx function, like so:

//Specify quality of service parameters. RPC_SECURITY_QOS qosSec; qosSec.Version = RPC_C_SECURITY_QOS_VERSION; qosSec.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT; qosSec.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC; qosSec.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY; status = RpcBindingSetAuthInfoEx(..., &qosSec);

ImpersonationType has four options: RPC_C_IMP_LEVEL_ANONYMOUS, which does not allow the recipient to know the identity of the caller; RPC_C_IMP_LEVEL_IDENTIFY, which allows the recipient to know the caller's identity; and RPC_C_IMP_LEVEL_IMPERSONATE and RPC_C_IMP_LEVEL_ DELEGATE, which allow the recipient to know the caller's identity and act on the caller's behalf.

Use Security Callbacks

The preferred way to secure your RPC server functions is to use security callback functions. This is achieved by using RpcServerRegisterIf2 or RpcServerRegisterIfEx rather than RpcServerRegisterIf when you perform RPC startup functions in the RPC server, and by setting the last argument to point to a function that is called by the RPC runtime to determine whether the client is allowed to call functions on this interface.

The following example code allows a client to connect only if it is using a connection secured using RPC_C_AUTHN_LEVEL_PKT or better:

/* Phones.cpp */  //Security callback function is automatically called when //any RPC server function is called. RPC_STATUS RPC_ENTRY SecurityCallBack(RPC_IF_HANDLE idIF, void *ctx) { RPC_AUTHZ_HANDLE hPrivs; DWORD dwAuthn; RPC_STATUS status = RpcBindingInqAuthClient( ctx, &hPrivs, NULL, &dwAuthn, NULL, NULL); if (status != RPC_S_OK) { printf("RpcBindingInqAuthClient returned: 0x%x\ n", status); return ERROR_ACCESS_DENIED; } //Now check the authentication level. //We require at least packet-level authentication. if (dwAuthn < RPC_C_AUTHN_LEVEL_PKT) { printf("Attempt by client to use weak authentication.\n"); return ERROR_ACCESS_DENIED; } return RPC_S_OK; }  void main() {  status = RpcServerRegisterIfEx(phone_v1_0_s_ifspec, NULL, NULL, 0, RPC_C_LISTEN_MAX_CALLS_DEFAULT, SecurityCallBack);  } 

NOTE
Some versions of MSDN and the Platform SDK incorrectly document the function signature to the security callback function as function(RPC_IF_ID *interface, void *context). It should be function(RPC_IF_HANDLE *interface, void *context).

You can also set a flag, RPC_IF_ALLOW_SECURE_ONLY, on the call to RpcServerRegisterIfEx and RpcServerRegisterIf2 to allow only secured connections. The flag limits connections to clients that use a security level higher than RPC_C_AUTHN_LEVEL_NONE. Clients that fail the RPC_IF_ALLOW_SECURE_ONLY test receive an RPC_S_ACCESS_DENIED error. This is an important optimization. If you do not set this flag but you allow only authenticated connections, the RPC runtime will still pass the client request to your application for processing, where it will be promptly denied access by your code. Setting this flag will force the RPC runtime to reject the request before your code has to deal with it. Also, for Windows NT 4 and Windows 2000, specifying this flag allows clients to use a NULL, or anonymous, session. On Windows XP, such clients are not allowed.

It is preferable to use RPC_IF_ALLOW_SECURE_ONLY flag for interface security rather than using a security descriptor in a call to RpcServerUseProtSeq for two reasons. First, security descriptors are used only when you use named pipes or local RPC as a transport. The security descriptor is ineffective if you use TCP/IP as a transport. Second, all endpoints are reachable on all interfaces, and that's the next topic.

Implications of Multiple RPC Servers in a Single Process

As you might be aware, RPC is network protocol agnostic. Any RPC server can be reached by any supported networking protocol. The side effect of this doesn't affect many people, but you should be aware of it.

If your RPC server resides in a process with other RPC servers for example, a single service hosting multiple RPC servers all applications listen on all selected protocols. For example, if three RPC servers exist in a single process RPC1 using named pipes and Local RPC (LRPC), RPC2 using sockets, and RPC3 using only LRPC all three servers will accept traffic from all three protocols (named pipes, LRPC, and sockets). Figure 16-4 outlines this.

figure 16-4 three rpc services listening on the sum of all requested network protocols.

Figure 16-4. Three RPC services listening on the sum of all requested network protocols.

If you thought you were safe listening on, say, LRPC only, you're incorrect because the other servers in the process are listening on named pipes or sockets, and therefore so is your RPC server application!

If you want to verify that the client request is made using a specific network protocol, you can use the RpcBindingToStringBinding function and then look for the protocol sequence by using RpcStringBindingParse. Here's a code sample to demonstrate the process in this case, the code will determine whether the context is using LRPC:

/* Phones.cpp */  BOOL IsLRPC(void *ctx) { BOOL fIsLRPC = FALSE; LPBYTE pBinding = NULL; if (RpcBindingToStringBinding(ctx, &pBinding) == RPC_S_OK) { LPBYTE pProtSeq = NULL; //We're interested only in the protocol sequence //so that we can use NULL for all other parameters. if (RpcStringBindingParse(pBinding, NULL, &pProtSeq, NULL, NULL, NULL) == RPC_S_OK) { printf("Using %s\n", pProtSeq); //Check that the client request //was made using LRPC. if (lstrcmpi((LPCTSTR)pProtSeq, "ncalrpc") == 0) fIsLRPC = TRUE; if (pProtSeq) RpcStringFree(&pProtSeq); } if (pBinding) RpcStringFree(&pBinding); } return flsLRPC; }  

Consider Adding an Annotation for Your Endpoint

Adding an annotation for your endpoint is not a security issue it's simply a good idea! When you create your RPC endpoint, call RpcEpRegister to define an annotation for the endpoint. This will make debugging easier because endpoint analysis tools, such as RPCDump.exe in the Windows 2000 Resource Kit, will show what the endpoint is used for. The following code shows how to do this:

RPC_BINDING_VECTOR *pBindings = NULL; if (RpcServerInqBindings(&pBindings) == RPC_S_OK) { if (RpcEpRegister(phone_v1_0_s_ifspec, pBindings, NULL,  "The Phone Application") == RPC_S_OK) { //Cool! Annotation added! } }

I added this recommendation simply because I've spent so much time trying to work out specific RPC endpoints, until finally the RPC guys told me about this function call.

Use Mainstream Protocols

Use the mainstream protocol sequences, such as ncacn_ip_tcp, ncacn_np, and ncalrpc. As the most popular protocol sequences, they receive the most rigorous testing by all application vendors.

NOTE
Sometimes your RPC client or server will fail and GetLastError or the function itself will return the error status code. If you're like me, you forget what the error codes mean, with the exception of Error 5 Access Denied! However, help is at hand. At the command prompt, you can enter net helpmsg nnnn, where nnnn is the error number in decimal, and the operating system will give you the textual version of the error.



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2001
Pages: 286

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net