Change user IDs to assign additional features

My user program runs with non-root privileges with user IDs: uid: 1000 euid: 0 , in which, after fork () in the child process, execv () is called to start the SSH client service. Since I run the program as an unprivileged user, when trying to bind a socket to a device, the Linux kernel performs all permissions checks, causing the sock_setbindtodevice() routine to sock_setbindtodevice() when checking the CAP_NET_RAW capability, as shown below.

The solution I was thinking about is to first get root privileges in the child process, perform a privileged operation, for example, install the necessary features and return to non-root.

The question is what is needed to bring it to non-root, because when the ssh command is executed, I want the generated DSA/RSA keys to be stored in $HOME/.ssh/known_hosts , and not root/.ssh/known_hosts .

The following is a snippet of code:

 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; /* Get the 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 the capabilities */ if (capset(cap_header, cap_data) < 0) { printf("failed capset error:%s", strerror(errno)); } /* Drop back 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 The authenticity of host '101.1.1.101 (101.1.1.101)' can't be established. DSA key fingerprint is 0c:61:df:01:93:74:1f:5f:49:34:f4:4e:06:e8:d7:5f. Are you sure you want to continue connecting (yes/no)? ^C [local]linux# 

The results show that ssh is successful, but the program runs as root at this point, which is incorrect. How can I go back to UID as uid: 1000 euid: 0 so that the ssh keys are stored in the correct directory.

Comment and suggest my solution, will it really solve the problem?

+4
source share
1 answer

If your program runs with an effective user ID, then you have root privileges.


Linux capabilities fall into three sets: inherited, allowed, and efficient. Inheritable defines which features remain valid in exec() . Allowed determines which features are allowed for the process. Effective determines which opportunities are currently in place.

Edited to add: when a file system containing a binary file that will be exec() 'd supports the capabilities of the file system, they always affect what capabilities will be executed during execution. See Converting features during execve() to the man 7 man page features .

If you change the owner or group of processes from root to non-root, the effective set of functions is always cleared. By default, the allowed feature set is also allowed, but the caller prctl(PR_SET_KEEPCAPS, 1L) before changing the identifier tells the kernel to keep the allowed prctl(PR_SET_KEEPCAPS, 1L) set intact.

Therefore, in order to be able to CAP_NET_RAW , your program must have it in both allowed and effective sets. If you want CAP_NET_RAW remain valid over exec() , it must be included in all three feature sets.

Edited to add: if file capabilities are supported for the exec() target, file capabilities should also contain these capabilities in legacy and efficient suites. (Only the inclusion of capabilities in inherited and effective sets does not provide an opportunity, since it is not in the allowed set in the file capabilities, but it is enough to allow the transfer of the opportunity by the executor execee, if the executor has the opportunity).


You can use the setcap command to provide certain features to a binary file. (Most Linux file systems currently support these file capabilities.) This should not be privileged or setuid. Do not forget to add the necessary features for both allowed and effective sets.

Edited to add a few examples:

Grant CAP_NET_RAW to /usr/bin/myprog (which should NOT be setuid or setgid root):

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

By default, do not provide CAP_NET_RAW /usr/bin/myprog , but if the executor has the opportunity (both inherited and allowed sets), keep the possibility (inherited and allowed sets and activating them in an effective set):

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

If in any case your program should be installed as root, you can use, for example,

 #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, probably not setuid root */ /* Add need_caps to 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 over an identity change */ if (prctl(PR_SET_KEEPCAPS, 1L)) return 1; /* Fatal error */ /* Return to original, real-user identity */ if (setresuid(real, real, real)) return 1; /* Fatal error */ /* Because the identity changed, we need to * re-install the effective set. */ if (cap_set_proc(caps)) return 1; /* Fatal error */ /* Capability set is no longer needed. */ cap_free(caps); /* You now have the CAP_NET_RAW capability. * It will be retained over fork() and exec(). */ return 0; } 
+5
source

Source: https://habr.com/ru/post/1492264/


All Articles