Execute commands as root without root password or sudo - c

I understand the implications of running a script as root, especially by a web application. However as part of my web application, I need to use curl with tor and this needs resetting the tor ip occasionally. tor can get a new ip when the service is restarted with service tor restart. Since only root can do that, I've written a C wrapper script to do what I need, and compiled it and set setuid root on it, and changed to root user ownership. However, it still asks me root password when it's run as an unprivileged user. As root, a service restart shouldn't ask password.
My script:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
void ExecAsRoot (char* str);
int main ()
{
setuid (0);
setvbuf(stdout, NULL, _IONBF, 0);
printf ("Host real ip is: ");
ExecAsRoot("ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/'");
ExecAsRoot("/usr/sbin/service tor restart");
// sleep(2);
printf ("Tor should have switched to a new ip by now.\nNew ip is: ");
ExecAsRoot("torify curl ifconfig.co 2>/dev/null");
return 0;
}
void ExecAsRoot (char* str) {
system (str);
}
I've done the following:
chown root restartor
chmod u=rwx,go=xr restartor
Output:
Host real ip is: 7.17.11.23
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
Authentication is required to restart 'tor.service'.
Authenticating as: root
Password:
How can I get this to run as web user without supplying root password?

You didn't set the setuid bit in the file permissions:
#-------v
chmod u=srwx,go=xr restartor

Related

Execute a shell command in C but not through web page

I have a program in C/java/html 5
The service file of the program runs as root user and it is on an Archlinux distribution.
I want to have the result IP address that SMB will find and show it in an html page i have.
The part of the code i am interested is this:
sds ns_server_list(sds buffer, sds method, int request_id)
{
FILE *fp = popen("/usr/bin/nmblookup -S \'*\' | grep \"<00>\" | awk \'{print $1}\'", "r");
// returns three lines per server found - 1st line ip address 2nd line name 3rd line workgroup
if (fp == NULL)
{
LOG_ERROR("Failed to get server list");
buffer = jsonrpc_respond_message(buffer, method, request_id, "Failed to get server list", true);
}
If i run the program through shell (./test) and after compile, it returns the results in HTML just fine.
If i run it through the web interface of program and through a service file, it returns these SMB errors in journalctl -xe:
WARNING: no network interfaces found
name_query failed to find name *
ERROR Executing syscmd "/usr/bin/nmblookup -S '*'" failed
SMB.conf
[global]
server string = SMB Server
security = user
guest account = root
map to guest = bad user
log level = 0
load printers = No
syslog = 0
directory mask = 0775
create mask = 0775
browseable = yes
#veto files = /._*/.DS_Store/
interfaces = eth0 192.168.2.0/24
Summarized...
If i run the program through service file and without IP address in SMB.conf i have the errors in journalctl. If i put the IP everything is ok but i do not want to work it like this way (dynamic ip and eth0 do not work, same errors).
If i run the program through shell with the executable compiled file (./test) everything play fine without ip address in smb.conf
Why is that?
Thank you.

Capturing tshark standard output with popen in C

I'm trying to capture the standard output from tshark through a program in C.
For that, I use popen() call to open tshark process and read from the returned FILE stream.
Code sample:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* pipe_fd = popen("tshark -i eth0 -R icmp -2 -T fields -e icmp.checksum -e icmp.seq", "r");
//FILE* pipe_fd = popen("lsof", "r");
if (!pipe_fd) {
fprintf(stderr, "popen failed.\n");
return EXIT_FAILURE;
}
char buffer[2048];
while (NULL != fgets(buffer, sizeof(buffer), pipe_fd)) {
fprintf(stdout, "SO: %s", buffer);
}
pclose(pipe_fd);
printf("tdr FINISHED!\n");
return 0;
}
When I run it, I get only the packet number count, i.e., I get no packet fields info, just the count of packets, with each number overriding the previous in the same place (no lines scroll happening).
Something like this, I guess for 4 packets:
tomas#ubuntu64:~$ sudo ./main
tshark: Lua: Error during loading:
[string "/usr/share/wireshark/init.lua"]:46: dofile has been disabled due to running Wireshark as superuser. See http://wiki.wireshark.org/CaptureSetup/CapturePrivileges for help in running Wireshark as an unprivileged user.
Running as user "root" and group "root". This could be dangerous.
Capturing on 'eth0'
4
But if I change in the C program the 'tshark' command argument by 'lsof', I get the standard output just fine.
tomas#ubuntu64:~$ sudo ./main
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs
Output information may be incomplete.
SO: COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME
SO: init 1 root cwd DIR 8,1 4096 2 /
SO: init 1 root rtd DIR 8,1 4096 2 /
SO: init 1 root txt REG 8,1 265848 791529 /sbin/init
SO: init 1 root mem REG 8,1 47712 824514 /lib/x86_64-linux-gnu/libnss_files-2.19.so
...
With this result, I'm assuming that there is something special with the way tshark sends the info to the standard output. Does anyone know about this behaviour?
I'm gonna check tshark source code, to see if it can clarify it.
Just a final note.
When I run tshark through the shell, I often get missing packet numbers like:
tomas#ubuntu64:~$ sudo tshark -i eth0 -R icmp -2 -T fields -e icmp.checksum -e icmp.seq
tshark: Lua: Error during loading:
[string "/usr/share/wireshark/init.lua"]:46: dofile has been disabled due to running Wireshark as superuser. See http://wiki.wireshark.org/CaptureSetup/CapturePrivileges for help in running Wireshark as an unprivileged user.
Running as user "root" and group "root". This could be dangerous.
Capturing on 'eth0'
0x0ee5 63045
1 0x8ae3 63046
2 0xcfdf 63047
3 0xe4d9 63048
4 0x9db7 63049
5 0x6798 63050
6 0x0175 63051
7 0x9e54 63052
0xa654 63052
9 0xe050 63053
0xe850 63053
11 0x8389 63054
0x8b89 63054
13 0x9b81 63055
0xa381 63055
Missing printed packet numbers 8, 10, 12, 14.
And when I redirect stdout to file, it does not send the count numbers:
tomas#ubuntu64:~$ sudo tshark -i eth0 -R icmp -2 -T fields -e icmp.checksum -e icmp.seq > kk
tomas#ubuntu64:~$ cat kk
0x2073 63321
0x2873 63321
0x7c6a 63322
Another clue that tshark is handling printed packet count and info differently.
Regards,
Tom
Well, even if I finally don't use this way of working with tshark, I think I found the option to use in order to popen tshark. From the manual page, option -l:
Flush the standard output after the information for each packet is printed. (This is not, strictly speaking, line-buffered if -V was
specified; however, it is the same as line-buffered if -V wasn't
specified, as only one line is printed for each packet, and, as -l is
normally used when piping a live capture to a program or script, so
that output for a packet shows up as soon as the packet is seen and
dissected, it should work just as well as true line-buffering. We do
this as a workaround for a deficiency in the Microsoft Visual C++ C
library.)
This may be useful when piping the output of TShark to another
program, as it means that the program to which the output is piped
will see the dissected data for a packet as soon as TShark sees the
packet and generates that output, rather than seeing it only when the
standard output buffer containing that data fills up.
I tested it, and it seems to work.
Just in case anyone needs it.

Application crashes only when launched from inittab on busybox

I'm writing an application for an embedded busybox system that allows TCP connections, then sends out messages to all connected clients. It works perfectly when I telnet to the box and run the application from a shell prompt, but I have problems when it is launched from the inittab. It will launch and I can connect to the application with one client. It successfully sends one message out to that client, then crashes. It will also crash if I connect a second client before any messages are sent out. Again, everything works perfectly if I launch it from a shell prompt instead.
The following errors are what comes up in the log:
<11>Jan 1 00:02:49 tmmpd.bin: ERROR: recvMessage failed, recv IO error
<11>Jan 1 00:02:49 tmmpd.bin: Some other LTK TCP error 103. Closing connection 10
<11>Jan 1 00:02:49 tmmpd.bin: ERROR: recvMessage failed, recv IO error
<11>Jan 1 00:02:49 tmmpd.bin: Some other LTK TCP error 103. Closing connection 10
Any suggestions would be greatly appreciated!
I was testing a bit in arm-qemu and busybox, and I was able to start a script as user test to run in background.
I have created a new user "test":
buildroot-dir> cat etc/passwd
test:x:1000:1000:Linux User,,,:/home/test:/bin/sh
Created a simple testscript.sh:
target_system> cat /home/test/testscript.sh
#!/bin/sh
while :
do
echo "still executing in bg"
sleep 10
done
To my /etc/init.d/rcS I added a startup command for it:
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/mdev -s
/bin/su test -c /home/test/testscript.sh& # < Added this
Now when I start the system, the script will run in the background, and when I grep for the process it has been started as user test (default root user is just 0):
target_system> ps aux | grep testscript
496 test 0:00 sh -c home/test/testscript.sh
507 test 0:00 {testscript.sh} /bin/sh home/test/testscript.sh

how to execute a command as root

I develop a C code on Linux (Debian). Time to time, I need to execute some commands through system()
I wonder if it is possible to execute a command via system()as root. If it is not the case, is there any function to execute a command (or run a binary) as root that I can use on the C code?
We met the situation before that we want to execute a root command by a normal user, here is our solution (using setuid/SUID):
assume that:
username: Tom
group: gTom
C program file: my_pro.c
Step 1: Write a C code tool: my_sudo.c
...
int main(int args, char *argv[]) {
if (args < 2)
printf("Usage: my_sudo [cmd] [arg1 arg2 ...]");
// cmd here is the shell cmd that you want execute in "my_pro"
// you can check the shell cmd privilege here
// example: if (argv[1] != "yum") return; we just allow yum execute here
char cmd[MAX_CMD];
int i;
for ( i = 2; i < args; i ++) {
// concatenate the cmd, example: "yum install xxxxx"
strcat(cmd, " ");
strcat(cmd, argv[i]);
}
system(cmd);
}
Step 2: Compile my_sudo.c to get a my_sudo executable file
sudo chown root:gTom my_sudo // user root && gTom group
sudo chmod 4550 my_sudo // use SUID to get root privilege
#you will see my_sudo like this(ls -l)
#-r-sr-x--- 1 root my_sudo 9028 Jul 19 10:09 my_sudo*
#assume we put my_sudo to /usr/sbin/my_sudo
Step 3: In your C code
...
int main() {
...
system("/usr/bin/mysudo yum install xxxxx");
...
}
#gcc && ls -l
#-rwxr--r-- 1 Tom gTom 1895797 Jul 23 13:55 my_pro
Step 4: Execute./my_pro
You can execute the yum install without sudo.
If you are a user on your system that has sudo privileges to run commands as root, just pre-pend sudo to the command.
system("sudo yum install some-package");
If you want anybody to be able to do it, then you have to be administrator on your system, change the owner of the file to be root, and modify the permissions of your executable to run as root. By doing so, you do not need to modify your system() command string with sudo.
chmod +s my_program
chown root my_program
Realize that doing this may open you up to security problems, unless you have proven that your program has no security issues.
The file-system may be such to disallow you from setting the setuid bit on your program. If you need more information along these lines, you should consult SuperUser.
This is one of those bag-o-tricks things to keep in mind. There are security risks, so just be aware of who will use it. In the "system" command you can even execute external scripts...although that opens major security risks because while this binary has to have the permissions re-set every time it's compiled, a script can be changed endlessly and this binary will keep calling it.
#include <stdio.h>
#include <stdlib.h>
//Create as root
//gcc fixmusic.c -o fixmusic
//chmod u+s fixmusic
//now run as non-root user and it should work despite limitations of user
int main(int argc, char *argv[] )
{
setuid(0);
char command[100];
sprintf(command,"/usr/bin/chmod -R a+w /mnt/Local/Music");
system(command);
//This is just optional info if someone cat's the binary
volatile const char comment [] = "INFO: Fixes music permissions";
return 0;
}

How to detect a user logged in through GUI in Linux

