You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用useradd --password传入加密密码后/etc/shadow存储异常

问题:useradd传入带斜杠的加密密码后,/etc/shadow内容被异常截断

问题详情

  • 用C++程序调用useradd命令,通过--password选项传入crypt_rn()生成的加密密码(明文密码未出现在命令行),加密后的字符串格式符合预期:$<algorithm>$<salt>$<encrypted-text>,示例为$6$abcdefg$xxxxx/yyyyyy
  • 执行useradd后,/etc/shadow中对应用户的密码字段被异常截断,变为bcdefg/yyyyyy,丢失了算法标识段、salt的第一个字符,以及salt末尾$到第一个/之间的内容,导致用户无法登录
  • 手动将加密字符串复制到/etc/shadow中,用户可正常登录,说明加密流程无问题;调试打印的完整useradd命令与crypt返回值一致,但直接在命令行执行该命令也会出现相同异常
  • 环境为Yocto 3.4,已排除SHA-512算法支持问题(通过命令行添加用户后用passwd设置密码,系统默认使用SHA-512可正常工作)

原因分析

核心问题是shell对命令参数中的特殊字符进行了解析:加密字符串包含$符号,shell会将$后的内容视为变量名尝试解析,若对应变量不存在则替换为空值,最终传递给useradd的参数已经是被截断/篡改后的内容,而非完整的加密密码。

解决方案

方案1:绕过shell直接调用exec系列函数

避免使用system()(会启动shell解析命令),改用execvp()这类直接执行程序的函数,传递参数数组,彻底规避shell对特殊字符的解析。

示例代码片段:

#include <unistd.h>
#include <cstdlib>
#include <cerrno>

int main() {
    const char* encrypted_pwd = "$6$abcdefg$xxxxx/yyyyyy";
    // 参数数组,最后一个元素必须为nullptr
    char* const argv[] = {
        const_cast<char*>("useradd"),
        const_cast<char*>("--password"),
        const_cast<char*>(encrypted_pwd),
        const_cast<char*>("new_test_user"),
        nullptr
    };

    execvp(argv[0], argv);
    // 若execvp返回,说明执行失败
    perror("execvp failed to run useradd");
    return EXIT_FAILURE;
}

方案2:对特殊字符进行shell转义(若必须使用shell)

如果一定要用system(),需将加密密码用单引号包裹(单引号内的内容不会被shell解析),确保完整传递给useradd。

示例代码片段:

#include <string>
#include <cstdlib>

int main() {
    std::string encrypted_pwd = "$6$abcdefg$xxxxx/yyyyyy";
    // 用单引号包裹密码,避免shell解析$等特殊字符
    std::string cmd = "useradd --password '" + encrypted_pwd + "' new_test_user";
    int ret = system(cmd.c_str());
    return ret;
}

注:crypt生成的加密密码不会包含单引号,因此无需额外处理单引号转义场景。

验证

执行修改后的程序后,查看/etc/shadow中对应用户的密码字段,应与crypt返回的加密字符串完全一致,此时用户可正常登录。

内容的提问来源于stack exchange,提问作者M. Krajnak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 04:05:07