diff --git a/dsl/extdeps/access/posix.dag b/dsl/extdeps/access/posix.dag index 3e95434463a..920761c1fe1 100644 --- a/dsl/extdeps/access/posix.dag +++ b/dsl/extdeps/access/posix.dag @@ -1,5 +1,6 @@ module extdeps.access.posix +import std.types { NonEmptyStr } import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } @@ -34,3 +35,16 @@ type FilePermissions { ownership: FileOwnership mode: FileMode } + +type PosixUser { + name: NonEmptyStr + uid: Int + gid: Int +} + + +data posix_root_uid: Int = 0 + +fn posix_user_is_root(user: PosixUser) -> Bool { + user.uid == posix_root_uid +} diff --git a/dsl/extdeps/bmc/access.dag b/dsl/extdeps/bmc/access.dag new file mode 100644 index 00000000000..fb40d03bfc4 --- /dev/null +++ b/dsl/extdeps/bmc/access.dag @@ -0,0 +1,63 @@ +module extdeps.bmc.access + +import extdeps.access.rbac { Permission, PermissionAssignment, RbacPolicy } +import extdeps.bmc.types { + RedfishAccountRole, + AccountRoleAdministrator, + AccountRoleOperator, + AccountRoleReadOnly, + redfish_account_role_wire, + RedfishPrivilege, + PrivilegeLogin, + PrivilegeConfigureManager, + PrivilegeConfigureUsers, + PrivilegeConfigureComponents, + PrivilegeConfigureSelf, + redfish_privilege_wire, +} +import std.types { NonEmptyStr } +import extdeps.external_authority { ExternalAuthority } +import extdeps.uri { Uri, Https } + +data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { + uri: Uri { + scheme: Https + locator: "www.dmtf.org/standards/redfish" + } +} + +data redfish_privilege_object: NonEmptyStr = "redfish" + +fn redfish_privilege_permission(privilege: RedfishPrivilege) -> Permission { + Permission { operation: redfish_privilege_wire(p: privilege), object: redfish_privilege_object } +} + +fn redfish_privilege_assignment(privilege: RedfishPrivilege, role: RedfishAccountRole) -> PermissionAssignment { + PermissionAssignment { + permission: redfish_privilege_permission(privilege: privilege), + role: redfish_account_role_wire(role: role), + } +} + +data redfish_rbac_policy: RbacPolicy = RbacPolicy { + users: [], + roles: [ + redfish_account_role_wire(role: AccountRoleAdministrator), + redfish_account_role_wire(role: AccountRoleOperator), + redfish_account_role_wire(role: AccountRoleReadOnly), + ], + user_assignments: [], + permission_assignments: [ + redfish_privilege_assignment(privilege: PrivilegeLogin, role: AccountRoleAdministrator), + redfish_privilege_assignment(privilege: PrivilegeConfigureManager, role: AccountRoleAdministrator), + redfish_privilege_assignment(privilege: PrivilegeConfigureUsers, role: AccountRoleAdministrator), + redfish_privilege_assignment(privilege: PrivilegeConfigureComponents, role: AccountRoleAdministrator), + redfish_privilege_assignment(privilege: PrivilegeConfigureSelf, role: AccountRoleAdministrator), + redfish_privilege_assignment(privilege: PrivilegeLogin, role: AccountRoleOperator), + redfish_privilege_assignment(privilege: PrivilegeConfigureComponents, role: AccountRoleOperator), + redfish_privilege_assignment(privilege: PrivilegeConfigureSelf, role: AccountRoleOperator), + redfish_privilege_assignment(privilege: PrivilegeLogin, role: AccountRoleReadOnly), + redfish_privilege_assignment(privilege: PrivilegeConfigureSelf, role: AccountRoleReadOnly), + ], + role_hierarchy: [], +} diff --git a/dsl/extdeps/bmc/types.dag b/dsl/extdeps/bmc/types.dag index 19a04d8385a..eeab55ad414 100644 --- a/dsl/extdeps/bmc/types.dag +++ b/dsl/extdeps/bmc/types.dag @@ -115,6 +115,13 @@ type RedfishAccountRole = | AccountRoleOperator | AccountRoleReadOnly +type RedfishPrivilege = + PrivilegeLogin + | PrivilegeConfigureManager + | PrivilegeConfigureUsers + | PrivilegeConfigureComponents + | PrivilegeConfigureSelf + fn redfish_boot_target_wire(t: RedfishBootSourceOverrideTarget) -> NonEmptyStr { match t { BootTargetNone => "None" @@ -153,3 +160,13 @@ fn redfish_account_role_wire(role: RedfishAccountRole) -> NonEmptyStr { AccountRoleReadOnly => "ReadOnly" } } + +fn redfish_privilege_wire(p: RedfishPrivilege) -> NonEmptyStr { + match p { + PrivilegeLogin => "Login" + PrivilegeConfigureManager => "ConfigureManager" + PrivilegeConfigureUsers => "ConfigureUsers" + PrivilegeConfigureComponents => "ConfigureComponents" + PrivilegeConfigureSelf => "ConfigureSelf" + } +} diff --git a/dsl/test/claim/access_layer_extension_witness_test.dag b/dsl/test/claim/access_layer_extension_witness_test.dag new file mode 100644 index 00000000000..0aa5a6aa68c --- /dev/null +++ b/dsl/test/claim/access_layer_extension_witness_test.dag @@ -0,0 +1,31 @@ +module test.claim.access_layer_extension_witness + +import std.types { NonEmptyStr, Bool, Int } +import extdeps.access.posix { PosixUser, posix_user_is_root } +import extdeps.access.rbac { RbacPolicy } +import extdeps.bmc.access { redfish_rbac_policy } +import extdeps.bmc.types { AccountRoleAdministrator, AccountRoleReadOnly, redfish_account_role_wire } + +fn role_has_privilege(policy: RbacPolicy, role: NonEmptyStr, privilege: NonEmptyStr) -> Bool { + fold(policy.permission_assignments, init: false, f: fn(acc, pa) { + if acc { true } + else { pa.role == role && pa.permission.operation == privilege } + }) +} + +data root_user: PosixUser = { name: "root", uid: 0, gid: 0 } + +data service_user: PosixUser = { name: "deploy", uid: 1000, gid: 1000 } + +test fn posix_root_identification_holds() -> Bool { + posix_user_is_root(user: root_user) && (posix_user_is_root(user: service_user) == false) +} + +test fn bmc_least_privilege_via_rbac_holds() -> Bool { + let admin = redfish_account_role_wire(role: AccountRoleAdministrator) + let reader = redfish_account_role_wire(role: AccountRoleReadOnly) + role_has_privilege(policy: redfish_rbac_policy, role: admin, privilege: "ConfigureUsers") + && (role_has_privilege(policy: redfish_rbac_policy, role: reader, privilege: "ConfigureUsers") == false) + && role_has_privilege(policy: redfish_rbac_policy, role: reader, privilege: "Login") + && role_has_privilege(policy: redfish_rbac_policy, role: reader, privilege: "ConfigureSelf") +}