Weblog geek del Rofi

Weblog amb documentació que considero interessant sobre GNU/Linux i programació en general.

URL

feed XML
http://rofi.pinchito.com/weblog/index.php?blogId=1

Última actualització

fa 3 years 7 setmanes

May 19, 2005

22:17
La gestió dels esdeveniments es realitza de diferents maneres en els diferents llenguatges de programació existents. Anem a veure per sobre com es fa en cada un d'ells.

Per gestió d'esdeveniments ens referim a quins mecanismes es poden utilitzar en els diferents llenguatges de programació per a la gestió d'esdeveniments asíncrons.

Quan es tracta de detectar un succés de tipus asíncron sempre tenim dues alternatives. L'una, anomenada model estirada, consisteix en anar consultant si l'esdeveniment ha succeït. Aquest mètode dóna a lloc a tècniques com ara el polling periòdic. Són solucions que funcionen bé per alguns casos però fan perdre temps ja que sovint suposen comprovacions innecessàries.

L'altra tècnica, anomenada model empenta, consisteix en què quan ha succeït un esdeveniment hi ha algú que ens avisa que ha succeït. Això suposa que l'encarregat d'avisar del succés ens ha de poder avisar, per tant hi ha d'haver una forma de registrar-nos a ell. Sinó no podrà avisar-nos que ha succeït l'event.

Aquest model té l'avantatge que no hi ha consultes innecessàries però a la contra és sempre més complex d'implementar. És un mecanisme força usat i dóna lloc a un patró de disseny propi anomenat patró Subjecte-Observador (es un cas més general d'un altre patró conegut com Model-Vista-Controlador).

Callbacks

Molts llenguatges disposen del que s'anomena els callbacks. En realitat és una tècnica que es basa en utilitzar un punter a una funció a fi de poder-la cridar des del programa. No només s'utilitza per a la comunicació d'esdeveniments sinó que pot servir com a mecanisme d'abstracció. Per exemple la rutina qsort de ISO C té la següent signatura:

void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));

El segon paràmetre és un punter a una funció que rep un parell d'elements const void* i ens retorna un enter indicant-nos la relació d'ordre que tenen entre ells.

Considerem un altre exemple. GTK+ és una llibreria gràfica portable molt utilitzada a GNU/Linux. Observem com podem detectar que s'ha premut un botó (això és un extracte d'un exemple del tutorial de GTK+).

