Access COM + applications as a user on a remote computer

Source: Internet
Author: User

Delphi programmers develop COM + applications very quickly, mainly because it encapsulates the windows of COM +
S underlying functions, developers can avoid complicated COM + underlying technical details through simple class inheritance, so that developers can
Focus on the functions of the application. Delphi adopts many trade-offs when encapsulating COM + applications, and retains Universality
At the same time, it also avoids some underlying features of COM +, which are difficult to implement but not widely used. Among these avoidance features
Security is a concern of Delphi com developers. Since Delphi 5, many people have faced such problems.
Question: COM applications are developed and run normally on the local machine, but once distributed for remote access, no
Method is running normally. I felt dizzy when I saw the "Access Denied" error message for a while. In fact, seriously pursue
The reason is that you do not know much about Windows security technology. Over the years, I have never found W
Indows Security comparison system documents and books until the Chinese version of Keith brownr <Windows security programming>
. Based on this book, I have tried some of the following experiments and learned why I always make up for the reasons for rejection. Lower
The above discussion is just a few small and difficult methods I have summarized when solving the security access problem of my existing code. Suggestion
If you want to learn about Windows security, go to <Windows security programming> and you will find that Windows security is not
Mysterious. This article describes how to activate COM + objects as a user on a remote workstation and use this user identity.
Access interface. 1. Delphi remote activation of COM + objects by default

In delph, Remote COM + object activation is generally implemented through tdispatchconnection and its subclass. In actual code
Tdcomconnection or tsocketconnectoion are used. The tdcomconnection component finally calls Co
Createinstanceex creates a COM + object. Cocreateinstanceex (const CLSID: tclsid; unkouter:
Iunknown; dwclsctx: longint; serverinfo: pcoserverinfo; dwcount: longint; rgmqres
Ults: pmultiqiarray): hresult. Tdcomconnection is pcose when cocreateinstanceex is called
Pauthinfo in the rverinfo parameter passes a null value, so what tdcomconnection uses when creating a COM object is
The User Token Of the attacker on the local computer. If the Login User auser on computer A uses the tdcomconnection class for connection
For the COM + object on the remote computer B, computer B uses the user name/password of auser to establish a logon session on computer B.
And finally create the COM + object. However, local users on a Windows Workstation can only log on locally, but cannot log on to other
Therefore, auser on computer A cannot establish a logon session on workstation B. Of course, C cannot be created.
Om + object. In this case, remote workstation B tries to use the Guest account to establish a session and use this account to activate the COM + object. In this
If the Guest account on workstation B is not enabled or the Guest account is not authorized to activate the COM + object, you will see
The dizzy prompt "Access Denied ". You are not using the most "popular" DCOM configuration method on the Internet.
I realized it. That method allows everyone to access, activate COM objects, and set "Default Authentication level"
. This method can enable your com application to "use", but it can be accessed by "anyone. And this
You will not be able to use the role-based security access control function of COM +. 2. How can I activate this function without using a guest account?
The problem is: how to use a user on a remote workstation to activate a Remote COM object. Actually, it is very difficult to solve this problem.
Simple: you only need to specify the user name and password on the remote workstation when you call cocreateinstanceex.
The account name/password is verified by a remote computer and the user is granted the permission to "remotely activate" COM + object.
The Remote Workstation uses this user identity to activate the COM + object. Take a look at the Code:


MTS: imtsxjpimsdb;

Ov: variant;

I: integer;

Cai: _ coauthinfo;

CID: _ coauthidentity;

CSI: coserverinfo;

Mqi: multi_qi;

Iid_unk: tguid;

IDSP: idispatch;

Wuser, wdomain, wpsw: widestring;


Wuser: = eduser. Text; // User Name

Wdomain: = edsvr. Text; // remote computer name

Wpsw: = edpsw. Text; // Password

CID. User: = punshort (@ wuser [1]);

CID. userlength: = length (wuser );

CID. Domain: = punshort (@ wdomain [1]);

CID. domainlength: = length (wdomain );

CID. Password: = punshort (@ wpsw [1]);

CID. passwordlength: = length (wpsw );

CID. Flags: = 2;

// The above filled _ coauthidentity Structure

Cai. dwauthnsvc: = 10; // Default Authentication Service of winnt

Cai. dwauthzsvc: = 0;

Cai. pwszserverprincname: = wdomain;

Cai. dwauthnlevel: = 0;

Cai. dwimpersonationlevel: = 3; // The value must be set to analog.

Cai. pauthidentitydata: [email protected];

Cai. dwcapabilities: = $0800; // The above filled _ coauthinfo Structure

Fillchar (CSI, sizeof (CSI), 0 );

CSI. dwreserved1: = 0;

CSI. pwszname: = pwidechar (wdomain );

CSI. pauthinfo: [email protected];

// Fill in the coserverinfo structure above

Iid_unk: = iunknown;

