Execlp with echo and md5sum - c

I am trying to write a program using execlp() to save the output of an md5sum of a string to the standard output. Basically, to simulate this:
echo "Hello!" | md5sum
Which gets this output:
31ebdfce8b77ac49d7f5506dd1495830 -
Here is what I tried first to figure this out:
char string[] = "Hello!";
execlp("md5sum", "md5sum", string, NULL);
Although, using execlp() in this way expects the argument to be a file, not a string. So then I tried this:
char string[] = "Hello!";
execlp("echo", "echo", string, "md5sum", NULL);
Although, this generates an output of Hello! md5sum. It recognizes the "md5sum" as a string, not the system call.
What can I do to make echo and md5sum cooperate together? Or what can I do to make md5sum work on a string, not a file? Maybe should I be using a different function than execlp() ?

Before I answer your question, some concerns.
And MD5 is long, long, long since broken. It's fairly trivial to create a file with a given MD5 sum. SHA1 is on its way out. Use SHA-256 or better. Doesn't matter if your application isn't about security, you and I aren't qualified to make decisions about attack surfaces, don't take the risk.
Have you considered doing the checksum in C? It will be faster, more portable, and more reliable. There's any number of checksum libraries. Gnome Lib provides checksums, and so much more.
#include <stdio.h>
#include <glib.h>
int main() {
char string[] = "Hello!";
printf("checksum for %s is %s\n",
string,
g_compute_checksum_for_string(G_CHECKSUM_SHA256, string, -1)
);
}
Ok, on to the question.
First problem is md5sum doesn't take a string, it takes a file. This has nothing to do with execlp, it's how the md5sum program works. You can make md5sum read from stdin, but that involves pipes and is more and I want to take on. Did I mention to use a library?
This leads to your second problem: error checking. I don't see any. Any error checking for an exec goes immediately after the exec; if it succeeded then the calling program will immediately exit.
Then problem is execlp is probably overkill unless you're changing the name of the program being run. Use execvp. I prefer it because it keeps all the program's arguments together in a nice list that can be used for error checking later.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main() {
char *args[] = { "md5um", "Hello!", NULL };
int exit_status = execvp(args[0], args);
fprintf(stderr, "executing %s ", args[0]);
for( int i = 1; args[i] != NULL; i++ ) {
fprintf(stderr, "%s ", args[i]);
}
fprintf(stderr, "exited with %d: %s\n", exit_status, strerror(errno));
}

Related

Using stdin in C exclusively through a piped in file

I wrote a file parser for a project that parses a file provided on the command line.
However, I would like to allow the user to enter their input via stdin as well, but exclusively through redirection via the command line.
Using a Linux based command prompt, the following commands should yield the same results:
./check infile.txt (Entering filename via command line)
./check < infile.txt
cat infile.txt | ./check
The executable should accept a filename as the first and only command-line argument. If no filename is specified, it should read from standard input.
Edit: I realized how simple it really was, and posted an answer. I will leave this up for anyone else who might need it at some point.
This is dangerously close to "Please write my program for me". Or perhaps it even crossed that line. Still, it's a pretty simple program.
We assume that you have a parser which takes a single FILE* argument and parses that file. (If you wrote a parsing function which takes a const char* filename, then this is by way of explaining why that's a bad idea. Functions should only do one thing, and "open a file and then parse it" is two things. As soon as you write a function which does two unrelated things, you will immediately hit a situation where you really only wanted to do one of them (like just parse a stream without opening the file.)
So that leaves us with:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "myparser.h"
/* Assume that myparser.h includes
* int parseFile(FILE* input);
* which returns non-zero on failure.
*/
int main(int argc, char* argv[]) {
FILE* input = stdin; /* If nothing changes, this is what we parse */
if (argc > 1) {
if (argc > 2) {
/* Too many arguments */
fprintf(stderr, "Usage: %s [FILE]\n", argv[0]);
exit(1);
}
/* The convention is that using `-` as a filename is the same as
* specifying stdin. Just in case it matters, follow the convention.
*/
if (strcmp(argv[1], "-") != 0) {
/* It's not -. Try to open the named file. */
input = fopen(argv[1], "r");
if (input == NULL) {
fprintf(stderr, "Could not open '%s': %s\n", argv[1], strerror(errno));
exit(1);
}
}
}
return parse(input);
}
It would probably have been better to have packaged most of the above into a function which takes a filename and returns an open FILE*.
I guess my brain is fried because this was a very basic question and I realized it right after I posted it. I will leave it up for others who might need it.
ANSWER:
You can fgets from stdin, then to check for the end of the file you can still use feof for stdin by using the following:
while(!feof(stdin))

Process returned -1 (0xFFFFFFFF)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
printf("Transactional Shell Command Test.\n");
while(1) {
printf("Queue:");
char input[500];
fgets (input, 500, stdin);
if(strstr(input, "qb-write")){
printf("These are the commands you have queued:\n");
FILE *cmd = popen("cat /home/$USER/.queueBASH_transactions", "r");
char buf[256];
while (fgets(buf, sizeof(buf), cmd) != 0) {
printf("%s\n",buf);
}
pclose(cmd);
}
system(strncat("echo ",strncat(input," >> /home/$USER/.qb_transactions",500),500));
usleep(20000);
}
return 0;
}
I am attempting to make a concept for a transactional shell, and I'm having it output every command you enter into a file in the user's home directory. It's not completely finished, but I'm doing one part at a time. When I put in any input to the "shell", it crashes. Codeblocks tells me "Process returned -1 (0xFFFFFFFF)" and then the usual info about runtime. What am I doing wrong here?
strncat appends to its first argument in place, so you need to pass it a writable buffer as the first argument. You're passing a string literal ("echo "), which depending on your compiler and runtime environment may either overwrite unpredictable parts of the memory, or crash because it's trying to write to read-only memory.
char command[500];
strcpy(command, "echo ");
strncat(command, input, sizeof(command)-1-strlen(command));
strncat(command, " >> /home/$USER/.qb_transactions", sizeof(command)-1-strlen(command));
system(command);
As with the rest of your code, I've omitted error checking, so the command will be truncated if it doesn't fit the buffer. Also note that repeated calls to strncat are inefficient since they involve traversing the string many times to determine its end; it would be more efficient to use the return value and keep track of the remaining buffer size, but I'm leaving this as a follow-up exercise.
Of course invoking a shell to append to a file is a bad idea in the first place. If the input contains shell special characters, they'll be evaluated. You should open the log file and write to it directly.
char log_file[PATH_MAX];
strcpy(log_file, getenv("HOME"));
strncat(log_file, "/.qb_transactions", PATH_MAX-1-strlen(log_file));
FILE *log_file = fopen(log_file, "a");
…
while (1) {
…
fputs(cmd, log_file);
}
fclose(log_file);
(Once again, error checking omitted.)

Why does using `execl` instead of `system` stops my program from working?

I'm trying to do basic IPC using pipes. I spent hours searching the internet, doing this and that, reading the API documentations, and ended up with the code below. But it does not work, as I quite expected. Just any help making my code 'work' would be many thanks.
<edit>
I've just found that using system instead of execl makes my program run perfectly as expected. So what is going wrong here when I use execl, while it doesn't happen with the system function?
</edit>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void){
int hInPipe[2];
int hOutPipe[2];
FILE *hInFile;
FILE *hOutFile;
char *s;
pipe(hInPipe);
pipe(hOutPipe);
if(fork()){
close(hInPipe[0]);
close(hOutPipe[1]);
hInFile=fdopen(hInPipe[1],"w");
fprintf(hInFile,"2^100\n");
fclose(hInFile);
hOutFile=fdopen(hOutPipe[0],"r");
fscanf(hOutFile,"%ms",&s);
fclose(hOutFile);
printf("%s\n",s);
free(s);
}else{
dup2(hInPipe[0],STDIN_FILENO);
dup2(hOutPipe[1],STDOUT_FILENO);
close(hInPipe[0]);
close(hInPipe[1]);
close(hOutPipe[0]);
close(hOutPipe[1]);
system("bc -q");/*this works*/
/*execl("bc","-q",NULL);*/ /*but this doesn't*/
}
}
Read the fine man page. :)
execl(const char *path, const char *arg0, ... /*, (char *)0 */);
arg0 (aka argv[0], the name the program is told it was invoked under) is not the same argument as the path (the location of the executable for said program). Moreover, execl takes, as its first argument, a fully-qualified pathname.
Thus, you want:
execl("/usr/bin/bc", "bc", "-q", NULL);
...or, to search the PATH for bc rather than hardcoding a location:
execlp("bc", "bc", "-q", NULL);

How to take the output of grep and write it to a new file

I want to take the output of the grep command on a file, create a new file and save that grep output to the new created file, can someone please point me to the right direction in how I would do that?
The path you choose depends a great deal on how simple you want it to be.
Perhaps the simplest method is the use of system:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
system ("grep a *.c >outfile.txt");
return 0;
}
though you also could construct the command dynamically if you have different arguments to grep or a non-fixed output file.
Beyond that, you could use popen() (if available on your implementation - it's not mandated by ISO but is instead a POSIX thing) along with fgets() or fgetc() to read the output of that command and do whatever you want with it:
#include <stdio.h>
int main (void) {
int chr;
FILE *echo = popen ("echo hello there", "r");
if (echo != NULL) {
while ((chr = fgetc (echo)) != EOF)
putchar (chr);
fclose (echo);
}
return 0;
}
The next step up from there may be to not rely on an external grep at all but instead include something like PCRE (Perl-compatible regular expressions) into your own code, giving you much finer control over what happens.

linux how to patch this code

#include <WhatHere?>
#include <WhatHere?>
#include <WhatHere?>
int main(int argc, char **argv) {
char command[50] = "echo ";
strcat(command,argv[1]); // concatenate the input so that the final command is "echo <input>"
system(command); // call the system() function to print the input
return 0; // denote that the program has finished executing successfully
}
Can we get a remote access by running this code ? I know it is possible but please help me patch it up.
Assuming that you're worried about the potential buffer overflow, you could fix it like this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, char **argv) {
char *command;
if (argc != 2) {
fprintf (stderr, "Wrong number of arguments\n");
return 1;
}
if ((command = malloc (strlen (argv[1]) + 6)) == NULL) {
fprintf (stderr, "Could not allocate memory\n");
return 1;
}
strcpy (command, "echo ");
strcat(command,argv[1]);
system(command);
free (command);
return 0;
}
This makes enough room for "echo " (5), argv[1] (string length) and the null terminator (1).
It's still potentially dangerous allowing user-specified stuff to be run but at least you won't get buffer overflows any more.
Paxdiablo gave a good solution to your buffer overflow problem, but that's really the least of your problems here. Your big issue is that you are blindly using input from the user without inspecting it first.
For example, running your program like:
./your_app "\"goodbye data\" && rm -rf /"
would end in disaster, even if you program had no buffer overflow problems. An attacker could just as easily pass in an entire shell script that did all sorts of nasty things, all they would have to do is re-write it to fit in a single line.
You need to inspect incoming user input before you pass it to system() and make sure that it looks like what you are expecting. Better yet, avoid using system() with user input entirely and instead use safer methods to do what you need (in your example, you can replace your call to system("echo ...") with printf()). If you absolutely must pass user input to system(), consider running your app in a restricted environment like a chroot jail to at least make it more difficult to do anything nasty.

Resources