Red de conocimiento informático - Conocimiento informático - ¿Cómo accede el cliente al servicio COM del servidor win2003 como usuario remoto a través de la programación Delphi?

¿Cómo accede el cliente al servicio COM del servidor win2003 como usuario remoto a través de la programación Delphi?

Este artículo explicará cómo activar el objeto com como usuario en la estación de trabajo remota y acceder a la interfaz como este usuario.

1. La activación remota del objeto com predeterminado de Delphi

La activación remota del objeto com en Delphi generalmente se implementa a través de TdispatchConnection y sus subclases. En el código real, a menudo se usan TDCOMConnection o TsocketConnectoion. el componente TDCOMConnection finalmente llama a CoCreateInstanceEx para crear el objeto com. CoCreateInstanceEx (const clsid: TCLSID; unkOuter: IUnknown; dwClsCtx: Longint; ServerInfo: PCoServerInfo; dwCount: Longint; rgmqResults: PMultiQIArray): HResult.

TDCOMConnection pasa un valor nulo para pAuthInfo en el parámetro pCoServerInfo cuando llama a CoCreateInstanceEx, por lo que TdcomConnection utiliza el token de usuario del inicio de sesión de la computadora local al crear el objeto Com. Si el usuario Auser que inició sesión en la computadora A usa la clase TDCOMConnection para conectarse al objeto com en la computadora remota B, la computadora B usará el nombre de usuario/contraseña de Auser para establecer una sesión de inicio de sesión en la computadora B y finalmente creará un objeto com. Sin embargo, un usuario local en una estación de trabajo de Windows solo puede iniciar sesión localmente y no puede iniciar sesión en otras computadoras. Por lo tanto, Auser en la computadora A no puede establecer una sesión de inicio de sesión en la estación de trabajo B. Por supuesto, no puede crear un objeto com en este momento. , la estación de trabajo remota B intentará establecer una sesión con la cuenta de Invitado y activar el objeto com usando esta cuenta. En este caso, si la cuenta de Invitado en la estación de trabajo B no está habilitada o el Invitado no tiene permiso para activar el objeto com, verá el vertiginoso mensaje "Acceso denegado". Al ver esto, es posible que tenga una idea del método de configuración de dcom más "popular" en Internet. Ese método consiste en permitir que todos accedan, activar el objeto COM y establecer el "Nivel de autenticación predeterminado" en Ninguno. Este método puede hacer que su aplicación de comunicación esté "disponible", pero "cualquiera" puede acceder a ella. Y con esta configuración no podrá aprovechar las funciones de control de acceso de seguridad basadas en roles de com.

2. Cómo activar sin cuenta INVITADO

La pregunta real debería ser: cómo activar el objeto de comunicación remota con el usuario en la estación de trabajo remota. Resolver este problema es realmente muy simple: siempre que especifique el nombre de usuario y la contraseña en la estación de trabajo remota al llamar a CoCreateInstanceEx, siempre que la computadora remota autentique el nombre de usuario/contraseña y se le otorgue al usuario el permiso para "activar de forma remota " el objeto com. La estación de trabajo remota activará el objeto com como ese usuario.

Eche un vistazo al código:

var

mts: IMTSXjpimsDB;

ov: Variant

i: integer

cai: _CoAuthInfo;

cid: _CoAuthIdentity;

csi: COSERVERINFO

mqi: MULTI_QI

iid_unk; : TGUID;

idsp: IDispatch;

wUser, wDomain, wPsw: WideString

comenzar

wUser:=eduser.text; ; // Nombre de usuario

wDomain:=edSvr.Text; //Nombre de la computadora remota

wPsw:=edPsw.Text;//Contraseña

cid.user := pUnshort(@wUser[1]);

cid.UserLength:=length(wUser);

cid.Domain:=pUnshort(@wDomain[1]);

cid.DomainLength:=longitud(wDomain);

cid.contraseña:=pUnshort(@wPsw[1]);

cid.PasswordLength:=longitud (wPsw );

cid.Flags:=2;

//La estructura _CoAuthIdentity completada arriba

cai.dwAuthnSvc:=10; Servicio de autenticación

cai.dwAuthzSvc:=0;

cai.pwszServerPrincName:=wDomain;

cai.dwAuthnLevel:=0;

cai.dwImpersonationLevel:=3; //Debe configurarse en suplantación

cai.pAuthIdentityData:=@cid;

cai.dwCapabilities:=$0800;

// Por encima de la estructura fill_CoAuthInfo