Mqi. IID: [email protected] _ unk; mqi. ITF: = nil; mqi. HR: = 0;

Screen. cursor: = crhourglass;
Olecheck (cocreateinstanceex (class_mtsxjpimsdb, nil, clsctx_remote_server, @ CSI, 1, @ mqi ));
In this Code, except for actually calling cocreateinstanceex, the previous Code sets parameters. For the meanings of these parameters, refer to msdn, except the user name and host name.
Except the password, there is only one important part to note: Cai. dwimpersonationlevel must be set to allow simulation (Value
3). Otherwise, the remote computer will not be able to recommend network sessions based on the provided user/password.

3. Can I activate it as a remote user without modifying the existing code? Of course. I have extended the tdcomconnection class and added the username and
Password, and modify its default doconnect method so that it uses the specified user name and
Password filling parameters. The Code is as follows:

Unit secdcomconnection;





Windows, sysutils, classes, ActiveX, DB, dbclient, mconnect, comobj, and Midas;




{Typedef struct _ sec_winnt_auth_identity

Unsigned short _ rpc_far * user;

Unsigned long userlength;

Unsigned short _ rpc_far * domain;

Unsigned long domainlength;

Unsigned short _ rpc_far * password;

Unsigned long passwordlength;

Unsigned long flags;

Sec_winnt_auth_identity, * psec_winnt_auth_identity;


{Typedef struct _ coauthidentity

Ushort * user;

Ulong userlength;

Ushort * domain;

Ulong domainlength;

Ushort * password;

Ulong passwordlength;

Ulong flags;

Coauthidentity ;}


{# Define rpc_c_authn_none 0

# Define rpc_c_authn_dce_private 1

# Define rpc_c_authn_dce_public 2

# Define rpc_c_authn_dec_public 4

# Define rpc_c_authn_gss_negotiate 9

# Define rpc_c_authn_winnt 10

# Define rpc_c_authn_gss_schannel 14

# Define rpc_c_authn_gss_kerberos 16

# Define rpc_c_authn_msn 17

# Define rpc_c_authn_dpa 18

# Define rpc_c_authn_mq 100

# Define rpc_c_authn_default 0 xffffffffl



{# Define rpc_c_authz_none 0

# Define rpc_c_authz_name 1

# Define rpc_c_authz_dce 2

# Define rpc_c_authz_default 0 xffffffff}



# 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_authn_level_pkt 4

# Define rpc_c_authn_level_pkt_integrity 5

# Define rpc_c_authn_level_pkt_privacy 6}


{Sec_winnt_auth_identity_unicode = 2}


Punshort = ^ word;


Pcoauthidentity = ^ _ coauthidentity;

_ Coauthidentity = record

User: punshort;

Userlength: ulong;

Domain: punshort;

Domainlength: ulong;

Password: punshort;

Passwordlength: ulong;

Flags: ulong;



_ Coauthinfo = record

Dwauthnsvc: DWORD;

Dwauthzsvc: DWORD;

Pwszserverprincname: widestring;

Dwauthnlevel: DWORD;

Dwimpersonationlevel: DWORD;

Pauthidentitydata: pcoauthidentity;

Dwcapabilities: DWORD;



Tsecdcomconnection = Class (tdcomconnection)


Fcai: _ coauthinfo;

Fcid: _ coauthidentity;

Fsvinfo: coserverinfo;

Fuser: widestring;

Fpassword: widestring;

Procedure setpassword (const value: widestring );

Procedure setuser (const value: widestring );

Procedure setsvinfo (const value: coserverinfo );


Procedure doconnect; override;



Property svinfo: coserverinfo read fsvinfo write setsvinfo;

Constructor create (aowner: tcomponent); override;

Procedure mysetblanket (ITF: iunknown; const vcai: _ coauthinfo );

Function getserver: iappserver; override;


Property User: widestring read Fuser write setuser;

Property password: widestring read fpassword write setpassword;



Procedure register;




Constructor tsecdcomconnection. Create (aowner: tcomponent );


Inherited create (aowner );

Fillmemory (@ fcai, sizeof (fcai), 0 );

Fillmemory (@ fcid, sizeof (fcid), 0 );

Fillmemory (@ fsvinfo, sizeof (fsvinfo), 0 );

With fcai do begin

Dwauthnsvc: = 10; // rpc_c_authn_winnt

Dwauthzsvc: = 0; // rpc_c_authz_none

Dwauthnlevel: = 0; // rpc_c_authn_level_default

Dwimpersonationlevel: = 3;

Pauthidentitydata: [email protected];

Dwcapabilities: =$ 0800;




Procedure tsecdcomconnection. doconnect;


Tmpcmpname: widestring;

Iid_iunknown: tguid;

IIU: idispatch;

Mqi: multi_qi;

Qr: hresult;


If (objectbroker) <> nil then



If computername = ''then

Computername: = objectbroker. getcomputerforguid (getserverclsid );


Setappserver (createremotecomobject (computername, getserverclsid) as idispatch );

Objectbroker. setconnectstatus (computername, true );


Objectbroker. setconnectstatus (computername, false );

Computername: = '';


Until connected;


Else if (computername <> '') then


With fcid do begin

User: = punshort (@ Fuser [1]);

Userlength: = length (Fuser );

Tmpcmpname: = computername;

Domain: = punshort (@ tmpcmpname [1]);

Domainlength: = length (tmpcmpname );

Password: = punshort (@ fpassword [1]);

Passwordlength: = length (fpassword );

Flags: = 2; // Unicode


Fsvinfo. pwszname: = pwidechar (tmpcmpname );

Fsvinfo. pauthinfo: [email protected];

Iid_iunknown: = iunknown;

Mqi. IID: [email protected] _ iunknown; mqi. ITF: = nil; mqi. 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 );

Mysetblanket (iunknown (IIU), fcai );

Setappserver (IIU );



Inherited doconnect;



Function tsecdcomconnection. getserver: iappserver;


Qiresult: hresult;


Connected: = true;

Qiresult: = idispatch (appserver). QueryInterface (iappserver, result );

If qiresult <> s_ OK then


Result: = tdispatchappserver. Create (iappserverdisp (idispatch (appserver )));


Mysetblanket (iunknown (result), fcai );




Procedure tsecdcomconnection. mysetblanket (ITF: iunknown;

Const vcai: _ coauthinfo );


With vcai do

Cosetproxyblanket (ITF, dwauthnsvc, dwauthzsvc, pwidechar (pauthidentitydata ^. domain ),

Dwauthnlevel, dwimpersonationlevel, pauthidentitydata, dwcapabilities );



Procedure tsecdcomconnection. setpassword (const value: widestring );


Fpassword: = value;



Procedure tsecdcomconnection. setsvinfo (const value: coserverinfo );


Fsvinfo: = value;



Procedure tsecdcomconnection. setuser (const value: widestring );


Fuser: = value;




Procedure register;


Registercomponents ('datasnap ', [tsecdcomconnection]);





The Code has some C-style annotations Because delphi did not predefine these variables and data structures for us. For example
How to use it? Install this component in the IDE and put it in the remote data module of your existing code.
Set the dataset control pointing to tdocmconnection to this new tsecdcomconnection control. Then you can
Set the strictest security options on the remote computer. But remember to set the appropriate permissions for the users you want to use:
Grant remote activation and remote access permissions. 4. access issues have not yet been discussed. Activate and access
Question is not the same thing. A user may have the activation permission but no access permission, or may have only the access permission but no access permission.
Activate permissions. As mentioned above, cocreateinstacnceex can activate an object with another identity and obtain one of the iunknown pointers.
Local references. If you directly use this pointer to obtain the iappserver interface and call the method, you may
See "Access Denied. This is the local reference of the iunknown pointer that exists in the client process and is no longer unique.
Before special settings, the pointer inherits the local token of the client process, that is, when this pointer is used to obtain the remote iappserv
The QueryInterface is called with the current logon token of the client.
The user name and password cached in the token will be re-logged on for verification. Of course, this will be rejected again, and then the remote computer will
Attempt to log on with the Guest account and obtain the COM object interface. If no guest access permission is available, the client access is lost.
Failed. Windows returns the "Access Denied" message. So how can I make QueryInterface call use remote user identity?
This requires calling cosetproxyblanket to forcibly set the local interface reference to use the token of a remote user. Above
In the code, I wrapped the API with mysetbalancer to call QueryInterface using the user identity during activation.
Then, call mysetblanket again on the obtained iappserver interface to ensure that this interface is also used remotely.
User identity.

Mysetblanket (mqi. ITF, fcai );

Qr: = mqi. ITF. QueryInterface (idispatch, IIU );

Olecheck (QR );

Mysetblanket (iunknown (IIU), fcai); to ensure direct reference to the tclientd of dataprovider
Ataset can also work according to the preceding requirements. The getserver method is overloaded in the extended tsecdcomconnection control.
In this way, tsecdcomconnection can completely replace tdcomconnection to implement convenient COM + application programming. Due to time
In a rush, many terms are not explained at the time of writing this article, so it may not be easy to understand, but for de
Lphi fans provides a simple method to implement secure access to com. I will post this article
A friend who needs to directly copy the code is used in his own application. After tsecdcomconnection is used, the server's COM +
The object can be forced to open the access check and open the component-level access check. When the access check is enabled
The User Name allowed to access the COM + object on the server can be added to the role for correct access. (The above code is in Delphi7/wi
NXP SP2 passes debugging. For Windows 98 and Windows NT4.0 and below, cocreateinsta
Nceex cannot directly generate the security context of the COM + object, so the code is unavailable)


Access COM + applications as a user on a remote computer

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.