Character escaping in C - c

I need to pass some commands to terminal throw C program and get it's input after that.
As a part of it, I have a line where Expect script must be placed.
FILE *command = popen("script here","r");
Script I need to execute is:
expect -c 'spawn ssh user#host cat /proc/stat
expect {
-re ".*yes/no.*" {
send "yes\r"
exp_continue
}
"password:" {
send -- "password\r"
}
}
interact'
So, I need to escape several characters so script worked as it need to work.
I tried different sequences of escaping, but all of them are not right.
And thank you for your attention.
UPD:
Without escaping I get error while compiling ( "syntax error before `*'" , "stray '\' in program" and others).
I think that problem caused by new lines, but script don't work if I simply write it in one line. I tried to use \n , but this did not helped me.
So, I cannot simply copy and paste script to C file, it need some processing

First things first, C's stringification can help you make multiline-strings easier on the eyes:
char *script = "expect -c 'spawn ssh user#host cat /proc/stat\n\n"
"expect {\n"
"-re \".*yes/no.*\"\n"
"send \"yes\\r\"\n"
...
The compiler will happily smash all those strings together for you.
Note of course that the \n are turned into newline characters in the string at compile time, while \\r is turned into \r in the string at compile time, which expect is hopefully turning into a carriage return at run time.
Second thing, are you sure embedding an expect script into an executable program is the right approach? Perhaps the host you are logging into will change along the way; replacing the script is much easier if it is broken out separate from the executable. (I can't tell you how many hundreds of pppd chat scripts I have written in my life, I'm just glad it didn't require a recompile of pppd to make them work!)

If you're hardcoding the "script" in your C program, you need to follow the C rules: that means escaping the embedded double-quotes and backslashes...
const char script[] =
"expect -c 'spawn ssh user#host cat /proc/stat\n"
"expect { -re \".*yes/no.*\" { send \"yes\\r\" exp_continue }\n"
" \"password:\" { send -- \"password\\r\" }\n"
" }\n"
"interact'\n"
Notice I've also terminated the lines with the C newline escape code '\n'.

Related

Strange behavior of argv when passing string containing "!!!!"

I have written a small program that takes some input parameters from *argv[] and prints them. In almost all use cases my code works perfectly fine. A problem only arises when I use more than one exclamation mark at the end of the string I want to pass as an argument ...
This works:
./program -m "Hello, world!"
This does NOT work:
./program -m "Hello, world!!!!"
^^ If I do this, the program output is either twice that string, or the command I entered previous to ./program.
However, what I absolutely don't understand: The following, oddly enough, DOES work:
./program -m 'Hello, world!!!!'
^^ The output is exactly ...
Hello, world!!!!
... just as desired.
So, my questions are:
Why does this strange behavior occur when using multiple exclamation marks in a string?
As far as I know, in C you use "" for strings and '' for single chars. So why do I get the desired result when using '', but not when using "" as I should (in my understanding)?
Is there a mistake in my code or what do I need to change to be able to enter any string (no matter if, what, and how many punctuation marks are used) and get exactly that string printed?
The relevant parts of my code:
// this is a simplified example that, in essence, does the same
// as my (significantly longer) code
int main(int argc, char* argv[]) {
char *msg = (char *)calloc(1024, sizeof(char));
printf("%s", strcat(msg, argv[2])); // argv[1] is "-m"
free(msg);
}
I already tried copying the content of argv[2] into a char* buffer first and appending a '\0' to it, which didn't change anything.
This is not related to your code but to the shell that starts it.
In most shells, !! is shorthand for the last command that was run. When you use double quotes, the shell allows for history expansion (along with variable substitution, etc.) within the string, so when you put !! inside of a double-quoted string it substitutes the last command run.
What this means for your program is that all this happens before your program is executed, so there's not much the program can do except check if the string that is passed in is valid.
In contrast, when you use single quotes the shell does not do any substitutions and the string is passed to the program unmodified.
So you need to use single quotes to pass this string. Your users would need to know this if they don't want any substitution to happen. The alternative is to create a wrapper shell script that prompts the user for the string to pass in, then the script would subsequently call your program with the proper arguments.
The shell does expansion in double-quoted strings. And if you read the Bash manual page (assuming you use Bash, which is the default on most Linux distributions) then if you look at the History Expansion section you will see that !! means
Refer to the previous command.
So !!!! in your double-quoted string will expand to the previous command, twice.
Such expansion is not made for single-quoted strings.
So the problem is not within your program, it's due to the environment (the shell) calling your program.
In addition to the supplied answers, you should remember that echo is your shell friend. If you prefix your command with "echo ", you will see what shell is actually sending to your script.
echo ./program -m "Hello, world!!!!"
This would have showed you some strangeness and might have helped steer you in the right direction.

using bash to execute a group of commands from C without storing them in a file

I've got a program which accepts a set of rules in the form of a single rules file.
When one of the conditions are considered met by my program, I seek to treat the block of commands associated with the condition as an independent bash script which needs to be executed. I would rather not deal with storing these commands in files as that leaves an undesirable attack vector. Is there a way to feed a line delimited list of bash commands to bash as a single group? I want if conditions and other things from the bash script to function correctly, not just executing each line raw on its own.
Example rules file:
if CONDITION
some nice
bash commands
pkill some process
./launching something!
endif
I want to be able to run the four lines of bash code as a group of bash commands, not independently of each other, when CONDITION is true, as determined by my C program.
Obviously this is from Linux, using C as the programming language.
You could also perhaps popen a bash process.
However, your approach suggests also to embed some scripting interpreter inside your application. Did you consider embedding e.g. lua inside it?
The simplest approach is probably to use sh -c "string containing commands to be executed". What's tricky is the embedded newlines. If the commands themselves won't contain single quotes, then you can wrap that multi-line string in single quotes. If it can contain single quotes, you'd want to escape the string to ensure that they are unchanged.
So:
read the commands into a buffer
do escape processing on the buffer; replace each ' with '\'' (remembering that the backslash must be in the output, so the string in C looks like "'\\''")
format the command: snprintf(command, sizeof(command), "sh -c '%s'", escaped_buffer);
ensure there was enough room
run system(command);

Preparing an input for a shell

I'm writing a simple shell in C and I want to implement the user input the same way the other shells do, or at least how bash does (never used others). If you enter a command with random whitespace then it can still run the command:
ex.
ls -1
Obviously strtok() wont work on this when separating the command and args...
How does bash do this? Should I search through the thousands of lines of the source code?
You can skip spaces while you're parsing your command:
while(*p==' '||*p=='\t') ++p;
whitespaces aren't problem. You can remove all whitespace which is not necesery.
But I'm not sure that shell do it in this way. I think shell may search all option in string which is input. Example
ls -a -l and ls -l -a
is the same. Maybe better will be if you will be search all possible option, or interpret all char step after step. Example, firstly search all "-", after it interpret option which is after "-"

command injection in C programming

I was implementing an echo command using the system() function. The argument for the echo command comes from a command line argument. But when used ';' in the argument it is showing the directory listing.
What should i do to avoid it? Is it because of command injection in my program?
update: code added from comment
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char **argv) {
char cmd[50] = "echo ";
strcat(cmd,argv[1]);
system(cmd);
}
I could compile the code but while executing if i give the command line argument as eg: './a.out hello;ls ' then directory listing is happening.
Why are you trying to use a shell access (which is exactly what System() does), and than attempt to restrict it?
If you need for some reason to use 'echo', please build your own execve() parameters, and launch /bin/echo directly.. this way you can restrict the damage only to the tasks 'echo' can do.
When attempting to run your program with the command ./a.out hello;ls, you are actually providing the shell with two separate commands that it executes in sequence. First the shell runs a.out with the command line parameter "hello" in argv[1], which prints it out using echo. Then your program exits, and the shell runs the next command, ls, and displays the directory listing.
If you want to pass that string to the program as a command line parameter, you need to escape the special shell character ;, so the shell does not parse it before giving it to your program. To escape a character, precede it with a \.
Try running the command with ./a.out hello\;ls, and then using printf instead of echo.
[can't respond to other answers yet, so reposting the question]
"Is possible to get the argument with ';', without using '\' in the command line argument. Is possible for me to include a '\' from my program after getting argv?"
No, it is not possible. The interpretation of ";" is done by the shell before getting to your program, so unless you escape at the call, your program will never be aware of the ";". i.e.
PROG1 parms ; PROG2
will cause the shell (which is interpreting what you type) to do the following:
start PROG1 and pass it parms.
once PROG1 is done, start PROG2
There are a number of special characters which the shell will take over by default and your program will never see: * for wildcards, | for pipes, & for parallel execution, etc... None of these will be seen by the program being run, they just tell the shell to do special things.
Alternatively to using the "\", you can enclose your parameter in single or double quotes (which are different, but for your example will both work). i.e.:
./a.out "hello;ls"
./a.out 'hello;ls'
Note that these will work for the printf option, if you call "system" you are in effect telling C to start a shell to run what you are passing in, so the input will once again be subject to shell interpretation.
system() is very difficult to use in a secure manner. It's much easier to just use one of the exec* functions.

How do I get Bison/YACC to not recognize a command until it parses the whole string?

I have some bison grammar:
input: /* empty */
| input command
;
command:
builtin
| external
;
builtin:
CD { printf("Changing to home directory...\n"); }
| CD WORD { printf("Changing to directory %s\n", $2); }
;
I'm wondering how I get Bison to not accept (YYACCEPT?) something as a command until it reads ALL of the input. So I can have all these rules below that use recursion or whatever to build things up, which either results in a valid command or something that's not going to work.
One simple test I'm doing with the code above is just entering "cd mydir mydir". Bison parses CD and WORD and goes "hey! this is a command, put it to the top!". Then the next token it finds is just WORD, which has no rule, and then it reports an error.
I want it to read the whole line and realize CD WORD WORD is not a rule, and then report an error. I think I'm missing something obvious and would greatly appreciate any help - thanks!
Also - I've tried using input command NEWLINE or something similar, but it still pushes CD WORD to the top as a command and then parses the extra WORD separately.
Sometimes I deal with these cases by flattening my grammars.
In your case, it might make sense to add tokens to your lexer for newline and command separators (;) so you can explicitly put them in your Bison grammar, so the parser will expect a full line of input for a command before accepting as a commmand.
sep: NEWLINE | SEMICOLON
;
command: CD sep
| CD WORD sep
;
Or, for an arbitrary list of arguments like a real shell:
args:
/* empty */
| args WORD
;
command:
CD args sep
;
Instead of calling actions directly, just build yourself an Abstract Syntax Tree first. Then depending on the result and your preference you either execute the part of it or nothing. If there is a parsing error during tree building you may want to use %destructor directive to tell bison how to do the cleanup.
That actually is a proper way of doing it as you get full control over the contents and logic and you let bison just take care of parsing.
Usually, things aren't done the way you describe.
With Bison/Yakk/Lex, one usually carefully designs their syntax to do exactly what they need. Because Bison/Yakk/Lex are naturally greedy with their regular expressions, this should help you.
So, how about this instead.
Since you are parsing whole lines at a time, I think we can use this fact to our advantage and revise the syntax.
input : /* empty */
| line
command-break : command-break semi-colon
| semi-colon
line : commands new-line
commands : commands command-break command
| commands command-break command command-break
| command
| command command-break
...
Where new-line, 'semi-colonis defined in yourlexsource as something like\n,\t` . This should give you the UNIX-style syntax for commands that you are looking for. All sorts of things are possible, and it is a little bloated allowing for multiple semicolons and doesn't take in consideration white-space, but you should get the idea.
Lex and Yakk are a powerful tool, and I find them quite enjoyable - at least, when you aren't on a deadline.
Couldn't you just change your rule match actions to append to a list of actions you want to perform if the whole thing works? Then after the entire input has been processed you decide if you want to do what was in that list of actions based on if you saw any parse errors.

Resources