声明转换规则语言

适用范围:Windows Server 2022、Windows Server 2019、Windows Server 2016、Windows Server 2012 R2、Windows Server 2012

使用跨林声明转换功能,可以通过对跨林信任设置声明转换策略,来跨林边界为动态访问控制桥接声明。 所有策略的主要组件是以声明转换规则语言编写的规则。 本主题提供有关此语言的详细信息,并提供有关创作声明转换规则的指导。

针对跨林信任的转换策略的 Windows PowerShell cmdlet 提供了用于设置常见方案中所需简单策略的选项。 这些 cmdlet 将用户输入转换为声明转换规则语言的策略和规则,然后以规定的格式将其存储在 Active Directory 中。 有关用于声明转换的 cmdlet 的详细信息,请参阅用于动态访问控制的 AD DS Cmdlet

根据声明配置和对 Active Directory 林中跨林信任施加的要求,声明转换策略可能必须比用于 Active Directory 的 Windows PowerShell cmdlet 支持的策略更复杂。 若要有效创作此类策略,必须了解声明转换规则的语言句法和语义。 Active Directory 中的此声明转换规则语言(简称“语言”)是 Active Directory 联合身份验证服务出于类似目的而使用的语言的子集,这两种语言的语法和语义非常相似。 但是,前者允许的操作更少,并且该语言的 Active Directory 版本中施加了额外的语法限制。

本主题简要介绍 Active Directory 中声明转换规则语言的语法和语义,以及创作策略时要考虑的事项。 其中提供了几组示例规则来帮助你入门,并提供了错误的语法示例及其生成的消息,以帮助你在创作规则时解读错误消息。

用于创作声明转换策略的工具

用于 Active Directory 的 Windows PowerShell cmdlet:这是用于创作和设置声明转换策略的首选方式,也是建议的方式。 这些 cmdlet 为简单策略提供开关,并可以验证针对更复杂策略设置的规则。

LDAP:可以通过轻型目录访问协议 (LDAP) 在 Active Directory 中编辑声明转换策略。 但是,这不是建议的方式,因为策略具有多个复杂组件,并且你使用的工具可能无法在将策略写入 Active Directory 之前对其进行验证。 随后可能需要花费大量时间来诊断问题。

Active Directory 声明转换规则语言

语法概述

下面是该语言的语法和语义的简要概述:

  • 声明转换规则集由零个或多个规则组成。 每个规则包括两个活动部分:选择条件列表和规则操作。 如果选择条件列表的计算结果为 TRUE,则执行相应的规则操作

  • 选择条件列表有零个或多个选择条件。 所有选择条件都必须计算为 TRUE,选择条件列表才能计算为 TRUE

  • 每个选择条件有零个或多个匹配条件。 所有匹配条件都必须计算为 TRUE,选择条件才能计算为 TRUE。 必须针对单个声明计算所有这些条件。 与选择条件匹配的声明可以由一个标识符标记,并在规则操作中引用

  • 每个匹配条件指定条件,该条件将通过不同的条件运算符和字符串文本与声明的类型、值或 ValueType 匹配

    • 为值指定匹配条件时,也必须为特定的 ValueType 指定匹配条件,反之亦然。 这些条件在语法中必须彼此相邻。

    • ValueType 匹配条件必须仅使用特定的 ValueType 文本

  • 规则操作可以复制一个使用标识符标记的声明,或者根据使用标识符和/或给定字符串文本标记的声明颁发一个声明

示例规则

此示例演示了一个可用于在两个林之间转换声明类型的规则,前提是这两个林使用相同的声明 ValueType,并且对此类型的声明值采用相同的解释。 该规则有一个匹配条件,和一个使用字符串文本和匹配声明引用的 Issue 语句。

C1: [TYPE=="EmployeeType"]
                 => ISSUE (TYPE= "EmpType", VALUE = C1.VALUE, VALUETYPE = C1.VALUETYPE);
[TYPE=="EmployeeType"] == Select Condition List with one Matching Condition for claims Type.
ISSUE (TYPE= "EmpType", VALUE = C1.VALUE, VALUETYPE = C1.VALUETYPE) == Rule Action that issues a claims using string literal and matching claim referred with the Identifier.

运行时操作

必须了解声明转换的运行时操作才能有效创作规则。 运行时操作使用三个声明集:

  1. 输入声明集:提供给声明转换操作的声明的输入集

  2. 工作声明集:在声明转换期间读取和写入的中间声明

  3. 输出声明集:声明转换操作的输出

下面是运行时声明转换操作的简要概述:

  1. 声明转换的输入声明用于初始化工作声明集。

    1. 处理每个规则时,工作声明集用于输入声明。

    2. 规则中的选择条件列表将与工作声明集中所有可能的声明集相匹配。

    3. 每个匹配声明集用于运行该规则中的操作。

    4. 运行某个规则操作会生成一个声明,该声明将追加到输出声明集和工作声明集。 因此,规则的输出将用作规则集中后续规则的输入。

  2. 规则集中的规则从第一个规则开始按顺序处理。

  3. 处理整个规则集时,将处理输出声明集以消除重复声明和其他安全问题。生成的声明是声明转换过程的输出。

可以根据以前的运行时行为编写复杂的声明转换。

示例:运行时操作

此示例演示了使用两个规则的声明转换的运行时操作。


     C1:[Type=="EmpType", Value=="FullTime",ValueType=="string"] =>
                Issue(Type=="EmployeeType", Value=="FullTime",ValueType=="string");
     [Type=="EmployeeType"] =>
               Issue(Type=="AccessType", Value=="Privileged", ValueType=="string");
Input claims and Initial Evaluation Context:
  {(Type= "EmpType"),(Value="FullTime"),(ValueType="String")}
{(Type= "Organization"),(Value="Marketing"),(ValueType="String")}
After Processing Rule 1:
 Evaluation Context:
  {(Type= "EmpType"),(Value="FullTime"),(ValueType="String")}
{(Type= "Organization"), (Value="Marketing"),(ValueType="String")}
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}
Output Context:
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}

After Processing Rule 2:
Evaluation Context:
  {(Type= "EmpType"),(Value="FullTime"),(ValueType="String")}
{(Type= "Organization"),(Value="Marketing"),(ValueType="String")}
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}
  {(Type= "AccessType"),(Value="Privileged"),(ValueType="String")}
Output Context:
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}
  {(Type= "AccessType"),(Value="Privileged"),(ValueType="String")}

Final Output:
  {(Type= "EmployeeType"),(Value="FullTime"),(ValueType="String")}
  {(Type= "AccessType"),(Value="Privileged"),(ValueType="String")}

特殊规则语义

以下是规则的特殊语法:

  1. 空规则集 == 无输出声明

  2. 空选择条件列表 == 每个声明与选择条件列表匹配

    示例:空选择条件列表

    以下规则与工作集中的每个声明匹配。

    => Issue (Type = "UserType", Value = "External", ValueType = "string")
    
  3. 空选择匹配列表 == 每个声明与选择条件列表匹配

    示例:空匹配条件

    以下规则与工作集中的每个声明匹配。 这是基本的“Allow-all”规则(如果单独使用)。

    C1:[] => Issule (claim = C1);
    

安全注意事项

进入林的声明

需要全面检查主体提供的、传入林的声明,以确保只允许或颁发正确的声明。 不当的声明会危及林的安全,在为进入林的声明创作转换策略时,这应该是首要考虑因素。

Active Directory 提供以下功能来防止错误配置的声明进入林:

  • 如果林信任未针对进入林的声明设置声明转换策略,出于安全考虑,Active Directory 将删除所有进入林的主体声明。

  • 如果针对进入林的声明运行规则集会生成林中未定义的声明,则将从输出声明中删除未定义的声明。

退出林的声明

与进入林的声明相比,退出林的声明给林带来的安全忧患更少。 即使未部署相应的声明转换策略,声明也可以按原样退出林。 还可以在转换退出林的声明的过程中颁发林中未定义的声明。 这样就可以轻松地与声明建立跨林信任。 管理员可以确定是否需要转换进入林的声明,并设置相应的策略。 例如,如果需要隐藏声明以防止信息泄露,则管理员可以设置策略。

声明转换规则中的语法错误

如果给定声明转换策略的规则集在语法上不正确,或者存在其他语法或存储问题,则该策略将被视为无效。 这种情况的处理方式与前面提到的默认条件不同。

在这种情况下,Active Directory 无法确定意向并会进入故障安全模式,在这种模式下,不会在该信任和遍历方向上生成任何输出声明。 需要管理员干预才能更正问题。 如果使用 LDAP 来编辑声明转换策略,则可能会发生这种情况。 用于 Active Directory 的 Windows PowerShell cmdlet 经验证可以防止编写存在语法问题的策略。

其他语言注意事项

  1. 该语言中有几个特殊的关键字或字符(称为终止符)。 本主题稍后的语言终止符表中将介绍这些字符。 错误消息使用这些终止符的标记来消除歧义。

  2. 终止符有时可以用作字符串文本。 但是,这种用法可能与语言定义冲突或产生意想不到的后果。 不建议这种用法。

  3. 规则操作无法对声明值执行任何类型转换,并且包含此类规则操作的规则集被视为无效。 这会导致运行时错误,并且不会生成任何输出声明。

  4. 如果规则操作引用未在规则的“选择条件列表”部分中使用的标识符,则这是无效用法。 这会导致语法错误。

    示例:错误的标识符引用。以下规则演示了在规则操作中使用错误的标识符

    C1:[] => Issue (claim = C2);
    

示例转换规则

  • 允许特定类型的所有声明

    确切类型

    C1:[type=="XYZ"] => Issue (claim = C1);
    

    使用正则表达式

    C1: [type =~ "XYZ*"] => Issue (claim = C1);
    
  • 禁止特定的声明类型 确切类型

    C1:[type != "XYZ"] => Issue (claim=C1);
    

    使用正则表达式

    C1:[Type !~ "XYZ?"] => Issue (claim=C1);
    

规则分析器错误示例

自定义分析器分析声明转换规则以检查语法错误。 在 Active Directory 中存储规则之前,相关的 Windows PowerShell cmdlet 将运行此分析器。 分析规则时发生的任何错误(包括语法错误)都会输出到控制台。 在使用转换声明的规则之前,域控制器也会运行分析器,并且会在事件日志中记录错误(添加事件日志编号)。

本部分演示了一些使用错误语法编写的规则示例,以及分析器生成的相应语法错误。

  1. 示例:

    c1;[]=>Issue(claim=c1);
    

    此示例错误地使用分号来代替冒号。 错误消息:POLICY0002: 无法分析策略数据。行号: 1,列号: 2,错误标记: ;。行: 'c1;[]=>Issue(claim=c1);'。分析器错误:“POLICY0030: 语法错误,意外的 ';',应该使用以下字符之一: ':'。”

  2. 示例:

    c1:[]=>Issue(claim=c2);
    

    在此示例中,副本颁发语句中的标识符标记未定义。 错误消息:POLICY0011: 声明规则中没有任何条件与 CopyIssuanceStatement 中指定的条件标记匹配: 'c2'

  3. 示例:

    c1:[type=="x1", value=="1", valuetype=="bool"]=>Issue(claim=c1)
    

    “bool”不是该语言中的终止符,且不是有效的 ValueType。 以下错误消息中列出了有效的终止符。 错误消息:POLICY0002: 无法分析策略数据。行号: 1,列号: 39,错误标记: "bool"。 行: 'c1:[type=="x1", value=="1",valuetype=="bool"]=>Issue(claim=c1);'。 分析器错误: 'POLICY0030: 语法错误,意外的 'STRING',应该使用以下类型之一: 'INT64_TYPE' 'UINT64_TYPE' 'STRING_TYPE' 'BOOLEAN_TYPE' 'IDENTIFIER'

  4. 示例:

    c1:[type=="x1", value==1, valuetype=="boolean"]=>Issue(claim=c1);
    

    此示例中的数字 1 不是该语言中的有效标记,不允许在匹配条件中使用它。 必须将它括在双引号中,使其成为字符串。 错误消息:POLICY0002: 无法分析策略数据。行号: 1,列号: 23,错误标记: 1。行: 'c1:[type=="x1", value==1, valuetype=="bool"]=>Issue(claim=c1);'。分析器错误: POLICY0029: 意外的输入

  5. 示例:

    c1:[type == "x1", value == "1", valuetype == "boolean"] =>
    
         Issue(type = c1.type, value="0", valuetype == "boolean");
    

    此示例使用了双等号 (==) 而不是单等号 (=)。 错误消息:POLICY0002: 无法分析策略数据。行号: 1,列号: 91,错误标记: ==。行: 'c1:[type=="x1", value=="1",valuetype=="boolean"]=>Issue(type=c1.type, value="0", valuetype=="boolean");'。分析器错误: POLICY0030: 语法错误,意外的 '==',应使用以下字符之一: '='

  6. 示例:

    c1:[type=="x1", value=="boolean", valuetype=="string"] =>
    
          Issue(type=c1.type, value=c1.value, valuetype = "string");
    

    此示例在语法上和语义上正确。 但是,使用“boolean”作为字符串值必然会导致混淆,应避免这种用法。 如前所述,应尽可能避免使用语言终止符作为声明值。