static void hola( GtkWidget *widget, gpointer data ) { // No posem accents perquè haurien d'anar en UTF-8 g_print ("Hola mon"); } int main(int argc, char* argv[]) { ... /* Crea un botó amb títol "Premeu-me" */ boto = gtk_button_new_with_label ("Premeu-me"); /* Quan el botó rebi el senyal "clicked" cridarà la funció hola() passant * NULL com a argument. */ g_signal_connect (G_OBJECT (boto), "clicked", G_CALLBACK (hola), NULL); ... }

El senyal clicked el llença un botó quan l'usuari fa clic sobre d'ell. En aquest cas el que hem fet és vincular aquest senyal amb una funció callback hola. D'aquesta manera quan l'usuari faci clic al botó aquest cridarà indirectament hola. Tant G_OBJECT com G_CALLBACK són macros de GTK+ que s'encarreguen de fer els casting apropiats perquè el compilador no llenci cap missatge de warning.

L'ús de punters a funcions és pràctic però com sempre té la pega que és arriscat. Passar un punter de funció incorrecte o que no té la signatura adequada pot provocar errors a qui crida la funció. D'altra banda està clar que amb C poca cosa més podrem fer per tant anem a veure quines possibilitats ofereixen d'altres llenguatges.

Classes

Ja hem vist una forma d'implementar el model empenta. Anem a veure com es pot millorar utilitzant la programació orientada a objectes.

El problema principal que ens hem trobat utilitzant punters a funcions és que és fàcil que el punter que passem no apunti a la funció adequada. Mitjançant l'ús de les classes abstractes en C++ i Java podem imposar una interfície. Per exemple, observem el següent exemple de la llibreria d'interfícies gràfiques Swing de Java:

public class ElMeuPrograma { ... //El codi d'inicialitzacio JButton boto = new JButton("Premeu-me"); boto.addActionListener(new BotoPremut()); } class BotoPremut implements ActionListener { public void actionPerformed(ActionEvent e) { System.out.println("Hola món"); } }

A Swing els esdeveniments provenen de event sources i són propagats als event listeners. Fixeu-vos que el mecanisme que hem d'utilitzar és crear una classe i implementar el mètode que rebrà el event. Per als ActionListeners és actionPerformed. Sobré un botó els ActionListeners s'executen quan l'usuari fa clic. Llavors només falta afegir un objecte d'aquesta classe.

Ara ja no podem tenir problemes de passar una signatura incorrecta. L'única pega és que necessitarem una classe per cada botó que calgui tractar diferent. A més d'haver de crear un objecte. També cal dir que té els seus avantatges, en ser una classe permet dotar al Listener de cert estat.

Signals i slots

Si canviem el nom de event source i event listener per signal i slot tenim l'altra tècnica per implementar esdeveniments de tipus asíncron. La primera solució que veurem és la que s'utilitza a QT, una altra llibreria de desenvolupament d'interfícies gràfiques portable molt utilizada a GNU/Linux.

class EnviaSignals : public QObject { private: int numero; public signal: void haCanviatElNumero(int); public: EnviaSignals() : numero(0) { } void incrementaNumero(int increment = 1) { numero += increment; emit haCanviatElNumero(numero); } }; class RepSignals : public QObject { public slot: void aviseuMeQuanCanvii(int numero) { std::cout class Saludador { public: sigc::signal signal_salutacio; Saludador(); void saluda() { // Aixo es el mateix que signal_salutacio.emit() signal_salutacio(); } }; void fer_salutacio() { cout

May 14, 2005

17:56
Llegint en un article d'una persona que experimentava amb Mono veig que ha d'instal·lar tot de paquets que la seva distribució no porta. Però aquesta no la forma adequada de fer les coses.

Sovint els no iniciats a les distribucions de GNU/Linux volen acabar d'instal·lar els programes per a fer-se l'entorn que necessiten. Els distribuïdors i empaquetadors inverteixen temps en empaquetar la majoria de programes de forma que s'integren adequadament amb tot el sistema.

En particular les distribucions més grans (tipus Debian, Suse, Red Hat, Mandrake, Gentoo, etc) disposen de paquets (precompilats normalment, tret de Gentoo). Aquests són els paquets que voldrem utilitzar en els nostres sistemes.

Però els usuaris novells, segurament perquè no han descobert l'eina d'instal·lació de paquets de la seva distribució, insisteixen a compilar el programa. I clar, sovint no tenen èxit.

I és que realment gairebé mai és necessari compilar les nostres aplicacions. Només és raonable fer-ho si volem modificar-les per algun motiu, o la versió que porta la nostra distribució és massa vella o bé no ha estat encara empaquetada pel distribuïdor.

En aquest cas s'ha de tenir en compte que cal instal·lar els paquets de desenvolupament de totes les llibreries que requereixi el programa. En particular a Debian són els paquets paquet-dev i a Red Hat, Suse i Mandrake els paquet-devel.

Quan ja tinguem les llibreries instal·lades llavors ja podrem fer el clàssic configure. En particular és recomanable indicar --prefix=/opt/nomprograma així tota l'aplicació s'instal·larà dins de /opt/nomprograma i sempre serà més fàcil esborrar-la i localitzar els fitxers.

May 12, 2005

16:17
Un cop ja hem conseguit tenir els usuaris introduïts al LDAP de forma que es poden autenticar des de qualsevol Linux anem a veure com conseguir que puguin entrar també des de Windows utilitzant un domini Windows NT i Samba.Controlador primari de domini

Les xarxes Windows NT es basen en els controladors de domini que són màquines que duen a terme l'autenticació. Hi ha la possibilitat que siguin primàries o que facin de backup per si la primària ha caigut.

Quan hom entra a un Windows que està en un domini i s'autentica amb èxit rep el seu perfil que s'emmagatzema al servidor. Si no en tenia es crea a partir d'un compte de client local Default User.

Aquest perfil no és més que el nostre home a Windows i la nostra configuració del registre. En sortir, Windows sincronitza el perfil que tenim localment amb el perfil remot (és en aquest punt on sol fer figa tot : manca d'espai, errors en la sincronització, etc) i deixa una còpia del perfil en aquest ordinador per accelerar un posterior login. És clar que en aquest posterior login pot ser necessari resincronitzar el perfil per si hem entrat des d'una altra màquina amb Windows.

És possible configurar Windows perquè no emmagatzemi una còpia local del perfil i un cop sincronitzada després sortir l'esborri. Això ajuda a reduir problemes amb el perfil de Windows ja que l'etapa de sincronització és crítica. Sol fer-se malbé el perfil amb una facilitat extraordinària en particular en sistemes amb configuracions no exactament iguals. A més no és qüestió d'anar deixant per tots els ordinadors on has passat una còpia del teu home.

És un problema molt greu per als administradors mantenir perfils de mida raonable. Tècnicament si un usuari sol entrar sempre des de la mateixa màquina la sincronització del perfil és una bona solució. El problema és quan entra des d'una altra màquina on no hi ha el perfil. Ara toca descarregar-lo tot per la xarxa. La qüestió està en què no és difícil, en particular utilitzant Outlook o per la pròpia cache del navegador, que el perfil assoleixi mides de diversos gigabytes. Noteu que, si l'usuari sempre utilitza la mateixa màquina no ha de ser massa problema perquè la sincronització es pot fer ràpidament, aquest problema és més freqüent en entorns acadèmics on es vol oferir l'accés al sistema des de qualsevol màquina.

No sé si és possible que Windows treballi totalment de forma remota amb el perfil, sí que és factible que treballi totalment de forma local, de forma que podríem tenir diferents perfils en diferents màquines i el controlador de domini bàsicament s'encarregaria de fer l'autenticació.

Bé, la idea és que Samba 3.x ens faci de controlador primari de domini operant contra LDAP per emmagatzemar la informació dels usuaris.

Preparar LDAP de nou

Amb els schema que teníem no en tindrem prou per poder desar els usuaris al LDAP. Per tant al fitxer /etc/ldap/slapd.conf haurem d'afegir un schema que porta Samba. El que farem és copiar-lo al directori /etc/ldap/schema.

include /etc/ldap/schema/core.schema include /etc/ldap/schema/cosine.schema include /etc/ldap/schema/nis.schema include /etc/ldap/schema/inetorgperson.schema include /etc/ldap/schema/samba.schema

Ara en la nostra base de dades d'usuaris afegirem uns quants índexos perquè LDAP pugui respondre més ràpid algunes consultes i afegirem alguna opcio de seguretat.

database ldbm suffix "dc=pigonet" # ... la resta d'opcions # �ndexos index objectClass eq index cn,uid eq index uidNumber eq index gidNumber eq index sambaSID eq index sambaPrimaryGroupSID eq index sambaDomainName eq # ... la resta del fitxer # Nova restricció de seguretat access to attrs=SambaLMPassword,SambaNTPassword by dn="cn=admin,dc=pigonet" write by * none

Com sempre, apaguem el servidor, reindexem i el reiniciem.

# /etc/init.d/slapd stop # slapindex # /etc/init.d/slapd start

Perquè tot pugui acabar funcionat ens faltarà disposar d'un lloc on desarem els comptes de confiança de les màquines. Per fer-ho afegirem el següent LDIF al LDAP.

$ cat maquines.ldif dn: ou=maquines,dc=pigonet ou: maquines objectClass: organizationalUnit $ ldapadd -x -W -f maquines.ldif -D cn=admin,dc=pigonet Password: (added new entry "ou=maquines,dc=pigonet") Configurar Samba per fer PDC i utilitzar LDAP

Primer configurarem Samba perquè pugui actuar de controlador primari del domini Windows NT (PDC). Per fer-ho configurarem el fitxer /etc/samba/smb.conf. La següent és una configuració mínima on només falta la informació de LDAP.

[global] ## Browsing/Identification ### # Nom del DOMINI windows workgroup = SMBFERRER # Nom d'aquesta màquina, hauria de coincidir amb la del host netbios name = tiresies # Com s'anuncia aquest servidor server string = %h PDC (Samba %v) # Configuració per a un PDC preferred master = yes local master = yes domain master = yes domain logons = yes wins support = no # Tothom s'oblida d'això i llavors # els fitxers queden desats com UTF-8 # al servidor i no ho volem! unix charset = ISO-8859-1 display charset = ISO-8859-1 # Aquest és el tipus de segurat que # es necessita per a un PDC security = user encrypt passwords = true # LDAP # Després afegirem informació per al LDAP [homes] comment = Homes dels usuaris read> Quant a la part de LDAP caldrà indicar a Samba 3 que volem que utilitzi el LDAP per desar tot el que li calgui. I llavors indicarem en quins punts del directori trobarà els diferents elements.

# LDAP # Sufix del nostre directori ldap suffix = dc=pigonet # DN de l'administrador ldap admin dn = cn=admin,dc=pigonet # No volem SSL, tot i que seria recomanable en # un entorn més segur ldap ssl = No # On volem que operi Samba passdb backend = ldapsam:ldap://localhost # Ara els diferents DN relatius al "ldap suffix" ldap user suffix = ou=usuaris ldap group suffix = ou=grups ldap machine suffix = ou=maquines ldap idmap suffix = ou=usuaris # Volem que Samba ens sincronitzi el password # de Windows i de Unix ldap passwd sync = yes # No volem de cap manera que si eliminem un usuari ens esborri tota l'entrada # (podríem voler-lo deixar sense accés als sistemes Windows) ldap delete dn = no

Ara només falta indicar-li a Samba quina és la contrassenya del superusuari de LDAP i ja podem iniciar Samba. L'emmagatzemarà de forma segura en un fitxer secrets.tdb

# smbpasswd -w passwordsupersecret # /etc/init.d/samba start Introduir una màquina en el nostre domini

Per introduir una màquina en el domini cal seguir un ritual molt específic. Primer cal crear un compte UNIX de nom maquina$ on maquina és el nom de NetBIOS de la màquina. Després cal bloquejar el compte i llavors des de Windows ja podrem entrar.

Malauradament ens caldrà tenir l'usuari root al LDAP ja que en el moment d'afegir la màquina al domini ens demanarà un usuari i contrassenya que per qüestions de Samba a UNIX tingui uid igual a zero, root normalment satisfà aquest requeriment. Noteu però que el compte que afegirem no serà pas de UNIX, només de Samba, per tant PAM no el podrà utilitzar per autenticar.

# smbpasswd -a root New SMB password: Retype SMB password:

Ara creem el compte de la màquina.

# useradd -c "Compte de la màquina TESEU" -s /dev/null -d /dev/null teseu$ # passwd -l teseu$

En aquest moment qualsevol podria fer-se passar per la màquina TESEU i entrar al domini. Ara anem a la màquina TESEU que té Windows entrant com Administrador. A l'opció Sistema del Panel de Control anem a la pàgina Identificación de Red i escollim entrar al domini (en el nostre cas SMBFERRER). Ara ens demanarà un usuari i password amb privilegis. Posem root i el password que hem usat al smbpasswd -a. Si tot va bé entrarem al domini i com de costum haurem de reiniciar la màquina.

En reiniciar podrem escollir Conectarse a: SMBFERRER. Ara ens falta l'usuari. Perquè un usuari pugui entrar ens cal que primer es trobi al LDAP com un usuari de UNIX. Després podem fer

# smbpasswd -a usuari New SMB password: Retype SMB password:

Noteu que li hem indicat a Samba que sincronitzi el password de Windows amb el de Linux per tant això ja hauria de configurar el password de Linux amb el mateix valor.

Consells

Si només teniu una màquina amb Windows o no voleu que els perfils es desin remotament utilitzeu l'editor de polítiques de Windows (Inicio, Ejecutar, gpedit.msc) i canvieu la política Permitir sólo perfiles de usuario locales que es troba dins de Configuración del equipo, Plantillas administrativas, Sistema, Inicio de sesión.

Són molt útils les eines de IDEALX que permeten gestionar els usuaris del LDAP i de Samba de forma còmoda.

Més informació
16:17
Aquest no és el primer document on s'explica com es pot unificar els logins en una xarxa que conté Windows i Linux. Aquest article suposa uns coneixements mitjans-alts d'administració de UNIX.Els actors de l'obra

En aquest drama hi intervenen per una banda un parell de sistemes Linux i un sistema Windows. Unificar logins estalvia feina, a casa meva només són tres màquines però m'evita de tenir tres passwords que he de sincronitzar.

Per resoldre aquest problema tenim el nostre amic LDAP. LDAP és una base de dades amb un model jeràrquic. Això vol dir que els elements s'identifiquen per la seva posició dins de l'arbre, enumerant els elements que hem anat creuant des de l'arrel fins al element.

La bondat de LDAP és que permet emmagatzemar directoris : estructures organitzatives i informació que adquireix una forma jeràrquica. Una forma típica de les organitzacions. L'inconvenient de LDAP és que hereta de diferents estàndards malignes com ara X.500 i abans d'entendre com funciona pot costar una mica. A més que la interacció amb el sistema LDAP és d'allò més primitiva (afortunadament hi ha alguna eina gràfica decent).

La idea és emmagatzemar la informació dels usuaris i els grups al LDAP. Per integrar-ho tot utilitzarem per una banda nss_ldap que proporciona als programes la informació dels usuaris i grups que tenim al LDAP. Per altra banda ens caldrà dur a terme autenticació contra LDAP, per això disposem de pam_ldap.

Als sistemes Windows no podem utilitzar NSS ni PAM. Afortunadament podem utilitzar Samba. La idea és que Samba desi i consulti al LDAP tot el que necessiti. En aquest punt la integració no és pas total ja que al LDAP podem tenir usuaris que no siguin usuaris de Windows. Tot i així Samba permet utilitzar el mateix nom d'usuari i sincronitzar la contrassenya tant de Unix com de Windows. Per tant si canviem el password de la forma correcta el canvi es veurà en els dos entorns.

Per poder fer això caldrà configurar Samba perquè sigui el controlador primari de domini de la xarxa Windows NT. En aquest primer post ens concentrarem en unificar els sistemes UNIX. En el següent capítol ens concentrarem a acabar d'integrar els sistemes Windows.

Arquitectura de la solució

Escollirem una màquina Linux perquè executi el servidor de LDAP i l'instal·larem i el configurarem. Després tant en aquesta màquina com en les altres màquines Linux configurarem perquè obtinguin la informació d'usuaris i grups (NSS) per LDAP i autentiquin (PAM) també contra el LDAP.

Preparació de LDAP

LDAP és un protocol basat en TCP/IP per a l'accés de directoris de tipus X.500. Els directoris són estructures jeràrquiques on cada element de l'estructura emmagatzema una certa informació. Quina informació desa el node ve definida pel tipus de classe del node i per la pròpia classe.

Cada node s'identifica pel camí que cal recórrer fins a arribar a ell. Aquesta estructura en arbre és molt convenient en organitzacions ja que és la que acaben adquirint a la pràctica. És usual associar l'arrel del directori amb el domini de l'organització per exemple lamevaempresa.com.

Estructura inicial

En el nostre cas hem utilitzat el nostre domini privat pigonet per construir la següent jerarquia.

Tal com s'ha indicat els nodes s'identifiquen pel seu recorregut des de l'arrel fins al node. Això s'anomena el dn (distinguished name). Fixeu-vos que la nostra jerarquia parteix del dc (domain component) pigonet. A sota hem afegit dos elements de tipus ou (organizational unit) que són usuaris i grups on desarem la informació dels usuaris i dels grups respectivament.

Servidor de LDAP : OpenLDAP 2

Ens caldrà una aplicació servidora de LDAP. Jo he escollit OpenLDAP perquè és lliure, però hi ha d'altres alternatives. És molt recomanable que utilitzeu la versió empaquetada de la vostra distribució així estalviareu problemes perquè l'heu compilada malament.

El servidor de LDAP s'anomena slapd i es configura normalment a /etc/ldap/slapd.conf (la ubicació exacta pot variar entre distribucions). Mireu el manual de slapd per saber com es configuren els paràmetres globals però assegureu-vos que permeteu binding anònim i que incloeu els schema's adequats.

# Schema's que utilitzarem. Indiquen els tipus de formats que podrem utilitzar # als nodes. Els necessitarem per poder desar usuaris i grups. include /etc/ldap/schema/core.schema include /etc/ldap/schema/cosine.schema include /etc/ldap/schema/nis.schema include /etc/ldap/schema/inetorgperson.schema # Permetem binding anonim allow bind_anon_dn

Binding consisteix en indicar a quin element de la jerarquia del LDAP ens connectarem, en funció de quina és, es pot configurar poder fer algunes operacions o d'altres, demanar password, etc. En el nostre cas per simplicitat permetrem bindings anònims, o sigui, que el client no indica a quin element de la jerarquia es connecta. En una instal·lació més segura aquesta opció seria inacceptable, evidentment.

En la secció de bases de dades del fitxer slapd.conf hem configurat la següent jerarquia.

# Format d'emmagatzematge de la BD database ldbm # El component arrel suffix "dc=pigonet" # L'element distingit que quan hom s'hi vinculi indicant la contrassenya # tindrà poders administratius rootdn "cn=admin,dc=pigonet" rootpw {SSHA}6JagzM6Hla8x3kiK64WK1w2TURSvVe5F # Directori dels fitxers físicament directory "/var/lib/ldap" # �ndexos que ens caldran per resoldre consultes de forma eficient index objectClass eq index cn,uid eq index uidNumber eq index gidNumber eq # Restringim l'accés a l'atribut del password # de l'usuari. # El pot modificar l'administrador # Es pot usar per autentificar # El seu propietari el pot modificar # La resta no pot accedir access to attribute=userPassword by dn="cn=admin,dc=pigonet" write by anonymous auth by self write by * none # L'administrador té accés a tot. # La resta només pot llegir access to * by dn="cn=admin,dc=pigonet" write by * read

Per a més detalls sobre la configuració consulteu la bibliografia. Ens assegurem, amb el servidor apagat, que els índexos estan correctament configurats. Per generar un password per a rootpw podem usar slappaswd. La ordre exacta per iniciar i finalitzar el servidor pot variar d'una distribució a una altra.

# /etc/init.d/slapd stop # slappindex # /etc/init.d/slapd start Poblar la base de dades

Ara que ja tenim el servidor LDAP presumiblement ben configurat ja podem afegir dades. Per fer-ho LDAP utilitza un format basat en text anomenat LDIF. Son conjunts de línies on la primera columna fins als dos punts indica l'atribut de l'element que anem a introduir, començant sempre pel dn (Distinguished Name). Acte seguit introduirem com a minim l'atribut nou del dn i la resta d'atributs que calguin. Els següents LDIF permetran inicialitzar el nostre LDAP.

$ cat arrel.ldif dn: dc=pigonet dc: pigonet objectClass: dcObject objectClass: organizationalUnit ou: Pigonet dn: ou=usuaris,dc=pigonet ou: usuaris objectClass: organizationalUnit dn: ou=grups,dc=pigonet ou: grups objectClass: organizationalUnit

El següent és un exemple de LDIF per a un usuari.

$ cat usuari_nou.ldif dn: uid=carlos,ou=usuaris,dc=pigonet uid: carlos cn: Carlos Del Ojo Elias givenname: Carlos sn: Del Ojo Elias mail: carlos@rofi.pinchito.com objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson objectClass: posixAccount objectClass: top objectClass: shadowAccount userPassword: {crypt}$1$77745874$yx3K7mB2L66JPS2fJPZHa0 shadowLastChange: 12473 shadowExpire: 12600 loginShell: /bin/bash uidNumber: 1002 gidNumber: 1011 homeDirectory: /home/carlos gecos: Carlos Del Ojo Elias

Fixeu-vos, el nou atribut del dn és uid=carlos i per tant ha d'acabar apareixent en la llista d'atributs. Fixeu-vos que el tipus de l'element (i per tant els atributs que pot tenir) són indicats en un atribut multivaluat que sempre hi és objectClass. Cal separar els diferents elements amb una línia en blanc (un nou usuari aniria separat amb una linia en blanc).

L'eina ldapadd permet afegir entrades en format LDIF al LDAP.

$ ldapadd -W -x -f arrel.ldif -D cn=admin,dc=pigonet Password: (added new entry "dc=pigonet") (added new entry "ou=usuaris,dc=pigonet") (added new entry "ou=grups,dc=pigonet") $ ldapadd -W -x -f usuari_nou.ldif -D cn=admin,dc=pigonet Password: (added new entry "uid=carlos,ou=usuaris,dc=pigonet")

Similarment amb els grups d'usuaris.

dn: cn=src,ou=grups,dc=pigonet objectClass: posixGroup objectClass: top cn: src userPassword: {crypt}x gidNumber: 40 memberUid: bernat memberUid: carlos

Com que afegir-ho a mà és un rotllo hi ha diferents eines que ens poden ajudar. Per una banda si volem migrar tot un /etc/passwd podem usar migrationtools. En el meu cas he hagut d'esmenar els scripts perquè no acabaven de rutllar bé. Si el que volem és afegir un usuari al LDAP podem usar algunes de les eines de IDEALX. Aquestes eines estan pensades per quan combinem LDAP amb SAMBA i potser ara mateix encara no ens serviran.

Una eina gràfica per manipular LDAP és GQ que ens permetrà navegar pel nostre LDAP.

Un cop tinguem els usuaris i grups al LDAP ja els podem eliminar de /etc/passwd i /etc/group. És evident que el root no el tindrem pas al LDAP i el deixarem a /etc/passwd.

Preparant NSS

Name Switch Service ens permet configurar des d'on obtindrem la informació de les diferents bases de dades del sistema. En particular per al nostre cas ens interessen els usuaris, shadow i els grups. Això ho farem a cada màquina on vulguem disposar dels usuaris de LDAP.

Haurem d'instal·lar el paquet nss_ldap (libnss-ldap a Debian) i configurar el fitxer /etc/nsswitch.conf de la forma següent:

passwd: files ldap group: files ldap shadow: files ldap # Aquí va la resta del fitxer

Ara ens manca configurar /etc/ldap.conf (/etc/libnss-ldap.conf a Debian).

host 127.0.0.1 base dc=pigonet ldap_version 3 rootbinddn cn=admin,dc=pigonet nss_base_passwd ou=usuaris,dc=pigonet?one nss_base_shadow ou=usuaris,dc=pigonet?one nss_base_group ou=grups,dc=pigonet?one

La contrassenya del superusuari la posarem a un fitxer /etc/ldap.secret en text clar i amb permisos 0600 només per a l'usari root.

Ara podem comprovar que NSS està accedint correctament al servidor LDAP fent

$ getent passwd ... carlos:x:1002:1011:Carlos Del Ojo Elias:/home/carlos:/bin/bash $ getent group src:x:40:bernat,carlos Preparant PAM

Caldrà instal·lar el paquet pam_ldap (libpam-ldap a Debian). Aquest modul PAM treballa amb la configuració que hem definit abans a /etc/ldap.conf (o en el fitxer /etc/pam_ldap.conf a Debian). Això ho haurem de fer a cada màquina que vulguem que s'autentifiqui contra LDAP.

El truc està en permetre que l'autentificació per ldap sigui suficent en els mòduls auth i account. Per exemple si volem provar primer d'autenticar per LDAP i després provar sort amb els fitxers de UNIX podem configurar-ho així. Aquest és un exemple de /etc/pam.d/sshd.

# AUTH auth required pam_nologin.so auth sufficient pam_ldap.so # És important que pam_unix.so utilitzi el primer password # sinó ens el demanaria un altre cop auth required pam_unix.so try_first_pass auth required pam_env.so # [1] # ACCOUNT account sufficient pam_ldap.so account required pam_unix.so # La resta del fitxer de configuració /etc/pam.d/sshd

En algunes distribucions com ara Gentoo s'utilitza el modul pam_stack.so que permet indicar un fitxer comú on fer la configuració de PAM. A Gentoo en particular aquest fitxer és /etc/pam.d/system-auth i el deixarem així

# AUTH auth required /lib/security/pam_env.so auth sufficient /lib/security/pam_ldap.so auth sufficient /lib/security/pam_unix.so likeauth nullok try_first_pass auth required /lib/security/pam_deny.so # ACCOUNT account sufficient /lib/security/pam_ldap.so account required /lib/security/pam_unix.so

En aquest cas hi ha establert un mòdul pam_deny.so que sempre denega l'autenticació. De forma que és suficient que pam_ldap.so o pam_unix.so ens deixin entrar per no examinar aques últim.

Conclusió

Comprenc que l'article deixa moltes aspectes per acabar de definir però dóna una idea de com s'hauria de configurar un Linux per treballar contra LDAP. Està clar que aquest és un tema d'administració lleugerament avançat però com a mínim s'intenta exposar les idees bàsiques de com s'ha de dur a terme.

En un proper article parlarem de com integrar SAMBA amb LDAP i per tant poder autenticar els usuaris amb el LDAP que hem instal·lat.

Més informació

May 4, 2005

08:17
En la gran majoria de cursos sobre administració de sistemes Unix (i GNU/Linux) es parla dels fitxers /etc/passwd i /etc/shadow. Afortunadament hi ha vida més enllà d'aquest sistema.Gestió tradicional dels usuaris

La gestió tradicional dels usuaris a Unix ha passat pels fitxers de text /etc/passwd, /etc/shadow i /etc/group. En el primer llistem els usuaris, en el segon hi tenim la contrassenya encriptada (és un fitxer de lectura restringida) i en el tercer els grups del sistema i els membres que hi pertanyen.

PAM

Durant molt de temps amb això n'hi ha hagut prou per a la gran majoria de situacions. De fet avui dia encara s'utilitza per a Unix en màquines personals perquè fa bé la seva funció. Malauradament amb la proliferació de les xarxes locals i l'administració centralitzada aquest sistema no s'adapta gens bé.

És per aquest motiu que Sun, i després fou adaptat a GNU/Linux, va crear el que s'anomena PAM (Pluggable Authentication Modules). Els mòduls d'autentificació són un conjunt de llibreries dinàmiques (.so) que es responsabilitzen de l'autenticació. Les aplicacions només han de sol·licitar via la llibreria de PAM que es dugui a terme l'autentificació. Cada aplicació pot tenir una configuració específica.

Per exemple, el programa login ens deixa entrar al sistema per un terminal. Les versions antigues es cuinaven elles l'autenticació consultant els fitxers abans esmentats. Ara, però, ho deleguen a libpam. L'autenticació dependrà de com estiguin configurat el programa.

Directori /etc/pam.d

La forma original desenvolupada per Sun consistia en configurar les aplicacions en un fitxer /etc/pam.conf. L'única pega d'aquest sistema és que el fitxer acaba quedant molt gran si hom hi té molts clients de PAM. A GNU/Linux cada aplicació es configura en un fitxer individual dins del directori /etc/pam.d.

Anem a mirar per exemple la configuració de PAM de login de Debian Woody 3.0 (les distribucions més modernes també porten PAM però el configuren perquè sigui molt més flexible i ens molestaria per l'explicació). El fitxer en qüestió és /etc/pam.d/login.

auth requisite pam_securetty.so auth requisite pam_nologin.so auth required pam_env.so auth required pam_unix.so nullok account required pam_unix.so session required pam_unix.so session optional pam_lastlog.so session optional pam_motd.so session optional pam_mail.so standard noenv password required pam_unix.so nullok obscure min=4 max=8 md5

La primera columna indica el tipus de mòdul. Hi ha quatre tipus de mòduls i no tots ells tenen sentit en totes les situacions.

  • auth. Aquests mòduls per una banda estableixen l'identitat de l'usuari indicant-li al programa que sol·liciti un password o d'alguna altra manera, per exemple a través d'un hipotètic escànner retinal. També permeten establir diferents credencials com ara a quins grups pertanyerà l'usuari autenticat o quines capacitats i privilegis tindrà.
  • account. Aquests mòduls realitzen gestió dels comptes que no està basada en l'autenticació pròpiament dita. Per exemple aquí hi situaríem mòduls que restringissin l'accés per unes determinades hores o pel màxim número d'usuaris.
  • session. Aquí es fa la feina que s'ha de fer just abans o després d'oferir el servei. Per exemple aquí s'hi inclouen mòduls que indiquen l'últim login, o si hi ha correu. Però també pot servir per muntar directoris que precisi l'usuari, etc.
  • password. Aquí s'indica com s'actualitza el token de seguretat de l'usuari (per exemple el password o la targeteta magnètica)

Els mòduls es van examinant seqüencialment quan accedim a un servei. Primer els auth, després els account, els session. Els mòduls poden impedir que puguem accedir al servei en funció del seu grau de requeriment.

  • required. És necessari que el mòdul permeti que accedim al servei. Si el mòdul no ens ho permet ens adonarem després que s'hagin executat tots els mòduls d'aquell tipus.
  • requisite. Igual que un required però si el mòdul impedeix l'accés al servei la denegació és immediata.
  • sufficient. Si el mòdul permet l'accés al servei llavors ja no s'examinen la resta de mòduls d'aquell tipus. Si un mòdul d'aquest tipus no permet l'entrada no es denega l'accés.
  • optional. No és rellevant que el mòdul accepti o denegui l'accés al servei. PAM es limitarà a executar-lo però no l'utilitzarà per determinar si l'autentificació ha fallat.

Cal a dir que aquests graus de requeriment es poden afinar millor però per a la majoria de situacions en tindrem prou.

Ara analitzem la configuració del fitxer /etc/pam.d/login.

auth requisite pam_securetty.so auth requisite pam_nologin.so auth required pam_env.so auth required pam_unix.so nullok

El mòdul pam_securetty.so ve per defecte amb Linux-PAM i permet configurar a través del fitxer /etc/securetty des de quins terminals pot fer login l'usuari root. És típic no permetre que l'usuari entri al sistema si no és per un dels terminals de text (o sigui davant del monitor i el teclat).

El mòdul pam_nologin.so també ve amb Linux-PAM i impedeix que els usuaris facin login si existeix el fitxer /etc/nologin i només permet que entri root.

El mòdul pam_env.so permet establir variables d'entorn en la fase de login. A priori sembla que hagi de ser quelcom per fer a account però les variables d'entorn s'assignen a la fase d'establiment de credencials.

Finalment el mòdul pam_unix.so fa l'autentificació contra els fitxers /etc/passwd i /etc/shadow. Alhora que llegeix de /etc/group els grups de l'usuari. El paràmetre nullok permet que puguin entrar usuaris amb un password en blanc (per defecte no ho permet).

account required pam_unix.so

Aquí es realitza la gestió del compte considerant la data de caducitat i d'altres paràmetres sobre el password que apareixen a /etc/shadow (data de l'últim canvi, etc).

session required pam_unix.so session optional pam_lastlog.so session optional pam_motd.so session optional pam_mail.so standard noenv

Aquí el mòdul pam_unix.so és afegir una entrada a syslog indicant que l'usuari ha fet login, també s'apuntarà quan faci logout. Els mòduls pam_lastlog.so i pam_motd.so mostren a l'usuari l'últim cop que va fer login i el message of the day (normalment /etc/motd). Finalment pam_mail.so ens indica si tenim correu electrònic nou al compte. Tots aquests missatges apareixen just abans d'obtenir el shell que login iniciarà.

Més enllà

La gràcia d'aquest sistema és que podem delegar l'autenticació als mòduls. Per exemple podem tenir tot un sistema unificat de logins a través de LDAP amb pam_ldap o a través d'un domini Windows NT utilitzant winbind i pam_winbind.

NSS

Fixeu-vos que ara només hem considerat l'autenticació. Però els sistemes Unix tenen diferents bases de dades que també voldrem delegar. Una d'elles és, com no, /etc/passwd. Si fem autenticació contra un LDAP, contra un NIS o contra un domini Windows NT està clar que la llista d'usuaris i grups no pot sortir de /etc/passwd.

Per resoldre aquest problema Sun va inventar NSS (Name Service Switch). La llibreria GNU/Libc incorpora un mecanisme molt similar (que no igual) que permet a través del fitxer /etc/nsswitch.conf configurar d'on obtindrem la informació de les diferents bases de dades del sistema (usuaris, grups, àlies de correus, protocols, hosts, etc).

Més informació

April 29, 2005

03:17
Entre ahir i avui he renovat parcialment el parc informàtic de casa meva.

Per una banda hem de dir adéu a un ordinador Pentium II 233 MHz que ha donat molt bon servei aquest temps. De tu només ens queda el record d'haver estat el meu primer ordinador i la seva CPU. Has estat un bon ordinador caecus.


La CPU de l'antic caecus.

D'altra banda donar la benvinguda a odisseu, penelope i tiresies. odisseu és una nova màquina AMD64 3200+ que utilitzaré jo personalment. penelope és el meu antic Athlon XP 1800+ (abans anomenat ordenata2) i que ara l'utilitza la meva família. Finalment tiresies, un Pentium III 800 antic ordinador familia i que ara fa de servidor de la xarxa.

Potser us fa curiositat saber el motiu dels noms. Tirèsies va ser condemant per Atenea a quedar cec després que aquest la sorprengués banyant-se. A canvi Atenea li va concedir el do de la profecia. (Hi ha altres històries sobre el motiu de la ceguesa de Tirèsies).

Penèlope era la dona d'Odisseu, rei d'�taca i fill de Laertes. Finalitzada la guerra de Troia, Odisseu havia de tornar a casa. Tothom donava per mort a Odisseu menys Penèlope qui enganyava i s'inventava argúcies per mantenir apartats els pretendents d'�taca.

April 23, 2005

22:17
No tots els programes que farem seran amb interfície gràfica i alguns d'ells són de línia d'ordres. La interacció sovint amb aquest tipus d'aplicacio es fa mitjançant paràmetres.

Les aplicacions en la gran majoria de sistemes arrenquen en una rutina main que té la capçalera següent.

int main(int argc, char* argv[]);

on argc indica el número d'arguments que ha rebut el programa (com a mínim un) i argv conté tots els arguments de 0 a argc-1. D'aquesta forma les següents invocacions suposaran els següents valors:

Invocació argc argv[0] argv[1] argv[2] ls -lh 2 ls -lh /usr/bin/ls -l -h 3 /usr/bin/ls -l -h ls --color -l 3 ls --color -l

Com es dedueix ràpidament analitzar aquests paràmetres és feina innecessària però que cal fer cada cop. Aquest problema sorgeix sovint en la gran majoria d'aplicacions de línia d'ordre i és per això que existeixen ja solucions per tractar els paràmetres d'un programa. Aquí parlarem de dos opcions que tenim disponibles a GNU/Linux, getopt i getopt_long, però existeixen altres opcions que trobareu al final.

getopt

L'opció de getopt és de les més senzilles però alhora més portables si hem de portar una aplicació fora de GNU/Linux. Bàsicament tot gira al voltant de la rutina del mateix nom, getopt on li indicarem quina mena de paràmetres ens esperem.

Suposem que estem fent una aplicació i volem tres paràmetres. Per una banda -o on especificarem un fitxer de sortida. També voldrem un paràmetre -d per activar opcions de depuració i finalment la típica opció -h per mostrar l'ajuda.

La rutina getopt rep tres paràmetres que són argc, argv i una cadena d'opcions que volem que reconegui. getopt només reconeix opcions d'un sol caràcter (-h però no --help). Cada opció pot anar seguida d'un valor (de la forma -ofitxer.txt o bé -o fitxer.txt). La rutina getopt ens anirà tornant quina opció ha reconegut.

En el nostre cas la cadena d'opcions a indicar és "o:hd". Els dos punts de o són per indicar que -o porta un valor associat. Com a extensió de GNU, les opcions poden anar seguides de :: per indicar que el paràmetre es optatiu (una cosa de l'estil -z, -z9 es podria indicar com "z::").

#include #include #include int main(int argc, char* argv[]) { char depuracio = 0, ajuda = 0, error = 0; char* fitxer_sortida = NULL; char param; while ( (param = getopt(argc, argv, "o:hd")) != (char)(-1) ) { switch(param) { case 'h' : ajuda = 1; break; case 'o' : // La variable "optarg" té l'argument fitxer_sortida = strdup(optarg); break; case 'd' : depuracio = 1; break; case '?': // Aquest és el valor quan getopt no enten una opció. // Noteu que getopt ja haurà escrit un missatge d'error, // no cal que ho fem nosaltres. error = 1; } } if (error) { // Aquí mostraríem l'ajuda mostrar_ajuda(); return (EXIT_FAILURE); } if (ajuda) { mostrar_ajuda(); return (EXIT_SUCCESS); } /* Hi ha paràmetres que no són opcions */ if (optind < argc) { int index; for (index = optind; index < argc; index++) { tractar_parametre(argv[index]); } } } Opcions llargues a l'estil GNU

La interfície getopt és senzilla d'utilitzar però sovint disposar d'opcions amb noms més descriptius que un sol caràcter és d'agraïr. En aquest sentit i com a extensió de GNU/Libc disposem de getopt_long, el seu ús és relativament més complex.

Per cada paràmetre cal inicialitzar una structura struct option on els diferents camps són el nom de l'argument, quin tipus d'argument és, una variable on podem escriure i un valor que utilitzarem quan ens trobem l'opció.

getopt_long es comporta igual que getopt quan troba una opció d'una sola lletra i utilitza una llista de struct option per saber què ha de fer amb una opció llarga.

#include #include #include #include int main(int argc, char* argv[]) { // Els fem int perquè és el que utilitza getopt_long int depuracio = 0, ajuda = 0, error = 0; struct option opcions_llargues[] = { // Quan trobem --debug llavors depuracio=1 {"debug", no_argument, &depuracio, 1}, // Quan trobem --nodebug llavors depuracio=0 {"nodebug", no_argument, &depuracio, 0}, // Quan trobi --output retornarà 'o' {"output", required_argument, NULL, 'o'}, // Quan trobi --output retornarà 'h' {"help", no_argument, NULL, 'h'}, // Cal acabar l'estructura amb NULLs {NULL, NULL, NULL, NULL} }; int index_opcions; char* fitxer_sortida = NULL; char param; while ( (param = getopt_long(argc, argv, "o:hd", opcions_llargues, &index_opcions)) != (char)(-1) ) { switch(param) { case 'h' : ajuda = 1; break; case 'o' : // La variable "optarg" té l'argument fitxer_sortida = strdup(optarg); break; case 'd' : depuracio = 1; break; case '?': // Aquest és el valor quan getopt no enten una opció. // Noteu que getopt ja haurà escrit un missatge d'error, // no cal que ho fem nosaltres. error = 1; } } if (error) { // Aquí mostraríem l'ajuda mostrar_ajuda(); return (EXIT_FAILURE); } if (ajuda) { mostrar_ajuda(); return (EXIT_SUCCESS); } /* Hi ha paràmetres que no són opcions */ if (optind < argc) { int index; for (index = optind; index < argc; index++) { tractar_parametre(argv[index]); } } } Més informació
  • gengetopt un generador d'analitzadors dels paràmetres que utilitza getopt_long i permet usar-la en altres entorns que no siguin GNU/Linux.
  • argp és una altra interfície oferta per GNU Libc. És força complexa i permet crear analitzadors molt sofisticats.
  • popt. Una altra llibreria per al processament dels paràmetres.
  • La llibreria GLib té suport per gestionar els paràmetres d'una aplicació.

March 23, 2005

07:55
Sovint ens toca actualitzar software perquè tenen fallades de seguretat que explotades amb èxit podrien tenir efectes devastadors en els nostres sistemes. Un error de programació clàssic que introdueix aquestes vulnerabilitats és l'anomenat buffer overflow.C i char*

Per tots és sabut que el llenguatge C no té un tipus de cadena específic com sí tenen molts llenguatges de programació. Per conseguir un efecte similar disposem de char* que no és més que un array de caràcters. Per saber on acaba la cadena s'utilitza un caràcter NUL (amb valor zero).

Les cadenes de C, char*, no tenen cap forma d'indicar la longitud sinó que s'ha de saber per on ens trobem el caràcter NUL. El problema succeeix quan aquest caràcter zero no apareix: les rutines de manipulació de cadenes de C seguiran considerant la resta de dades com a part de la cadena. Això fa que es puguin explotar per modificar zones de memòria difícilment accessibles des de la interfície del programa.

Exemple senzill

Anem a veure un exemple senzill de buffer overflow en el següent codi.

void funcioVictima(char* c) { // El compilador l'alinearà a 24 char buff[10]; strcpy(buff, c); }

Aquesta funció fa ús de la funció de la llibreria estàndard de C strcpy. El que fa és copiar una cadena a l'adreça que li indiquem. En aquest cas hem reservat estàticament 10 caràcters a la pila (variable buff). La pregunta que tothom s'està fent ara és què passa si la cadena que li arriba pel paràmetre c és de més de 10 caràcters?

Bàsicament el que passarà és que ens carregarem la pila. I llavors podríem controlar-la al nostre antuvi. Per exemple podem utilitzar aquest error de programació per executar una funció que no crida ningú.

void noEmCrideu(void) { printf("Qui m'ha cridat? "); } void funcioVictima(char* c) { // El compilador GCC l'alinearà a 24 char buff[10]; strcpy(buff, c); } int main(int argc, char* argv[]) { unsigned int* overflow = (int*)malloc(20*sizeof(int)); unsigned char* overflow_char = (char*) overflow; // Omplim els 24 primers caràcters // aixo son els 6 primers "int" int i; for (i = 0; i < 24; i++) { overflow_char[i] = '.'; } // Copiem el EBP vell a la pila // Només es pot fer amb assembler __asm__ ( "mov %%ebp, %0" : "=m" (overflow[6]) : ); // L'adreça de retorn de la rutina overflow[7] = (unsigned int)noEmCrideu; // L'adreça de retorn de "noEmCrideu" overflow[8] = (unsigned int) &&etiqueta; // Overflowegem la pila funcioVictima(overflow_char); // Si tot ha anat bé hauríem d'estar aquí etiqueta: printf ("Aquí no ha passat res :P "); return 0; }

Bàsicament això passa perquè el bloc d'activació de la funcioVictima és el següent:

AdreçaContingutValor del buffer overflowNotes EBP-24 Adreça del vector buf strcpy copiarà el buffer. Per tant aquí correspon amb *((char*)overflow) El compilador l'ha posat aqui per eficiència. ... ... ... EBP+0 El contingut del EBP anterior a aquest bloc d'activació Això és overflow[6]. Aquesta adreça la posem a mà amb assembler. Usarem el valor de main així a noEmCrideu li semblarà que l'haguem cridat des de main. EBP+4 L'adreça de retorn de funcioVictima Això és overflow[7]. Aquí posarem l'adreça de noEmCrideu EBP+8 L'adreça de retorn de noEmCrideu Això és overflow[8]. Aquí posarem l'adreça de etiqueta per tornar al main correctament.

Si compilem i executem aquest programa ens donarà el resultat següent:

$ gcc -g -o prova_salt prova_salt.c $ ./prova_salt Qui m'ha cridat? Aquí no ha passat res :P

Hem estat capaços de modificar correctament la pila. Això ens ha permès saltar a una funció que no crida ningú i després tornar al main.

Un altre exemple

Ara suposem que la cadena que li passem a la rutina fa un salt a la pròpia cadena i aquesta conté codi màquina. Malauradament el nivell de protecció d'execució a IA32 és a nivell de segment i no de pàgina. Per tant les pàgines de pila i del heap normalment són executables. Simplement el que fem és desapilar el paràmetre que ha rebut funcioVictima i posar un 3 a eax de forma que el resultat del nostre programa sigui 3.

Noteu que les noves arquitectures compatibles amb IA32 com ara AMD64 i IA32-EMT64 incorporen protecció d'execució a nivell de pàgina. És l'anomenat NX (Not Executable) i l'estan comercialitzant com a "protecció antivirus". Per tant en aquests sistemes on el sistema operatiu impedeixi l'execució de pàgines de la pila i el heap aquest exemple no funcionarà.

int main(int argc, char* argv[]) { unsigned int* overflow = (int*)malloc(20*sizeof(int)); unsigned char* overflow_char = (char*) overflow; // Omplim els 24 primers caràcters // aixo son els 6 primers "int" int i; for (i = 0; i < 24; i++) { overflow_char[i] = '.'; } // Copiem el EBP vell a la pila // Només es pot fer amb assembler __asm__ ( "mov %%ebp, %0" : "=m" (overflow[6]) : ); // L'adreça de retorn de la rutina overflow[7] = (unsigned int)&overflow[8]; /* Codi màquina de IA32 0: 83 c4 04 add $0x4,%esp 3: b8 03 00 00 00 mov $0x3,%eax 8: c9 leave 9: c3 ret IA32 és una arquitectura little endian. Ens cal donar la volta als enters de 32 bit. 83 c4 04 b8 -> b8 04 c4 83 03 00 00 00 -> 00 00 00 03 c9 c3 -> 90 90 c3 c9 (90 es el codi del "nop", podíem haver posat qualsevol cosa) */ overflow[8] = 0xb804c483; overflow[9] = 0x00000003; overflow[10] = 0x9090c3c9; // Overflowegem la pila funcioVictima(overflow_char); // Si tot ha anat bé hauríem d'estar aquí etiqueta: printf ("Aquí no ha passat res :P "); return 0; }

Efectivament el resultat del programa acaba sent 3.

$ gcc -g -o prova_codi prova_codi.c $ ./prova_codi ; echo $? 3 Com evitar-los

Els buffer overflows s'han d'evitar sempre que sigui possible. Per una banda ja hem vist que algunes funcions de C no hi ajuden. Per tant és important utilitzar les versions segures. Per a l'exemple que hem utilitzat tenim la funció strcpy. L'equivalent segura és strncpy on s'especifica el número de caràcters a copiar (noteu que a l'exemple li podem passar 10 però en el cas que s'hagin copiat 10 caràcters caldrà triturar amb NUL el caràcter buff[9]).

Malauradament això suposa reescriure molt de codi i sovint no és possible fer-ho. Una altra solució és establir mecanismes de protecció. Aquests mecanismes de protecció de la pila afegeixen un canari aleatori a la pila. Si el valor d'aquest canari ha canviat llavors vol dir que algú s'ha passejat per la pila. Cal dir que aquesta comprovació s'ha de fer sempre just abans de sortir d'una funció i per tant que causa un impacte en les aplicacions.

Hi ha diferents mecanismes de protegir-nos. Un de prou extès ja que el porta la distribució Gentoo per defecte és ssp de IBM. Però hi ha altres implementacions com ara StackGuard de Immunix. El nostre gcc porta ssp i l'hem posat a prova.

$ gcc -fstack-protector-all -o prova_salt prova_salt.c $ ./prova_salt prova_salt: stack smashing attack in function funcioVictima() Avortat $ gcc -g -fstack-protector-all -o prova_codi prova_codi.c $ ./prova_codi prova_codi: stack smashing attack in function funcioVictima() Avortat

El problema en realitat no s'ha resolt però ara hem conseguit passar d'una vulnerabilitat seriosa que podria implicar execució (possiblement remota) de codi a una simple denegació de servei. El programa aborta en detectar una corrupció de la pila.

La solució definitiva, és però, ser molt curós a l'hora d'escriure codi que manipuli cadenes.

Més informació
07:55
Per allà el 1992, quan Borland va treure la seva setena (i última) versió de Turbo Pascal per a DOS a la caixa del producte hi mencionava una dada que avui dia és difícil de trobar en les eines de desenvolupament. Turbo Pascal 7 era capaç de compilar 85.000 línies de codi per minut en un Compaq Deskpro 386/33.

El motiu de tan insòlita publicitat era deguda a una de les deficiències principals del llenguatge C/C++ i que no patia Turbo Pascal. En el llenguatge C/C++, cada cop que es compila un fitxer cal carregar totes les capçaleres a les que es refereix.

Això en Turbo Pascal no passa ja que les unitats d'un programa (un fitxer de codi podia contenir un program pròpiament dit o una unit que implementava el concepte de mòdul) es compilaven en un format intermedi binari (amb extensió TPU). Quan el compilador compilava un program o una unit que referís a d'altres unit consultava l'existencia de la versió compilada. Si no la trobava compilava la unit referida. Si la trobava es limitava a consultar els símbols (variables i funcions) que definia en un índex que contenia el fitxer binari. Aquí és on raïa el secret de la velocitat de Turbo Pascal: no haver de recompilar cada cop.

En aquella època Turbo Pascal era un seriós competidor del llenguatge C/C++ en quant a velocitat. La gent no li importava programar en un llenguatge o altre perquè la interoperabilitat no era tan crucial a DOS. Però carai, era difícil compilar 85.000 línies de codi en un minut (més de 1400 línies per segon) quan usaves C/C++ ja que cada fitxer podia suposar haver-ne d'incorporar moltes.

Precompilació de capçaleres

Evidentment haver de llegir cada cop els fitxers de capçalera per compilar és una operació inútilment costosa. És per aquest motiu que els fabricants de compiladors de C/C++ van decidir donar una solució acceptable : precompilar les capçaleres. La tècnica consisteix en fer el mateix que feia Turbo Pascal, precompilar les capçaleres i desar-les en un format binari eficient. D'aquesta manera quan es compila un fitxer només cal fer una consulta a l'índex del precompilat i no cal compilar-lo de nou.

Els compiladors comercials més coneguts (Visual C++, Borland C++, Intel C++) disposen de suport a les capçaleres precompilades. El compilador de GNU GCC no ha disposat d'aquesta característica fins a la recent versió 3.4. Per fer-ho simplement cal compilar la capçalera com un fitxer normal.

Per al nostre exemple usarem la llibreria gtkmm, un wrapper en C++ de la llibreria GTK+. Suposem el fitxer header.h següent:

#ifndef HEADER_H #define HEADER_H #include // Un include de conveniència que inclou tota la llibreria #endif

Anem a calcular quant ocupen totes les capçaleres de la llibreria.

$ g++ -E `pkg-config --cflags gtkmm-2.0` header.h > mida_headers $ ls -lh mida_headers -rw-r--r-- 1 roger users 2,1M 10 oct 22:08 mida_headers

com podeu observar haver de compilar més de 2 megabytes de codi cada cop és bastant costós. Anem a precompilar les capçaleres.

$ g++ -c `pkg-config --cflags gtkmm-2.0` header.h $ ls -l header.h.gch -rw-r--r-- 1 roger users 33M 10 oct 22:11 header.h.gch

Això genera un fitxer header.h.gch que ocupa força espai. És el preu que haurem de pagar per la velocitat. En el meu sistema ocupa 33 Mb (!!). Sigui ara un fitxer header.c que inclogui header.h. Anem a comparar la velocitat de compilar usant un header precompilat a no usar-lo.

$ time g++ `pkg-config --cflags gtkmm-2.0` -c header.c real 0m0.227s user 0m0.140s sys 0m0.047s $ rm header.h.gch $ time g++ `pkg-config --cflags gtkmm-2.0` -c header.c real 0m3.183s user 0m2.868s sys 0m0.225s

Com podeu observar la diferència és molt significativa.

El suport de capçaleres precompilades a GCC 3.4 és encara força simple i subjecte a restriccions relativament estrictes. A més se sap que pot provocar que el compilador es pengi. En qualsevol cas és un primer pas per situar-se a un nivell similar a la resta de compiladors. És d'esperar que en la nova sèrie GCC 4.x el suport de capçaleres precompilades millori.

Més informació
07:55
Un problema freqüent que se solen trobar els programadors quan implementen una aplicació és la de resoldre els problemes de memòria dinàmica. Sempre és font de problemes i maldecaps. Malauradament afecta a quasi tots els entorns de desenvolupament però sol ser especialment greu en llenguatges com C i C++.Problemes de memòria

La gran majoria de llenguatges de programació es veuen afectats pels possibles errors de programació en l'aspecte de memòria dinàmica. D'errors de memòria dinàmica en trobem molts i variats però tots es podrien classificar en algun dels dos casos següents:

  • Accessos incorrectes. Se sol donar quan una referència a memòria és incorrecta, perquè no s'ha inicialitzat o ha deixat vàlida, o l'accés que fem a través d'ella és incorrecte, per exemple accedim més enllà de la mida real de l'objecte referenciat. Aquests errors s'haurien de resoldre sempre.
  • Fugues de memòria (memory leaks). Es donen quan es perd la referència a memòria sol·licitada anteriorment. La memòria sol·licitada passa a ser malgastada sense possibilitat de recuperar-se per al programa posteriorment. Aquest cas és resolt pels recol·lectors de memòria (garbage collectors) de forma automàtica.

Quant a les fugues de memòria no sempre és necessari resoldre-les. Si el sistema operatiu garanteix que s'alliberen els recursos sol·licitats en finalitzar l'aplicació i la quantitat de memòria que malgastem no augmenta en funció de la duració de l'execució del programa llavors no és imprescindible esmenar-ho. De fet aquesta estatègia l'utilitzen moltes llibreries que sol·liciten memòria per a buffers interns que seran usats durant l'execució (i així estalviar sol·licitar i lliberar memòria contínuament).

L'eina Valgrind

El propòsit de l'article, però, era parlar d'aquesta eina tan magnífica com és Valgrind. Valgrind és una eina pensada per a l'arquitectura IA32 que permet executar l'aplicació que estem desenvolupant a la cerca de problemes de memòria. En realitat Valgrind porta més opcions com un analitzador de la quantitat de memoria dinàmica gastada o un analitzador del rendiment de la cache.

Per il·lustrar-ho he escollit un error clàssic quan es programa amb C++. Tot i que aparentment el codi sembla perfectament correcte conté un memory leak potencial. No intenteu trobar-li massa sentit al programa, bàsicament Numero i Parell guarden un punter a un enter, només que Parell utilitza el valor de Numero per construir el doble del valor usat per construir-la.

07:55
Sovint ens trobem amb problemes quan una aplicació vol produir un so. Resulta que algú altre ja està ocupant el dispositiu de so. Anem a veure una manera de resoldre-ho.El so a Linux

El subsistema de so tradicional disponible a tots els UNIX és l'anomenat Open Sound System o simplement OSS. Aquest subsistema de so està disponible a la gran majoria de *nix: Linux, BSD, Solaris, AIX, etc.

Quan es va dissenyar OSS cobria bona part de les necessitats de so que hi havia per aquell moment. Però al llarg del temps en l'entorn Linux s'ha impulsat una alternativa a OSS. Aquesta alternativa ve motivada per algunes limitacions del sistema OSS i per altra banda la manca de drivers lliures. En aquest sentit cal dir que l'empresa 4Front ofereix comercialment drivers OSS per a diferents UNIX inclòs Linux.

Aquesta alternativa s'anomena Advanced Linux Sound Architecture, ALSA, i ve a proporcionar una arquitectura amb majors possibilitats que OSS. ALSA proporciona els drivers de les targetes de so, que es troben de sèrie al kernel 2.6 de linux, i una llibreria de programació de forma que les aplicacions poden usar ALSA nativament. A més ALSA porta una capa de compatibilitat amb OSS de forma que les aplicacions fetes per a OSS poden seguir funcionant amb els drivers d'ALSA.

El repte de l'escriptori multimèdia

Deixant de banda si GNU/Linux està preparat per a l'escriptori, és clar que en aquests últims anys s'ha incrementat l'activitat centrada en el desenvolupament d'escriptoris per a GNU/Linux.

En aquest sentit s'ha observat que tant ALSA com OSS no proporcionen, almenys de forma directa, mecanismes perquè les diferents aplicacions puguin emetre sons alhora. La majoria de targetes de so porten un o dos processadors de so que no pot fer mescla (algunes noves targetes permeten la mescla per hardware) de forma que quan una aplicació ha obert el dispositiu de so cap altra pot utilitzar-lo. Això fa que només pugui estar "sonant" una aplicació alhora.

Hi ha diferents formes de resoldre aquest problema però cap ens proporcionarà una solució total (a menys que usem targetes amb mescla hardware). Una d'elles consisteix en utilitzar servidors de so on les aplicacions de l'escriptori enviaran el so. El servidor mesclarà el so i l'enviarà adequadament a la targeta de so. Exemple d'aquestes solucions son Enlightenment Sound Daemon (ESD) i KDE/aRTs. Malauradament aquesta solució sol imposar massa latència i la sensació que es percep és que tot va molt feixuc. Cal a dir que aquest mecanisme obliga a escriure les aplicacions expressament perquè utilitzin aquests servidors.

Una altra solució és usar el mecanisme de plugins d'ALSA. Un d'aquests plugins s'anomena dmix i permet que diverses aplicacions ALSA puguin reproduir so alhora. Un altre plugin és el de JACK. JACK és un servidor de so però dissenyat per a tenir baixa latència i pensat perquè diferents aplicacions s'intercanviïn el so.

Configurar dmix

Per configurar dmix crearem un fitxer .asoundrc al nostre HOME. Per començar podem provar amb la configuració següent:

pcm.!default { type plug slave.pcm "dmix" }

el que està dient és que es redirigeixi el dispositiu de so per defecte a dmix. Aquest és el dispositiu que obren les aplicacions si no se'ls ha indicat explícitament que n'obrin un altre. Automàticament serà redirigit a dmix qui farà la mescla pertinent. Per provar que funciona necessitem l'eina aplay del paquet alsa-utils i un fitxer WAV que duri uns segons.

$ (aplay fitxer.wav &); aplay fitxer.wav

Si tot ha anat bé s'executaran dos aplay en paral·lel i sentirem dos cops fitxer.wav amb una petita separació en el temps entre els dos.

Configuració més sofisticada

Ja s'ha comentat que hi ha targetes amb mesclador per hardware (com la SoundBlaster Live!) o targetes amb més d'un processador de so (com la SoundBlaster 128 PCI). En aquest últim cas pot ser molt útil redirigir tot el so de les aplicacions ALSA al segon processador de so i deixar el primer lliure. Així, si hem d'executar una aplicació OSS aquesta obrirà per defecte el primer processador de so (noteu que una segona aplicació OSS alhora ja no funcionaria). En aquest cas la configuració és una mica més complexa:

pcm.!default { type plug slave.pcm "ossmix" } pcm.ossmix { type dmix ipc_key 1099 # Ha de ser un valor únic slave { pcm "hw:0,1" # hw:0,1 és el segon processador # hw:0,0 seria el primer } bindings { 0 0 1 1 } } "ALSAitzant" aplicacions OSS

El paquet alsa-utils porta un script, aoss, que permet executar aplicacions OSS de forma que acabin utilitzant ALSA. Per fer-ho s'aprofita de la llibreria libaoss que intercepta els accessos al dispositiu de so OSS i els redirigeix a ALSA. No funciona pas amb totes les aplicacions però si amb la gran majoria. Ens caldrà afegir la següent entrada al nostre fitxer .asoundrc.

pcm.dsp0 { type plug slave.pcm "ossmix" }

Ara ja podem usar aoss per a l'aplicació:

$ aoss aplicacio_que_nomes_usa_oss Més informació
07:55
Tot i que no fa massa temps que el tinc, he instal·lat GNU/Linux a hydra. Afortunadament sembla que la branca PPC32 del kernel Linux disposa de la gran majoria de coses que necessitarem.

Abans de començar si desitgem tenir arrencada dual Mac OS X i Linux haurem de particionar el disc en dos trossos. El primer l'haurem de marcar com a espai lliure i el segon partició HFS+ de Apple. Això es pot fer des del gestor de disc de l'instal·lador de Mac OS X. La instal·lació de Mac OS X s'haurà de fer sobre la segona partició. No sobre la primera. Així quan més endavant creem les particions de Linux una d'elles haurà de ser bootstrap i com que serà la primera és la que arrencarà OpenFirmware (una espècie de BIOS dels PowerPC).

La distribució que he escollit és Gentoo. El motiu és ben simple, la coneixo raonablement. La instal·lació està prou ben documentada. A més em permetia aprofitar el codi font que ja tinc descarregat en un altre ordinador amb Gentoo, via NFS, així que almenys puc estalviar temps de descàrrega, que és l'únic que es pot estalviar ja que caldrà invertir molt de temps compilant (un G4 a 1.06 GHz no és precisament una fletxa ;)

Sembla que la branca PPC de Gentoo està una mica antiquada i això em va suposar haver d'actualitzar molts paquets. Vaig decidir utilitzar la branca inestable ~ppc de Gentoo perquè pel que volia m'anava millor. La instal·lació no va estar exempta de problemes. Cal instal·lar explícitament els headers de la branca 2.6 del kernel, compilar GNU GCC 3.4 abans que GNU Libc (perquè el GCC 3.2 que ve amb el livecd li falten opcions GNU Libc 2.3 els demana) i corregir un include del kernel perquè mancava una macro i no compilava el paquet kbd. Aquest últim fenomen també afecta al paquet mac-fdisk que simplement no compila i el codi sembla ser força antiquat, em vaig limitar copiar el binari del livecd.

Quant al kernel, val la pena saber que porta algunes opcions menys que el seu equivalent de IA32, cosa que facilita trobar els drivers i les opcions apropiades per a els Apple. En particular hi ha diverses opcions per a iBook i PowerBook. Són interessants les que tracten del control d'energia: escalat de freqüència de la CPU (útil per reduir la velocitat de la CPU quan la càrrega és baixa), control dels ventiladors i els sensors, i emulació APM per consultar la bateria. Cal tenir una versió superior a 2.6.7-bk16 perquè tot funcioni en el meu iBook (he posat la versió 2.6.8.1).

Tot i això no podrem gaudir de tots els dispositius. La ATI Radeon 9200 no porta acceleració nativa 3D, hom confia que algun dia hi haurà drivers no només per a IA32. Tampoc podrem gaudir del modem USB intern, ja que és un dispositiu Conexant i sembla que els drivers per a Linux no el detecten correctament (potser és un problema amb la branca 2.6).

En definitiva, tot i els petits entrebancs que m'he anat trobant, tinc la sensació d'estar al teu Linux de sempre encara que sigui en una arquitectura totalment diferent com és la PowerPC.

Més informació
07:55
Recentment he adquirit un iBook. En particular el model de 12" on només li he afegit 256 MB de RAM més, em semblava que només 256 MB era ben poc pels temps que corren. L'he batejat amb el nom hydra en referència al sistema Aqua.

Els meus interessos com a estudiant d'enginyeria informàtica són força particulars. Tot i que no era directament una necessitat imperiosa tenir un ordinador portàtil sí que havia sospirat sovint per algun ordinador que no fos de l'arquitectura IA32, independentment que fos portàtil o no. Perquè? Per què massa temps amb una mateixa cosa acaba fent tuf. A més crec que val la pena tenir d'altres perspectives en quant a computadores, ni que sigui esbiaixadament amb ordinadors projectats per al mercat domèstic.

Malauradament qualsevol cosa que no sigui tipus PC és massa car per a una pretensió domèstica. Arquitectures alternatives com ara Sun SPARC o SGI són massa cares per a un interès particular com el meu. Algun cop havia estat a punt d'adquirir algun equipament de segona mà, però sempre m'havia fet enrere.

Apple va decidir crear un sistema operatiu basat en UNIX BSD (fet que li confereix major interoperabilitat i portabilitat que mai) anomenat Mac OS X. Mac OS X no seria un altre UNIX més si no fos que va potenciat per una interfície gràfica molt espectacular i per un entorn d'escriptori realment integrat.

En comparació amb les versions anteriors, Mac OS X ha despertat un major interès ja que és relativament més fàcil portar aplicacions a Mac, encara que no facin ús de totes les característiques avançades de la interfície.

A més, l'arquitectura que utilitza Apple no és IA32 sinó PowerPC, una arquitectura RISC i per tant força diferent de la que ens trobem a un PC convencional. Això li confereix un interès especial per part meva.

Com a portàtil i de cara a un usuari corrent val a dir que és atractiu pel disseny i pel software que porta. En aquest últim punt recalcar una excelsa integració de totes les aplicacions que venen amb el sistema. En particular cobreixen la gran majoria de necessitats (o si més no a nivell bàsic) d'un usuari convencional: correu electrònic, música, processament senzill de text, gravació de CD-R, navegació per internet, etc.

Com a entorn per a un desenvolupador el seu atractiu es troba en la seva natura UNIX i en eines proporcionades per la pròpia Apple de forma gratuïta, en particular de l'eina XCode que és un complet entorn de programació. Apple sap que els desenvolupadors són normalment la punta de llança i els ofereix una solució amigable d'un entorn potent com XCode. Aquest paquet incorpora molta documentació, diversos kits de desenvolupament i, com no, la família de compiladors de GNU, GCC.

Inconvenients sempre n'hi ha arreu. El preu és força car. PC's portàtils de preu similar tenen molts cops més molta més potència. Però clar, Apple fa pagar l'exclusivitat del seu sistema. Un portatil de disseny acurat, un software molt vistós, cuidat fins a l'últim detall i molt agradable a la vista. I un sistema operatiu bastit en la robustesa de UNIX. Però no només això, compatibilitat total amb el hardware que ve equipat. Tot això no hi és en un portàtil de PC, Apple ho sap i ho cobra.

07:55
Quan reproduïm un CD de música posat al lector, o gravadora, de CD/DVD del nostre ordinador, la gran majoria de cops li demanem que es comporti com un lector de CD de música convencional. El so s'envia directament a la tarja de so que es limita a fer-lo sortir directament pels altaveus.

Aquesta és l'aproximació que prenen la gran majoria d'aplicacions de reproduccio de CD. Té avantatges: no afegeix cap cost de processament per a la CPU ja que el lector s'encarrega de la feina. Llegeix el CD com si fos un lector de música de cadena, converteix la música a analògic i l'envia a la tarja de so per un cable que hem instal·lat a tal efecte. La tarja de so s'encarrega d'enviar-ho a la sortida sense fer-ne cap processament. També te l'avantatge que no ocupa un dispositiu d'àudio i per tant no porta els problemes perquè una aplicació s'ha apropiat del dispositiu de so.

És aquesta manca de processament que pot portar-nos algun problema. En general no és possible capturar el so que surt pels altaveus. Capturar el so del CD pot ser útil per exemple quan hem muntat una ràdio i en comptes d'usar una llista de reproducció capturem el so per a retransmetre'l.

També ens pot passar que no tinguem el cable connectat del CD a la tarja de so (per exemple, tenim dos lectors de CD) i per algun motiu volem que sonin el dos alhora.

L'extracció digital consisteix en llegir les dades del CD directament i enviar-les a la tarja de so perquè les processi. Això fa que ara el so surti per la tarja de so. Té l'avantatge que podem capturar aquest so (si la tarja és full duplex que quasi totes ho són). Ara el control del volum ja no serà el del CD sinó el de PCM (o Wave). Inconvenient és que hi ha un major cost de processament: cal enviar-ho a la tarja de so i ocuparem el dispositiu d'àudio. També te l'avantatge que el lector no cal que estigui connectat a la tarja de so.

El reproductor de so XMMS porta un connector (plugin) d'entrada per al CD (libcdaudio.so). En una de les opcions es pot configurar si es vol usar la sortida analògica o l'extracció digital.

Si disposeu dels drivers i les eines d'ALSA i de l'eina cdparanoia es pot aconseguir un efecte similar. Primer creem una pipe amb nom on el programa aplay (ALSA player) pugui llegir. Li indicarem que és so de CD directe (16 bit little endian, 44100, stereo) amb -f cd.

$ mkfifo soCD $ aplay -f cd soCD

Ara en un altre terminal li direm a cdparanoia que extragui el so de les pistes però sense aplicar correcció d'errors (-Z). La correcció d'errors reduiria molt la velocitat d'extracció. Li indicarem que extragui el so en format little endian perquè l'aplay el pugui fer sonar (-R). També hem indicat que extragui a partir de la primera pista. Finalment li diem que ho faci a la pipe amb nom que hem creat i que aplay està llegint.

$ cdparanoia -Z -R 1 soCD
07:55
El meu ordinador d'escriptori té una placa mare Asus A7S333 amb chipset SiS 745. Com ja vaig comentar en el m'agrada apagar el PC i que s'apagui sol.

Malauradament aquest chipset té algun comportament místic inexplicable i l'ordinador no sap apagar-se sol. Un cop premut el botó i iniciat el procés d'apagat, al final no es desconnecta del corrent i es limita a mostrar Power down.

Després de molta investigació empírica he descobert quines son les opcions que fa que es desconnecti correctament del corrent.

Han estat moltes hores de recompilar el kernel, reiniciar, provar d'apagar i tornar a recompilar... El problema principal són els canvis al codi de ACPI del kernel. Cada versió de 2.6.x porta codi molt diferent que fa que calgui canviar les opcions. Al final la combinació que sembla que funciona bé per a kernels 2.6.6 i superior és la següent:

  • Cal passar l'opció acpi=noirq com a paràmetre del kernel. Això es fa al LILO o al grub. Aquesta opció és un altre exemple de característica no documentada(TM) al kernel. La vaig descobrir en un fòrum per casualitat.
  • Cal desactivar el kernel preemptiu. [ ] Preemptible Kernel
  • I cal tenir activades les opcions de ACPI com a mínim: [*] ACPI Support Button

Aquest post està dedicat a tots els propietaris de la placa Asus A7S333 que desitgen fervorosament que sàpiga apagar-se sola a Linux. Espero que serveixi a algú més que a mi :)

pàgina generada en: 0.39 segons.