The following C program compiles and spits out a a directory listing of "/Users/home/tempdir" directory.
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char * argv[])
{
execl("/bin/ls", "ls", "/Users/home/tempdir");
// execl("/usr/bin/scp", "scp", "myfile.txt home#127.0.0.1:/Users/home/scpdir");
return 0;
}
This shows it is possible to execute a binary file from a C program. I am trying to figure out how to execute the "scp" binary file and input the required passphrase. How can I modify the above code to achieve a successful file transfer via scp ?
I attempted the line that is commented out above however I get the following message when it is uncommented:
usage: scp [-1246BCEpqrv] [-c cipher] [-F ssh_config] [-i identity_file]
[-l limit] [-o ssh_option] [-P port] [-S program]
[[user#]host1:]file1 ... [[user#]host2:]file2
So it's obviously trying to perform the scp without the password. How do I get around this?
While you could call scp from the command line using expect or something, you are much better off using a library if at all possible.
The general rule when developing is to prefer libraries to system executions whenever possible. It keeps your code cleaner and more portable. The exception is if you are writing a shell script.
http://www.libssh2.org/
libssh2 is a free C library that supports password authentication as well as SCP/SFTP and supports OS X.
http://curl.haxx.se/libcurl/
libcurl supports pretty much every file transfer type you could want, works on just about every platform you could want, and has bindings for pretty much every language you could want.
Short answer, I know, but you'd typically use something like Expect for automating interactive applications in this way.
It appears to me that the output you are printing is from incorrect command syntax. According to the manual page for execl (at least on Arch Linux):
The const char *arg and subsequent ellipses in the execl(), execlp(),
and execle() functions can be thought of as arg0, arg1, ..., argn.
So it would seem the correct way to call it is:
execl("/usr/bin/scp", "scp", "myfile.txt", "home#127.0.0.1:/Users/home/scpdir");
Also, if you want to avoid having to put in the password completely, SSH (which scp is built on top of) can be configured with an "authorized key," which bypasses the password. It may still ask about unknown key fingerprints, though.
An easy way to set up password-less scp access is to run (from the computer initiating the scp):
ssh-copy-id home#127.0.0.1
The above command will ask for a password once, they you shouldn't need to enter it again on subsequent ssh operations.
The following code works for me:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
execl("/usr/bin/scp", "scp", "myfile.txt", "user#IP:path/to/file", (char *)0);
return 0;
}
The following code work for me:
#include <stdio.h>
#include <unistd.h>
int main()
{
execl("/usr/bin/sshpass","sshpass","-p","typeYourPasswd","/usr/bin/scp", "scp", "/home/harekrishna/Downloads/LoRaModule/ArduinoWithMeterCode/dlmsLoraV1.ino", "harekrishna#172.29.118.18:/home/harekrishna",(char *)0);
return 0;
}
but this work for me:
#include <stdio.h>
#include <unistd.h>
int main()
{
execl("/usr/bin/sshpass","sshpass","-p","typeYourPasswd", "scp", "/home/harekrishna/Downloads/LoRaModule/ArduinoWithMeterCode/dlmsLoraV1.ino", "harekrishna#172.29.118.18:/home/harekrishna",(char *)0);
return 0;
}
Related
For the problem described at bash - Detect if a script is being run via shebang or was specified as a command line argument - Unix & Linux Stack Exchange, we need to distinguish between cases when a script is run via shebang and as an argument to the interpreter.
An answer to that question suggests getting the pre-shebang executable name using getauxval(AT_EXECFN) -- which works, but only in Linux.
Since the Pyenv project also officially supports MacOS, we need an equivalent for that if we are to consider that solution.
I've checked Finding current executable's path without /proc/self/exe -- but both _dyld_get_image_name(0) and _NSGetExecutablePath give the post-shebang name. Here's a sample program that I used to do the checking (see the question link above on how it's used; its compilation result needs to be put in place of the python3 Bash script given in that question):
#include <stdio.h>
#include <unistd.h>
/*#include <sys/auxv.h>*/
#include <mach-o/dyld.h>
#include <sys/param.h>
#include <alloca.h>
int main(int argc, char** argv) {
//char *at_execfn = (char*)getauxval(AT_EXECFN);
//const char *at_execfn = _dyld_get_image_name(0);
char *at_execfn = (char*)alloca(MAXPATHLEN);
uint32_t at_execfn_len = MAXPATHLEN;
_NSGetExecutablePath(at_execfn,&at_execfn_len);
printf("original executable: '%s'\n",at_execfn);
for(int i=0; i<argc; i++) {
printf("'%s'\n",argv[i]);
}
execvp("python3",argv);
}
This answer is based on the following assumptions; I'm sure others will vet whether they are true, but to my understanding, they are:
Python scripts will only use the shebang if they are executed directly.
Otherwise, the first command line argument will always be python, python3, or some other variation (python3.x, etc.).
You can already get the path to the original file, which is good because you can read what the shebang says, but you don't yet know whether the shebang was used, right? Python 3.10 offers an appealing solution: sys.orig_argv, which includes all the command line arguments, not just those from the program name forward as you get with normal sys.argv.
However, I'm sure you won't be implementing a 3.10-exclusive feature into pyenv! If that is the case, you can see the older C-API Py_GetArgcArgv, whose docs simply state:
Get the original command line arguments, before Python modified them.
Either way, I think that having the file path so you can read the shebang is the first part of the puzzle. The second part is figuring out if the shebang was actually used, and I think that the answer is in the command line arguments for most cases.
I am trying to write a tiny program in C that will open a file and then run the filename in terminal to stream the file to my Apple TV.
The reason I want to do this is so I can right click a media file, select 'Open With', choose 'Apple TV' from the list and then have it stream to my Apple TV via the airstream program.
My code so far is as follows
#include <stdio.h>
#include <string.h>
int main ()
{
char command[50];
strcpy( command, "airstream '/home/steve/media.mp4' -o 192.168.0.2" );
system(command);
return(0);
}
Very simple, but I'm not sure how to handle a file being passed to the application to allow it to get the filename and modify the command.
(First, let me make a comment on strcpy(): as-is, the call to strcpy() is superfluous (and imposes a security issue), because you are using a constant string. You could have written system("airstream '/home/steve/media.mp4' -o 192.168.0.2") instead.)
If you want to construct a command given a filename, you could in theory write
char command[LINE_MAX];
snprintf(command, sizeof command, "some_command %s", argv[1]);
system(command);
But that again rises a security problem, because now your program can be hijacked to execute an arbitrary external program, by passing it a malformed command-line argument, similarly to an SQL injection attack.
You'd be better off finding the library/API the airstream executable uses, and incorporate that directly into your program. If no such thing exists, you have to make sure to at least validate the user input (i. e. escape special characters, etc.) before handing it over to the shell for execution.
You need to use the arguments passed to the main() function. Change the function's signature to:
int main(int argc, char *argv[])
Then loop over the string pointers in argv[], which will be the command-line arguments your program was given.
I'm about to write a simple shell in C on Ubuntu.
I thought about using the exevcp() function.
I'm only able to run the "ls" command, none of the other commands seems to work.
Can someone tell me why is it happening or give me an idea of a better way to build the shell?
My purpose is to build this shell; I don't understand why can't I just take the command line, and put it as it is, into the execvp() function.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main(int argc,char **argv,char **envp)
{
char* args[] = {"history" , NULL};
execvp(args[0],args);
}
Can you explain to me, please?
exec(3) can only be used to run external programs. history is a built-in in most shells, and cannot be run this way.
The execvp() system call has two arguments: the name of the program to be executed, and a pointer to a null-terminated list of strings that are the arguments to the command.
For example:
char *args[] = { "ls", "-l", "-t", "-r", ".", 0 };
execvp(args[0], args);
...report error...
You can't simply pass a whole string into execvp() and expect it to split it up; the invoking code must do that. Note that if string pointed at by the first argument to execvp() contains a slash, then the $PATH mechanism is not used to find the command. A plain name (as shown, "ls") is searched using $PATH.
You also have to remember that some commands, such as history and cd, are shell built-ins; there is no external executable that you can run to get the same effect You won't be able to run those. Other commands, such as test (aka [) are usually implemented as built-ins, but there's also usually a binary in /bin or /usr/bin that does (more or less) the same job, which you can therefore invoke.
I know that use the command "chroot" in linux need some files or directories such as usr, bin and so on. But when I use the function chroot() in C, do I need these files?
Here is my code, which "hw.out" is a binary file which just print "Hello, world". I compiled it and run it as root, but it was failed to print "Hello, world". What else should I do? Thank you!
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int result = chroot(".");
if(result == 0)
printf("Chroot Succese.\n");
char *arrays[]={"./hw.out",NULL};
execvp("./hw.out", arrays);
return 0;
}
execvp is most likely failing, probably with ENOENT: no such file or directory, if hw.out is a dynamically linked executable.
For that to work, all the libraries required by hw.out need to be findable in the chrooted environment.
Try linking hw.out statically, and it should work. (And add error checking after execvp to see what errno is set to after the call if it returns.)
Please test that your hw.out works with command line chroot.
Perhaps hw.out is dynamically linked and is missing some libraries or ld-linux.so in the chroot directory.
Nitpicks 1, what's the point of return 0 after execvp? it never gets executed unless there is an error. I would rather have perror("can't exec"); return 1;
Nitpick 2, chroot() doesn't change working directory, although it works in your case, as you are chrooting to ".", it won't work as you expect if you later change it to chroot("somedir").
Make sure that hw.out is in the correct direct. Perhaps it is easier to have it statically linked if it is using libraries. Otherwise need to enable after chroot that it can access the dynamic libraries.
I am executing a program say A from another by first fork-ing followed by execve(). Now the problem is I would want A to use my library that I would generaly do by using LD_PRELOAD. How do I do it within execve().
Thanks
you can pass the LD_PRELOAD in envp execve's argument:
the program that gets execved, named "run":
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printf("%s\n",getenv("LD_PRELOAD"));
}
the program that does the execve, named "ex":
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char *const args[] = {"./run",NULL};
char *const envs[] = {"LD_PRELOAD=caca",NULL};
execve("./run",args,envs);
}
running it:
root#pinkpony:~# ./ex
ERROR: ld.so: object 'caca' from LD_PRELOAD cannot be preloaded: ignored.
caca
EDIT:
the error shown gets thrown because "caca" lib can't be preloaded for run, so it works. (I skipped the fork() part for clarity but the usage is the same)
EDIT:
doing something like:
LD_PRELOAD=caca ./ex
will not automagically preload caca lib when execve()-ing run if you're not passing it via envp execve()'s argument
If you want to use LD_PRELOAD just for program A (and not for its parent) you could load it via the shell; pass to the shell the name of the program to execute and add LD_PRELOAD to the environment.
Update
After reading the added info from the question, I'm guessing that you might have to specify a complete path, or set LD_LIBRARY_PATH as well? Since the loader is acknowledging the fact that the preload is ordered.
Otherwise, I can imagine there being a security restriction (allthough it would have to be tied to being run invoked from a login shell, which seems quite brittle to detect). Nonetheless, you may wish to try running as root (use sudo -E to keep your environment)
It would appear from this earlier question that such behaviour is the default
LD_PRELOAD affects new child even after unsetenv("LD_PRELOAD")
Have you tested it?