Tag: GNU-Linux

Resetear contraseña olvidada de tu Raspberry Pi

La semana pasada me encontré con el problema de tener que acceder vía SSH a una Raspberry Pi (rpi), para poder gobernar una serie de maquinas accesibles desde la red local a la que se conectaba dicha rpi, y no poder hacerlo porque no recordaba la contraseña…

Al ver que no recordaba la contraseña me puse a buscar la mejor solución para poder volver a tener acceso a la rpi. Alguno puede pensar que con volver a poner el sistema operativo Raspbian, queda todo solucionado, y tiene razón. El problema, es que toda la configuración y software específico se borra, teniendo que volver a cargar el sistema operativo, instalar el software y generar de nuevo las configuraciones… vamos un peñazo XD .

Alguno se le ocurriría montar un script en el que ir probando las posibles combinaciones por fuerza bruta. En mi caso no era una opción, ni suele serlo en general. Con que cumplas los criterios básicos de una contraseña decente la combinatoria se dispara… Los tiempos en los que las claves eran de hasta 6 caracteres normalmente sólo letras y/o números han quedado atrás.

Con todo ello en mente le di alguna vuelta más al asunto…

Me puse a pensar cuál es nivel de seguridad que me podría permitir resetear la clave, y pronto caí que en el físico. Tenía acceso físico al hardware de la rpi, con lo que cogí la tarjeta SD y la introduje en mi ordenador. En su interior enseguida encontré un archivo, que mirando un poco en Internet, entendí que me podía dar la clave para poder acceder al sistema antes de que completamente se cargase y me obligase a idetificarme con las credenciales de acceeso, de la cual había perdido la contraseña.

El fichero en cuestión es el «cmdline.txt» y contiene parámetros específicos para la carga del kernel de Raspbian:

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

Según la documentación es posible cargar la ejecución de cualquier binario con el parámetro «init«, y en este caso nos interesa cargar la shell. Para ello añadimos «init=/bin/sh» al final de la línea precedido por un espacio:

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait init=/bin/sh

Hecha esta modificación en el fichero lo guardamos, volvemos a introducir la SD en la rpi y la arrancamos. Esta vez durante el arranque, se parará en cierto momento la carga del sistema y nos aparecerá un prompt en el que podremos escribir comandos.

Antes de escribir cualquier comando debemos remontar el volumen para que efectivamente guarde los cambios. Para ello vamos a volver a montar el sistema de ficheros como lectura y escritura con el siguiente comando.

mount -rw -o remount /

Una vez hecho, vamos a invocar al comando que nos permite resetear la contraseña de nuestro usuario (normalmente pi si no los has cambiado):

passwd pi

Te pedirá que introduzcas la contraseña y otra vez más para confirmarla. Si todo ha ido bien verás un mensaje de este tipo:

passwd: password updated successfully

Ahora sincronizaremos los cambios que hemos hecho que están en memoria, para que queden persistentes en la memoria de la SD. Después reanudaremos el boot del sistema:

sync
exec /sbin/init

Una vez se haya cargado el sistema comprueba que puedes acceder con la clave que intruduciste y apaga la rpi con:

sudo poweroff

Una vez apagada la rpi volvemos a cargar la SD en el ordenador y editamos de nuevo el fichero «cmdline.txt» y lo volvemos a dejar como al inicio sin el parámetro «init«:

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

Ahora ya podemos volver a poner la tarjeta SD en la Raspberry Pi y podremos volver a tener el sistema Raspbian como si nunca hubiéramos perdido la contraseña.

Recordad que esto ha sido posible gracias a tener acceso físico al hardware que ejecuta el software. El nivel físico siempre es el nivel más débil a nivel técnico y de infraestructura, por eso en Seguridad Informática hay que cuidar no sólo a quién damos acceso mediante credenciales, sino también quién tiene acceso físicamente al hardware donde se corre el software.

Averiguar dominio final de un nombre de dominio alternativo

Recientemente me he encontrado en la situación de tener que usar un SMTP para mandar emails. En estos casos el servicio suele estar vinculado a un dominio que apunta a la maquina donde se encuentra corriendo el servidor SMTP corriendo el servicio. Suele ser cada vez más muy habitual que además estos  servicios estén securizados con SSL para proteger las conexiones.

Por otro lado, puede darse que para un dominio se tengan dominios alternativos que aseguren el acceso a la máquina, que en mi caso, es la que corre el servidor SMTP. Esto asegura que se pueda acceder a la máquina usando distintos dominios que se proveen desde distintos proveedores de dominio. Esta no es la única situación que lo justifica, sino que también puede hacerse porque un proveedor de servicios ofrezca personalización de los servicios con el dominio del cliente, permitiendo que una empresa o un usuario particular, tenga dichos servicios usando su propio dominio.

Aquí  es donde ocurre el problema, porque los certificados SSL para cifrar las conesxiones se asocian a un dominio y la redirección de dominios alternativos  provoca que no coincida el certificado del dominio final con el dominio alternativo con el que se accede, dando como resultado un error del tipo:

Error: Hostname/IP doesn't match certificate's altnames: "Host: smtp.mascandobits.es. is not in the cert's altnames: DNS:*.servidoresdns.net, DNS:servidoresdns.net"

Los certificados SSL se asocian a un único demonio por normal general, aunque es posible generar certificados que abarquen varios dominios (dominio + dominios alternativos).

Si tienes este problema y quieres seguir usando la conexión segura con SSL, tu única opción (si has contratado con el proveedor de servicios) es averiguar el dominio principal para el que es válido el certificado. Normalmente va a ser el del dominio final.

Desde Linux esta tarea es bien sencilla, basta con usar primero el comando «nslookup» para comprobar la dirección IP final, obteniendo cómo resuelve el DNS dicho dominio. Pongamos un ejemplo de un servicicio SMTP con dominio «smtp.mascandobits.es«:

nslookup smtp.mascandobits.es
Server:         208.67.222.223
Address:        208.67.222.223#53

Non-authoritative answer:
Name:   smtp.mascandobits.es
Address: 217.76.127.101

En la parte de «Address» de la sección «Non-authoritative answer» obtenemos la dirección IP final. Con esa IP aplicamos el comando «host» que permite resolver de manera inversa el dominio. Es decir, pasar de una IP a un nombre de dominio.

host 217.76.127.101
100.128.76.218.in-addr.arpa domain name pointer smtp-13.servidoresdns.net.

En este ejemplo el comando nos resuelve que el dominio principal que apunta a esa IP es «smtp-13.servidoresdns.net«. Este dominio resulta ser el que coincide con el certificado SSL, solventando el error que se presentaba al inicio de la entrada.

Alguno pensará que para qué complicarse tanto si se puede obtener la IP final de la máquina que da el servicio. La respuesta es bien sencilla. En un dominio se puede redirigir hacia donde apunta, y de manera transparente un cambio de IP en la máquina que aloja el servicio, no conlleva el cambiar el nombre de dominio hacia donde se apunta el servicio. Es decir, basta con que el dominio apunte a la nueva IP donde se encuentra la máquina.

OpenCV – Mouse Event Handler (parte 2)

Continuando la entrada «OpenCV – Mouse Event Handler (parte 1)«, que ya publiqué hace un tiempo, os traigo la segunda parte que da cierre al manejo de eventos del ratón con OpenCV. Si en la primera parte me centraba en el manejo puro de los eventos del ratón, en esta entrada hablaré sobre la combinación de eventos de ratón con eventos de teclado.

opencv_mouse-event_display

La combinación de eventos de teclado con los eventos de ratón, son muy útiles por ejemplo cuando queremos asegurarnos que las acciones con el ratón no son fortuitas, y por ello se asegura la acción del ratón con la pulsación de alguna tecla. Un ejemplo común puede ser el pulsar la tecla «CRTL» o «SHIFT» cuando se hace click con el ratón. Otro ejemplo puede ser la de diferenciar dos acciones distintas desde un click de ratón, gracias a la pulsación combinada con distintas teclas.

