字体: | 推荐给好友 上一篇 | 下一篇

Windows2000服务器端应用程序开发设计指南-信任成员的管理1

发布: 2008-5-01 21:10 | 作者: Jeffrey Richter Jaso | 来源: 网络转载 | 查看: 385次

9. 信任成员的管理

Microsoft的开发者已经完成Microsoft Windows 2000安全性特色的设计工作,这些安全性特色比大多数人所习惯的环境更复杂且更有弹性。事实上,若加上适当的管理和软件开发,Windows 2000是现今最先进且安全的操作环境之一。然而,由于如此复杂而产生的大量细节以及纷繁难懂之处,将使得对Windows安全性没经验的大多数开发者感到气馁。就本书的读者和我而言,这个安全性的主题虽然意味着一个挑战,但应付挑战之后报偿将是具有安全性的伺服端软件和实际的成就感。

在「安全性」这个主题下,Windows 2000有许多适当的特色,大部份将在本书的这一篇谈论。本书将Windows安全性相关的特色区分为一或更多个如下所述的标题:

  •  身份识别及使用者内容 许多Windows安全性以使用者为基础,并且将它的能力应用在使用者识别以及维护「使用者内容」上。这个能力可让系统决定使用者是否被允许执行某些动作。
     
  •  存取控制 存取控制与私有资料和文件及注册密钥等系统物件之安全性有关。Windows 2000的存取控制特色使用在身份识别和使用者内容上,其依赖的程度在之后的两个章节中开始讨论时会更清楚。
     
  •  安全性连线 通讯安全性是Windows 2000的一个重要部份,包括资料加密和认证,两者皆确保您正在与一个可信赖的实体通讯。
     

因为安全性非常依赖于使用者身份识别的建立和管理,所以我们要开始讨论Windows 2000之信任成员的使用。

何谓信任成员?
 

当您登入一台执行Windows的机器,通常会输入使用者名称及密码。使用者名称会向系统确认这个代表您的帐号。像这样的帐号通常被称为 使用者帐户 

使用者帐户维持一组与使用者相关的资讯,例如,名字、密码(或身分证件)、注释及地址。有两个必要项目与使用者帐户一起储存,即是使用者在系统上的权限清单以及数位身份识别-被称为安全识别项或是SID(读为「sid」)-当在保护物件及私人资料的安全时,系统会使用它们来确认帐户。SID是用来确认信任成员帐户的可变长度二进制结构(权限和SID在稍后将有详细的讨论)。第叁个与使用者帐户同样重要的资讯是使用者成员的群组清单。

Windows开发人员实作了群组帐户,以管理复杂性的事物。 群组帐户 包含一组可以是使用者帐户或是通用群组帐户的成员。群组帐户维持着与使用者帐户相似的资讯,例如系统权限及SID。只要经由将使用者帐户指派到群组帐户,可在一个地方管理一组使用者安全性而不必为每个使用者作调整。

使用者帐户及群组帐户是一个系统的信任成员。 信任成员 是 在Windows中可以拒绝或允许存取的实体。有些信任成员隐含在系统中(例如Everyone之内建群组),另一些则由系统动态地建立(例如在第十及十一章会更详细讨论的登入SID),还有其他因为已建立帐户而存在的信任成员。使用者、群组及电脑都是与信任成员帐户有关的Windows信任成员。所有的信任成员分享遍及系统的公共函数,尤其是在处理存取控制时(在下一章讨论)。


说明

使用者帐户及群组帐户是称为 信任成员 的广泛类别成员。因为这一章主要是讨论使用者及群组帐户,所以除非详细指明,否则 信任成员  信任成员帐户 这两个术语即代表使用者及群组帐户。


系统经由信任成员帐户授予某个文件的存取或拥有权。当系统执行某个特许的权限函数时,因为信任成员帐户已授予这个权限,所以它是被允许的。当使用者被系统识别时,是被她(他)的信任成员帐户及群组的信任成员帐户识别,而且使用者是这些帐户的成员。如您所见,信任成员是Windows安全性的重要部分。

在我们进入计划性的管理使用者及群组帐户的讨论之前,让我们先看一下管理系统信任成员的使用者介面。

Windows 2000的安全性工具
 

为了有效地编写具有安全性的软件,了解Windows 2000中如何管理安全性及熟悉一些有帮助的工具是必要的。

首先,您要有Windows 2000系统的管理权限(或者,就网域系统而言是帐户操作者的权限)。如果必要的话,安装一个以您为管理者的Windows 2000系统。

假如还没准备好的话,现在也应该对Microsoft Management Console(MMC)较为熟悉了。这个应用程序会与系统一起安装,而且属于MMC.exe。MMC可让您执行管理的任务,从安装系统上的硬盘到监视事件日志,包括安全性管理皆是。实作MMC的每个函数如同被载入的嵌入式管理单元一般。有关使用MMC的更多细节,请参阅Windows 2000 Help中的「使用Microsoft Management Console」主题。

本机使用者及MMC的群组嵌入式管理单元不但可让您建立使用者及群组,也可以增加成员到群组中。嵌入式管理单元还可以让您改变使用者的密码及规定一些适用于信任成员的原则。理想上,本书的范例应由管理群组成员的使用者执行(尽管您将发现建立权限较小的使用者及群组,才能使您感受到权限较小的方式更容易学习到东西)。

