使用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
相关产品推荐
相关产品推荐

