Доработка учебного приложения

Для реализации учебного примера распределенного приложения в сети ФКН необходим учет политик безопасности, которые требуют использования сервисов аутентификации. В противном случае попытка соединения по протоколу TCP/IP (например, с указанием последовательности протоколов "ncacn_ip_tcp") потерпит неудачу с кодом исключения ERROR_ACCESS_DENIED (=5). В самом простом случае от каждой из сторон распределенного приложения требуется по одному дополнительному действию.

Клиент должен установить информацию аутентификации и авторизации для используемого дескриптора связывания:

status=RpcBindingSetAuthInfo(

     hello_IfHandle,   // дескриптор связывания

     NULL,             // имя принципала

     RPC_C_AUTHN_LEVEL_CONNECT, // уровень аутентификации

     RPC_C_AUTHN_WINNT,              // сервис аутентификации

     NULL,    // пользовательский credential

     0);       // сервис авторизации серверной стороны

printf("RpcBindingSetAuthInfo returned 0x%x\n", status);

if (status) exit(status);

В нашем примере hello_IfHandle – дескриптор связывания, построенный предыдущим вызовом RpcBindingFromStringBinding; выбран уровень аутентификации при инициализации соединения; в качестве сервера аутентификации будет задействован NT Security Service. Кроме этого, процесс аутентификации использует в качестве credential-информации текущий login, а авторизация сервера производиться не будет. Таким образом, если Microsoft (или наши админы) что-нибудь еще придумают, чтобы помешать нам соединиться, у нас здесь есть еще простор для творчества! J

Сервер перед вхождением в режим прослушивания должен зарегистрироваться как принципал, использующий аутентификацию:

status=RpcServerRegisterAuthInfo(

     NULL,             // имя принципала

     RPC_C_AUTHN_WINNT,     // сервис аутентификации

     NULL,NULL);

printf("RpcBindingSetAuthInfo returned 0x%x\n", status);

if (status) exit(status);

Здесь также простор для эксперимента. Можно даже реализовать на сервере функцию-генератор ключей шифрования, заменив ею стандартный метод системы безопасности (последние два параметра).

Ниже приводится набор сведений, необходимый для понимания предлагаемой модифицированной реализации учебного примера. Материал заимствован из "MS SDK Help Files", раздел "Microsoft RPC Programmer’s Guide and Reference".

Microsoft RPC Security

Microsoft RPC supports two different methods for adding security to your distributed application:

·        Use a security package that can be accessed using the RPC functions.

·        Use the security features built into Windows NT transport protocols.

The transport-level security method is not the preferred method. We recommend that you use RPC security because it works on all transports, across platforms, and provides a high levels of security (including Privacy).

While previous versions of Microsoft RPC depended on the security built into the named pipes transport, this version also implements the transport-independent security functions from DCE RPC, using the Windows NT Security Service as the default security provider. This higher-level security enables servers to filter client requests based on an authenticated identity associated with each request.

To use authenticated RPC, a client passes its user security information to the runtime library. This security information is called the client credentials. The client runtime library forwards the credentials to the server run-time library. The server runtime library then passes it to the relevant security provider for verification. (In this version of Microsoft RPC, the NT Security Service is the only supported security provider. Other security providers may be added in the future.) When a call is made, the security saver ensures the credentials are valid. If so, the server stub is called and the call proceeds. Otherwise, the client is denied access and the call fails.

Authenticated RPC involves a series of tasks performed by all servers every time a client tries to connect. The server must:

1.              Extract binding information about the client from the incoming call.

2.              Extract the authentication information from the binding handle and check the credentials with the NT Security Service.

3.              Compare the client's authentication information with the access control list (ACL) on the security server's database.

Writing a Secure Server

Depending on your security provider, invalid credentials may or may not be rejected. By default, the RPC runtime library will dispatch calls. It is your responsibility (ответственность) to protect yourself by either:

·        Determining exactly who the client is, or

·        Using impersonation (выдавать себя за другого).

Note  Disable the guest account on all computers where security is a significant factor.

If you need additional information about how to write a secure server, check with the manufacturer of your security provider.

Implementing Security for Clients

To set up a binding handle for authenticated RPC, a client application calls RpcBindingSetAuthInfo. Without this call, all remote procedure calls on the binding handle will be unauthenticated. The chosen (избранный) level of security and authentication applies only to that binding handle. Context handles derived (наследуемые) from the binding handle will use the same security information, but subsequent (последующие) modifications to the binding handle will not be reflected in the context handles. The security and authentication level stays in effect (остается в силе) until another level of security is chosen, or until the process terminates. Most applications will not require a change in the security level.