群组原则嵌入式管理单元可让您调整委派给信任成员的权限。因为系统上有大量群组原则的功能,所以这个嵌入式管理单元比本机使用者及群组嵌入式管理单元更复杂。经由展开以下的路径可以直接到群组原则嵌入式管理单元的权限功能:本机电脑原则\电脑配置\Windows设定\安全性设定\本机原则\使用者权限委派。图9-1说明了应显示的画面。

 图9-1 显示在群组原则嵌入式管理单元中的权限画面

说明

透过一个称为群组原则的逻辑层来了解Windows 2000中有关权限之MMC嵌入式管理单元介面是重要的。群组原则是个复杂的主题,可以独立写成好几章。这个委派权限的方法是从Windows NT直接委派权限给信任成员的使用者管理员工具中分离出来的措施。透过呼叫系统函数,直接的权限操作仍旧是可能的;然而,Windows 2000提供的使用者介面只有用到群组原则。 使用MMC嵌入式管理单元,您可以体验委派使用者权限的过程。此外,委派使用者权限可能会被网域群组原则完全地覆盖。您可以使用直接管理权限的工具来处理这些潜在的问题。


最后,在进入计划性管理信任成员的讨论之前,我应当提到在本章稍后所叙述的TrusteeMan范例应用程序,该范例程序能被用来建立及删除使用者和群组帐户,并委派权限给信任成员帐户的工具。尽管这个范例是个教学用的工具,但它也对Windows 2000所包含的MMC嵌入式管理单元提供一个简单的选择。

管理信任成员帐户
 

在典型的企业环境里,系统管理者或帐户操作者可以在系统上建立使用者及群组帐户。在这种环境下执行的服务器软件可以和已启用的安全性感知以及使用者识别一起运作,不用直接建立或管理信任成员帐户。这个软件仅假定这些帐户已经由另一个实体建立,这样的话,许多的服务开发人员似乎都不必计划性地建立信任成员帐户。

尽管这可能是真的,但是您会发现Windows 2000的安全性具有很大的弹性。您可以自行创造信任成员,不需与特定的使用者直接联系在一起,也不用总是被已登入系统的使用者使用。举例来说,Windows 2000可让您把使用者帐户的存取限定为与它的原始权限以及由软件决定的信任成员权限相结合,它是一种 建立Restricted Token 的技巧(Restricted Token在第十一章有详细的讨论)。如此一来,您的软件就可以建立系统管理者无法(或要求)手动建立的帐户。

此外,假如您正在编写可以直接支援建立帐户的服务器软件,例如Internet gaming群体(Internet gaming community)或是前端(Front End)连线银行,您的服务必须能够计划性的建立及管理信任成员帐户。Windows 2000提供两组可让您如此做的函数:

  • Net API 「Net」函数可让您建立及管理任何Windows 2000系统上的使用者帐户,包括工作站以及服务器系统,不管系统是否为网域控制站。
     
  • Active Directory Services Interface(ADSI) ADSI是一组COM物件,可让您在Windows 2000上管理Active Directory。Active Directory是Windows 2000网域上所有使用者及群组帐户(在许多其他的物件中)的容器,对Windows来说是个非常重要的附加部分。您的软件可以使用ADSI物件来建立、删除及管理网域控制站之Active Directory上的使用者及群组物件。
     

根据您的需求来决定使用哪一组APIs。Active Directory是个企业管理工具,就其本身而言,假如您的软件正在企业环境中建立使用者帐户的话,应该使用Active Directory的ADSI介面。然而,如果您的服务软件正在可能不是网域控制站的单一服务器中建立本机的信任成员帐户时,您会发现Net函数的使用变得更简单且更直接。


说明

ADSI物件可让您的软件管理非网域控制站系统上的使用者及群组,但是因为这种系统不会维护Active Directory,所以ADSI物件可以只透过Net函数传递。同样地,假如Net函数被用来增加、移除或管理网域控制站上的信任成员,他们将会自动地使用ADSI物件去修改机器上的Active Directory。


由于Net函数最常被使用,因此这一章会集中于这个部分。然而,可被用来执行类似任务的ADSI元件也会以注释的方式列举出来,以便您可以在《Platform SDK》文件中寻找特定的主题。

认识Net函数
 

Net API有很多的特色且包含许多使管理信任成员帐户更为容易的函数及结构。Net函数可让您管理使用者帐户、本机群组帐户(只与单一机器相关联)及存在于网域控制站和整个网域函数的网域或通用群组帐户。这些函数以一贯且符合逻辑的方式设计,我们可以经由对一些规则的了解而学习到很多。


说明

假如您正在编写一个可能呼叫Net函数的原始模组,您的原始模组及专案必须执行几个步骤。跟大部分Win 32中的API函数不同的是,Net函数不需在Windows.h的标头档中宣告。因此,您必须在模组的一开始就含入Lm.h标头档。同样地,您必须将NetApi32.lib文件加到与专案连结的程序库文件清单中。


