¿Es una buena idea llamar a comandos de shell desde C?

49

Hay un comando de shell de Unix ( udevadm info -q path -n /dev/ttyUSB2 ) al que quiero llamar desde un programa en C. Probablemente con una semana de lucha, podría volver a implementarlo, pero no quiero hacerlo.

¿Es una buena práctica ampliamente aceptada que yo solo llame a popen("my_command", "r"); , o eso introducirá problemas de seguridad inaceptables y enviará problemas de compatibilidad? Me parece mal hacer algo como esto, pero no puedo señalar por qué sería malo.

    
pregunta johnny_boy 19.06.2017 - 16:58

5 respuestas

58

No es particularmente malo, pero hay algunas advertencias.

  1. ¿Qué tan portátil será tu solución? ¿El binario elegido funcionará igual en todas partes, generará los resultados en el mismo formato, etc.? ¿Tendrá una salida diferente en la configuración de LANG etc.?
  2. ¿cuánta carga adicional agrega esto a su proceso? Bifurcar un binario da como resultado una carga mucho mayor y requiere más recursos que ejecutar llamadas de biblioteca (en general). ¿Es esto aceptable en su escenario?
  3. ¿Hay problemas de seguridad? ¿Puede alguien sustituir su binario elegido por otro y realizar actos nefarios a partir de entonces? ¿Utiliza argumentos proporcionados por el usuario para su binario, y podrían proporcionar ;rm -rf / (por ejemplo) (tenga en cuenta que algunas API le permitirán especificar argumentos de forma más segura que simplemente proporcionándolos en la línea de comandos)

En general, estoy contento de ejecutar binarios cuando estoy en un entorno conocido que puedo predecir, cuando la salida binaria es fácil de analizar (si es necesario, es posible que solo necesite un código de salida) y no necesito hazlo muy a menudo.

Como has notado, el otro problema es ¿cuánto trabajo es replicar lo que hace el binario? ¿Utiliza una biblioteca que también puedes aprovechar?

    
respondido por el Brian Agnew 19.06.2017 - 17:05
37

Se requiere mucho cuidado para protegerse contra las vulnerabilidades de inyección una vez que haya introducido un vector potencial. Ahora está en la vanguardia de su mente, pero más adelante puede necesitar la capacidad de seleccionar ttyUSB0-3 , luego esa lista se usará en otros lugares para que se elimine el principio de responsabilidad única, luego el cliente tendrá un requisito para poner un dispositivo arbitrario en la lista, y el desarrollador que haga ese cambio no tendrá idea de que la lista eventualmente se usará de manera insegura.

En otras palabras, codifique como si el desarrollador más descuidado que usted conoce esté realizando un cambio inseguro en una parte del código que aparentemente no tiene relación.

En segundo lugar, la salida de las herramientas CLI generalmente no se consideran interfaces estables a menos que la documentación las marque específicamente como tales. Podría estar bien si cuenta con ellos para una secuencia de comandos que ejecuta que puede solucionar usted mismo, pero no para algo que implemente a un cliente.

Tercero, si desea una manera fácil de extraer un valor de su software, es probable que alguien más lo quiera también. Busca una biblioteca que ya hace lo que quieres. libudev ya estaba instalado en mi sistema:

#include <libudev.h>
#include <sys/stat.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
    struct stat statbuf;

    if (stat("dev/ttyUSB2", &statbuf) < 0)
        return -1;
    struct udev* udev = udev_new();
    struct udev_device *dev = udev_device_new_from_devnum(udev, 'c', statbuf.st_rdev);

    printf("%s\n", udev_device_get_devpath(dev));

    udev_device_unref(dev);
    udev_unref(udev);
    return 0;
}

Hay otra funcionalidad útil en esa biblioteca. Supongo que si necesita esto, algunas de las funciones relacionadas también pueden ser útiles.

    
respondido por el Karl Bielefeldt 20.06.2017 - 01:31
16

En su caso específico, donde desea invocar udevadm , sospecho que podría ingresar udev como una biblioteca y realizar las llamadas de función apropiadas como una alternativa?

por ejemplo, puede ver lo que hace udevadm cuando se invoca en modo "información": enlace y haga llamadas equivalentes a las que está haciendo udevadm.

Esto evitaría muchas de las desventajas enumeradas en la excelente respuesta de Brian Agnew , por ejemplo, no confiar en el binario existente en una ruta determinada, evitando el gasto de bifurcación, etc.

    
respondido por el Adam Krouskop 19.06.2017 - 21:54
7

Tu pregunta parecía requerir una respuesta de bosque, y las respuestas aquí parecen respuestas de árbol, así que pensé que te daría una respuesta de bosque.

Esto es muy raro en cómo se escriben los programas en C. Siempre es cómo se escriben los scripts de shell y, a veces, cómo se escriben los programas de Python, Perl o Ruby.

La gente normalmente escribe en C para facilitar el uso de las bibliotecas del sistema y el acceso directo de bajo nivel a las llamadas del sistema operativo, así como para la velocidad. Y C es un lenguaje difícil de escribir, por lo que si las personas no necesitan esas cosas, entonces no usan C. También se espera que los programas C solo tengan dependencias de bibliotecas compartidas y archivos de configuración.

Desglosar a un subproceso no es particularmente rápido, y no requiere un acceso controlado y detallado a las instalaciones del sistema de bajo nivel, e introduce una dependencia posiblemente sorprendente en un ejecutable externo, por lo que es poco común para ver en los programas de C.

Hay algunas preocupaciones adicionales. Las preocupaciones de seguridad y portabilidad que mencionan las personas son completamente válidas. Por supuesto, son igualmente válidos para los scripts de shell, pero la gente espera este tipo de problemas en los scripts de shell. Pero normalmente no se espera que los programas de C tengan esta clase de problemas de seguridad, lo que los hace más peligrosos.

Pero, en mi opinión, las mayores preocupaciones tienen que ver con la forma en que popen interactuará con el resto de su programa. popen tiene que crear un proceso hijo, leer su salida y recopilar su estado de salida. Mientras tanto, el proceso 'stderr' se conectará al mismo stderr que su programa, lo que puede causar resultados confusos, y su stdin será el mismo que su programa, lo que podría causar otros problemas interesantes. Puedes resolverlo incluyendo </dev/null 2>/dev/null en la cadena que pasas a popen desde que es interpretado por el shell.

Y popen crea un proceso secundario. Si usted mismo hace algo con el manejo de la señal o los procesos de bifurcación, puede terminar recibiendo señales impares SIGCHLD . Sus llamadas a wait pueden interactuar de manera extraña con popen y posiblemente crear condiciones de carrera extrañas.

Las preocupaciones de seguridad y portabilidad están ahí, por supuesto. Como son para los scripts de shell o cualquier cosa que inicie otros ejecutables en el sistema. Y debe tener cuidado de que las personas que usan su programa no puedan obtener los metacaracteres de shell en la cadena que pasa a popen porque esa cadena se asigna directamente a sh con sh -c <string from popen as a single argument> .

Pero no creo que sea por eso que es extraño ver un programa C usando popen . La razón por la que es extraño es porque C es típicamente un lenguaje de bajo nivel, y popen no es un bajo nivel. Y debido a que el uso de popen coloca restricciones de diseño en su programa, ya que interactuará de manera extraña con la entrada y salida estándar de su programa y le resultará difícil realizar su propia gestión de procesos o manejo de señales. Y debido a que normalmente no se espera que los programas C tengan dependencias de ejecutables externos.

    
respondido por el Omnifarious 20.06.2017 - 17:22
0

Su programa puede estar sujeto a piratería, etc. Una forma de protegerse de este tipo de actividad es crear una copia del entorno de su máquina actual y ejecutar su programa utilizando un sistema llamado chroot.

Desde el punto de vista de su programa, se ejecuta en un entorno normal, desde un punto de vista de seguridad, si alguien rompe su programa, solo tiene acceso a los elementos que proporcionó cuando realizó la copia.

Tal configuración se llama chroot jail para obtener más detalles, consulte chroot jail .

Normalmente se utiliza para configurar servidores de acceso público, etc.

    
respondido por el Dave 20.06.2017 - 12:54

Lea otras preguntas en las etiquetas