Для реализации
учебного примера распределенного приложения в сети ФКН необходим учет политик
безопасности, которые требуют использования сервисов аутентификации. В
противном случае попытка соединения по протоколу 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 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.
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.
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.
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.
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.
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
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.
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).
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.
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