FillChar(csi, sizeof(csi), 0

csi.dwReserved1:=0

csi.pwszName: =pwidechar(wdomain);

csi.pAuthInfo:=@cai;

//Lo anterior completa la estructura COSERVERINFO

iid_unk:=IUnknown;

mqi.IID:=@iid_unk; mqi.Itf:=nil; mqi.hr:=0;

Screen.Cursor:=crHourGlass; olecheck(CoCreateInstanceEx(CLASS_MTSXjpimsDB, nil, CLSCTX_REMOTE_SERVER, @ csi, 1, @mqi));

Excepto por la llamada real a CoCreateInstanceEx al final de este código, el código anterior trata sobre la configuración de parámetros.

Consulte msdn para conocer el significado de estos parámetros. Además del nombre de usuario, el nombre de host y la contraseña, solo hay una parte importante que explicar: cai.dwImpersonationLevel debe configurarse para permitir la suplantación (el valor es 3), de lo contrario. la computadora remota no podrá presionar la sesión de red de sugerencia de usuario/contraseña proporcionada.

3. ¿Puedo activar como usuario remoto sin modificar el código existente?

Por supuesto, amplié la clase TDcomConnection, le agregué un nombre de usuario y una contraseña y modifiqué su método DoConnect predeterminado para que complete los parámetros con el nombre de usuario y la contraseña especificados al llamar a CoCreateInstanceEx.

El código es el siguiente:

unidad SecDComConnection

interfaz

usa

windows, SysUtils, Classes, ActiveX, DB, DBClient; , MConnect, comobj, Midas;

tipo

{typedef struct _SEC_WINNT_AUTH_IDENTITY

usuario __RPC_FAR* corto sin firmar;

longitud de usuario largo sin firmar;

Dominio __RPC_FAR* corto sin firmar;

Longitud de dominio larga sin firmar

Contraseña __RPC_FAR* corta sin firmar

Longitud de contraseña larga sin firmar

p>

Banderas largas sin firmar;

SEC_WINNT_AUTH_IDENTITY, *PSEC_WINNT_AUTH_IDENTITY;

}

{typedef struct _COAUTHIDENTITY

USHORT * Usuario ;

p>

ULONG Longitud de usuario

USHORT * Dominio

ULONG Longitud de dominio

USHORT * Contraseña; p> ULONG Longitud de contraseña;

Banderas ULONG;

COAUTHIDENTITY;}

{#define RPC_C_AUTHN_NONE 0

#define RPC_C_AUTHN_DCE_PRIVATE 1

#definir RPC_C_AUTHN_DCE_PUBLIC 2

#definir RPC_C_AUTHN_DEC_PUBLIC 4

#definir RPC_C_AUTHN_GSS_NEGOTIATE 9

#definir RPC_C_AUTHN_WINNT 10

# definir RPC_C_AUTHN_GSS_SCHANNEL 14

#definir RPC_C_AUTHN_GSS_KERBEROS 16

#definir RPC_C_AUTHN_MSN 17

#definir RPC_C_AUTHN_DPA 18

#definir RPC_C_AUTHN_MQ 100

# define RPC_C_AUTHN_DEFAULT 0xFFFFFFFFL

}

{#define RPC_C_AUTHZ_NONE 0

#define RPC_C_AUTHZ_NAM

E 1

#define RPC_C_AUTHZ_DCE 2

#define RPC_C_AUTHZ_DEFAULT 0xFFFFFFFF }

{

#define RPC_C_AUTHN_LEVEL_DEFAULT 0

#define rpc_c_authn_level_none 1

#define rpc_c_authn_level_connect 2

#define rpc_c_authn_level_call 3

#define rpc_c_c_authn_level_pkt 4

_Level_pk t_integrity 5

#define RPC_C_AUTHN_LEVEL_PKT_PRIVACY 6 }

{SEC_WINNT_AUTH_IDENTITY_UNICODE=2 }

pUnShort=^Word;

pCoAuthIdentity=^_CoAuthIdentity;

_CoAuthIdentity=record

usuario: pUnShort;

Longitud de usuario: ULONG

Dominio: pUnShort; Ulong ;

contraseña: pUnShort;

Longitud de contraseña:

Banderas:

fin; > _CoAuthInfo=record

dwAuthnSvc: DWORD;

dwAuthzSvc: DWORD;

pwszServerPrincName: WideString

dwAuthnLevel: Dword; p >

dwImpersonationLevel: dword;

pAuthIdentityData: pCoAuthIdentity;

dwCapabilities: DWORD

fin; ( TDCOMConnection)

privado

FCai: _CoAuthInfo

FCid: _CoAuthIdentity

FSvInfo: COSERVERINFO

FUser: WideString;

FPassWord: WideString;

procedimiento SetPassword(const Valor: WideString

procedimiento SetUser(contras);

t Valor: cadena ancha);

procedimiento SetSvInfo(valor const: COSERVERINFO);

procedimiento protegido

anulación de DoConnect;

público;

propiedad SvInfo: COSERVERINFO lee FSvInfo escribe SetSvInfo;

constructor Create(AOwner: TComponent);

procedimiento MySetBlanket(itf: IUnknown; const vCai: _CoAuthInfo);

función GetServer: IAppServer; anular;

propiedad publicada

Usuario: cadena ancha lee FUser escribe SetUser;

Contraseña de propiedad :wideString lee FPassword escribe SetPassword;

final;

procedimiento Registrar

implementación

constructor TSecDCOMConnection.Create(AOwner: TComponent) ;

comenzar

heredado Crear(AOwner);

FillMemory(@Fcai, sizeof(Fcai),

FillMemory); (@FCid, sizeof(FCid), 0);

FillMemory(@FSvInfo, sizeof(FSvInfo),

con FCai comienza

; dwAuthnSvc:=10; //RPC_C_AUTHN_WINNT

dwAuthzSvc:=0; //RPC_C_AUTHZ_NONE

dwAuthnLevel:=0; //RPC_C_AUTHN_LEVEL_DEFAULT

dwImpersonationLevel:=3;

pAuthIdentityData:=@fcid;

dwCapabilities:=$0800

fin

fin; procedimiento TSecDCOMConnection.DoConnect;

var

tmpCmpName: cadena ancha

IID_IUnknown: TGUID

iiu: IDispatch

Mqi: MULTI_QI;

qr: HRESULT;

comenzar

si (ObjectBroker) lt; comenzar

n

repetir

si ComputerName = '' entonces

ComputerName := ObjectBroker.GetComputerForGUID(GetServerCLSID

intentar

SetAppServer(CreateRemoteComObject(ComputerName, GetServerCLSID) como IDispatch);

ObjectBroker.SetConnectStatus(ComputerName, True);

excepto

ObjectBroker.SetConnectStatus (NombreEquipo, Falso);

NombreEquipo := '';

fin

hasta que esté conectado

fin

;

else if (ComputerName lt; gt; '') entonces

comenzar

con fcid comenzar

usuario:=pUnshort(@fuser[1 ]);

UserLength:=length(fuser);

tmpCmpName:=ComputerName;

Domain:=pUnshort(@tmpCmpName[1]);

Longitud del dominio: =longitud(TmpCmpName);

Contraseña: =pUnShort(@FPassword[1]);

Longitud de la contraseña: =longitud(FPassword);

p>

Banderas:=2; //Unicode

fin;

FSvInfo.pwszName:=pwidechar(tmpCmpName);

FSvinfo.pAuthInfo: =@Fcai;

IID_IUnknown:=IUnknown;

mqi.IID:=@IID_IUnknown; mqi.Itf:=nil.hr:=0;

olecheck(CoCreateInstanceEx(GetServerCLSID, nil, CLSCTX_REMOTE_SERVER, @FSvinfo, 1, @mqi));

olecheck(mqi.hr

MySetBlanket(); mqi.Itf, Fcai);

qr:=mqi.Itf.QueryInterface(idispatch, iiu);

olecheck(qr);

anket(IUnknown(iiu), FCai);

SetAppServer(iiu);

end

else

heredó DoConnect;

p>

fin;

función TSecDComConnection.GetServer: IAppServer;

var

QIResult: HResult;

begin

p>

Conectado := Verdadero

QIResult := IDispatch(AppServer).QueryInterface(IAppServer, Resultado

if QIResult lt; gt; S_OK entonces

comenzar

Resultado:= TDispatchAppServer.Create(IAppServerDisp(IDispatch(AppServer))); > MySetBlanket(IUnknown(Resultado) , FCai);

fin;

procedimiento TSecDCOMConnection.MySetBlanket(itf: IUnknown;

const vCai: _CoAuthInfo); /p>

comience

con vCai do

CoSetProxyBlanket(Itf, dwAuthnSvc, dwAuthzSvc, pwidechar(pAuthIdentityData^.Domain),

dwAuthnLevel, dwImpersonationLevel , pAuthIdentityData, dwCapabilities);

end;

procedimiento TSecDCOMConnection.SetPassword(valor constante: cadena ancha);

comenzar

FPassword: = Valor;

fin;

procedimiento TSecDCOMConnection.SetSvInfo(valor constante: COSERVERINFO);

comenzar

FSvInfo:= Valor;

fin;

procedimiento TSecDCOMConnection.SetUser(valor const: cadena ancha);

comienzo

FUser:= Valor;

fin;

procedimiento Registrar;

comenzar

RegisterComponents('DataSnap', [TSecDComConnection]);

fin ;

fin.

Hay algunos comentarios de estilo C en el código porque Delphi no predefine estas variables y estructuras de datos para nosotros.

¿Cómo utilizarlo? Instale este componente en el IDE, colóquelo en el módulo de datos remoto de su código existente y configure el control del conjunto de datos original que apunta a TDOCMConnection en este nuevo control TSecDCOMConnection. Luego puede configurar las opciones de seguridad más estrictas en la computadora remota. Pero recuerde establecer los permisos adecuados para los usuarios que desea utilizar: otorgar permisos de activación remota, otorgar permisos de acceso remoto.

4. El tema del acceso aún no se ha discutido. La primera activación y el acceso no son lo mismo. Un usuario puede tener derechos de activación pero no derechos de acceso, o puede tener derechos de acceso pero no derechos de activación. Como se mencionó anteriormente, CoCreateInstacnceEx puede activar el objeto con otra identidad y obtener una referencia local del puntero IunKnown. Si utiliza directamente este puntero para obtener la interfaz IappServer y llama al método, es probable que vuelva a ver el mensaje "Acceso denegado". Esta es una referencia local del puntero IUnKnown que existe en el proceso del cliente. Antes de realizar configuraciones especiales, este puntero hereda el token local del proceso del cliente. Es decir, cuando este puntero se usa para obtener la interfaz IappServer remota. se utilizará el cliente. Se llama a QueryInterface con el token de inicio de sesión actual de la máquina. Durante el proceso de llamada, la computadora remota tendrá el nombre de usuario y la contraseña almacenados en caché en este token para la verificación de inicio de sesión nuevamente. esta vez, y luego la computadora remota intenta iniciar sesión con la cuenta INVITADO nuevamente y obtiene la interfaz de Objeto com. Si el permiso de acceso INVITADO no se abre en este momento, el acceso del cliente falla y Windows devuelve un mensaje de "Acceso denegado". . Entonces, ¿cómo podemos hacer que la llamada QueryInterface también use la identidad del usuario remoto? Esto requiere llamar a CoSetProxyBlanket para forzar que la referencia de la interfaz local use el token del usuario remoto. En el código anterior, envuelvo la API con MySetBlanket para que se llame a QueryInterface con la identidad del usuario en el momento de la activación. Luego llame a MySetBlanket nuevamente en la interfaz IappServer obtenida para asegurarse de que la identidad del usuario remoto también se use al usar esta interfaz.

MySetBlanket(mqi.Itf, Fcai);

qr:=mqi.Itf.QueryInterface(idispatch, iiu);

olecheck(qr); /p>

MySetBlanket(IUnknown(iiu), FCai);

Para garantizar que el TclientDataSet que hace referencia directa al DataProvider también pueda funcionar de acuerdo con los requisitos anteriores, el método GetServer está sobrecargado en el formato extendido. TSecDCOMControl de conexión. De esta manera, TSecDCOMConnection puede reemplazar completamente a TDCOMConnection para implementar una programación conveniente de aplicaciones COM.

Debido a la prisa, muchos términos no se explicaron al escribir este artículo, por lo que puede haber algunos que no sean fáciles de entender. Sin embargo, para brindar a los fanáticos de Delphi un método simple para lograrlo. Acceso COM seguro, sigo publicando este artículo, principalmente para que los amigos que lo necesiten puedan copiar el código directamente y usarlo en sus propias aplicaciones. Después de usar TSecDCOMConnection, el objeto com del lado del servidor puede forzar la activación de las comprobaciones de acceso y activar las comprobaciones de acceso a nivel de componente. Cuando la verificación de acceso está activada, el nombre de usuario en el servidor al que se le permite acceder al objeto com se debe agregar a la función para un acceso correcto.

(El código anterior se depuró en delphi7/winXP sp2. Para sistemas operativos windows98 y windows nt4.0 e inferiores, debido a que CoCreateInstanceEx no puede generar directamente el contexto de seguridad del objeto com, el código no está disponible)