domingo, 24 de marzo de 2013

Linux Mint en Bangho A1 con wifi RTL8723

Como equipos para desarrolladores, compramos unas notebooks Bangho A1, que tienen una buena relación precio / prestaciones. Traen 8GB de RAM y 750GB de disco con una cpu AMD C70 dual core. La CPU es medio floja pero para el tipo de desarrollo que hacemos, lo que mas impacta es la cantidad de RAM disponible.
Otro tema fundamental es que las notebooks vienen SIN sistema operativo. No vamos a pagar una licencia a Microsoft por un Windows 8 que vamos a borrar inmediatamente.

Instalamos Linux Mint Nadia 14 (64 bits) (http://www.linuxmint.com). Bajando la imagen de internet a un pen drive (o un DVD si les queda mejor) e instalando desde allí.
La instalación no tiene misterios, salvo porque no funciona el WiFi.

El WiFi RTL8723 es un chipset nuevo que no tiene soporte "out-of-the-box" para las distros actuales. Pero si tiene soporte en un kernel mas nuevo, el 3.8.

Una vez completada la instalación, reiniciamos y usando la red cableada o la ayuda de otro equipo con internet descargamos estos archivos:

linux-headers-3.8.4-030804-generic_3.8.4-030804.201303201832_amd64.deb
linux-headers-3.8.4-030804_3.8.4-030804.201303201832_all.deb
linux-image-3.8.4-030804-generic_3.8.4-030804.201303201832_amd64.deb
linux-image-extra-3.8.4-030804-generic_3.8.4-030804.201303201832_amd64.deb

Una vez descargados, estan en la carpeta ~/Descargas. Abrimos la terminal y escribimos:

sudo dpkg -i *.deb

Eso va a instalar el kernel 3.8.4 que trae soporte nativo para esta placa de wifi. Pero tiene un bug y es que le falta el archivo del firmware del chip. 
Vamos a conseguirlo desde otra fuente (el git ya viene instalado en Mint):

git clone git://git.kernel.org/pub/scm/linux/kernel/git/dwmw2/linux-firmware.git

sudo cp -v linux-firmware/rtlwifi/rtl8723* /lib/firmware/rtlwifi/.





Ya estamos listos. A reiniciar la maquina ya esta funcionando el wifi.

El resto del hardware funciona sin problemas (camara web, audio, red cableada, bluetooth, usb, touchpad con multitouch).


jueves, 13 de septiembre de 2012

Buffer overflow

Ataque via Buffer Overflow.

Este tipo de ataque se basa en que el software atacado recibe datos que no valida en su longitud y el lenguaje utilizado para la programación permite el manejo libre de la memoria (Caso del lenguaje C y C++).

Enviando mayor cantidad de datos que los esperados por el software, se puede llegar a escribir otra área de la memoria que está ocupada por otras variables u código y hacer que el programa deje de funcionar o ejecute un código inyectado por el atacante.

Supongamos que para el programa espera como entrada el nombre de una persona, y el programador reservó 250 caracteres para almacenar este dato. Si no valida la longitud, al mandar como entrada un string de mas de 250 caracteres, el excedente va a sobreescribir los datos de otras variables o hará que el programa deje de funcionar. (se interrumpa la ejecución o sea que se cuelgue)

El arte consiste en saber de antemano que es lo que hay luego del final del buffer para ver que cosas se pueden llegar a hacer.

Factores que mitigan este problema:
- El exploit funciona para una versión especifica del software.
- Se ejecuta con los privilegios del usuario que tenga el software atacado.

Contramedidas de los navegadores de internet
- Utilización de la memoria al azar. Esto hace el Internet explorer. Hace muy dificil predecir que se va a afectar con el overflow, ya que cada vez que se ejecuta, la memoria vecina es diferente.

- Bit de ejecución. Los procesadores marcan las páginas de memoria como segmentos "ejecutables" y "no ejecutables". Los datos van en páginas "no ejecutables" y ante un eventual buffer overflow, el código inyectado no podrá ejecutarse nunca.

- Sandbox. Esto hace el google chrome. El proceso esta contenido en un "arenero" donde todo proceso se ejecuta dentro del mismo y no puede salir fuera. De esta forma si se compromete el proceso de la página web, el daño solo puede hacerse sobre esa página y no se puede alterar el contenido del sistema de archivos, o robar datos del mismo.

En conclusión es un método muy complicado, pero una vez ubicado en alguna pieza clave del software y se libera un exploit en internet, es capaz de ejecutar cualquier cosa sobre la computadora de la víctima, como hacer que la misma descargue e instale un root-kit.

Ejemplo

Un caso famoso fue el de una vulnerabilidad en la librería que procesa imágenes. Como todo programa despliega imágenes, la librería se usa en todos lados por el sistema operativo y el navegador de internet.
El formato de archivo JPEG contiene, aparte de la imagen, datos en modo texto que se llaman "metadatos". Estos metadatos indican donde se tomó la foto, el nombre de la cámara utilizada, etc. El error consistía en que la librería no validaba la longitud de los metadatos.
El exploit permitía hacer una foto JPEG que al ser visualizada por casi cualquier programa de windows infectaba la PC. Estamos hablando de cualquier imagen que me llega en un email, o simplemente posteada en Facebook o un blog.
Estos bufferoverflows "de oro" no se ven todos los días, pero la capacidad de daño es muy grande.

(si te interesa saber mas de este, buscar en Google por la gdiplus.dll buffer overflow)

Igualmente problemas de seguridad surgen todos los días y el mundo sigue dando vueltas. Podes consultar el servicio de alertas de Microsoft:


********************************************************************
Microsoft Security Bulletin Summary for September 2012
Issued: September 11, 2012
********************************************************************

This bulletin summary lists security bulletins released for 
September 2012.

The full version of the Microsoft Security Bulletin Summary for 
September 2012 can be found at 
http://technet.microsoft.com/security/bulletin/ms12-sep.

lunes, 14 de mayo de 2012

Validaciones de formulario asincrónicas

Muchas veces necesitamos hacer validaciones del formulario que requieren un procesamiento que es imposible de hacer en el browser y requieren un contacto con otro servidor vía ajax. Por ejemplo si queremos georeferenciar una dirección, o validar una cuenta de Twitter.

La forma correcta de hacer esto, es hacer una validación asincrónica.
Para hacer esto, se declara la función de validación tradicional en el presentation PHP. El cambio viene en la parte de Javascript.

Para hacer que el framework interprete que la validación es asincrónica, la función DEBE retornar el string "async". Si retorna OTRA COSA, se lo considera un mensaje de error de una función de validación sincrónica.

Veamos un ejemplo sencillo, donde simulamos un evento asincrónico mediante un setTimeout().

En el constructor del presentation ponemos:




$fld = $parent;
$fld->m_js_validate = "organizaciones.validate";




Esto indica que para validar, vamos a buscar un objeto "organizaciones" que implementa un método "validate".

El javascript es este:


function organizaciones_validate() {
this.fieldID = "";
this.callback = null;
this.validate = function(fieldID,FieldLabel,callback) {
this.fieldID = fieldID;
this.callback = callback;
setTimeout(this.async_validate, 1000);
return "async";
};
this.async_validate = function () {
organizaciones.callback(organizaciones.fieldID, false, "Un error simulado");
};
}

var organizaciones = new organizaciones_validate();


La función "validate" recibe como parámetros, el ID del campo, el Label y una función de callback para informar, cuando sea oportuno del resultado de la validación.

En el ejemplo, salvamos los datos del ID del campo que vamos a necesitar cuando tengamos la respuesta, y la función de callback. Disparamos un timer que ejecutará la función "async_validate" un segundo mas tarde.

Al ejecutar "async_validate",  vamos a llamar a la función de callback, pasando como parámetros el ID del campo, si la validación terminó bien (true) o mal (false) y un mensaje de error.

La ejecución del proceso de validación va a esperar que todas las funciones de validación asincrónicas terminen de ejecutar, antes de mostrar el mensaje de error o bien continuar y salvar el formulario.

Exactamente igual es el caso de un editor de un tabla.

Si vamos a usar un ajax, seguramente vamos a reemplazar a setTimeout por $.ajax() pero el resto del código es idéntico.

martes, 24 de abril de 2012

Creando un dashboard

Creando un dashboard en una home page para un rol



El resultado final de la home queda así:


Y haciendo un drill-down sobre la primera columna:







Para generar los gráficos, usamos la librería Highcharts.
La estrategia es la siguiente:
1) Creamos una home page dinamica y le asignamos un rol. En la home page, incluimos el CSS y los javascript necesarios para que funcione el Higcharts.


