C starting a X11 session using system() vs using execl()












1















I have created a daemon-like program that will start a X11 session for a specific user under its own enviroment, after he authenticates himself.



The first approach was to use a command using system(), where I would impersonate the user and start the x11 session as follows:



std::string cmd = "echo daemonuserpwd | sudo -S su " + unixUser + " -c 'xinit -- :4' &";
system(cmd.c_str());


This works flawless, and calls the .xinitrc file located in the user's home directory, which is a necessary step as I use it to start up the required programs which I need for the purpose the application Im working in.



However, I read about the problems with system(), so I tried to go bit further and use fork to create the user enviroment, and start the session using execl(), as follows:



int child = fork();
if(child == 0)
{
struct passwd * userInfo = getpwnam(unixUser.c_str());
setgid(userInfo->pw_gid);
setuid(userInfo->pw_uid);
system("whoami");
execl("/usr/bin/xinit", "xinit", "--", ":4", (char*)0);
//system("xinit -- :4");
}


This also works, the debugging command system("whoami"); says im the right user. The X11 session is started, however, the .xinitrc file is not called when the session is started from the fork process. I also tried to execute the command with system, after setting up the user enviroment, with same result (Both options call the default xinitrc in /etc/X11/xinit/xinitrc.



Am I missing something in order the .xinitrc file to be called as well when using the fork() approach?



Disclaimer:
User authentication is performed with libpam, and the user input is sanitetized properly to prevent injections.





EDIT: The final workaround using execle as suggested by @LieRyan:



struct passwd * userInfo = getpwnam(unixUser.c_str());
char buf[0xff];
sprintf(buf, "HOME=%s", userInfo->pw_dir);
char *env = {buf, NULL};
execle("/usr/bin/sudo", "sudo", "-u", unixUser.c_str(), "xinit", "--", ":4", (char*)0, env);









share|improve this question




















  • 1





    Another thing you may want to consider, to use setuid/setgid, your program had to run as root. Consider, if this is an unnecessary risk. You may want to consider running execl("/usr/bin/sudo", "-u", unixUser, "xinit", "--", ":4", (char*)0) and have your daemon user be whitelisted in /etc/sudoers to NOPASSWD when running xinit.

    – Lie Ryan
    Nov 20 '18 at 11:00











  • @LieRyan Thank you very much! I followed both of your advices (and also found a workaround to use pam_authenticate() without root privileges), and Im able to run the daemon without root privs.

    – Nadir
    Nov 20 '18 at 15:18
















1















I have created a daemon-like program that will start a X11 session for a specific user under its own enviroment, after he authenticates himself.



The first approach was to use a command using system(), where I would impersonate the user and start the x11 session as follows:



std::string cmd = "echo daemonuserpwd | sudo -S su " + unixUser + " -c 'xinit -- :4' &";
system(cmd.c_str());


This works flawless, and calls the .xinitrc file located in the user's home directory, which is a necessary step as I use it to start up the required programs which I need for the purpose the application Im working in.



However, I read about the problems with system(), so I tried to go bit further and use fork to create the user enviroment, and start the session using execl(), as follows:



int child = fork();
if(child == 0)
{
struct passwd * userInfo = getpwnam(unixUser.c_str());
setgid(userInfo->pw_gid);
setuid(userInfo->pw_uid);
system("whoami");
execl("/usr/bin/xinit", "xinit", "--", ":4", (char*)0);
//system("xinit -- :4");
}


This also works, the debugging command system("whoami"); says im the right user. The X11 session is started, however, the .xinitrc file is not called when the session is started from the fork process. I also tried to execute the command with system, after setting up the user enviroment, with same result (Both options call the default xinitrc in /etc/X11/xinit/xinitrc.



Am I missing something in order the .xinitrc file to be called as well when using the fork() approach?



Disclaimer:
User authentication is performed with libpam, and the user input is sanitetized properly to prevent injections.





EDIT: The final workaround using execle as suggested by @LieRyan:



struct passwd * userInfo = getpwnam(unixUser.c_str());
char buf[0xff];
sprintf(buf, "HOME=%s", userInfo->pw_dir);
char *env = {buf, NULL};
execle("/usr/bin/sudo", "sudo", "-u", unixUser.c_str(), "xinit", "--", ":4", (char*)0, env);









share|improve this question




















  • 1





    Another thing you may want to consider, to use setuid/setgid, your program had to run as root. Consider, if this is an unnecessary risk. You may want to consider running execl("/usr/bin/sudo", "-u", unixUser, "xinit", "--", ":4", (char*)0) and have your daemon user be whitelisted in /etc/sudoers to NOPASSWD when running xinit.

    – Lie Ryan
    Nov 20 '18 at 11:00











  • @LieRyan Thank you very much! I followed both of your advices (and also found a workaround to use pam_authenticate() without root privileges), and Im able to run the daemon without root privs.

    – Nadir
    Nov 20 '18 at 15:18














1












1








1








I have created a daemon-like program that will start a X11 session for a specific user under its own enviroment, after he authenticates himself.



The first approach was to use a command using system(), where I would impersonate the user and start the x11 session as follows:



std::string cmd = "echo daemonuserpwd | sudo -S su " + unixUser + " -c 'xinit -- :4' &";
system(cmd.c_str());


This works flawless, and calls the .xinitrc file located in the user's home directory, which is a necessary step as I use it to start up the required programs which I need for the purpose the application Im working in.



However, I read about the problems with system(), so I tried to go bit further and use fork to create the user enviroment, and start the session using execl(), as follows:



int child = fork();
if(child == 0)
{
struct passwd * userInfo = getpwnam(unixUser.c_str());
setgid(userInfo->pw_gid);
setuid(userInfo->pw_uid);
system("whoami");
execl("/usr/bin/xinit", "xinit", "--", ":4", (char*)0);
//system("xinit -- :4");
}


This also works, the debugging command system("whoami"); says im the right user. The X11 session is started, however, the .xinitrc file is not called when the session is started from the fork process. I also tried to execute the command with system, after setting up the user enviroment, with same result (Both options call the default xinitrc in /etc/X11/xinit/xinitrc.



Am I missing something in order the .xinitrc file to be called as well when using the fork() approach?



Disclaimer:
User authentication is performed with libpam, and the user input is sanitetized properly to prevent injections.





EDIT: The final workaround using execle as suggested by @LieRyan:



struct passwd * userInfo = getpwnam(unixUser.c_str());
char buf[0xff];
sprintf(buf, "HOME=%s", userInfo->pw_dir);
char *env = {buf, NULL};
execle("/usr/bin/sudo", "sudo", "-u", unixUser.c_str(), "xinit", "--", ":4", (char*)0, env);









share|improve this question
















I have created a daemon-like program that will start a X11 session for a specific user under its own enviroment, after he authenticates himself.



The first approach was to use a command using system(), where I would impersonate the user and start the x11 session as follows:



std::string cmd = "echo daemonuserpwd | sudo -S su " + unixUser + " -c 'xinit -- :4' &";
system(cmd.c_str());


This works flawless, and calls the .xinitrc file located in the user's home directory, which is a necessary step as I use it to start up the required programs which I need for the purpose the application Im working in.



However, I read about the problems with system(), so I tried to go bit further and use fork to create the user enviroment, and start the session using execl(), as follows:



int child = fork();
if(child == 0)
{
struct passwd * userInfo = getpwnam(unixUser.c_str());
setgid(userInfo->pw_gid);
setuid(userInfo->pw_uid);
system("whoami");
execl("/usr/bin/xinit", "xinit", "--", ":4", (char*)0);
//system("xinit -- :4");
}


This also works, the debugging command system("whoami"); says im the right user. The X11 session is started, however, the .xinitrc file is not called when the session is started from the fork process. I also tried to execute the command with system, after setting up the user enviroment, with same result (Both options call the default xinitrc in /etc/X11/xinit/xinitrc.



Am I missing something in order the .xinitrc file to be called as well when using the fork() approach?



Disclaimer:
User authentication is performed with libpam, and the user input is sanitetized properly to prevent injections.





EDIT: The final workaround using execle as suggested by @LieRyan:



struct passwd * userInfo = getpwnam(unixUser.c_str());
char buf[0xff];
sprintf(buf, "HOME=%s", userInfo->pw_dir);
char *env = {buf, NULL};
execle("/usr/bin/sudo", "sudo", "-u", unixUser.c_str(), "xinit", "--", ":4", (char*)0, env);






c unix fork






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 20 '18 at 15:26







Nadir

















asked Nov 20 '18 at 9:49









NadirNadir

1,447717




1,447717








  • 1





    Another thing you may want to consider, to use setuid/setgid, your program had to run as root. Consider, if this is an unnecessary risk. You may want to consider running execl("/usr/bin/sudo", "-u", unixUser, "xinit", "--", ":4", (char*)0) and have your daemon user be whitelisted in /etc/sudoers to NOPASSWD when running xinit.

    – Lie Ryan
    Nov 20 '18 at 11:00











  • @LieRyan Thank you very much! I followed both of your advices (and also found a workaround to use pam_authenticate() without root privileges), and Im able to run the daemon without root privs.

    – Nadir
    Nov 20 '18 at 15:18














  • 1





    Another thing you may want to consider, to use setuid/setgid, your program had to run as root. Consider, if this is an unnecessary risk. You may want to consider running execl("/usr/bin/sudo", "-u", unixUser, "xinit", "--", ":4", (char*)0) and have your daemon user be whitelisted in /etc/sudoers to NOPASSWD when running xinit.

    – Lie Ryan
    Nov 20 '18 at 11:00











  • @LieRyan Thank you very much! I followed both of your advices (and also found a workaround to use pam_authenticate() without root privileges), and Im able to run the daemon without root privs.

    – Nadir
    Nov 20 '18 at 15:18








1




1





Another thing you may want to consider, to use setuid/setgid, your program had to run as root. Consider, if this is an unnecessary risk. You may want to consider running execl("/usr/bin/sudo", "-u", unixUser, "xinit", "--", ":4", (char*)0) and have your daemon user be whitelisted in /etc/sudoers to NOPASSWD when running xinit.

– Lie Ryan
Nov 20 '18 at 11:00





Another thing you may want to consider, to use setuid/setgid, your program had to run as root. Consider, if this is an unnecessary risk. You may want to consider running execl("/usr/bin/sudo", "-u", unixUser, "xinit", "--", ":4", (char*)0) and have your daemon user be whitelisted in /etc/sudoers to NOPASSWD when running xinit.

– Lie Ryan
Nov 20 '18 at 11:00













@LieRyan Thank you very much! I followed both of your advices (and also found a workaround to use pam_authenticate() without root privileges), and Im able to run the daemon without root privs.

– Nadir
Nov 20 '18 at 15:18





@LieRyan Thank you very much! I followed both of your advices (and also found a workaround to use pam_authenticate() without root privileges), and Im able to run the daemon without root privs.

– Nadir
Nov 20 '18 at 15:18












1 Answer
1






active

oldest

votes


















1














Calling setuid changes who the process belongs to, but it doesn't alter the environment variables that would be set if that user had logged in so $HOME won't be pointing at the right place to pick up the ".xinitrc" file.



The following line of code should fix that for you.



setenv("HOME",userInfo->pw_dir,1);





share|improve this answer
























  • That was it. Thank you very much

    – Nadir
    Nov 20 '18 at 10:44






  • 1





    Note that setenv changes the environment variable of the current process as well, which may have unintended side effects, if this is undesirable, you may want to use execle instead.

    – Lie Ryan
    Nov 20 '18 at 11:06






  • 2





    True, @LieRyan, but not much relevant when you've fork()ed, and / or if you're assuming (unsafely) that execl() will not fail, as the OP appears to do, or if you ensure that the process terminates in the event that execl() does fail.

    – John Bollinger
    Nov 20 '18 at 15:41













  • @JohnBollinger if you are referring to check the returned value by execl(), I left it out for the sake of simplicity.

    – Nadir
    Nov 20 '18 at 15:48











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53390266%2fc-starting-a-x11-session-using-system-vs-using-execl%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














Calling setuid changes who the process belongs to, but it doesn't alter the environment variables that would be set if that user had logged in so $HOME won't be pointing at the right place to pick up the ".xinitrc" file.



The following line of code should fix that for you.



setenv("HOME",userInfo->pw_dir,1);





share|improve this answer
























  • That was it. Thank you very much

    – Nadir
    Nov 20 '18 at 10:44






  • 1





    Note that setenv changes the environment variable of the current process as well, which may have unintended side effects, if this is undesirable, you may want to use execle instead.

    – Lie Ryan
    Nov 20 '18 at 11:06






  • 2





    True, @LieRyan, but not much relevant when you've fork()ed, and / or if you're assuming (unsafely) that execl() will not fail, as the OP appears to do, or if you ensure that the process terminates in the event that execl() does fail.

    – John Bollinger
    Nov 20 '18 at 15:41













  • @JohnBollinger if you are referring to check the returned value by execl(), I left it out for the sake of simplicity.

    – Nadir
    Nov 20 '18 at 15:48
















1














Calling setuid changes who the process belongs to, but it doesn't alter the environment variables that would be set if that user had logged in so $HOME won't be pointing at the right place to pick up the ".xinitrc" file.



The following line of code should fix that for you.



setenv("HOME",userInfo->pw_dir,1);





share|improve this answer
























  • That was it. Thank you very much

    – Nadir
    Nov 20 '18 at 10:44






  • 1





    Note that setenv changes the environment variable of the current process as well, which may have unintended side effects, if this is undesirable, you may want to use execle instead.

    – Lie Ryan
    Nov 20 '18 at 11:06






  • 2





    True, @LieRyan, but not much relevant when you've fork()ed, and / or if you're assuming (unsafely) that execl() will not fail, as the OP appears to do, or if you ensure that the process terminates in the event that execl() does fail.

    – John Bollinger
    Nov 20 '18 at 15:41













  • @JohnBollinger if you are referring to check the returned value by execl(), I left it out for the sake of simplicity.

    – Nadir
    Nov 20 '18 at 15:48














1












1








1







Calling setuid changes who the process belongs to, but it doesn't alter the environment variables that would be set if that user had logged in so $HOME won't be pointing at the right place to pick up the ".xinitrc" file.



The following line of code should fix that for you.



setenv("HOME",userInfo->pw_dir,1);





share|improve this answer













Calling setuid changes who the process belongs to, but it doesn't alter the environment variables that would be set if that user had logged in so $HOME won't be pointing at the right place to pick up the ".xinitrc" file.



The following line of code should fix that for you.



setenv("HOME",userInfo->pw_dir,1);






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 20 '18 at 10:39









Chris TurnerChris Turner

7,15011017




7,15011017













  • That was it. Thank you very much

    – Nadir
    Nov 20 '18 at 10:44






  • 1





    Note that setenv changes the environment variable of the current process as well, which may have unintended side effects, if this is undesirable, you may want to use execle instead.

    – Lie Ryan
    Nov 20 '18 at 11:06






  • 2





    True, @LieRyan, but not much relevant when you've fork()ed, and / or if you're assuming (unsafely) that execl() will not fail, as the OP appears to do, or if you ensure that the process terminates in the event that execl() does fail.

    – John Bollinger
    Nov 20 '18 at 15:41













  • @JohnBollinger if you are referring to check the returned value by execl(), I left it out for the sake of simplicity.

    – Nadir
    Nov 20 '18 at 15:48



















  • That was it. Thank you very much

    – Nadir
    Nov 20 '18 at 10:44






  • 1





    Note that setenv changes the environment variable of the current process as well, which may have unintended side effects, if this is undesirable, you may want to use execle instead.

    – Lie Ryan
    Nov 20 '18 at 11:06






  • 2





    True, @LieRyan, but not much relevant when you've fork()ed, and / or if you're assuming (unsafely) that execl() will not fail, as the OP appears to do, or if you ensure that the process terminates in the event that execl() does fail.

    – John Bollinger
    Nov 20 '18 at 15:41













  • @JohnBollinger if you are referring to check the returned value by execl(), I left it out for the sake of simplicity.

    – Nadir
    Nov 20 '18 at 15:48

















That was it. Thank you very much

– Nadir
Nov 20 '18 at 10:44





That was it. Thank you very much

– Nadir
Nov 20 '18 at 10:44




1




1





Note that setenv changes the environment variable of the current process as well, which may have unintended side effects, if this is undesirable, you may want to use execle instead.

– Lie Ryan
Nov 20 '18 at 11:06





Note that setenv changes the environment variable of the current process as well, which may have unintended side effects, if this is undesirable, you may want to use execle instead.

– Lie Ryan
Nov 20 '18 at 11:06




2




2





True, @LieRyan, but not much relevant when you've fork()ed, and / or if you're assuming (unsafely) that execl() will not fail, as the OP appears to do, or if you ensure that the process terminates in the event that execl() does fail.

– John Bollinger
Nov 20 '18 at 15:41







True, @LieRyan, but not much relevant when you've fork()ed, and / or if you're assuming (unsafely) that execl() will not fail, as the OP appears to do, or if you ensure that the process terminates in the event that execl() does fail.

– John Bollinger
Nov 20 '18 at 15:41















@JohnBollinger if you are referring to check the returned value by execl(), I left it out for the sake of simplicity.

– Nadir
Nov 20 '18 at 15:48





@JohnBollinger if you are referring to check the returned value by execl(), I left it out for the sake of simplicity.

– Nadir
Nov 20 '18 at 15:48


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53390266%2fc-starting-a-x11-session-using-system-vs-using-execl%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

MongoDB - Not Authorized To Execute Command

Npm cannot find a required file even through it is in the searched directory

in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith