Repository navigation
Conversation
Staging objects are created with a protected, creator-only DACL so partial bytes are never readable. The create path never restored inheritance after publication, so a new file carried exactly one explicit ACE: a confined workspace token authorizes through inherited capability grants and therefore could not unlink a file it had just written, and every other grant the directory carries was lost. publish_new now adopts the directory's object-inherit ACEs before the hard link is made, which is after the bytes are complete, so the private staging DACL keeps its purpose. The creator keeps full control, so publication never narrows the publisher's own access. Overwrites are unchanged: they still capture and restore the target's own descriptor. Refs T-Auto#420
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #420
现象 / What was wrong
ops_write_file新建(expectedRevision: "absent")的文件只带一条非继承 ACE:同一个工作区里,内置
write建出的文件有 6 条继承 ACE(含沙箱能力 SIDS-1-4-…)。后果是受限 token 连自己刚写的文件都删不掉——workspace-write下的 token 只有能力 SID 授权,不含用户 SID——其他工具与用户也被同样切断。环境:Windows 10 19045 / dsh-ops 0.4.1 /
workspace-write(main 上同样复现)。最小复现就是工作区里ops_write_file新建一个文件再icacls。#420 的「机制线索」猜是 staging 改名带过来的安全描述符,实测不是。确切链路:
crates/ops-files/src/file_publication.rs:99-106:create_sibling的 Windows 分支丢弃source,一律调dsh_ops_windows_metadata::create_private;windows-metadata/src/lib.rs:106-119:private_descriptor()生成D:P(A;;FA;;;<当前进程用户>),D:P= 受保护、无继承项,经OBJECT_ATTRIBUTES.SecurityDescriptor在FILE_CREATE时下发;crates/ops-files/src/file_single.rs:377-378:新建路径的preserved来自source.map(...),新文件source == None,因此发布后没有任何一步恢复继承,hard_link把该 inode 的安全描述符原样带走;crates/ops-files/src/file.rs:297:覆盖路径先Preserved::capture(目标)再restore,只会把目标原有的 DACL 搬回去,所以坏文件覆盖后仍是坏的。改动 / The change
windows-metadata/src/lib.rs— 新增adopt_parent_dacl(parent, child):读父目录 DACL,只取带OBJECT_INHERIT_ACE的允许/拒绝 ACE(容器专用 ACE 不作用于文件,对象/回调 ACE 带 GUID 作用域,重建会丢作用域,故跳过),以SetKernelObjectSecurity(…, DACL_SECURITY_INFORMATION, …)写回未保护的 DACL。创建者自身的FA头一条,因此发布永不收窄发布者自己的访问。crates/ops-files/src/file_single.rs—publish_new在hard_link之前、字节写完之后,对source.is_none()的 Windows 分支调用它;失败则丢弃暂存件并返回METADATA_UNSUPPORTED(与既有元数据失败语义一致)。file_publication.rs—metadata_error提到pub(super)以便复用同一个错误映射。刻意没做:
create_private的私有 DACL。暂存期私有是正当防护(半截字节不该被读到),缺的只是「发布后回落」;去掉它会泄露写入中的内容。create_cache_object(缓存件必须保持私有)。adopt_parent_dacl里手工拼D:P之外的 ACL 语义,也没有用NULLDACL 让系统补算(pDacl == NULL且置了SE_DACL_PRESENT会得到 everyone 完全控制)。新增 dev-dependency
windows-permissions = "=0.2.4"(测试用,仅cfg(windows)):这是 workspace 里dsh-ops-windows-metadata已经在用的同一精确版本,不引入新下载、不改变任何发布代码路径,Cargo.lock只多一行依赖边。Validation
新增断言(
-p ops-files):new_file_adopts_directory_grants:目录里放一条带唯一掩码0x10001的可继承 ACE,发布后断言产物 DACL 里存在「带INHERITED_ACE标记、同一掩码、同一 SID」的 ACE,即发布后确实继承了目录授权。撤掉adopt_parent_dacl调用后此用例变红。a_directory_without_inheritable_grants_still_publishes_an_accessible_file:父目录是受保护 DACL、没有任何可继承 ACE 时,产物仍可被发布者读回并删除(防止修法在「无可继承项」场景把创建者 ACE 一起丢掉)。端到端(把本分支重编的 worker 换进已安装插件后,用真正的
ops_write_file造文件):7 条与目录的可继承集合逐条对应(改前只有第 1 条 + 标签)。
受限令牌下的 A/B(同一台机、同一工作区、
workspace-write沙箱、只换 worker 二进制):同一个受限令牌,改前
Permission denied且文件仍在,改后removed。CI run(
pull_request事件):https://github.com/T-Auto/dsh-ops/actions/runs/40526360681 —— 当前conclusion=action_required,首次贡献需维护者批准后才会真正执行。已知局限与升级注意:修复只作用于新建文件,已存在的旧文件不会自愈(实测:用原始 worker 建的文件在换装补丁版后仍
Permission denied),需要icacls <file> /inheritance:e一次性恢复继承;覆盖路径保持原语义(抓目标旧 DACL 再写回),所以旧文件被覆盖也仍是旧的 DACL。撤掉实现后的那次运行读数是14 passed; 1 failed。