The levels of security and authentication available for authenticated RPC are shown in the following table:

Authentication-Level Constant     Description

RPC_C_AUTHN_LEVEL_DEFAULT     Uses the default authentication level for the specified authentication service.

RPC_C_AUTHN_LEVEL_NONE     Performs no authentication.

RPC_C_AUTHN_LEVEL_CONNECT     Authenticates only when the client establishes a relationship with the server.

RPC_C_AUTHN_LEVEL_CALL     Authenticates only at the beginning of each remote procedure call when the server receives the request. Does not apply to remote procedure calls made using the connection-based protocol sequences (those that start with the prefix "ncacn"). If the protocol sequence in a binding handle is a connection-based protocol sequence and you specify this level, this routine instead uses the RPC_C_AUTHN_LEVEL_PKT constant.

RPC_C_AUTHN_LEVEL_PKT     Authenticates that all data received is from the expected client.

RPC_C_AUTHN_LEVEL_PKT_INTEGRITY     Authenticates and verifies that none of the data transferred between client and server has been modified.

RPC_C_AUTHN_LEVEL_PKT_PRIVACY     Authenticates all previous levels and encrypts the argument value of each remote procedure call.

Note  For Windows 95 platforms, RPC_C_AUTHN_LEVEL_CALL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, and RPC_C_AUTHN_LEVEL_PKT_PRIVACY are only supported for a Windows 95 client communicating with a Windows NT server. These levels are never supported for a Windows 95 client communicating with a Windows 95 server.

The level of security required depends entirely (целиком зависит) on the application. When considering security levels, remember that the higher the protection level, the greater the overhead (накладные расходы) required to create and maintain the levels. Additionally, there are performance tradeoffs (компромисс) to consider. The checksum computation and encryption required by the RPC runtime library can make data protection a time-consuming operation. The more often credentials are checked, the more time it will take to get on with the business of the application. When selecting a security level, choose the elements needed by the application, mixing and matching only as required.

Providing Client Credentials to the Server

Servers use the client's binding information to enforce security. Clients always pass a binding handle as the first parameter of a remote procedure call; however, servers cannot use the handle unless it's declared as the first parameter to remote procedures in either the IDL file or in the server's ACF. You can choose (предпочесть) to list the binding handle in the IDL file, but this forces all clients to declare and manipulate the binding handle rather than using automatic or implicit (неявный) binding if they choose. Another method is to leave the binding handles out (не включать) of the IDL file and to place the explicit_handle attribute into the server's ACF. In this way, the client can use whatever type of binding is best suited to the application (какой угодно тип связывания, наиболее подходящий для приложения), while the server uses the binding handle as though it were declared explicitly (явно).

The processs of extracting the client credentials from the binding handle is as follows:

·        RPC clients call RpcBindingSetAuthInfo and include their authentication information as part of the binding information passed to the server.

·        Usually, the server calls RpcImpersonateClient in order to behave as though it were the client. If the binding handle is not authenticated, the call fails with RPC_S_NO_CONTEXT_AVAILABLE. To obtain the client's user name, call GetUserName while impersonating.

·        The server will normally create objects with ACLs by using the Windows NT call CreatePrivateObjectSecurity. After this is accomplished, later security checks become automatic.

Security Packages

The RPC run-time API set includes several functions that let your application manage security.

A security package such as the Kerberos Authentication Protocol can be supplied as a DLL that interoperates with the Microsoft RPC run-time libraries.

RpcBindingSetAuthInfo

The RpcBindingSetAuthInfo function sets authentication and authorization information into a binding handle.

#include <rpc.h>

RPC_STATUS RPC_ENTRY

RpcBindingSetAuthInfo(

     RPC_BINDING_HANDLE  Binding,

     unsigned char *  ServerPrincName,

     unsigned long  AuthnLevel,

     unsigned long  AuthnSvc,

     RPC_AUTH_IDENTITY_HANDLE  AuthIdentity,

     unsigned long  AuthzSvc);

Parameters

Binding

Specifies the server binding handle into which authentication and authorization information is set.

ServerPrincName

Points to the expected principal name of the server referenced by the binding handle specified in the Binding argument. The content of the name and its syntax are defined by the authentication service in use.

AuthnLevel