2) Creamos un javascript especifico para esta home page, que cuando este listo el DOM traerá los datos via ajax.


3) Creamos un presentation, donde se implementan los métodos de ajax que llama el javascript en la parte 2)


Home Page Dinámica:


<?php
if(!class_exists('home_terminales'))
{
class home_terminales {

public function Render($context)
{
global $sess;
$html = '
<script type="text/javascript" src="'.WEB_PATH.'/common/Highcharts-2.1.9/js/highcharts.js"></script>

<script type="text/javascript" src="'.WEB_PATH.'/includes/homepage/comp/home_terminales.js"></script>

<style>
.botones {text-align:left; width:950px;margin:0 auto;}
#phome {text-align:left; width:950px; margin:0 auto; min-height:800px;}
#shortcuts {display:none;}
#graf_eventos {width:950px;height:320px;margin-top:10px;}
#graf_sitios {width:950px;height:320px;;margin-top:10px;}
#botones {border:solid 1px #ddd; border-radius:5px; padding:10px;text-align:right;height:50px;}
#navegador {font: 18px Helvetica,Arial;font-weight:bold;float:left;}
#fechas, #totales {font: 12px Helvetica,Arial;float:left;clear:left;}
#volver {display:none;}
#cambio_fecha {display:inline;}
</style>
<style media="print">
#botones button, #volver, #main {display:none;}
</style>

<div class="botones">
</div>

<div id="phome">
<div id="botones">
<div id="navegador"></div>
<div id="fechas"></div>
<div id="totales"></div>
<div id="cambio_fecha">
<button onclick="setDias(1)">Dia</button>
<button onclick="setDias(7)">Semana</button>
<button onclick="setDias(15)">Quincena</button>
<button onclick="setDias(30)">Mes</button>
<button onclick="setDias(180)">Semestre</button>
</div>
<button class="btn_imprimir" onclick="print()">Imprimir</button>
<button  id="volver" onclick="goHome()">Volver atrás</button>
</div>
<div id="graf_eventos"></div>
<div id="graf_sitios"></div>
</div>
';
$content["home_terminales"] = $html;
$content["upload"] = "";
return array( $content, array() );
}
}
}



