Skip to content

fix: disable SetPassword DBus method and prevent chpasswd injection#1119

Merged
caixr23 merged 1 commit into
linuxdeepin:masterfrom
caixr23:master
May 22, 2026
Merged

fix: disable SetPassword DBus method and prevent chpasswd injection#1119
caixr23 merged 1 commit into
linuxdeepin:masterfrom
caixr23:master

Conversation

@caixr23
Copy link
Copy Markdown
Contributor

@caixr23 caixr23 commented May 22, 2026

  1. SetPassword now returns error directly, marking the DBus
    method as deprecated and no longer supported
  2. ModifyPasswd rejects password hashes containing \n\r
    to prevent chpasswd stdin injection

fix: 禁用 SetPassword DBus 方法并防止 chpasswd 注入

  1. SetPassword 直接返回错误,标记该 DBus 方法为已废弃
  2. ModifyPasswd 拒绝包含 \n\r 的密码哈希,
    防止通过 chpasswd stdin 注入额外记录

Summary by Sourcery

Enforce proper authorization and input validation for password changes to close security gaps.

Bug Fixes:

  • Require administrator polkit authorization for DBus SetPassword calls to prevent unauthorized password changes.
  • Reject password hash inputs containing newline or colon characters in ModifyPasswd to avoid chpasswd stdin injection.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 22, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Enforces administrator authorization for DBus SetPassword calls and hardens password modification against command injection via chpasswd by rejecting unsafe password hash inputs.

Sequence diagram for DBus SetPassword with enforced polkit authorization

sequenceDiagram
    actor User
    participant DBus
    participant UserService as User
    participant Polkit

    User ->> DBus: SetPassword
    DBus ->> UserService: SetPassword(sender, password)
    UserService ->> Polkit: checkAuth(sender, false, polkitActionUserAdministration)
    alt authorized
        Polkit -->> UserService: ok
        UserService -->> DBus: success
        DBus -->> User: success
    else denied
        Polkit -->> UserService: error
        UserService -->> DBus: dbusutil.ToError(err)
        DBus -->> User: access denied
    end
Loading

File-Level Changes

Change Details Files
Require administrator polkit authorization for SetPassword DBus method to prevent permission bypass.
  • Updated SetPassword to call checkAuth with polkitActionUserAdministration instead of an empty action.
  • Ensured that any non-empty password change through DBus now goes through polkit admin authentication.
  • Added a clarifying comment that password changes always require admin authorization.
accounts1/user_ifc.go
Harden ModifyPasswd against command injection via chpasswd stdin.
  • Added validation that rejects password hashes containing newline or colon characters.
  • Return errInvalidParam when unsafe characters are detected in the password hash.
  • Documented the validation with a comment explaining it prevents command injection.
accounts1/users/prop.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread accounts1/users/prop.go Outdated
return errInvalidParam
}
// 防止命令注入
if strings.ContainsAny(words, "\n\r:") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hashed passphrases are always entirely printable ASCII, and do not contain any whitespace or the characters ‘:’, ‘;’, ‘*’, ‘!’, or ‘\’. (These characters are used as delimiters and special markers in the passwd(5) and shadow(5) files.)