语言终止符

下表列出了声明转换规则语言中使用的终止字符串和关联语言终止符的完整集。 这些定义使用不区分大小写的 UTF-16 字符串。

字符串 终端
[.] IMPLY
[.] SEMICOLON
[.] COLON
[.] COMMA
[.] DOT
[.] O_SQ_BRACKET
[.] C_SQ_BRACKET
[.] O_BRACKET
[.] C_BRACKET
.- . EQ
.- . NEQ
.- . REGEXP_MATCH
.- . REGEXP_NOT_MATCH
[.] ASSIGN
.- . AND
"issue" 问题
"type" TYPE
"value"
"valuetype" VALUE_TYPE
"claim" CLAIM
"[_A-Za-z][_A-Za-z0-9]*" IDENTIFIER
"\"[^\"\n]*\"" 字符串
"uint64" UINT64_TYPE
"int64" INT64_TYPE
"string" STRING_TYPE
"boolean" BOOLEAN_TYPE

语言语法

以下声明转换规则语言以 ABNF 形式指定。 除了此处定义的 ABNF 生成内容之外,此定义还使用上表中指定的终止符。 规则必须采用 UTF-16 编码,字符串比较必须被视为不区分大小写。

Rule_set        = ;/*Empty*/
             / Rules
Rules         = Rule
             / Rule Rules
Rule          = Rule_body
Rule_body       = (Conditions IMPLY Rule_action SEMICOLON)
Conditions       = ;/*Empty*/
             / Sel_condition_list
Sel_condition_list   = Sel_condition
             / (Sel_condition_list AND Sel_condition)
Sel_condition     = Sel_condition_body
             / (IDENTIFIER COLON Sel_condition_body)
Sel_condition_body   = O_SQ_BRACKET Opt_cond_list C_SQ_BRACKET
Opt_cond_list     = /*Empty*/
             / Cond_list
Cond_list       = Cond
             / (Cond_list COMMA Cond)
Cond          = Value_cond
             / Type_cond
Type_cond       = TYPE Cond_oper Literal_expr
Value_cond       = (Val_cond COMMA Val_type_cond)
             /(Val_type_cond COMMA Val_cond)
Val_cond        = VALUE Cond_oper Literal_expr
Val_type_cond     = VALUE_TYPE Cond_oper Value_type_literal
claim_prop       = TYPE
             / VALUE
Cond_oper       = EQ
             / NEQ
             / REGEXP_MATCH
             / REGEXP_NOT_MATCH
Literal_expr      = Literal
             / Value_type_literal

Expr          = Literal
             / Value_type_expr
             / (IDENTIFIER DOT claim_prop)
Value_type_expr    = Value_type_literal
             /(IDENTIFIER DOT VALUE_TYPE)
Value_type_literal   = INT64_TYPE
             / UINT64_TYPE
             / STRING_TYPE
             / BOOLEAN_TYPE
Literal        = STRING
Rule_action      = ISSUE O_BRACKET Issue_params C_BRACKET
Issue_params      = claim_copy
             / claim_new
claim_copy       = CLAIM ASSIGN IDENTIFIER
claim_new       = claim_prop_assign_list
claim_prop_assign_list = (claim_value_assign COMMA claim_type_assign)
             /(claim_type_assign COMMA claim_value_assign)
claim_value_assign   = (claim_val_assign COMMA claim_val_type_assign)
             /(claim_val_type_assign COMMA claim_val_assign)
claim_val_assign    = VALUE ASSIGN Expr
claim_val_type_assign = VALUE_TYPE ASSIGN Value_type_expr
Claim_type_assign   = TYPE ASSIGN Expr