Net函数的其中一个特色是以一致的方式呈现。因为这个原因,才能够在不涵盖API命名集的每个单一函数的情形下,提供了解这些函数之通用规则的内容:

  • Net函数使用Net+[信任成员类型]+[执行的动作]格式为一贯的命名。例如:NetUserAdd、NetLocalGroupGetInfo以及NetUserSetGroups。
     
  • Net函数只支援Unicode字串。假如您的专案以ANSI建立的话,在传递字串到Net函数前必须为每个字串缓冲器制作Unicode副本。(当在编写Windows 2000的服务器软件时,这是另一个经常使用Unicode的原因。)
     
  • Net函数经由系统及帐户名称来识别信任成员帐户。假设您在远端机器上拥有足够的权限,便可以在远端系统上使用这些函数管理信任成员。
     
  • Net函数实作了一组可用来向软件回报信任成员资讯或是被软件用来设定信任成员资讯的结构。因为牵涉到许多结构的部份,所以这个Net函数的特性可能会令人感到困惑,不过了解后会非常合理且有用。
     

这里有个例子:假如您使用NetLocalGroupAdd函数(稍后将详细地讨论)来建立本机群组,您可以选择输入及传递一个LOCALGROUP_INFO_0结构的实例,只包含群组名称,或是可让您指定群组名称及注解字串的LOCALGROUP_INFO_1结构。这取决于您,而经由一个称为level参数的传递,Net函数可让您选择要使用的结构。在我们的例子中,LOCALGROUP_INFO_0为level 0结构,而LOCALGROUP_INFO_1则是level 1结构。


说明

因为一个单一的函数可以取得或回传一个以上的系统定义结构类型,Net函数定义的类型为PBYTE,并根据您所传递的资讯而适当的转型。假如您传递一个结构给函数,则您应该传递此特定结构的指标,并将它转型为PBYTE。如果Net函数回传一个结构给您,此时函数将会预期为一个PBYTE的指标,您可以在稍后将它转型为适当的结构类型。


我在这一章谈论到的结构集将以星号(*)代替level标记,以便归纳资讯而不用详细说明每个可用的结构。举例来说,在上一段所提到的两个结构都在LOCALGROUP_INFO_* 结构集中。我将会解释一些经常使用的结构以及包含较少结构成员的一些大结构。所有结构的详细说明皆可在《Platform SDK》文件中找到。

表9-1列出为信任成员管理所定义的结构集以及可一起使用的函数。

 表9-1 管理信任成员的结构集
结构集用法及函数
GROUP_INFO_*这些结构为通用或网域群组取得资讯以及设定通用群组资讯,在建立群组及对现存的群组操作时会使用到。

 函数: 

NetGroupAdd、NetGroupEnum、NetGroupGetInfo、NetGroupSetInfo

GROUP_USERS_INFO_*在管理通用或网域群组中的使用者成员时会使用到这些结构,可用来为群组设定或取得使用者资讯。

 函数: 

NetGroupGetUsers、NetGroupSetUsers、NetUserGetGroups、NetUserSetGroups

LOCALGROUP_INFO_*用来为本机群组取得以及设定资讯,不但会被现存的群组使用,在建立新的本机群组时也会用到。

 函数: 

NetLocalGroupAdd、NetLocalGroupEnum、NetLocalGroupGetInfo、NetLocalGroupSetInfo

LOCALGROUP_MEMBERS_INFO_*用来为本机群组设定及取得成员清单。

 函数: 

NetLocalGroupAddMembers、NetLocalGroupDelMembers、NetLocalGroupGetMembers、NetLocalGroupSetMembers

LOCALGROUP_USERS_INFO_*此组结构只包含一个称为LOCALGROUP_ USERS_INFO_0的结构,呼叫NetUserGet-Local Groups以取得本机群组成员之所有使用者帐户清单时使用。

 函数: 

NetUserGetLocalGroups

USER_INFO_*由于Windows 2000使用者帐户联系的大量资讯,此组结构显然包含了最大的结构数量。建立使用者以及为现存使用者帐户取得和设定资讯时会使用到这些结构。

函数:

NetUserAdd、NetUserEnum、NetUserSetInfo、NetUserGetInfo

当Net函数将有关信任成员的资讯回传到软件时,它将指派一个缓冲器给您。如前所述,Net函数需要您传递指标位址到转型成PBYTE* 的适当结构类型中。当您用完系统传回的缓冲器后,应该将它传递到NetApiBufferFree,以释放缓冲器。这个函数定义如下:

NET_API_STATUS NetApiBufferFree(PVOID pvBuffer);

在设定信任成员资讯时,Net函数可能会判定软件并没有正确地设定结构成员。在这个情况下,「Set」函数(如NetUserSetInfo)会回传ERROR_INVALID_PARAMETER。为了确定错误的原因,您可以使用函数的错误参数,这个参数会被填入一个事先定义的值,以指出第一个导致错误的结构成员。假如您对这个资讯不感兴趣,可以传递NULL给「Set」函数。

有了Net函数的常识,您将发现学习这些细节是件轻而易举的事。所以,让我们开始建立信任成员帐户吧!

建立信任成员帐户
 

就使用者及群组帐户在系统中控制存取安全物件的角色,以及系统权限的功能来说是类似的。系统可让您交替地指派以及否决使用者和群组帐户的权限。然而,使用者可以利用使用者帐户登入系统,反之,群组帐户却无法以这种方式使用。这种与使用者结合的情形,使得系统必须为使用者帐户维护大量的资讯。


