diff --git a/auto/lib/capabilities/conf b/auto/lib/capabilities/conf new file mode 100644 index 0000000..a3207d4 --- /dev/null +++ b/auto/lib/capabilities/conf @@ -0,0 +1,19 @@ + +# Copyright (C) Kirill A. Korinskiy + + +ngx_feature="capabilities" +ngx_feature_name="NGX_HAVE_CAPABILITIES" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs="-lcap" +ngx_feature_test="cap_t caps; + caps = cap_get_proc(); + return -1;" +. auto/feature + + +if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS $ngx_feature_libs" +fi diff --git a/auto/os/linux b/auto/os/linux index 3c2c841..e4e036c 100644 --- a/auto/os/linux +++ b/auto/os/linux @@ -121,3 +121,6 @@ ngx_feature_libs= ngx_feature_test="long mask = 0; sched_setaffinity(0, 32, (cpu_set_t *) &mask)" . auto/feature + +# capabilities +. auto/lib/capabilities/conf diff --git a/src/core/nginx.c b/src/core/nginx.c index e6ebae5..e2b0306 100644 --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -153,6 +153,17 @@ static ngx_command_t ngx_core_commands[] = { #endif +#if (NGX_HAVE_CAPABILITIES) + + { ngx_string("use_bind_capability"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + 0, + offsetof(ngx_core_conf_t, use_bind_capability), + NULL }, + +#endif + ngx_null_command }; @@ -197,6 +208,9 @@ main(int argc, char *const *argv) ngx_log_t *log; ngx_cycle_t *cycle, init_cycle; ngx_core_conf_t *ccf; +#if (NGX_HAVE_CAPABILITIES) + cap_value_t cap_list[1]; +#endif #if (NGX_FREEBSD) ngx_debug_init(); @@ -322,6 +336,23 @@ main(int argc, char *const *argv) ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); +#if (NGX_HAVE_CAPABILITIES) + if (!geteuid() && ccf->use_bind_capability) { + if (ngx_capabilities_prepare(log) != NGX_OK) { + return NGX_OK; + } + + if (ngx_switch_user(cycle) != NGX_OK) { + return 1; + } + + cap_list[0] = CAP_NET_BIND_SERVICE; + if (ngx_set_capability(log, 1, cap_list) != NGX_OK) { + return 1; + } + } +#endif + ngx_process = ccf->master ? NGX_PROCESS_MASTER : NGX_PROCESS_SINGLE; #if (NGX_WIN32) @@ -761,6 +792,10 @@ ngx_core_module_create_conf(ngx_cycle_t *cycle) ccf->thread_stack_size = NGX_CONF_UNSET_SIZE; #endif +#if (NGX_HAVE_CAPABILITIES) + ccf->use_bind_capability = NGX_CONF_UNSET; +#endif + if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t)) != NGX_OK) { @@ -805,6 +840,10 @@ ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf) #endif +#if (NGX_HAVE_CAPABILITIES) + ngx_conf_init_value(ccf->use_bind_capability, 0); +#endif + #if !(NGX_WIN32) if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) { diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h index e42f46b..233e8c4 100644 --- a/src/core/ngx_cycle.h +++ b/src/core/ngx_cycle.h @@ -103,6 +103,10 @@ typedef struct { size_t thread_stack_size; #endif +#if (NGX_HAVE_CAPABILITIES) + ngx_flag_t use_bind_capability; +#endif + } ngx_core_conf_t; diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c index a14a03c..9ae0052 100644 --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -854,27 +854,15 @@ ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority) } #endif - if (geteuid() == 0) { - if (setgid(ccf->group) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "setgid(%d) failed", ccf->group); - /* fatal */ - exit(2); - } - - if (initgroups(ccf->username, ccf->group) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "initgroups(%s, %d) failed", - ccf->username, ccf->group); - } - - if (setuid(ccf->user) == -1) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - "setuid(%d) failed", ccf->user); - /* fatal */ - exit(2); - } +#ifdef NGX_HAVE_CAPABILITIES + if (ngx_clear_capability(cycle->log) != NGX_OK) { + exit(2); } +#else + if (ngx_switch_user(cycle) == NGX_ERROR) { + exit(2); + } +#endif #if (NGX_HAVE_SCHED_SETAFFINITY) diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c index 4bad1c3..7fdc02d 100644 --- a/src/os/unix/ngx_user.c +++ b/src/os/unix/ngx_user.c @@ -7,6 +7,10 @@ #include #include +#if (NGX_HAVE_CAPABILITIES) +#include +#endif + /* * Solaris has thread-safe crypt() @@ -106,3 +110,128 @@ ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) #endif #endif /* NGX_CRYPT */ + +ngx_int_t ngx_switch_user(ngx_cycle_t *cycle) +{ + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (setgid(ccf->group) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "setgid(%d) failed", ccf->group); + return NGX_ERROR; + } + + if (initgroups(ccf->username, ccf->group) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "initgroups(%s, %d) failed", + ccf->username, ccf->group); + return NGX_ERROR; + } + + if (setuid(ccf->user) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "setuid(%d) failed", ccf->user); + return NGX_ERROR; + } + + return NGX_OK; +} + +#if (NGX_HAVE_CAPABILITIES) + +ngx_int_t ngx_capabilities_prepare(ngx_log_t *log) +{ + if (geteuid()) { + return NGX_DONE; + } + + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "prctl(PR_SET_KEEPCAPS, 1) failed"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t ngx_set_capability(ngx_log_t *log, ngx_int_t ncap, cap_value_t *cap_list) +{ + cap_t caps; + + caps = cap_get_proc(); + if (caps == NULL) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_get_proc() failed"); + return NGX_ERROR; + } + + if (cap_set_flag(caps, CAP_EFFECTIVE, ncap, cap_list, CAP_SET) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_set_flag(CAP_EFFECTIVE) failed"); + return NGX_ERROR; + } + + if (cap_set_flag(caps, CAP_PERMITTED, ncap, cap_list, CAP_SET) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_set_flag(CAP_PERMITTED) failed"); + return NGX_ERROR; + } + + if (cap_set_flag(caps, CAP_INHERITABLE, ncap, cap_list, CAP_SET) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_set_flag(CAP_INHERITABLE) failed"); + return NGX_ERROR; + } + + if (cap_set_proc(caps) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_set_proc() failed"); + return NGX_ERROR; + } + + if (cap_free(caps) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_free() failed"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t ngx_clear_capability(ngx_log_t *log) +{ + cap_t caps; + + caps = cap_get_proc(); + if (caps == NULL) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_get_proc() failed"); + return NGX_ERROR; + } + + if (cap_clear(caps) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_clear() failed"); + return NGX_ERROR; + } + + if (cap_set_proc(caps) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_set_proc() failed"); + return NGX_ERROR; + } + + if (cap_free(caps) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "cap_free() failed"); + return NGX_ERROR; + } + + return NGX_OK; +} + +#endif diff --git a/src/os/unix/ngx_user.h b/src/os/unix/ngx_user.h index a24a66b..3fea196 100644 --- a/src/os/unix/ngx_user.h +++ b/src/os/unix/ngx_user.h @@ -11,14 +11,36 @@ #include #include +#if (NGX_HAVE_CAPABILITIES) +#include +#endif + typedef uid_t ngx_uid_t; typedef gid_t ngx_gid_t; +#if (NGX_CRYPT) + +#if (NGX_HAVE_GNU_CRYPT_R) + ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted); +#endif + +#endif /* NGX_CRYPT */ + +ngx_int_t ngx_switch_user(ngx_cycle_t *cycle); + +#if (NGX_HAVE_CAPABILITIES) + +ngx_int_t ngx_capabilities_prepare(ngx_log_t *log); + +ngx_int_t ngx_set_capability(ngx_log_t *log, ngx_int_t ncap, cap_value_t *cap_list); + +ngx_int_t ngx_clear_capability(ngx_log_t *log); +#endif #endif /* _NGX_USER_H_INCLUDED_ */