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..3584ecd 100644 --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -197,6 +197,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[4]; +#endif #if (NGX_FREEBSD) ngx_debug_init(); @@ -222,6 +225,13 @@ main(int argc, char *const *argv) ngx_ssl_init(log); #endif +#if (NGX_HAVE_CAPABILITIES) + if (ngx_capabilities_prepare(log) == NGX_ERROR) { + return NGX_ERROR; + } +#endif + + /* init_cycle->log is required for signal handlers and ngx_getopt() */ ngx_memzero(&init_cycle, sizeof(ngx_cycle_t)); @@ -322,6 +332,22 @@ 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()) { + if (ngx_switch_user(cycle) != NGX_OK) { + return 1; + } + + cap_list[0] = CAP_DAC_OVERRIDE; + cap_list[1] = CAP_DAC_READ_SEARCH; + cap_list[2] = CAP_NET_BIND_SERVICE; + cap_list[3] = CAP_CHOWN; + if (ngx_set_capability(log, 4, cap_list) != NGX_OK) { + return 1; + } + } +#endif + ngx_process = ccf->master ? NGX_PROCESS_MASTER : NGX_PROCESS_SINGLE; #if (NGX_WIN32) 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_ */