说明

网域或通用群组帐户在很多观念及软件上皆与本机群组帐户一样。尽管我从这里开始将讨论本机群组的管理细节,但是在计划性地管理通用群组时,许多观念还是适用的。有关Net API通用群组功能的完整讨论,请参阅《Platform SDK》文件中字首为「NetGroup」的函数组。


您可以使用NetLocalGroupAdd函数来建立本机群组帐户,其原型如下:

NET_API_STATUS NetLocalGroupAdd(
PCWSTR pstrServername,
DWORD dwLevel,
PBYTE pbBuf,
PDWORD pdwParmErr);

第一个参数是您想建立之群组帐户的系统名称。传递NULL给pstrServername表示您希望在本机系统上建立一个群组。dwLevel参数指出您将传递给pbBuf参数作为参考的结构类型。NetLocalGroupAdd使用 表9-1 中所讨论的LOCALGROUP_INFO_* 结构集。您可以传递0或者1以作为此函数的level值。这两个结构的定义如下:

typedef struct _LOCALGROUP_INFO_0 {
PWSTR lgrpi0_name;
}LOCALGROUP_INFO_0;
typedef struct _LOCALGROUP_INFO_1 {
PWSTR lgrpi0_name;
PWSTR lgrpi0_comment;
}LOCALGROUP_INFO_1;

以上两个结构都包含一个群组名称字串的指标,LOCALGROUP_INFO_1结构还包含一个注解字串的附加指标。当您在建立群组时,这两个结构都可以使用。

最后一个传递给NetLocalGroupAdd的参数是pdwParmErr,是个DWORD的指标。对于不适当的参数,系统传回一个指出在pdwParmErr参考的变数中,不适当的参数值。只有NetLocalGroupAdd的传回值等于ERROR_INVALID_PARAMETER时,传回pdwParmErr的值才有效。表9-2列出这些可能的值。假如您不想要收到这些资讯,可以传递NULL给pdwParmErr。

 表9-2 pdwParamErr参数中可能被传回的值
叙述
LOCALGROUP_NAME_PARMNUM这个值表示您指定了一个无效的群组名称给新的群组。
LOCALGROUP_COMMENT_PARMNUM这个值表示呼叫NetLocalGroupAdd中的注解值是无效的。

您应该经常检查NetLocalGroupAdd的传回值以确保系统已经传回NERR_Success。否则表示系统在建立的新本机群组时执行失败。


说明

ADSI套件也可以用来建立群组帐户。Active Directory被组织成层级物件,可以在任何的容器物件中建立群组物件。您可以使用IADsContainer介面的Create方法建立一个群组物件。一旦建立物件后,您可以使用QueryInterface方法获得IADsGroup介面的指标,它可以用来更进一步地管理新的群组物件。要取得更多的细节,请参阅《Platform SDK》文件。


建立使用者帐户与建立群组帐户的方法类似;然而,您可以提供更多使用者帐户的资讯给系统。您可以使用NetUserAdd函数在Windows 2000系统上建立新的使用者。

NET_API_STATUS NetUserAdd(
PCWSTR pstrServername,
DWORD dwLevel,
PBYTE pbBuf,
PDWORD pdwParmErr);

请注意到NetUserAdd与NetLocalGroupAdd的参数清单完全相同,差别在于传递到pbBuf参数的结构类型。您可以传递指标到level 1、2或3的USER_INFO_* 结构,以建立新的使用者。这里是USER_INFO_1(可以与NetUserAdd一起使用的最简单结构)及USER_INFO_3(您可以使用的最广泛结构)的定义。

typedef struct _USER_INFO_1 {
PWSTR usri1_name;
PWSTR usri1_password;
DWORD usri1_password_age;
DWORD usri1_priv;
PWSTR usri1_home_dir;
PWSTR usri1_comment;
DWORD usri1_flags;
PWSTR usri1_script_path;
}USER_INFO_1 ;
typedef struct _USER_INFO_3 {
PWSTR usri3_name;
PWSTR usri3_password;
DWORD usri3_password_age;
DWORD usri3_priv;
PWSTR usri3_home_dir;
PWSTR usri3_comment;
DWORD usri3_flags;
PWSTR usri3_script_path;
DWORD usri3_auth_flags;
PWSTR usri3_full_name;
PWSTR usri3_usr_comment;
PWSTR usri3_parms;
PWSTR usri3_workstations;
DWORD usri3_last_logon;
DWORD usri3_last_logoff;
DWORD usri3_acct_expires;
DWORD usri3_max_storage;
DWORD usri3_units_per_week;
PBYTE usri3_logon_hours;
DWORD usri3_bad_pw_count;
DWORD usri3_num_logons;
PWSTR usri3_logon_server;
DWORD usri3_country_code;
DWORD usri3_code_page;
DWORD usri3_user_id;
DWORD usri3_primary_group_id;
PWSTR usri3_profile;
PWSTR usri3_home_dir_drive;
DWORD usri3_password_expired;
}USER_INFO_3 ;

如您所见,当您在建立使用者时,可以选择传递大量的资讯给系统。有关USER_INFO_2及USER_INFO_3结构中每个成员的详细叙述,请参阅《Platform SDK》文件。此处的讨论只需叙述在USER_INFO_1结构中出现的成员,这些成员会在表9-3中列出。

 表9-3 USER_INFO_1的成员
成员叙述如果ERROR_INVALID_ PARAMETER,pdwParmErr 的传回值
usri1_name指向一个包含建立使用者帐户的Unicode使用者名称缓冲器。此成员必须包含一个有效的指标,否则NetUserAdd将传回ERROR_INVALID_PARAMETER。USER_NAME_PARMNUM
usri1_password指向一个包含建立使用者帐户的Unicode密码缓冲器。密码限制为14个字元而且必须符合您正在新增使用者帐户之系统提出的规则。USER_PASSWORD_PARMNUM
usri1_password_ageNetUserAdd忽略此成员。USER_PASSWORD_AGE_PARMNUM
usri1_priv当您在建立使用者时,这个成员的值必须设定为USER_PRIV_USER。任何其他的值将会导致NetUserAdd传回ERROR_INVALID_PARAMETER。USER_PRIV_PARMNUM
usri1_home_dir使用者可以为其帐户定义主目录。假如您想要联合主目录与新的使用者,此成员应该要指向一个包含Unicode字串目录路径的缓冲器。您可以指派NULL给这个成员。USER_HOME_DIR_PARMNUM
usri1_comment这个成员指向一个包含新使用者的Unicode注解缓冲器。假如您不想要结合注解与新的使用者帐户,可以指派NULL给这个成员。USER_COMMENT_PARMNUM
usri1_flags这个成员可以结合指示新使用者帐户功能的标记(请见 表9-4 )。USER_FLAGS_PARMNUM
usri1_script_path当使用者登入系统时,系统将执行与您的使用者结合之 .exe、.cmd或.bat档。您可以使用此成员来设定登入指令档的路径。USER_SCRIPT_PATH_PARMNUM

呼叫NetUserAdd时,表9-4中任何一种标记的结合都可以被指派给USER_INFO_1的usri1_flags成员,其他的标记则可用来指定额外的特征。有关这些其他标记的资讯,请参阅《Platform SDK》文件。

 表9-4 与USER_INFO_1有关的标记
标记叙述
UF_ACCOUNTDISABLE建立一个无效的帐户。
UF_PASSWD_NOTREQD建立一个不需要密码的帐户。对于要求所有帐户都需使用密码的系统或网域来说,可能是个适当的原则。
UF_PASSWD_CANT_CHANGE建立一个使用者无法改变自己密码的帐户。系统管理者仍旧可以改变密码。
UF_DONT_EXPIRE_PASSWD这个使用者的密码永不过期。
UF_NOT_DELEGATED这个帐户不能被委派(有关委派的讨论,请参阅 第十一章 及 十二章 )。
UF_SMARTCARD_REQUIRED要求使用者使用智慧卡登入这个新帐户。
UF_TRUSTED_FOR_DELEGATION这个帐户可被委派(有关委派的讨论,请参阅 第十一章 及 十二章 )。

不管它看起来像什么,用NetUserAdd来建立新使用者帐户之最低编写程序必要条件实际上是非常合理的。以下的程序代码说明了最简单的实例:

BOOL CreateUser(PWSTR pszSystem, PWSTR pszName, PWSTR pszPassword){
USER_INFO_1 userInfo ={0 };
userInfo.usri1_name = pszName;
userInfo.usri1_password = pszPassword;
userInfo.usri1_priv = USER_PRIV_USER;
NET_API_STATUS netStatus =
NetUserAdd(pszSystem, 1, (PBYTE) &userInfo, NULL);
return(netStatus == NERR_Success);
}

如您所见,这是个相当琐碎的程序代码,用来在给定的系统上与给定的名称及密码建立一位使用者。使用以下的方式呼叫此范例函数将会在本机系统上建立一个名字为「MrMan」,密码为「HowDoYouDo」的使用者:

CreateUser(NULL, L"MrMan", L"HowDoYouDo");

说明

ADSI套件也可被用来建立使用者帐户。如同群组物件一般,可以在任何的容器物件中建立使用者物件。您可以使用IADsContainer介面的Create成员建立一个使用者物件。一旦建立了这个物件,便可以使用QueryInterface方法,以取得IADsUser介面的指标,它可被用来进一步地管理您的新使用者物件。有关这个主题的细节,请参阅《Platform SDK》文件。


设定使用者及群组资讯
 

系统也为了取得及设定已被建立之使用者或群组帐户的资讯而提供Net函数。您可以看到这些函数的呼叫格式与NetGroupAdd及NetUserAdd非常类似。要取得及设定使用者帐户资讯,可以使用NetUserGetInfo及NetUserSetInfo函数,其原型如下:

NET_API_STATUS NetUserGetInfo(
PCWSTR pszServerName,
PCWSTR pzUsername,
DWORD dwLevel,
PBYTE *ppbBuf);
NET_API_STATUS NetUserSetInfo(
PCWSTR pszServerName,
PCWSTR pszUsername,
DWORD dwLevel,
PBYTE pbBuf,
PDWORD pdwParmErr);

