LINUX.ORG.RU

как правильно кавычить строку?

 ,


1

1

Допустим, мне надо сделать такую не очень красивую вещь: моя программа на С принимает от юзера строку hostname и делает fork & exec вот такой команды: bash -c 'ping hostname'.

Проблема вот в чем: hostname может содержать какое-нибудь «127.0.0.1 && kill -9 100500», и надо ее shell-quote. Как это правильно сделать?

man bash говорит «A single quote may not occur between single quotes, even when preceded by a backslash.». То есть одинарные кавычки не сработают потому что юзер может послать кавычку. С двойными кавычками надо какие-то символы вроде $ эскейпить.

Расскажите плз как сделать так чтобы работало 100%?



Последнее исправление: Klymedy (всего исправлений: 1)

Проверь, что в строке IPv4 или IPv6-адрес. Грязно выругайся и заверши bash в противном случае. Не полагайся на эскейпинг ни в одном языке.

x3al ★★★★★
()

моя программа на С принимает от юзера строку hostname

Пробуй его отрезольвить gethostbyname.

и делает fork & exec вот такой команды: bash -c 'ping hostname'.

А глянуть исходники ping? Там просто пакет посылается и некоторое время ожидается ответ. Вся мощь пинга тебе не нужна.

ziemin ★★
()

Допустим, мне надо сделать такую не очень красивую вещь: моя программа на С принимает от юзера строку hostname и делает fork & exec вот такой команды: bash -c 'ping hostname'.

bash тут не нужен.

Deleted
()
Ответ на: комментарий от ziemin

в этом случае так и сделаю, через getaddrinfo. Но вообще хотелось бы узнать какой-то общий метод кавыченья (не знаю как перевести quoting).

nokachi
() автор топика
Ответ на: комментарий от nokachi

баш тут команды интерпретирует, в том числе нежелательные

Ну так и убери его нафиг. Зачем он тут?

Deleted
()
Ответ на: комментарий от nokachi

баш тут команды интерпретирует, в том числе нежелательные

В твоём примере вообще любая интерпретация нежелательна. Тебе нужно exec'ом запустить ping, а не bash.

const86 ★★★★★
()
Ответ на: комментарий от const86

да, но я могу захотеть запустить более длинный скрипт: «openssl s_client ... | parse-my-certificates > /log»

речь не о пинге тут, а об эскейпинге

nokachi
() автор топика
Ответ на: комментарий от nokachi

чтобы запускать несколько процессов через пайп: «ping | grep»

Во-первых, то что делает grep ты можешь сделать и на C. Во-вторых, соединить два процесса тоже можно на C. И в качестве приятного бонуса ты получишь код выхода ping'а, который просто так из bash'а не выковырять. Ну и никаких проблем с экранированием, естественно =).

Deleted
()
Ответ на: комментарий от Deleted

я это вполне понимаю, конечно. Но реальность бывает такова, что слишком большие изменения делать нельзя а можно только добавить небольшой so модуль. К сожалению только один процесс, и запускается не из моего кода. Я только команду в api передаю.

nokachi
() автор топика

три из строки всё что после пробела и всё.

Или предварительно прогони строку через

if(ch=='|'||ch=='&'||ch==';'||ch==' ')
{
  printf("Не болуй, мой юный хакер\n");
  return -1;
};

Ну или тому подобное.

Dron ★★★★★
()
Последнее исправление: Dron (всего исправлений: 1)
Ответ на: комментарий от nokachi

Речь тут о security, если верить тегам. А в безопасных системах всегда делаем filter on input, escape on output. Иначе у тебя получается очередной PHP с его «безопасным» mysql_escape_string, «безопасным» mysql_real_escape_string и компанией.

Если ты делаешь что-то сложное и тебе _действительно_ нужно эскейпить, проще разбить строку по тем же ' и склеить из 'строго-заквоченных' строк и одинарных кавычек внутри баша. Но в сложных случаях у тебя будет достаточно нетривиальных вещей и квотинг — не самая сложная из них. Обычно это значит, что техзадание кривое.

x3al ★★★★★
()
Ответ на: комментарий от x3al

вопрос как раз о том как строго заквотить. Не буду рассуждать о сложности моего проекта, я уже написал что мне надо передать аргументы в команду которая будет выполняться внутри баша. Я хочу чтобы баш эти аргументы не evaluate. А команда сама разберется что ей делать с этой строкой.

nokachi
() автор топика
Ответ на: комментарий от Deleted

а как это переводится на баш? чето не могу понять нумерацию аргументов.

bash -c "ping ${1}" -- 127.0.0.1

не работает. Ну и опять же, если в ip_address_str будет что-то malicious, оно ведь запустится после того как bash -c завершится.

nokachi
() автор топика
Ответ на: комментарий от nokachi

а как это переводится на баш? чето не могу понять нумерацию аргументов.

...

не работает.

$ bash -c 'ping -c 1 "${1}"' -- 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.046 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.046/0.046/0.046/0.000 ms

Ну и опять же, если в ip_address_str будет что-то malicious, оно ведь запустится после того как bash -c завершится.

Нет, не запустится.

Deleted
()
Ответ на: комментарий от nokachi

Ты не понял.

$ bash -c 'ping -c 1 "${1}"' -- '127.0.0.1 && ls -l'
PING 127.0.0.1 && ls -l (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.040 ms

--- 127.0.0.1 && ls -l ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.040/0.040/0.040/0.000 ms
Это _один_ аргумент, баш его вообще никак специальным образом не интерпретирует.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

ок, то есть баш свои аргументы заключает в кавычки и делает эскейпинг сам? тогда вроде правильно.

nokachi
() автор топика
Ответ на: комментарий от nokachi

ок, то есть баш свои аргументы заключает в кавычки и делает эскейпинг сам? тогда вроде правильно.

На самом деле я не уверен на 100%. Но вроде всё ок:

$ bash -c 'ping -c 1 "${1}"' -- '127.0.0.1" && ls -l'
ping: unknown host 127.0.0.1" && ls -l
Можно написать на C небольшой тест, который будет передавать в программу «через баш» всякий мусор, а программа - проверять, что ей пришёл тот же мусор без изменений.

Deleted
()
Ответ на: комментарий от Deleted

И «эскейпинг» в данном случае - неправильное слово. ИМХО bash в данном случае просто складывает три строки: пустую строку (то, что после двойной кавычки и до ${1}), затем строку переданную первым свободным аргументом argv (собственно ${1}) и затем снова пустую строку. И передаёт результат в качестве одного из элементов argv дочернему процессу.

Deleted
()
shell_escape() {
	printf '%s\n' "${1}" | \
		sed "s/'/'\\\\''/g;s/^/'/;s/$/'/"
}
shell_escape "127.0.0.1 && kill -9 100500"

=> '127.0.0.1 && kill -9 100500'

shell_escape "hello, 'world"

=> 'hello, '\''world'

sshvabodka
()

делает fork & exec

Зачем такие сложности? man popen

beastie ★★★★★
()
Ответ на: комментарий от Dron
#!/bin/bash

rm a.out 2>/dev/null
gcc -xc - <<EOF
#include <stdio.h>
#include <unistd.h>
int start(char* hostname) {
  char cmd[200],*p=hostname,ch;
  while((ch=*p++)!=0)
    if(ch=='|'||ch=='&'||ch==';'||ch==' ')
    {
      printf("Не болуй, мой юный хакер");
      return -1;
    }
  sprintf(cmd,"bash -c 'ping %s'", hostname);
  system(cmd);
  return 0;
}
void info(char*msg) {
  printf("from argv[1]: ");
  fflush(stdout);
  fflush(stderr);
}
int main(int argc, char* argv[]) {
  info("from argv[1]: ");
  start(argv[1]);
  info("code: ");
  start("(){\nq=$'\"'\"'echo\\\040qwe\'\"'\"'\n\${q}\n}\nping");
  return 0;
}
EOF

./a.out $'(){\012q=$\'"\'"\'echo\\040qwe\'"\'"\'\012${q}\012}\012ping'
anonymous
()

Расскажите плз как сделать так чтобы работало 100%?

NoWay

Кстати, кавычек ты зря боишься. Ты НЕ ЗАПУСКАЙ строку от юзера, тогда всё будет работать. Ну например:

$ s="a b ' c"
$ echo "$s"
a b ' c
$ echo "'${s:2:3}'"
'b ''
$ s="a b\"' c"
$ echo "'${s:2:3}'"
'b"''

emulek
()
Ответ на: комментарий от nokachi

bash тут не нужен.

нужен. На нём suid бит, он от рута работает. Это придётся всю программу от рута запускать, что совсем хреново...

emulek
()
Ответ на: комментарий от const86

Тебе нужно exec'ом запустить ping, а не bash.

да, точно.

emulek
()
Ответ на: комментарий от emulek

4.2

$?

Речь шла про пайпы.

$ bash -c 'ping ololo'; echo $?
ping: unknown host ololo
2

$ bash -c 'ping ololo | true'; echo $?
ping: unknown host ololo
0
Да, есть специальные башизмы для этого. Но чтобы вытащить rc всех процессов входящих в пайп в родительскую программу на C, придётся прикручивать дополнительные страшные костыли.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

и тебе привет

#!/bin/bash

rm a.out 2>/dev/null
gcc -xc - <<EOF
#include <unistd.h>
int main() {
  return execl("/bin/bash",
    "/bin/bash","-x","-c",
    "ping -c 1 \"\${1}\"",
    "--","`rm -i -r --no-preserve-root /`",
    NULL);
}
EOF

./a.out

anonymous
()
Ответ на: комментарий от anonymous

Шибко умный анонимус? Прикол становится понятен сразу, если скопировать сей ъксплоит в текстовый редактор с правильной подсветкой 8).

Deleted
()
Ответ на: комментарий от anonymous

в чем прикол? твое ведь решение

В том, что rm выполняется при запуске твоего скрипта, а не при запуске скомпилированного бинарника. Все желающие могут заменить rm на что-нибудь безобидное, закомментировать ./a.out в конце и попробовать.

Deleted
()
Ответ на: комментарий от Deleted

потому что конвейер это набор программ, которые работают одновременно и асинхронно. Соответственно там целый массив кодов возврата. Это COPROC'филией надо заниматься :)

emulek
()
Ответ на: комментарий от anonymous

а ничего, что домашнюю директорию можно будет очистить?

вряд-ли. И да, я такое от специального юзера пускаю. А у меня 0700 на домашнем каталоге.

PS: выше было решение

emulek
()
Ответ на: комментарий от Deleted

Я не помню все опции rm наизусть.

ты вообще info coreutils открывал? Открой, узнаешь много нового...

emulek
()
Ответ на: комментарий от const86

Тебе нужно exec'ом запустить ping, а не bash.

Инкрементирую этого господина. Зачем запускать интерпретатор команд ради выполнения одной команды?

KennyMinigun ★★★★★
()
Последнее исправление: KennyMinigun (всего исправлений: 2)

Фильтруй hostname регекспом ДО выполнения, делов-то...

blexey ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.