Como se ve, la home page es sencilla. Sigue la convención de estar sobre /includes/homepage/comp y el archivo llamarse como la clase que implementa, en este caso, 'home_terminales.php'


Luego vamos a saltar al presentation, quien se encarga de buscar los datos que luego se usan para hacer los gráficos. La misión es bien especifica, generar un objeto JSON con los datasets.



<?php
 include_once "common/cdatatypes.php";

class CDH_HOME_TERMINALES_AJAX extends CDataHandler 
{
function __construct($parent) 
{
    parent::__construct($parent);
}

function render_eventos($p) {
global $terminales_db;
$total = 0;
$bars = array();
$sql = "select count(*), organismo from log l join terminales t on l.ip=t.ip where fecha>=(NOW() - INTERVAL {$p} DAY) group by organismo order by 1 desc";
$rs = $terminales_db->do_execute($sql);
while( $row = $terminales_db->_fetch_row($rs) ) {
$bars[] = array("cant"=>intval($row[0]), "organismo"=>$row[1]);
$total+=intval($row[0]);
}
$pie = array();
$sql = "select count(*), sitio from log l join terminales t on l.ip=t.ip where fecha>=(NOW() - INTERVAL {$p} DAY) group by sitio order by 1";
$rs = $terminales_db->do_execute($sql);
while( $row = $terminales_db->_fetch_row($rs) ) {
$pie[] = array("cant"=>intval($row[0]), "organismo"=>$row[1]);
}
return json_encode((object) Array("bars"=>$bars, "pie"=>$pie, "total"=>$total));
}


function render_eventos_tiempo($p) {
global $terminales_db;
list($dias, $terminal) = explode("|",$p);
$total=0;
$spline = array();
$sql = "select count(*), UNIX_TIMESTAMP(DATE(fecha)) from log l join terminales t on l.ip=t.ip where  t.organismo='{$terminal}' and fecha>=(NOW() - INTERVAL {$dias} DAY) group by TO_DAYS(fecha) order by 2";
$rs = $terminales_db->do_execute($sql);
while( $row = $terminales_db->_fetch_row($rs) ) {
$spline[] = array("cant"=>intval($row[0]), "fecha"=>intval($row[1]));
$total+=intval($row[0]);
}

$pie = array();
$sql = "select count(*), sitio from log l join terminales t on l.ip=t.ip where t.organismo='{$terminal}' and fecha>=(NOW() - INTERVAL {$dias} DAY) group by sitio order by 1";
$rs = $terminales_db->do_execute($sql);
while( $row = $terminales_db->_fetch_row($rs) ) {
$pie[] = array("cant"=>intval($row[0]), "organismo"=>$row[1]);
}
return json_encode((object) Array("spline"=>$spline, "pie"=>$pie, "total"=>$total));
}

}


En este caso, hay dos funciones declaradas, la primera genera el Dashboard inicial, y la segunda genera la vista de drill-down. En este problema en particular, la consulta a la base de datos es trivial. Esa es la parte donde hay que evaluar con mucho cuidado si conviene usar la base transaccional o pensar un esquema de datamart, donde los resultados esten precalculados por otro script que se ejecuta cada X tiempo.


Finalmente, se junta una cosa con otra mediante el javascript que se ejecuta al completar el DOM.


var chart1 = null;
var chart2 = null;
var dias = 7;
var drilling = "";


$(document).ready(function() {


Highcharts.setOptions({
  global: {
     useUTC: false
  }
});


setDias(7);
});


function actualiza_eventos() {


var j = new rem_request(this,function(obj,json){
var jdata = eval("("+json+")");

$("#totales").html("Total eventos: "+jdata.total);
$("#cambio_fecha").show();


chart1=null;
var options = {
       chart: {
          renderTo: 'graf_eventos',
          defaultSeriesType: 'column',
          height: 300,
          width: 950
       },
       title: {
          text: 'Eventos por terminal'
       },
       xAxis: {
           categories: ['Terminal']
       },
       yAxis: {
          title: {
             text: 'Eventos'
          }
       },
       series: [],
       credits: { enabled: false },
       plotOptions: {
           column: {
              cursor: 'pointer',
              point: { events: {'click':drill_eventos} }
           }               
        }
    };


var bars = jdata.bars;
for( var k=0; k<bars.length; k++) {
var n = bars[k].organismo;
var v = bars[k].cant;
options.series.push( {"name":n, "data":[v], dataLabels: {
enabled: true,
rotation: 0,
color: '#444444',
align: 'right',
x: -3,
y: -3,
formatter: function() {
return this.y;
},
style: {
font: 'normal 13px Verdana, sans-serif'
}
} } );
}

chart1 = new Highcharts.Chart(options);


chart2=null;

var options = {
       chart: {
          renderTo: 'graf_sitios',
          defaultSeriesType: 'pie',
          height: 300,
          width: 950
       },
       title: {
          text: 'Eventos por sitio'
       },
       plotOptions: {
           pie: {
              allowPointSelect: true,
              cursor: 'pointer',
              dataLabels: {
                 enabled: true,
                 color: '#000000',
                 connectorColor: '#000000',
                 formatter: function() {
                    return '<b>'+ this.point.name +'</b>: '+ Highcharts.numberFormat(this.percentage, 0) +' %';
                 }
              }
           }
       },
       series: [{"name":"pie", "type":"pie", "data":[] }],
       credits: { enabled: false },
       tooltip: {
           formatter: function() { 
            return Highcharts.numberFormat(this.y, 0)+ " eventos";
           }
        }
    };


var pie = jdata.pie;
for( var k=0; k<pie.length; k++) {
var n = pie[k].organismo;
var v = pie[k].cant;
options.series[0].data.push( {"name":n, "y":v} );
}

chart2 = new Highcharts.Chart(options);


},"HOME_TERMINALES_AJAX","render_eventos",dias);
}




