登录
注册
开源
企业版
高校版
搜索
帮助中心
使用条款
关于我们
开源
企业版
高校版
私有云
模力方舟
AI 队友
登录
注册
轻量养虾,开箱即用!低 Token + 稳定算力,Gitee & 模力方舟联合出品的 PocketClaw 正式开售!点击了解详情
代码拉取完成,页面将自动刷新
仓库状态说明
捐赠
捐赠前请先登录
取消
前往登录
扫描微信二维码支付
取消
支付完成
支付提示
将跳转至支付宝完成支付
确定
取消
Watch
不关注
关注所有动态
仅关注版本发行动态
关注但不提醒动态
7
Star
0
Fork
47
src-openEuler
/
sssd
关闭
代码
Issues
0
Pull Requests
0
Wiki
统计
流水线
服务
JavaDoc
PHPDoc
质量分析
Jenkins for Gitee
腾讯云托管
腾讯云 Serverless
悬镜安全
阿里云 SAE
Codeblitz
SBOM
开发画像分析
我知道了,不再自动展开
更新失败,请稍后重试!
移除标识
内容风险标识
本任务被
标识为内容中包含有代码安全 Bug 、隐私泄露等敏感信息,仓库外成员不可访问
sss_nss_mc_destroy_ctx()函数可能会关闭daemon进程打开的文件
已完成
#I891RW
缺陷
jixiang
创建于
2023-10-18 15:23
在libnss_sss.so库中使用全局变量保存打开/var/lib/sss/mc/passwd文件的文件描述符,在daemon进程中,daemonlize过程会关闭除了0、1、2外的其他文件,从而关闭了libnss_sss.so库缓存的文件描述符,后续libnss_sss.so库再关闭缓存的文件描述符时,对应的文件描述符可能已经是进程打开的其他文件,导致libnss_sss.so关闭了进程打开的其他文件。 一、daemon程序启动过程如下: 1.进程A调用getpwuid函数,然后进行daemonlize,close(3~63),fork进程B; 2.进程B创建tcp套接字,然后fork进程C;进程B监控进程C的状态,进程C异常退出后,再fork一个进程C; 3.进程C启动后首先调用getpwuid函数,然后监听从B进程继承来的tcp套接字; 二、出问题的流程如下: 1、启动sssd服务,然后停止sssd服务(此时创建了/var/lib/sss/mc/passwd文件); 2、启动测试用例的; 3、使用useradd命令添加用户; 4、kill掉进程C,B进程会重新fork进程C; 5、新fork的进程C,从B进程继承的tcp套接字被关闭; 三、daemon例程代码: ``` #define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <pwd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> static int enter_daemon(void) { int pid = fork(); if(pid < 0) { fprintf(stderr, "fork failure:%s\n", strerror(errno)); return pid; } else if(pid == 0) { //the chile process close all file descriptors for(int fd = 3; fd <= 32; ++fd) { close(fd); } setgid(getpid()); return 0; } else { //parent process exits fprintf(stdout, "original process %d goto exiting...\n", getpid()); exit(EXIT_SUCCESS); } return 0; } static int tcp_socket_init(void) { int ret; int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) { fprintf(stderr, "socket failure:%s\n", strerror(errno)); return fd; } struct sockaddr_in listen_addr = { .sin_family = AF_INET, .sin_port = ntohs(6666), .sin_addr = { .s_addr = 0, }, }; ret = bind(fd, (struct sockaddr*)&listen_addr, sizeof(listen_addr)); if(ret < 0) { fprintf(stderr, "bind failure:%s\n", strerror(errno)); close(fd); return ret; } ret = listen(fd, 1024); if(ret < 0) { fprintf(stderr, "listen failure:%s\n", strerror(errno)); close(fd); return ret; } return fd; } void loop() { while(1) {} } int main(int argc, char *argv[]) { //libnss_sss.so will open /var/lib/sss/mc/passwd and will occupy a fd when calling getpwnam() getpwnam("any"); if(enter_daemon() < 0) { return -1; } printf("parent process is: %d\n", getpid()); //create a tcp socket int net_fd = tcp_socket_init(); while(1) { int pid = fork(); if(pid < 0) { fprintf(stderr, "fork failure:%s\n", strerror(errno)); return 0; } else if(pid == 0) { //child process call getpwnam() printf("child process is: %d\n", getpid()); getpwnam("any"); loop(); } else { //parent process monitors child process int child_state; pid_t child = waitpid(pid, &child_state, 0); if(child < 0) { fprintf(stderr, "waitpid failure:%s\n", strerror(errno)); } else { fprintf(stdout, "Test finish,%d quit...\n", pid); } } } return 0; } ``` 四、问题分析 1。daemon启动过程中,A进程调用getpwuid,触发libnss_sss.so库打开/var/lib/sss/mc/passwd文件,并缓存了打开的文件描述符; 2.随后A进程daemonlize过程,关闭了libnss_sss.so打开的/var/lib/sss/mc/passwd文件,但此时libnss_sss.so还持有对应的文件描述符; 3.useradd命令导致libnss_sss.so库缓存的passwd信息失效; 4.C进程异常重启后,先调用getpwuid函数,导致libnss_sss.so关闭缓存的文件描述符,但是此时对应的文件描述符是从B进程继承的tcp套接字;所以导致libnss_sss.so关闭了C进程的tcp套接字;
在libnss_sss.so库中使用全局变量保存打开/var/lib/sss/mc/passwd文件的文件描述符,在daemon进程中,daemonlize过程会关闭除了0、1、2外的其他文件,从而关闭了libnss_sss.so库缓存的文件描述符,后续libnss_sss.so库再关闭缓存的文件描述符时,对应的文件描述符可能已经是进程打开的其他文件,导致libnss_sss.so关闭了进程打开的其他文件。 一、daemon程序启动过程如下: 1.进程A调用getpwuid函数,然后进行daemonlize,close(3~63),fork进程B; 2.进程B创建tcp套接字,然后fork进程C;进程B监控进程C的状态,进程C异常退出后,再fork一个进程C; 3.进程C启动后首先调用getpwuid函数,然后监听从B进程继承来的tcp套接字; 二、出问题的流程如下: 1、启动sssd服务,然后停止sssd服务(此时创建了/var/lib/sss/mc/passwd文件); 2、启动测试用例的; 3、使用useradd命令添加用户; 4、kill掉进程C,B进程会重新fork进程C; 5、新fork的进程C,从B进程继承的tcp套接字被关闭; 三、daemon例程代码: ``` #define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <pwd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> static int enter_daemon(void) { int pid = fork(); if(pid < 0) { fprintf(stderr, "fork failure:%s\n", strerror(errno)); return pid; } else if(pid == 0) { //the chile process close all file descriptors for(int fd = 3; fd <= 32; ++fd) { close(fd); } setgid(getpid()); return 0; } else { //parent process exits fprintf(stdout, "original process %d goto exiting...\n", getpid()); exit(EXIT_SUCCESS); } return 0; } static int tcp_socket_init(void) { int ret; int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) { fprintf(stderr, "socket failure:%s\n", strerror(errno)); return fd; } struct sockaddr_in listen_addr = { .sin_family = AF_INET, .sin_port = ntohs(6666), .sin_addr = { .s_addr = 0, }, }; ret = bind(fd, (struct sockaddr*)&listen_addr, sizeof(listen_addr)); if(ret < 0) { fprintf(stderr, "bind failure:%s\n", strerror(errno)); close(fd); return ret; } ret = listen(fd, 1024); if(ret < 0) { fprintf(stderr, "listen failure:%s\n", strerror(errno)); close(fd); return ret; } return fd; } void loop() { while(1) {} } int main(int argc, char *argv[]) { //libnss_sss.so will open /var/lib/sss/mc/passwd and will occupy a fd when calling getpwnam() getpwnam("any"); if(enter_daemon() < 0) { return -1; } printf("parent process is: %d\n", getpid()); //create a tcp socket int net_fd = tcp_socket_init(); while(1) { int pid = fork(); if(pid < 0) { fprintf(stderr, "fork failure:%s\n", strerror(errno)); return 0; } else if(pid == 0) { //child process call getpwnam() printf("child process is: %d\n", getpid()); getpwnam("any"); loop(); } else { //parent process monitors child process int child_state; pid_t child = waitpid(pid, &child_state, 0); if(child < 0) { fprintf(stderr, "waitpid failure:%s\n", strerror(errno)); } else { fprintf(stdout, "Test finish,%d quit...\n", pid); } } } return 0; } ``` 四、问题分析 1。daemon启动过程中,A进程调用getpwuid,触发libnss_sss.so库打开/var/lib/sss/mc/passwd文件,并缓存了打开的文件描述符; 2.随后A进程daemonlize过程,关闭了libnss_sss.so打开的/var/lib/sss/mc/passwd文件,但此时libnss_sss.so还持有对应的文件描述符; 3.useradd命令导致libnss_sss.so库缓存的passwd信息失效; 4.C进程异常重启后,先调用getpwuid函数,导致libnss_sss.so关闭缓存的文件描述符,但是此时对应的文件描述符是从B进程继承的tcp套接字;所以导致libnss_sss.so关闭了C进程的tcp套接字;
评论 (
3
)
登录
后才可以发表评论
状态
已完成
待办的
已挂起
修复中
已确认
已完成
已验收
已取消
负责人
未设置
hzero1996
hzero1996
负责人
协作者
+负责人
+协作者
标签
sig/Base-service
未设置
项目
未立项任务
未立项任务
里程碑
未关联里程碑
未关联里程碑
Pull Requests
未关联
未关联
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
未关联
分支 (
-
)
标签 (
-
)
开始日期   -   截止日期
-
置顶选项
不置顶
置顶等级:高
置顶等级:中
置顶等级:低
优先级
不指定
严重
主要
次要
不重要
预计工期
(小时)
参与者(3)
1
https://gitee.com/src-openeuler/sssd.git
git@gitee.com:src-openeuler/sssd.git
src-openeuler
sssd
sssd
点此查找更多帮助
搜索帮助
Git 命令在线学习
如何在 Gitee 导入 GitHub 仓库
Git 仓库基础操作
企业版和社区版功能对比
SSH 公钥设置
如何处理代码冲突
仓库体积过大,如何减小?
如何找回被删除的仓库数据
Gitee 产品配额说明
GitHub仓库快速导入Gitee及同步更新
什么是 Release(发行版)
将 PHP 项目自动发布到 packagist.org
评论
仓库举报
回到顶部
登录提示
该操作需登录 Gitee 帐号,请先登录后再操作。
立即登录
没有帐号,去注册