Specifies the level of authentication to be performed on remote procedure calls made using the Binding binding handle. For a list of RPC-supported authentication levels, see the RPC data types and structures reference entry for authentication-level constants.

AuthnSvc

Specifies the authentication service to use. For a list of RPC-supported authentication services, see the RPC data types and structures reference entry for authentication-service constants.

Specify RPC_C_AUTHN_NONE to turn off authentication for remote procedure calls made using the Binding binding handle.

If RPC_C_AUTHN_DEFAULT is specified, the RPC run-time library uses the RPC_C_AUTHN_WINNT authentication service for remote procedure calls made using the Binding binding handle.

AuthIdentity

Specifies a handle for the data structure that contains the client's authentication and authorization credentials appropriate for the selected authentication and authorization service.

When using the RPC_C_AUTHN_WINNT authentication service AuthIdentity should be a pointer to a SEC_WINNT_AUTH_IDENTITY structure (defined in rpcdce.h).

Specify a null value to use the security login context for the current address space.

AuthzSvc

Specifies the authorization service implemented by the server for the interface of interest. The validity and trustworthiness of authorization data, like any application data, depends on the authentication service and authentication level selected. This parameter is ignored when using the RPC_C_AUTHN_WINNT authentication service.

For a list of constants for the AuthzSvc argument, see the RPC data types and structures reference entry for authorization-service constants.

Remarks

A client application calls the RpcBindingSetAuthInfo routine to set up a server binding handle for making authenticated remote procedure calls.

Unless a client calls RpcBindingSetAuthInfo, all remote procedure calls on the Binding binding handle are unauthenticated. A client is not required to call this routine.

Note  As long as the binding handle exists, RPC maintains a pointer to AuthIdentity. Be sure it is not on the stack and is not freed until the binding handle is freed. If the binding handle is copied, or if a context handle is created from the binding handle, then the AuthIdentity pointer will also be copied.

Return Values

Value     Meaning

RPC_S_OK     Success

RPC_S_INVALID_BINDING     Invalid binding handle

RPC_S_WRONG_KIND_OF_BINDING     Wrong kind of binding for operation

RPC_S_UNKNOWN_AUTHN_SERVICE     Unknown authentication service

See Also

RpcBindingInqAuthInfo, RpcServerRegisterAuthInfo

Authentication-Service Constants

The AuthnSvc argument represents the authentication service supplied to the RpcBindingInqAuthInfo and RpcBindingSetAuthInfo run-time functions.

The following constants represent valid values for the AuthnSvc argument:

Constant     Value     Service

RPC_C_AUTHN_DCE_PRIVATE     1     DCE private key authentication

RPC_C_AUTHN_DCE_PUBLIC 2     DCE public key authentication  (reserved for future use)

RPC_C_AUTHN_DEC_PUBLIC 4     DEC public key authentication  (reserved for future use)

RPC_C_AUTHN_DEFAULT     0xffffffff     Default authentication service

RPC_C_AUTHN_NONE     0     No authentication

RPC_C_AUTHN_WINNT     10     NT LM SSP (NT Security Service)

Specify RPC_C_AUTHN_NONE to turn off authentication for remote procedure calls made using the binding handle.

When you specify RPC_C_AUTHN_DEFAULT, the RPC run-time library uses the RPC_C_AUTHN_DCE_PRIVATE authentication service for remote procedure calls made using the binding handle.

Authorization-Service Constants

The AuthzSvc argument represents the authorization service supplied to the RpcBindingInqAuthInfo and RpcBindingSetAuthInfo run-time functions.

The following constants represent valid values for the AuthzSvc argument:

Constant     Value     Service

RPC_C_AUTHZ_NONE     0     Server performs no authorization.

RPC_C_AUTHZ_NAME     1     Server performs authorization based on the client's principal name.

RPC_C_AUTHZ_DCE     2     Server performs authorization checking using the client's DCE privilege attribute certificate (PAC) information, which is sent to the server with each remote procedure call made using the binding handle. Generally, access is checked against DCE access control lists (ACLs).

SEC_WINNT_AUTH_IDENTITY

For Windows 3.x and MS-DOS:

typedef struct _SEC_WINNT_AUTH_IDENTITY {

char     __RPC_FAR     *User;

char     __RPC_FAR *Domain;

char     __RPC_FAR     *Password;

} SEC_WINNT_AUTH_IDENTITY;

For Windows NT:

typedef struct _SEC_WINNT_AUTH_IDENTITY {

unsigned short     __RPC_FAR     *User;

unsigned long     __Userlength;

unsigned short     __RPC_FAR     *Domain;

unsigned long     Domian Length;

unsigned short     __RPC_FAR     *Password;

unsigned long     Password length;

} SEC_WINNT_AUTH_IDENTITY,

 *PSEL_WINNT_AUTH_IDENTITY;

User

String containing the user name.

Domain

String containing the domain name or the workgroup name.

Password

String containing the user's password in the domain or workgroup.

Remarks

The SEC_WINNT_AUTH_IDENTITY structure allows you to pass a particular user name and password to the runtime library for the purpose of authentication.

For Windows 3.x and MS-DOS, the strings are ANSI; for Windows NT, the strings are Unicode. For Windows NT, the values for Userlength; DomainLength, and Password Length are the length of the corresponding string without the terminating 0X0000.

RpcServerRegisterAuthInfo

The RpcServerRegisterAuthInfo function registers authentication information with the RPC run-time library.

#include <rpc.h>

RPC_STATUS RPC_ENTRY

RpcServerRegisterAuthInfo(

     unsigned char *  ServerPrincName,

     unsigned long  AuthnSvc,

     RPC_AUTH_KEY_RETRIEVAL_FN  GetKeyFn,

     void *  Arg);

This function is supported by both the 32-bit Windows NT and Windows 95 platforms.

Parameters

ServerPrincName

Points to the principal name to use for the server when authenticating remote procedure calls using the service specified by the AuthnSvc argument. The content of the name and its syntax are defined by the authentication service in use.

AuthnSvc

Specifies an authentication service to use when the server receives a request for a remote procedure call.

GetKeyFn

Specifies the address of a server-application-provided routine that returns encryption keys.

Specify a NULL argument value to use the default method of encryption-key acquisition. In this case, the authentication service specifies the default behavior. Set this parameter to NULL when using the RPC_C_AUTHN_WINNT authentication service.

Authentication service      GetKeyFn      Arg      Run-time behavior

RPC_C_AUTHN_DCE_PRIVATE     NULL     Non-null Uses default method of encryption-key acquisition from specified key table; specified argument is passed to default acquisition function

RPC_C_AUTHN_DCE_PRIVATE     Non-null NULL     Uses specified encryption-key acquisition function to obtain keys from default key table

RPC_C_AUTHN_DCE_PRIVATE     Non-null Non-null Uses specified encryption-key acquisition function to obtain keys from specified key table; specified argument is passed to acquisition function

RPC_C_AUTHN_DEC_PUBLIC     Ignored     Ignored     Reserved for future use

RPC_C_AUTHN_WINNT     Ignored     Ignored  Does not support

The following C-language definition for RPC_AUTH_KEY_RETRIEVAL_FN illustrates the prototype for RpcServerRegisterAuthInfo:

typedef void (* RPC_AUTH_KEY_RETRIEVAL_FN)(

    void * arg, /* in */

    unsigned char * ServerPrincName, /* in */

    unsigned int key_ver, /* in */

    void * * key, /* out */

    unsigned int * status /* out */);

The RPC run-time library passes the ServerPrincName argument value from RpcServerRegisterAuthInfo as the ServerPrincName argument value to the GetKeyFn acquisition function.

The RPC run-time library automatically provides a value for the key version (KeyVer) argument. For a KeyVer argument value of zero, the acquisition function must return the most recent key available.

The retrieval function returns the authentication key in the Key argument.

If the acquisition function called from RpcServerRegisterAuthInfo returns a status other than RPC_S_OK, RpcServerRegisterAuthInfo fails and returns an error code to the server application.

If the acquisition function called by the RPC run-time library while authenticating a client's remote procedure call request returns a status other than RPC_S_OK, the request fails and the RPC run-time library returns an error code to the client application.

Arg

Points to an argument to pass to the GetKeyFn routine, if specified.

Remarks

A server application calls the RpcServerRegisterAuthInfo routine to register an authentication service to use for authenticating remote procedure calls. A server calls this routine once for each authentication service and/or principal name the server wants to register.

The authentication service specified by a client application (using RpcBindingSetAuthInfo or RpcServerRegisterAuthInfo) must be one of the authentication services specified by the server application. Otherwise, the client's remote procedure call fails and an RPC_S_UNKNOWN_AUTHN_SERVICE status code is returned.

Return Values

Value     Meaning

RPC_S_OK     Success

RPC_S_UNKNOWN_AUTHN_SERVICE     Unknown authentication service