I would like to capture the user name logged in through GUI in my program. My program is running as a daemon from root login. If a non root user logs in through GUI my program should be notified.
I am pasting my current program which calls a perl script making use of system call to check who is the current user logged in. I am pasting my perl script too for reference.
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/Xfuncs.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
int main()
{
char *user;
char buf[1024];
int fd, ret;
fd = open("/tmp/log", O_TRUNC|O_RDWR|O_CREAT);
if (!fd) {
printf("Error opening file\n");
exit(1);
}
chmod("/tmp/log", S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP
| S_IROTH | S_IWOTH | S_IXOTH);
daemon(0, 0);
while (1) {
system("perl /home/curr-usr.pl");
sleep(5);
}
return 0;
}
The perl script which is used to get the current user logged in.
#!/usr/bin/perl
my $result;
$result = `whoami`;
open FH, "+>>", "/tmp/log" or die $!;
print FH "$result ";
close (FH);
In the c program above I am calling the perl script in a while loop every 5 seconds. The perl script makes use of the command "whoami" to get the current user logged in & dumps it into the /tmp/log file.
What I want to achieve is if user1 logs in the perl script should give me the current user to be user1. Instead the perl script gives me root as the current user irrespective of the user I am logged in through GUI as I am running the C program & perl script with root user.
Could anyone please advise me with a mechanism by which the C program could get to know the current user logged in through GUI ?
Any help is greatly appreciated.
You can detect the user using the main display like this:
#!/bin/bash
#Detect the name of the display in use
display=":$(ls /tmp/.X11-unix/* | sed 's#/tmp/.X11-unix/X##' | head -n 1)"
#Detect the user using such display
user=$(who | grep '('$display')' | awk '{print $1}')
#Detect the id of the user
uid=$(id -u $user)
I am using XFCE4 and LXDM. "who" and "users" report only users that are logged on a terminal. GUI logon is not reported as Nominal Animal pointed out (Thanks!). I use "pgrep xfce" to check if XFCE4 is running. Following prints out current xfce-user:
#!/usr/bin/perl
# Get all processes
my #xfce_processes = `pgrep xfce`;
# If processes exist, get user of first process in list.
if(scalar #xfce_processes) {
print `ps -o user h $xfce_processes[0]`;
}
else
{
# No xfce processes.
;
}
As you mentioned your program runs as a daemon. Consequently any process it spawns would be run as the same user as one that started that daemon. The user that logs in via UI (or any other method) would never be the user you can get by calling whoami from your daemon.
Instead what you should do is explicitly notify your daemon of a login event or, if that is not an option, keep a list of all the logged-in sessions currently running on the box and see if new sessions appear - that would be a session of a newly logged-in user.
The programms who and users get their information from the file /var/run/utmp.
The file contains N entries of the size of "struct utmp", defined in <utmp.h>
You are interested in the USER_PROCESS type entries. The host field contains the display.
Note that there are multiple entries for the same display if the user opened some terminal emulations (xterm, konsole...).
You could monitor this file or /var/log/wtmp for a history
struct utmp ut_entry;
FILE *fp = fopen(UTMP_FILE, "r");
if( !fp )
{
printf("Could not open utmp file!");
return;
}
while(fread(&ut_entry, sizeof(struct utmp), 1, fp) == 1)
{
if(ut_entry.ut_type != USER_PROCESS)
continue;
// string entries are not 0 terminated if too long...
// copy user name to make sure it is 0 terminated
char tmpUser[UT_NAMESIZE+1] = {0};
strncpy(tmpUser, ut_entry.ut_user, UT_NAMESIZE);
// do more stuff... read the display from ut_entry.host
}
For more information see the utmp manual page
You probably want to investigate either ConsoleKit or its newer incarnation loginctl.
These tools are specifically designed for managing seats and sessions, while maintaining distinction between local text console, GUI and remote sessions. They are not guaranteed to be present on every X11 machine, but if yours is relatively recent, chances are it uses either one or the other tool.
For anyone else coming here for xfce4, what I had to do was run ps -aux | grep xfce4 then there was a line:
root 2497 0.0 0.3 323052 13408 ? Sl 01:24 0:00 xfce4-session
I run kill -9 2497 and that got rid of it. For me this was a hung instance I wanted to kill from SSH.

Resources