El siguiente ejemplo toma como partida el ya expuesto en la entrada de la parte 1 y modifica la función «mouseEventHandler» de la siguiente forma:

void mouseEventHandler(int event, int x, int y, int flags, void* param)
{
     if ( flags == (EVENT_FLAG_CTRLKEY + EVENT_FLAG_LBUTTON) )
     {
          cout << "Left mouse button is clicked while pressing CTRL key - position (" << x << ", " << y << ")" << endl;
     }
     else if ( flags == (EVENT_FLAG_SHIFTKEY + EVENT_FLAG_RBUTTON) )
     {
          cout << "Right mouse button is clicked while pressing SHIFT key - position (" << x << ", " << y << ")" << endl;
     }
     else if ( event == EVENT_MOUSEMOVE && flags == EVENT_FLAG_ALTKEY)
     {
          cout << "Mouse is moved over the window while pressing ALT key - position (" << x << ", " << y << ")" << endl;
     }
}

Cada vez que se produzca alguno de los siguientes eventos:

  • EVENT_FLAG_LBUTTON
  • EVENT_FLAG_RBUTTON
  • EVENT_FLAG_MBUTTON
  • EVENT_FLAG_CTRLKEY
  • EVENT_FLAG_SHIFTKEY
  • EVENT_FLAG_ALTKEY

Se invocará a la función callback “mouseEventHandler“. En este caso, en la sucesión de los distintos “if” que permite identificar el tipo de evento, se incluye además  una condición de teclado. En el caso de los clicks izquierdo y derecho del ratón, se puede combinar las flags de pulsación del ratón con los del teclado, aplicando una suma aritmética de los valores. En el caso del movimiento del ratón, esto no es aplicable, ya que el movimiento del teclado es de tipo «EVENT» y el de la tecla es de tipo «EVENT_FLAG«. A causa de ello se realiza una operación lógica entre los resultados obtenidos en las comparaciones de cada  tipo.

opencv_mouse-event_key-event_display

A continuación dejo el código completo de la arquitectura básica para el manejo de eventos con pulsaciones de teclado:

OpenCV_Mouse_Event_Handler_2.cpp

 

Mapeo persistente de dispositivos USB en Linux

Actualmente los sistemas operativos son capaces de detectar y montar dispositivos para poder ser usados por el usuario a través del sistema, haciendo que estos dispositivos conectados al sistema sean accedidos de manera transparente.

En Linux cuando conectamos cualquier dispositivo como un dispositivo USB, el sistema lo mapea automáticamente como un terminal en «/dev«. El dispositivo aparece con el prefijo «tty» más un identificador del tipo de conexión que implementa a nivel de hardware el dispositivo (USB, ACM, S, …) y un número que numera los distintos dispositivos del mismo tipo. Para ver los dispositivos que tiene conectados y mapeados en «/dev» ejecuta los siguientes comandos:

cd /dev   # Movernos a /dev
ls   # Listar elementos del directorio

Debido a que el nombre del mapeo de cualquier dispositivo depende de los dispositivos que haya conectados previamente al sistema operativo, puede resultar complicado automatizar procesos que dependen de dispositivos que pueden ser conectados y desconectados en caliente (mientras el sistema operativo está encendido). Para solucionar el problema vamos a realizar un re-mapeo sobre el mapeo automático que hace el sistema.

Lo primero que hay que identificar es el IDVENDOR y IDPROUDCT del dispositivo que queremos mapear de manera persistente. El IDVENDOR es un número que identifica al fabricante del dispositivo y el IDPRODUCT es otro número que identifica al producto del vendedor. Con estos dos valores, suele ser suficiente para mapear unívocamente dispositivos (a no ser que tengamos varios dispositivos idénticos conectados 🙁 , pero eso también tiene solución 😉 ). Para listar los dispositivos y ver ambos valores ejecuta «lsusb«:

lsusb
# Result
Bus 001 Device 004: ID 0846:9041 NetGear, Inc. WNA1000M 802.11bgn [Realtek RTL8188CUS]
Bus 001 Device 005: ID 16d0:0856 MCS 
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. 
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Detectar cuál es el dispositivo es tan fácil como hacer «lsusb» antes de conectar el dispositivo  y volver a hacer «lsusb» con el dispositivo conectado. El valor del IDVENDOR es el valor comprendido entre el ID y los dos puntos, y el IDPRODUCT el valor comprendido entre los dos puntos y el primer espacio en blanco.

Si necesitamos mapear varios dispositivos de un mismo producto que son de un mismo fabricante, repetiremos el mismo proceso pero listando los puertos conectados y mapedados en «/dev» para conocer el mapeo que le asigna el sistema (ver primer snippet de código) y procederemos a ejecutar el siguiente comando para conocer el el valor SERIAL del dispositivo:

udevadm info -a -n /dev/ttyXXX | grep '{serial}' | head -n1
# Resultado
ATTRS{serial}=="XXXXXXXX"

Donde ttyXXX puede ser ttyUSB0, ttyUSB1, ttyUSB2 …, ttyACM0, ttyACM1, ttyACM2 …, ttyS0, ttyS1, ttyS2 … El valor que nos devuelve de SERIAL es un identificador único para el dispositivo conectado.

Con esta información vamos a crear unas reglas para que al conectar el dispositivo nos realice el mapeo de manera persistente y estática de los dispositivos conectados. Para ello vamos a definir unas reglas UDEV en el siguiente directorio «/etc/udev/rules.d«, creando el fichero «99-usb-serial.rules«:

sudo nano 99-usb-serial.rules

El fichero lo crearemos con la siguiente estructura si queremos mapear un dispositivo de acuerdo a un un fabricante y producto:

SUBSYSTEM=="tty", ATTRS{idVendor}=="XXXX", ATTRS{idProduct}=="XXXX", SYMLINK+="nombre_mapeo_1"
SUBSYSTEM=="tty", ATTRS{idVendor}=="XXXX", ATTRS{idProduct}=="XXXX", SYMLINK+="nombre_mapeo_2"
...

Si además necesitas diferencia diversos dispositivos de un mismo fabricante, añade el serial del dispositivo:

SUBSYSTEM=="tty", ATTRS{idVendor}=="XXXX", ATTRS{idProduct}=="XXXX", ATTRS{serial}=="XXXXXXXX", SYMLINK+="nombre_mapeo_1"
SUBSYSTEM=="tty", ATTRS{idVendor}=="XXXX", ATTRS{idProduct}=="XXXX", ATTRS{serial}=="XXXXXXXX", SYMLINK+="nombre_mapeo_2"
...

Deberemos completar los valores «XXXX» con la información que hemos recogido en los pasos anteriores y guardamos haciendo CTRL+O y salimos con CTRL+X.

El fichero de reglas que hemos definido crea enlaces simbólicos que apuntan a las direcciones de acceso de los dispositivos y dichos enlaces simbólicos siempre apuntarán al dispositivo que hemos definido.

Si tras generar el fichero de reglas, no se realiza el mapeo que hemos definido, prueba conectar y desconectar el dispositivo, o a reiniciar el sistema. Os aparecerá en «/dev» un enlace simbólico con el nombre que hayáis definido en «SYMLINK+»

A la hora de usar estos mapeos desde scripts de Linux, debeís usar el comando:

readlink -f [symbolic_link]

De esta forma se obtiene la ruta completa a donde apunta el enlace simbólico y podremos programar scripts de manera más transparente, gracias al mapeo, el cual  nos generará un enlace simbólico persistente al dispositivo que conectemos.