ngx_open_file_wrapper

ngx_open_file_wrapper 1 定义ngx_open_file_wrapper 函数 定义在 ./nginx-1.24.0/src/core/ngx_open_file_cache.cstaticngx_fd_tngx_open_file_wrapper(ngx_str_t*name,ngx_open_file_info_t*of,ngx_int_tmode,ngx_int_tcreate,ngx_int_taccess,ngx_log_t*log){ngx_fd_tfd;#if!(NGX_HAVE_OPENAT)fdngx_open_file(name-data,mode,create,access);if(fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_open_file_n;returnNGX_INVALID_FILE;}returnfd;#elseu_char*p,*cp,*end;ngx_fd_tat_fd;ngx_str_tat_name;if(of-disable_symlinksNGX_DISABLE_SYMLINKS_OFF){fdngx_open_file(name-data,mode,create,access);if(fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_open_file_n;returnNGX_INVALID_FILE;}returnfd;}pname-data;endpname-len;at_name*name;if(of-disable_symlinks_from){cppof-disable_symlinks_from;*cp\0;at_fdngx_open_file(p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0);*cp/;if(at_fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_open_file_n;returnNGX_INVALID_FILE;}at_name.lenof-disable_symlinks_from;pcp1;}elseif(*p/){at_fdngx_open_file(/,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0);if(at_fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_openat_file_n;returnNGX_INVALID_FILE;}at_name.len1;p;}else{at_fdNGX_AT_FDCWD;}for(;;){cpngx_strlchr(p,end,/);if(cpNULL){break;}if(cpp){p;continue;}*cp\0;if(of-disable_symlinksNGX_DISABLE_SYMLINKS_NOTOWNER){fdngx_openat_file_owner(at_fd,p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0,log);}else{fdngx_openat_file(at_fd,p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,NGX_FILE_OPEN,0);}*cp/;if(fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_openat_file_n;gotofailed;}if(at_fd!NGX_AT_FDCWDngx_close_file(at_fd)NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,ngx_close_file_n \%V\ failed,at_name);}pcp1;at_fdfd;at_name.lencp-at_name.data;}if(pend){/* * If pathname ends with a trailing slash, assume the last path * component is a directory and reopen it with requested flags; * if not, fail with ENOTDIR as per POSIX. * * We cannot rely on O_DIRECTORY in the loop above to check * that the last path component is a directory because * O_DIRECTORY doesnt work on FreeBSD 8. Fortunately, by * reopening a directory, we dont depend on it at all. */fdngx_openat_file(at_fd,.,mode,create,access);gotodone;}if(of-disable_symlinksNGX_DISABLE_SYMLINKS_NOTOWNER!(create(NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE))){fdngx_openat_file_owner(at_fd,p,mode,create,access,log);}else{fdngx_openat_file(at_fd,p,mode|NGX_FILE_NOFOLLOW,create,access);}done:if(fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_openat_file_n;}failed:if(at_fd!NGX_AT_FDCWDngx_close_file(at_fd)NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,ngx_close_file_n \%V\ failed,at_name);}returnfd;#endif}ngx_open_file_wrapper 函数是 Nginx 对文件打开操作的统一安全封装。 它根据 disable_symlinks 指令的配置在不限制符号链接时直接调用 open 在需要限制时通过逐级解析路径、使用 openat 并设置 O_NOFOLLOW 等标志 确保不会跟随不符合安全策略的符号链接从而防止路径穿越攻击。 同时处理跨平台差异并提供统一的错误报告。2 详解1 函数签名staticngx_fd_tngx_open_file_wrapper(ngx_str_t*name,ngx_open_file_info_t*of,ngx_int_tmode,ngx_int_tcreate,ngx_int_taccess,ngx_log_t*log)返回值 成功返回有效 FD 失败返回 NGX_INVALID_FILE参数1 ngx_str_t *name 要打开的文件的完整路径参数2 ngx_open_file_info_t *of 指向 ngx_open_file_info_t 结构体的指针 承载输入控制参数和输出结果参数3 ngx_int_t mode 表示打开文件的访问模式和标志参数4 ngx_int_t create 当文件不存在时的创建行为以及是否截断等参数5 ngx_int_t access 在创建文件时使用的访问权限参数6 ngx_log_t *log 日志对象2 逻辑流程1 不支持openat 2 支持openat 1 允许符号链接 2 限制符号链接{ngx_fd_tfd;声明文件描述符变量 fd 用于临时存储打开操作的结果。1 不支持 openat#if!(NGX_HAVE_OPENAT)fdngx_open_file(name-data,mode,create,access);if(fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_open_file_n;returnNGX_INVALID_FILE;}returnfd;2 支持openat#elseu_char*p,*cp,*end;ngx_fd_tat_fd;ngx_str_tat_name;声明局部变量if(of-disable_symlinksNGX_DISABLE_SYMLINKS_OFF){fdngx_open_file(name-data,mode,create,access);if(fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_open_file_n;returnNGX_INVALID_FILE;}returnfd;}完全允许符号链接 当配置为不限制符号链接时直接使用普通 open。 失败 记录错误 返回 无效文件描述符 成功 返回 文件描述符限制符号链接pname-data;endpname-len;at_name*name;if(of-disable_symlinks_from){cppof-disable_symlinks_from;*cp\0;at_fdngx_open_file(p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0);*cp/;if(at_fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_open_file_n;returnNGX_INVALID_FILE;}at_name.lenof-disable_symlinks_from;pcp1;}elseif(*p/){at_fdngx_open_file(/,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0);if(at_fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_openat_file_n;returnNGX_INVALID_FILE;}at_name.len1;p;}else{at_fdNGX_AT_FDCWD;}for(;;){cpngx_strlchr(p,end,/);if(cpNULL){break;}if(cpp){p;continue;}*cp\0;if(of-disable_symlinksNGX_DISABLE_SYMLINKS_NOTOWNER){fdngx_openat_file_owner(at_fd,p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0,log);}else{fdngx_openat_file(at_fd,p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,NGX_FILE_OPEN,0);}*cp/;if(fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_openat_file_n;gotofailed;}if(at_fd!NGX_AT_FDCWDngx_close_file(at_fd)NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,ngx_close_file_n \%V\ failed,at_name);}pcp1;at_fdfd;at_name.lencp-at_name.data;}if(pend){/* * If pathname ends with a trailing slash, assume the last path * component is a directory and reopen it with requested flags; * if not, fail with ENOTDIR as per POSIX. * * We cannot rely on O_DIRECTORY in the loop above to check * that the last path component is a directory because * O_DIRECTORY doesnt work on FreeBSD 8. Fortunately, by * reopening a directory, we dont depend on it at all. */fdngx_openat_file(at_fd,.,mode,create,access);gotodone;}if(of-disable_symlinksNGX_DISABLE_SYMLINKS_NOTOWNER!(create(NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE))){fdngx_openat_file_owner(at_fd,p,mode,create,access,log);}else{fdngx_openat_file(at_fd,p,mode|NGX_FILE_NOFOLLOW,create,access);}done:if(fdNGX_INVALID_FILE){of-errngx_errno;of-failedngx_openat_file_n;}failed:if(at_fd!NGX_AT_FDCWDngx_close_file(at_fd)NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,ngx_close_file_n \%V\ failed,at_name);}returnfd;#endif}