如您所见,NetUserGetInfo及NetUserSetInfo函数拥有几乎跟NetUserAdd一样的参数清单。事实上,唯一额外的参数是pstrUsername,它指出被取得或设定资讯的使用者名称。

请注意NetUserGetInfo的部份,其pbBuf参数已经被改成缓冲器的指标位址。如同先前所指出的,这是因为系统实际上会指派被要求的结构给您,并经由ppbBuf参数在变数中传回一个指向新缓冲器的指标。

dwLevel参数指出在呼叫NetUserGetInfo或NetUserSetInfo中,将会使用到的USER_INFO_* 结构level。这些函数接受USER_INFO_1或USER_INFO_3,如同前面所定义的,以及NetUserAdd不支援的其他几个USER_INFO_* 结构。有关这些结构的完整叙述,请参考《Platform SDK》文件。


说明

设定使用者资讯时,您应该使用一个只包含您想设定的资讯或接受NULL,如同一个成员值的结构level。举例来说,仅设定使用者帐户的密码可以使用USER_INFO_1003结构类型,因为它只为使用者的新密码包含一个成员。


以下的两个范例函数,说明了如何使用NetUserSetInfo及NetUserGetInfo来设定和取得使用者帐户资讯的方法。第一个函数显示如何为使用者帐户设定密码;第二个函数则说明如何撷取使用者的注解栏位。这些函数使用的原则可以为使用者帐户取得或设定任何有效的资讯。

BOOL SetUserPassword(PWSTR pszSystem, PWSTR pszName, PWSTR pszPassword){
USER_INFO_1003 userInfo = {0};
userInfo.usri1003_password = pszPassword;
NET_API_STATUS netStatus =
NetUserSetInfo(pszSystem, pszName, 1003, (PBYTE) &userInfo, NULL);
return (netStatus == NERR_Success);
}
BOOL GetUserComment(PWSTR pszSystem, PWSTR pszName, PWSTR pszComment,
int nBufLen){
USER_INFO_10 *puserInfo;
BOOL fSuccess = FALSE;
NET_API_STATUS netStatus =
NetUserGetInfo(pszSystem, pszName, 10, (PBYTE*) &puserInfo);
if (netStatus == NERR_Success){
if (nBufLen > lstrlen(puserInfo->usri10_comment)) {
lstrcpy(pszComment, puserInfo->usri10_comment);
fSuccess = TRUE;
}
NetApiBufferFree(puserInfo);
}
return(fSuccess);
}

说明

所有成功呼叫的NetUserGetInfo必须呼叫NetApiBufferFree,以释放NetUserGetInfo传回的缓冲器。NetApiBufferFree函数被定义为取得一个参数,它是被释放的缓冲器指标。


您可能已经猜到,系统也提供了类似的函数,用来取得及设定群组信任成员帐户的资讯。要取得信任成员资讯,您可以呼叫NetLocalGroupGetInfo;而设定群组资讯则要呼叫NetLocalGroupSetInfo。这些函数定义如下:

NET_API_STATUS NetLocalGroupGetInfo(
PCWSTR pservername,
PCWSTR groupname,
DWORD dwLevel,
PBYTE *ppbBuf);
NET_API_STATUS NetLocalGroupSetInfo(
PCWSTR servername,
PCWSTR groupname,
DWORD dwLevel,
PBYTE pbBuf,
PDWORD pdwParmErr);

如您所见,呼叫这些函数与呼叫取得及设定使用者帐户资讯的函数几乎完全相同,差别在于您要处理的群组资讯及先前讨论的LOCALGROUP_INFO_* 结构。

请记得群组成员资讯也与群组帐户一起储存,尽管名称及注解看起来可能是与群组帐户结合的最少资讯。由于它与SID主题密切相关,所以这个主题将延到本章的最后部分,在稍后的〈认识SIDs〉一节中将讨论SIDs内容。

列举使用者及群组
 

通常会在给定的系统上取得现存使用者或群组的清单。再一次地说明,Net API提供两个类似的函数来执行这些任务。您可以使用NetLocalGroupEnum去取得系统上的群组清单;同样地,也可以使用NetUserEnum取得系统上使用者帐户的清单。这些函数定义如下:

NET_API_STATUS NetLocalGroupEnum(
PCWSTR pszServerName,
DWORD dwLevel,
PBYTE* ppbBuf,
DWORD dwPrefMaxLen,
PDWORD pdwEntriesRead,
PDWORD pdwTotalEntries,
PDWORD_PTR pdwResumeHandle);
NET_API_STATUS NetUserEnum(
PCWSTR pszServerName,
DWORD dwLevel,
DWORD dwFilter,
PBYTE* ppbBuf,
DWORD dwPrefMaxLen,
PDWORD pdwEntriesRead,
PDWORD pdwTotalEntries,
PDWORD_PTR pdwResumeHandle);

这些函数很类似,但有一些差别值得注意。从LOCALGROUP_INFO_* 结构集中传回结构到NetLocalGroupEnum的同时,将会从USER_INFO_* 结构集传回结构到NetUserEnum函数。此外,NetUserEnum可让您指定筛选条件,以减少使用者帐户清单传回的范围。传递0给NetUserEnum的filter参数,表示没有筛选条件,所以任何的帐户类型都会被传回。您通常要传递FILTER_NORMAL_ACCOUNT给filter参数;然而,您也可以传递表9-5中列出的任何一个值。

 表9-5 可以传递给NetUserEnum的filter参数的筛选条件值
意义
FILTER_NORMAL_ACCOUNT传回系统上的通用使用者帐户资料
FILTER_TEMP_DUPLICATE_ACCOUNT传回网域控制站上的本机使用者帐户资料
FILTER_INTER网域_TRUST_ACCOUNT传回网域控制站上的网域信任帐户资料
FILTER_WORKSTATION_TRUST_传回网域控制站上的成员服务器或工作站帐 ACCOUNT 户资料
FILTER_SERVER_TRUST_ACCOUNT传回网域控制站上的网域控制站帐户资料

除了回传filter参数及结构外,NetUserEnum及NetLocalGroupEnum的使用是完全相同的。就像我们先前看到的函数一样,pszServerName参数指出我们想要列举信任成员帐户的系统。dwLevel参数指出与ppbBuf参数相关的LOCALGROUP_ INFO_* 或USER_INFO_* 结构的版本。

您应该传递要求作为ppbBuf参数之结构类型指标变数的位址。根据NetUserEnum及NetLocalGroupEnum是否被使用的情形,系统将指派一个缓冲器以持有USER_INFO_* 或LOCALGROUP_INFO_* 结构的阵列,ppbBuf是一个缓冲器位址的指标。这个缓冲器将是函数列举的帐户。记得就像NetUserGetInfo一样,必须呼叫NetApiBufferFree来释放系统指派给您的缓冲器。

您应该指示出呼叫函数所传回的缓冲器最大容量,以位元组来传递这个大小以作为dwPrefMaxLen参数。假如您要系统尽可能指派更大的缓冲器,可以传递MAX_PREFERRED_LENGTH。这样的话,系统通常会个别呼叫函数来完成列举型别(Enumeration)的动作。

您可能会问为什么总是选择限制「Net*Enum」传回的缓冲器大小,从而必须多次呼叫函数。这里有几个原因:不保证少数的帐户,而大量的帐户可能会超出系统指派的最大缓冲器大小。此外,如果列举型别是个冗长的程序,您可能会想要应用程序定期地收回对它的控制。


说明

因为您无法确定系统能够指派多少的内存,所以不管是否选择MAX_PREFERRED_LENGTH值为缓冲器的长度,您的程序代码都应该要能处理要求多次呼叫列举函数的情况。


系统传回读取pdwEntriesRead参数所指的变数项目数量。其馀可用的项目(包括这些被传回的项目)总数会储存在pdwTotalEntries参数所指的变数中。

最后一个参数是系统传回的值,在随后呼叫的列举函数中可以被传递回系统,用来继续接收帐户资讯。在您最初呼叫列举函数时,应该将pdwResume Handle参数所指的变数设定为0,并且不应该修改传回的值。

假如已经成功地传回资料,可是有更多帐户被列举时,列举函数将传回ERROR_MORE_DATA。当最后的可用帐户被传回后,列举函数将回传NERR_ Success。

以下的程序代码说明了如何使用NetLocalGroupEnum列举本机系统上的群组,并显示在主控台视窗:

void PrintLocalGroups() {
ULONG_PTR lResume = 0;
ULONG lTotal = 0;
ULONG lReturned = 0;
ULONG lIndex = 0;
NET_API_STATUS netStatus;
LOCALGROUP_INFO_0* pinfoGroup;
do {
netStatus = NetLocalGroupEnum(NULL, 0, (PBYTE*) &pinfoGroup,
MAX_PREFERRED_LENGTH, &lReturned, &lTotal, &lResume);
if ((netStatus == ERROR_MORE_DATA) ||
(netStatus == NERR_Success)) {
for (lIndex = 0; lIndex < lReturned; lIndex++) {
wprintf(L"%s\n", pinfoGroup[lIndex].lgrpi0_name);
}
NetApiBufferFree(pinfoGroup);
}
}while (netStatus == ERROR_MORE_DATA);
}

这个函数只需要稍微修改就可以使用NetUserEnum来列举系统上的使用者。

摧毁使用者及群组帐户
 

在切换工具及讨论识别信任成员帐户、SIDs的系统结构之前,我想要谈论如何摧毁使用者及群组帐户的部份,以涵盖使用者及群组管理的讨论。

在这个时候谈到SID,是因为它的确与信任成员帐户的解构有些关联。尽管Net函数能让您处理信任成员帐户的名称,而其馀的系统大部分皆忽视与信任成员帐户关联的名称。取而代之的是系统会使用与帐户关联的SID二进制值来识别某个帐户。这与摧毁信任成员帐户有什么关系呢?

假如您使用名称为「JClark」建立一个信任成员,然后再用这个帐户登入并建立一个文件,系统会主张我是这个物件的所有者。然而,如果您摧毁了我的使用者帐户,然后再以名称「JClark」重新建立一个新的使用者帐户,此时系统会指派不同的SID值,因此新的帐户不会被视为旧文件物件的所有者。

那就是说,您可以使用如下的函数来删除使用者帐户:

NET_API_STATUS NetUserDel(
PCWSTR pszServerName,
PCWSTR pszUsername);

如您所看到的,这个简单的函数只使用系统名称及使用者帐户的名称作为参数。PszServerName参数可以为NULL,表示本机系统。假如这个函数执行成功,将会传回NERR_Success。

Net API为删除群组实作了一个类似的函数,其定义如下:

NET_API_STATUS NetLocalGroupDel(
PCWSTR pszServerName,
PCWSTR pszGroupname);

说明

您也可以使用ADSI物件来列举及删除使用者及群组帐户,并取得以及设定使用者及群组资讯。有关IADsUser及IADsGroup介面和用来搜寻及列举IdirectorySearch介面的讨论部份,请参阅《Platform SDK》文件。


管理群组成员
 

群组成员资讯能以几个方式修改,但是我们首先需要学习几个撷取群组成员资讯的方法。第一个方法使用NetUserGetLocalGroups函数撷取为群组成员之使用者清单;第二个方法使用NetLocalGroupGetMembers函数撷取与个别群组关联的成员组。

使用NetUserGetLocalGroups函数
 

NetUserGetLocalGroups函数的原型如下:

NET_API_STATUS NetUserGetLocalGroups(
PCWSTR pszServerName,
PCWSTR pszUsername,
DWORD dwLevel,
DWORD dwFlags,
PBYTE* ppbBuf,
DWORD dwPrefMaxLen,
PDWORD pdwEntriesRead,
PDWORD pdwTotalEntries);

如您所见,这个函数与我们讨论过的NetLocalGroupEnum及NetUserEnum函数看起来很类似。事实上,主要的不同处在于这个函数使用了LOCALGROUP_USERS_INFO_* 结构集(LOCALGROUP_USERS_INFO_0至今仍存在)。

dwFlags唯一有效的值即是LG_INCLUDE_INDIRECT,它指示NetUserGetLocalGroups也应该传回为间接成员群组的使用者(由pszUsername指出),或者是0,即指示函数应该只传回为直接成员群组的使用者。当pszUsername为通用群组或是网域群组的成员时-即本机群组的成员,可能也会产生间接成员的情形。

您可以传递MAX_PREFERRED_LENGTH给dwPrefMaxLen参数,但是无论使用何种方式,您都必须注意经由pdwEntriesRead参数及pdwTotalEntries参数传回的值,以确定所有可能的项目皆被传回。

和之前的列举函数不同,它没有再次呼叫NetUserGetLocalGroups继续列举的方法。您不太可能会面临列举多于几十个群组的状况,而列举使用者可能会传回数千个帐户项目。

使用NetLocalGroupGetMembers函数
 

第二个列举成员资讯的方式是撷取与个别群组关联的成员组。您应该使用NetLocalGroupGetMembers函数这样做:

NET_API_STATUS NetLocalGroupGetMembers(
PCWSTR pszServerName,
PCWSTR pszLocalGroupName,
DWORD dwLevel,
PBYTE* ppbBuf,
DWORD dwPrefMaxLen,
PDWORD pdwEntriesRead,
PDWORD pdwTotalEntries,
PDWORD_PTR pdwResumeHandle);

这个函数会取得服务器名称及本机群组名称以作为它的第一个参数。常见的dwLevel参数指出您想要经由ppbBuf参数传回的LOCALGROUP_MEMBERS_INFO_* 结构集level。

LOCALGROUP_MEMBERS_INFO_* 结构允许您处理信任成员名称及网域名称,或是它们的SIDs,下一节将作详细地说明。以下是LOCALGROUP_MEMBERS_ INFO_0 及 LOCALGROUP_MEMBERS_INFO_3 结构的定义:

typedef struct _LOCALGROUP_MEMBERS_INFO_0 {
PSID lgrmi0_sid;
}LOCALGROUP_MEMBERS_INFO_0;
typedef struct _LOCALGROUP_MEMBERS_INFO_3 {
PWSTR lgrmi3_domainandname;
}LOCALGROUP_MEMBERS_INFO_3;

说明

处理信任成员时,了解许多安全性函数所需的SID值是很重要的。再者,就SIDs来说,本机群组成员的操作函数可让您撷取信任成员项目。


LOCALGROUP_MEMBERS_INFO_0中使用的PSID类型指出一个指向SID结构的指标。这些结构的任何一个都可根据您软件的需要而与NetLocalGroupGetMembers一起使用。

dwPrefMaxLen、pdwEntriesRead、pdwTotalEntries及pdwResumeHandle参数是惯用缓冲器大小、读取的项目、剩下的项目以及继续列举型别的值。这些参数和已经讨论过的NetUserEnum及NetLocalGroupEnum函数中相同名称的参数一样,以完全相同的方式运作。事实上,为了与NetLocalGroupGetMembers一起使用,NetLocalGroupEnum的一部分范例函数PrintLocalGroups,可以容易地修改如下:

void PrintLocalGroupMembers(WCHAR *pszGroup) {
ULONG_PTR lResume = 0;
ULONG lTotal = 0;
ULONG lReturned = 0;
ULONG lIndex = 0;
NET_API_STATUS netStatus;
LOCALGROUP_MEMBERS_INFO_3* pi

TAG: Windows2000 信任成员 应用程序 开发 服务器端 设计指南

 

评分:0

我来说两句

seccode