https://manpages.debian.org/unstable/libcrypt-dev/crypt.5.en.html

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@caixr23 caixr23 force-pushed the master branch 2 times, most recently from 9c4ea61 to 7496abf Compare May 22, 2026 08:48
Comment thread accounts1/users/prop.go Outdated
return errInvalidParam
}
// 防止命令注入
if strings.ContainsAny(words, "\n\r") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if strings.ContainsAny(words, "\n\r") {
if strings.ContainsAny(words, "\n\r :;*!\\") {

我也不太确定

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please mention GHSA-rj6j-h4mp-mcxj.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里是加密后的密码

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里是加密后的密码

是的,加密后的密码不应有空格、冒号、分号等。

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里是加密后的密码

是的,加密后的密码不应有空格、冒号、分号等。

@caixr23

用户名:加密后的密码:密码最后修改日期:最小密码年龄:最大密码年龄:密码过期之前提前警告天数:密码过期后仍然接受此密码的天数:过期时间:保留

Comment thread accounts1/users/prop.go Outdated
return errInvalidParam
}
// 防止命令注入
if strings.ContainsAny(words, "\n\r") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please mention GHSA-rj6j-h4mp-mcxj.

Comment thread accounts1/user_ifc.go Outdated

err := u.checkAuth(sender, false, "")
// 修改密码,一律需要鉴权管理员
err := u.checkAuth(sender, false, polkitActionUserAdministration)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GHSA-j889-pfmh-4p5f

@UTsweetyfish
Copy link
Copy Markdown
Member

UTsweetyfish commented May 22, 2026

风险二:用户名注入(prop.go - 超出Diff范围的隐患)
虽然这次Diff没有修改 username 的来源,但在 fmt.Sprintf("%s:%s\n", username, words) 中,如果 username 是从外部可控的输入获取的(例如通过DBus传入),且没有经过严格过滤,攻击者可以通过构造 username = "root\nuser2:hackpwd" 来进行同样的命令注入。

改进建议:在拼接命令输入时,必须确保 username 也是安全的。

这个有必要吗

@caixr23 caixr23 changed the title fix: enforce polkit auth and prevent chpasswd injection fix: disable SetPassword DBus method and prevent chpasswd injection May 22, 2026
@caixr23
Copy link
Copy Markdown
Contributor Author

caixr23 commented May 22, 2026

风险二:用户名注入(prop.go - 超出Diff范围的认知)
虽然这次Diff没有修改用户名的来源,但在fmt.Sprintf("%s:%s\n", username, Words)中,如果用户名是从外部手动输入的获取的(例如通过DBus形式),且没有经过严格过滤,攻击者可以通过构造username = "root\nuser2:hackpwd"来进行同样的命令注入。
改进建议:在命令输入时,必须确保用户名也是安全的。

这个有必要吗

用户名不是外部传入的

1. SetPassword now returns error directly, marking the DBus
   method as deprecated and no longer supported
2. ModifyPasswd rejects password hashes containing \n\r:
   to prevent chpasswd stdin injection

fix: 禁用 SetPassword DBus 方法并防止 chpasswd 注入

1. SetPassword 直接返回错误,标记该 DBus 方法为已废弃
2. ModifyPasswd 拒绝包含 \n\r: 的密码哈希,
   防止通过 chpasswd stdin 注入额外记录
@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这份 Git Diff 主要做了两件事:

  1. 废弃并移除了 SetPassword 方法的原有实现,将其改为直接返回“已废弃且不再支持”的错误。
  2. ModifyPasswd 函数中增加了输入校验,防止在执行系统命令时发生命令注入。

以下是对该 Diff 的详细代码审查,涵盖语法逻辑、代码质量、代码性能和代码安全四个方面:

一、 语法与逻辑

  1. SetPassword 逻辑变更

    • 移除了原有的复杂逻辑,直接返回错误。逻辑上清晰明确,符合废弃 API 的常规做法。
    • 改进建议:返回错误时,建议保留原有的日志记录习惯,以便在排查问题时知道是谁在调用这个已废弃的接口:
      logger.Warning("[SetPassword] deprecated method called")
      return dbusutil.ToError(fmt.Errorf("SetPassword is deprecated and no longer supported"))
  2. ModifyPasswd 校验逻辑

    • 增加了对换行符 \n\r 和冒号 : 的过滤。从后续的 fmt.Sprintf("%s:%s\n", username, words) 可以看出,这是为了防止用户名或密码中包含这些字符导致格式被破坏或命令注入,逻辑是正确的。

二、 代码质量

  1. 错误信息风格

    • SetPassword 中返回的错误信息是纯英文小写开头 "SetPassword is deprecated and no longer supported"。在 Go 语言中,标准库的 fmt.Errorf 生成错误时通常首字母小写。这里符合规范。
    • 但如果这是一个通过 DBus 暴露给上层(如前端 UI)的接口,建议确认上层是否能正确处理并展示该错误信息。如果有国际化(i18n)需求,可能需要返回特定的错误码而非纯字符串。
  2. 代码清理

    • SetPassword 方法中移除了大量代码,这使得代码库更加整洁,避免了死代码的存在,这是非常好的实践。

三、 代码性能

  1. 原有的轮询(Polling)逻辑被移除

    • 原代码中存在一个 for 循环,每次 time.Sleep(time.Second) 最多睡眠 10 秒来等待 ShadowInfo 就绪。这种轮询等待是非常低效的,会阻塞当前 goroutine 且浪费 CPU 时间片。
    • 评价:随着 SetPassword 的废弃,这个性能隐患也被彻底消除了,这是一个极大的性能改善。
  2. strings.ContainsAny 的性能

    • strings.ContainsAny 在底层会对字符串进行遍历匹配。对于短字符串(如用户名和密码),其性能损耗可以忽略不计。这里为了安全牺牲微乎其微的性能是绝对值得的。

四、 代码安全 🚨 (重点)

  1. ModifyPasswd 的命令注入防范(不完整)

    • 当前仅过滤了 \n\r:。虽然这堵住了通过换行符截断执行多条命令的漏洞,也堵住了破坏 username:password 格式的冒号漏洞,但这不足以完全防止命令注入
    • 风险场景:如果 pwdCmdModify(通常是 chpasswd 或类似命令)通过 Shell 执行,或者输入被传递给支持转义或特殊展开的工具,密码中包含的 `$"! 等 Shell 元字符依然可能引发命令注入或意外行为。
    • 改进建议
      • 确保不通过 Shell 执行:请确认 exec.Command(pwdCmdModify, "-e") 的底层实现没有结合 sh -c 运行。Go 的 exec.Command 默认不通过 Shell,这是安全的,但需防范被调用的二进制程序自身解析特殊字符。
      • 使用更严格的白名单校验:对于 username,应该只允许字母、数字、下划线和连字符。对于 words(密码),虽然允许的字符较多,但仍建议过滤掉所有控制字符(ASCII < 32)。
      • 代码修改建议
        // 过滤所有控制字符,而不仅仅是换行符
        if strings.ContainsAny(words, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f") || strings.ContainsAny(username, "\n\r:") {
            return errInvalidParam
        }
        // 或者对 username 使用正则白名单
        if !regexp.MustCompile(`^[a-zA-Z0-9_-]+$`).MatchString(username) {
            return errInvalidParam
        }
  2. 日志泄露敏感信息

    • 原代码中 logger.Warning("DoAction: modify password failed:", err) 被移除了。这是好事,因为在日志中记录密码相关的错误(有时会包含上下文密码)是非常危险的。新代码中 SetPassword 直接返回错误,不再记录敏感日志,提升了安全性。

总结

这个 Diff 整体方向正确:废弃了存在性能隐患(轮询)的旧接口,并在底层函数增加了安全校验。

必须修改项

  • ModifyPasswd 中的安全校验不够严谨,建议将 username 改为白名单校验(只允许合法字符),并将 words 的校验扩大到所有控制字符(ASCII < 32),而不仅仅是换行符。

建议修改项

  • SetPassword 返回错误前增加一行 logger.Warning,以便于后续追踪谁还在调用这个废弃接口。

Comment thread accounts1/users/prop.go
Comment on lines +165 to +167
if strings.ContainsAny(words, "\n\r") || strings.ContainsAny(username, "\n\r:") {
return errInvalidParam
}
Copy link
Copy Markdown
Member

@UTsweetyfish UTsweetyfish May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hashed passphrases are always entirely printable ASCII, and do not contain any whitespace or the characters ‘:’, ‘;’, ‘*’, ‘!’, or ‘\’. (These characters are used as delimiters and special markers in the passwd(5) and shadow(5) files.)

这里说的就是哈希后的密码不应包含冒号等

https://manpages.debian.org/unstable/libcrypt-dev/crypt.5.en.html

Copy link
Copy Markdown
Member

@UTsweetyfish UTsweetyfish May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

使用更严格的白名单校验:对于 username,应该只允许字母、数字、下划线和连字符。对于 words(密码),虽然允许的字符较多,但仍建议过滤掉所有控制字符(ASCII < 32)。

我也建议用白名单校验,
先只允许 printable ASCII,再禁掉 :;*!\

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

words是加密后的密码,\n已经能防注入了,先保证不影响原有功能

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: caixr23, mhduiy

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@caixr23 caixr23 merged commit 9befeaa into linuxdeepin:master May 22, 2026
18 checks passed
@UTsweetyfish
Copy link
Copy Markdown
Member

dde-control-center 创建新用户还在用 SetPassword 这个接口,现在创建出的新用户是没有密码的

@UTsweetyfish
Copy link
Copy Markdown
Member

UTsweetyfish commented May 25, 2026

# grep user /etc/shadow
user:!:20598:0:99999:7:::

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants