c - Changing user IDs for assigning additional capabilities -


problem statement: custom program executes non-root privileges user id's as: uid: 1000 euid: 0 in after fork(), in child process execv() invoked run ssh client service. since being non-root user, while socket bind device linux kernel performs permission checks, causing failure in subroutine sock_setbindtodevice() while checking cap_net_raw capability shown below.

solution thinking in child process first root privilege , privilege operation such setting required capabilities , drop non-root.

a possible question here be, need drop down non-root: when ssh command executed, generated dsa/rsa keys need stored in $home/.ssh/known_hosts rather root/.ssh/known_hosts.

please find code snippet below,

void global_exec_func (const char *proc_name, const char *proc_path, char **arg_list) {     pid_t   pid;     int     status, euid;      struct __user_cap_header_struct cap_header_data;     cap_user_header_t cap_header = &cap_header_data;     struct __user_cap_data_struct cap_data_data;     cap_user_data_t cap_data = &cap_data_data;      pid = fork();     if (pid < 0) {        printf("%% can't fork process %s", proc_name);        return;     }         /*        * child process.      */     if (pid == 0) {          euid = geteuid();          /* storing euid */           /*gaining root privileges */          if (setuid(0) < 0) {                     printf("setuid(0) failed");          }          printf("after setting: getuid: %d geteuid: %d\n", getuid(),                   geteuid());           cap_header->pid = 0;          cap_header->version = _linux_capability_version;           /* capabilities */          if(capget(cap_header, cap_data)  < 0) {              printf("failed capget error:%s", strerror(errno));          }          cap_data->effective = (1 << cap_net_raw);    /* set bit 13 */          cap_data->inheritable = 0;           /* set capabilities */          if (capset(cap_header, cap_data) < 0) {              printf("failed capset error:%s", strerror(errno));          }           /* drop privileges */          if (seteuid(euid) < 0) {                  printf("seteuid(euid) failed");          }          printf("after drop: getuid: %d geteuid: %d\n", getuid(), geteuid());           prctl(pr_set_keepcaps, 1);          execv(proc_path, arg_list);          exit(1);      }      /*       * parent process code follows       */   result:    [local]linux#ssh 101.1.1.101  after setting: getuid: 0 geteuid: 0  after drop: getuid: 0 geteuid: 0  authenticity of host '101.1.1.101 (101.1.1.101)' can't established.  dsa key fingerprint 0c:61:df:01:93:74:1f:5f:49:34:f4:4e:06:e8:d7:5f.  sure want continue connecting (yes/no)? ^c  [local]linux# 

results tells ssh successful root yet, incorrect. how can uid's uid: 1000 euid: 0 ssh keys store in right directory.

kindly comment , suggest on solution, solves problem.

thanks in advance!

if program executes effective user id root, have root privileges.


in linux, capabilities divided 3 sets: inheritable, permitted, , effective. inheritable defines capabilities stay permitted across exec(). permitted defines capabilities permitted process. effective defines capabilities in effect.

edited add: when filesystem containing binary exec()'d support filesystem capabilities, these always affect capabilities executed process have. see transformation of capabilities during execve() in man 7 capabilities man page.

when changing owner or group of process root non-root, effective capability set cleared. default, permitted capability set cleared, calling prctl(pr_set_keepcaps, 1l) before identity change tells kernel keep permitted set intact.

therefore, have cap_net_raw capability, program has have in both permitted , effective sets. if wish cap_net_raw remain in effect on exec(), must included in 3 capability sets.

edited add: if file capabilities supported target of exec(), file capabilities must contain capabilities in inherited , effective sets. (only including capability in inherited , effective sets not grant capability, since it's not in permitted set in file capabilities; but, enough allow passing capability executor execee, if executor has capability).


you can use setcap command grant specific capabilities binary. (most linux filesystems nowadays support these file capabilities.) not need privileged, or setuid. remember add desired capabilities both permitted , effective sets.

edited add examples:

grant cap_net_raw /usr/bin/myprog (which must not setuid or setgid root):

sudo setcap 'cap_net_raw=pe' /usr/bin/myprog 

by default, not grant cap_net_raw /usr/bin/myprog, if executor has capability (in both inheritable , permitted sets), retain capability (in inheritable , permitted sets, , activating in effective set):

sudo setcap 'cap_net_raw=ie' /usr/bin/myprog 

if program has setuid root anyway, can use e.g.

#define  _gnu_source #include <unistd.h> #include <sys/types.h> #include <sys/capability.h> #include <sys/prctl.h>  #define   need_caps 1 static const cap_value_t need_caps[need_caps] = { cap_net_raw };  int main(void) {     uid_t  real = getuid();     cap_t  caps;      /* elevate privileges */     if (setresuid(0, 0, 0))         return 1; /* fatal error, not setuid root */      /* add need_caps current capabilities. */     caps = cap_get_proc();     if (cap_set_flag(caps, cap_permitted,   need_caps, need_caps, cap_set) ||         cap_set_flag(caps, cap_effective,   need_caps, need_caps, cap_set) ||         cap_set_flag(caps, cap_inheritable, need_caps, need_caps, cap_set))         return 1; /* fatal error */      /* update capabilities */     if (cap_set_proc(caps))         return 1; /* fatal error */      /* retain capabilities on identity change */     if (prctl(pr_set_keepcaps, 1l))         return 1; /* fatal error */      /* return original, real-user identity */      if (setresuid(real, real, real))         return 1; /* fatal error */      /* because identity changed, need      * re-install effective set. */     if (cap_set_proc(caps))         return 1; /* fatal error */      /* capability set no longer needed. */     cap_free(caps);      /* have cap_net_raw capability.      * retained on fork() , exec().     */      return 0; } 

Comments