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 yo mismo, pero no quiero hacer eso.
¿Es una buena práctica ampliamente aceptada para mí simplemente llamar popen("my_command", "r");
, o eso introducirá problemas de seguridad inaceptables y remitirá problemas de compatibilidad? Me parece mal hacer algo como esto, pero no puedo señalar por qué sería malo.
udevadm
ejecutable malicioso en el mismo directorio que su programa y usarlo para escalar privilegios? No sé mucho sobre la seguridad de Unix, pero ¿se ejecutará su programasetuid root
?PATH
, entonces no, tendría que ejecutarse con él./udevadm
. Sin embargo, IIRC Windows ejecutaría ejecutables del mismo directorio.Respuestas:
No es particularmente malo, pero hay algunas advertencias.
LANG
etc.?;rm -rf /
(por ejemplo) (tenga en cuenta que algunas API le permitirán especificar args de manera más segura que solo proporcionarlos en la línea de comandos)En general, estoy contento de ejecutar archivos 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 hacerlo también 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 puede aprovechar?
fuente
Forking a binary results in a lot more load and requires more resources than executing library calls (generally speaking).
Si esto fuera en Windows, estaría de acuerdo. Sin embargo, el OP especifica Unix donde la bifurcación tiene un costo lo suficientemente bajo como para que sea difícil decir si cargar dinámicamente una biblioteca es peor que la bifurcación.exec
que delfork
. Cargando secciones, vinculando objetos compartidos, ejecutando código de inicio para cada uno. Y luego tener que convertir todos los datos en texto para analizarlos en el otro lado, tanto para las entradas como para las salidas.udevadm info -q path -n /dev/ttyUSB2
no va a funcionar en Windows de todos modos, por lo que toda la discusión es un poco teórica.Se debe tener mucho cuidado para evitar vulnerabilidades de inyección una vez que haya introducido un vector potencial. Ahora está en la vanguardia de su mente, pero más tarde puede necesitar la capacidad de seleccionar
ttyUSB0-3
, luego esa lista se usará en otros lugares, por lo que se tendrá en cuenta para seguir el principio de responsabilidad única, luego un cliente tendrá que poner un dispositivo arbitrario en la lista, y el desarrollador que realiza ese cambio no tendrá idea de que la lista eventualmente se utilizará de manera insegura.En otras palabras, codifique como si el desarrollador más descuidado que conoce esté haciendo un cambio inseguro en una parte aparentemente no relacionada del código.
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. Puede que esté bien contar con ellos para un script que ejecute y que pueda solucionarlo usted mismo, pero no para algo que implemente en un cliente.
En tercer lugar, si desea una manera fácil de extraer un valor de su software, es probable que alguien más también lo desee. Busque una biblioteca que ya haga lo que quiere.
libudev
ya estaba instalado en mi sistema:Hay otra funcionalidad útil en esa biblioteca. Supongo que si necesitabas esto, algunas de las funciones relacionadas también podrían ser útiles.
fuente
ttyUSB2
y usassprintf(buf, "udevadm info -q path -n /dev/%s", argv[1])
. Ahora eso parece bastante seguro, pero ¿y si loargv[1]
es"nul || echo OOPS"
?En su caso específico, donde desea invocar
udevadm
, sospecho que podría ingresarudev
como una biblioteca y realizar las llamadas a funciones apropiadas como alternativa.por ejemplo, podría echar un vistazo a lo que está haciendo udevadm cuando se invoca en modo "info": https://github.com/gentoo/eudev/blob/master/src/udev/udevadm-info.c y hacer llamadas equiv. en cuanto a esos udevadm está haciendo.
Esto evitaría muchas de las desventajas enumeradas en la excelente respuesta de Brian Agnew, por ejemplo, no depender del binario existente en un camino determinado, evitar el gasto de bifurcación, etc.
fuente
Su pregunta parecía requerir una respuesta de bosque, y las respuestas aquí parecen respuestas de árbol, así que pensé en darle una respuesta de bosque.
Raramente es así como 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 Python, perl o Ruby.
Las personas suelen escribir en C para facilitar el uso de las bibliotecas del sistema y el acceso directo de bajo nivel a las llamadas al 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 dependan de bibliotecas compartidas y archivos de configuración.
Desplazarse 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 ver en los programas 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 ese tipo de problemas en los scripts de shell. Pero, por lo general, no se espera que los programas C tengan este tipo de preocupación de seguridad, lo que lo hace más peligroso.
Pero, en mi opinión, las mayores preocupaciones tienen que ver con la forma en
popen
que 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 estará conectado 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. Puede resolver eso al incluir</dev/null 2>/dev/null
en la cadena a la que pasa,popen
ya que es interpretada por el shell.Y
popen
crea un proceso hijo. Si hace algo con el manejo de la señal o los procesos de bifurcación, puede terminar recibiendoSIGCHLD
señales extrañas . Sus llamadas await
pueden interactuar de manera extrañapopen
y posiblemente crear condiciones de carrera extrañas.Las preocupaciones de seguridad y portabilidad están ahí, por supuesto. Como son para 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 metacaracteres de shell en la cadena que ingresa
popen
porque esa cadena se le da directamentesh
consh -c <string from popen as a single argument>
.Pero no creo que sea por eso que es extraño ver un programa en C usando
popen
. La razón por la que es extraño es porque C es típicamente un lenguaje de bajo nivel ypopen
no es de bajo nivel. Y debido al uso depopen
restricciones de diseño de lugares en su programa porque interactuará de manera extraña con la entrada y salida estándar de su programa y hará que sea difícil hacer 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.fuente
Su programa puede estar sujeto a piratería, etc. Una forma de protegerse de este tipo de actividad es crear una copia del entorno actual de su máquina y ejecutar su programa utilizando un sistema llamado chroot.
Desde el punto de vista de su programa, se está ejecutando en un entorno normal, desde un aspecto de seguridad, si alguien interrumpe su programa, solo tiene acceso a los elementos que proporcionó cuando realizó la copia.
Tal configuración se llama chroot jail. Para más detalles, vea chroot jail .
Normalmente se usa para configurar servidores de acceso público, etc.
fuente