function setDias(cant) {

dias = cant;
chart1=null;
chart2=null;
actualiza_eventos();

if(cant==1) {
$("#navegador").html("Datos de las últimas 24hs.");
var hoy = new Date();
$("#fechas").html( date_to_string(hoy) );
}
if(cant==7) {
$("#navegador").html("Datos de los últimos 7 días.");
var hasta = new Date();
var desde = new Date();
desde.setDate(desde.getDate()-7);
$("#fechas").html( date_to_string(desde) + " al " + date_to_string(hasta) );
}
if(cant==15) {
$("#navegador").html("Datos de los últimos 15 días.");
var hasta = new Date();
var desde = new Date();
desde.setDate(desde.getDate()-15);
$("#fechas").html( date_to_string(desde) + " al " + date_to_string(hasta) );
}
if(cant==30) {
$("#navegador").html("Datos de los últimos 30 días.");
var hasta = new Date();
var desde = new Date();
desde.setDate(desde.getDate()-30);
$("#fechas").html( date_to_string(desde) + " al " + date_to_string(hasta) );
}
if(cant==180) {
$("#navegador").html("Datos de los últimos 6 meses.");
var hasta = new Date();
var desde = new Date();
desde.setDate(desde.getDate()-180);
$("#fechas").html( date_to_string(desde) + " al " + date_to_string(hasta) );
}
}


function date_to_string( d) {
var e = "DomLunMarMieJueVieSab";
return e.substr(d.getDay()*3, 3) + " " + d.getDate() + "/" + (d.getMonth()+1.0) + "/" + d.getFullYear();
}


function drill_eventos(e) {

drilling = e.point.series.name;

var j = new rem_request(this,function(obj,json){
var jdata = eval("("+json+")");

$("#totales").html("Total eventos: "+jdata.total);
$("#volver").show();
$("#cambio_fecha").hide();


chart1 = null;
var options = {
       chart: {
          renderTo: 'graf_eventos',
          defaultSeriesType: 'spline',
          height: 300,
          width: 950
       },
       title: {
          text: 'Eventos de terminal '+drilling
       },
       xAxis: {
        type: 'datetime',
           tickPixelInterval: 150
       },
       yAxis: {
          title: {text: 'Eventos'},
          plotLines: [{
              value: 0,
              width: 1,
              color: '#808080'
           }]
       },
       series: [{name:"Eventos", data:[] }],
       credits: { enabled: false },
       tooltip: {
           formatter: function() {
          
            return Dia(Highcharts.dateFormat('%a', this.x)) + " " + Highcharts.dateFormat('%d-%m-%Y', this.x) +'<br/>Eventos: '+ Highcharts.numberFormat(this.y, 2);
           }
        }
    };


var spline = jdata.spline;
for( var k=0; k<spline.length; k++) {
var t = spline[k].fecha; //Formato unix timestamp
var v = spline[k].cant;
options.series[0].data.push( {'x':t*1000,'y':v} );
}

chart1 = new Highcharts.Chart(options);


chart2=null;

var options = {
       chart: {
          renderTo: 'graf_sitios',
          defaultSeriesType: 'pie',
          height: 300,
          width: 950
       },
       title: {
          text: 'Eventos por sitio de ' + drilling
       },
       plotOptions: {
           pie: {
              allowPointSelect: true,
              cursor: 'pointer',
              dataLabels: {
                 enabled: true,
                 color: '#000000',
                 connectorColor: '#000000',
                 formatter: function() {
                    return '<b>'+ this.point.name +'</b>: '+ Highcharts.numberFormat(this.percentage, 0) +' %';
                 }
              }
           }
       },
       series: [{"name":"pie", "type":"pie", "data":[] }],
       credits: { enabled: false },
       tooltip: {
           formatter: function() { 
            return Highcharts.numberFormat(this.y, 0)+" eventos";
           }
        }
    };


var pie = jdata.pie;
for( var k=0; k<pie.length; k++) {
var n = pie[k].organismo;
var v = pie[k].cant;
options.series[0].data.push( {"name":n, "y":v} );
}

chart2 = new Highcharts.Chart(options);


},"HOME_TERMINALES_AJAX","render_eventos_tiempo",dias+"|"+drilling);
}




function Dia(eng) {
var i = "MonTueWedThuFriSatSun";
var e = "LunMarMieJueVieSabDom";
return e.substr(i.indexOf(eng, 0), 3);
}


function goHome() {
$("#volver").hide();
setDias(dias);
}


En este caso, los datos que llegan por JSON son la información pura. Toda la data para formatear los gráficos, se declara en este objeto Javascript.


Recomiendo ver los ejemplos de Highchart sobre la página de la librería y eventualmente cortar y pegar el ejemplo.


http://www.highcharts.com/demo/