[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [volume] [parte]


Capitolo 129.   Controllo degli accessi

In ogni sistema operativo multiutente c'è la necessità di controllare gli accessi. Nei sistemi Unix un utente che può accedere ha un account: letteralmente si tratta di un conto, o in altri termini un «accredito», o meglio ancora una specie di contratto di utenza, che gli permette di esistere nel sistema in qualità di «utente logico».

La tabella 129.1 elenca i programmi e i file a cui si accenna in questo capitolo.

Tabella 129.1. Riepilogo dei programmi e dei file per il controllo degli accessi e per le operazioni correlate.

Nome Descrizione
login Permette l'accesso a un sistema.
/etc/passwd Tabella delle caratteristiche salienti degli utenti.
/etc/group Tabella delle caratteristiche salienti dei gruppi.
/etc/shadow Tabella delle parole d'ordine quando non sono in /etc/passwd.
/var/run/utmp Elenco degli accessi in corso.
/var/log/wtmp Elenco degli accessi trascorsi.
/etc/motd Messaggio di apertura o messaggio del giorno.
/etc/nologin Messaggio di impedimento all'accesso.
/etc/securetty Elenco dei terminali da cui è consentito l'accesso all'utente root.
/var/mail/* Messaggi di posta elettronica degli utenti.
~/.hushlogin Accesso rapido.
/var/log/lastlog Data e orario dell'ultimo accesso.
su Permette di operare con l'identità di un altro utente.
newgrp Permette di cambiare gruppo.
users Elenca i nomi degli utenti che accedono al sistema.
w Elenca i nomi e altre notizie degli utenti che accedono.
who Elenca i nomi degli utenti che accedono al sistema.
finger Fornisce notizie sugli utenti di un certo elaboratore nella rete.
pinky Fornisce notizie sugli utenti dell'elaboratore locale.
whoami Emette il nome dell'utente.
logname Emette il nome dell'utente.
groups Elenca i gruppi a cui appartiene un utente.
id Elenca i dati identificativi dell'utente.

129.1   Login, ovvero la procedura di accesso

Il login è la procedura di accesso attraverso la quale un utente, registrato precedentemente, viene riconosciuto e gli viene concesso di utilizzare il sistema. Il concetto è simile a quello di una firma di ingresso. Quando un utente conclude la sua attività con il sistema, esegue un logout. Il concetto del logout è simile a quello di una firma di uscita.

La procedura di accesso è controllata normalmente dal programma login, che si prende cura di verificare la parola d'ordine fornita, prima di consentire l'ingresso dell'utente. Tuttavia, i programmi login non sono uguali in tutte le distribuzioni GNU/Linux e ognuno può essere stato predisposto per una politica differente. A titolo di esempio, un programma login potrebbe accettare l'accesso da parte di utenti per i quali non sia stata definita una parola d'ordine, mentre un altro potrebbe escluderlo. In queste sezioni si affronta il problema in modo superficiale, cercando di fare riferimento alle consuetudini che sembrano consolidate; il lettore deve tenere presente che l'unica documentazione certa sul funzionamento di login è quella fornita assieme alla sua distribuzione GNU/Linux: la pagina di manuale login(1).

129.1.1   Utilizzo di «login»

Il programma login permette l'accesso dell'utente al sistema. Di solito non si usa direttamente, anzi, ciò dovrebbe essere impossibile: è compito del programma di gestione del terminale, Getty o simili, di avviarlo dopo aver ottenuto il nominativo-utente.

login [utente]

Ogni utente registrato nel sistema, cioè ogni utente che (teoricamente) può accedere al sistema, ha una directory personale, o directory home, all'interno della quale si trova posizionato al momento dell'accesso. Questa directory contiene dei file riguardanti la configurazione particolare dell'utente a cui appartiene. La directory personale è collocata normalmente in /home/nome_utente/ e questa, se la shell lo consente, viene abbreviata utilizzando il simbolo tilde (~). La directory personale dell'utente root è speciale e dovrebbe trovarsi in /root/. Durante un accesso normale da parte di un utente qualunque, compreso root, vengono richiesti il nome dell'utente (se non è già stato fornito nella riga di comando) e la parola d'ordine. Quindi vengono visualizzati:

Se si tratta di un utente al quale è associata una parola d'ordine, questa viene richiesta e controllata. Se risulta errata, vengono consentiti un numero limitato di tentativi. Generalmente, gli errori vengono riportati all'interno del registro del sistema. Se l'utente che chiede di accedere non è root e se esiste il file /etc/nologin, ne viene visualizzato il contenuto sullo schermo e non viene consentito l'accesso. Ciò serve per impedire l'accesso al sistema, tipicamente quando si intende chiuderlo. Perché l'accesso possa essere effettuato come utente root, occorre che il terminale (TTY) da cui si intende accedere sia elencato all'interno di /etc/securetty. I tentativi di questo tipo che provengono da terminali non ammessi, vengono annotati all'interno del registro del sistema. Se esiste il file ~/.hushlogin, viene eseguito un accesso silenzioso, nel senso che vengono disattivati:

Se esiste il file /var/log/lastlog, viene visualizzata la data e l'ora dell'ultimo accesso e ne viene registrato quello in corso. Al termine della procedura di accesso viene avviata la shell dell'utente. Se all'interno del file /etc/passwd non è indicata la shell da associare all'utente che accede, viene utilizzato /bin/sh. Se all'interno del file /etc/passwd non è indicata la directory personale dell'utente, viene utilizzata la directory radice (/).

Quanto affermato dovrebbe essere sufficiente per capire che la semplice rimozione dell'indicazione della shell o della directory personale da un record del file /etc/passwd, non è sufficiente per impedire l'accesso a un utente.

129.1.2   File «/etc/passwd»

Il file /etc/passwd è un elenco di utenti, parole d'ordine, directory home (directory personali nel caso di utenti umani), shell e altre informazioni personali utilizzate da Finger (294.3). La struttura dei record (righe) di questo file è molto semplice:

utente:parola_d'ordine_cifrata:uid:gid:dati_personali:directory_home:shell

Segue la descrizione dei campi.

  1. utente

    È il nome utilizzato per identificare l'utente logico che accede al sistema.

  2. parola_d'ordine_cifrata

    È la parola d'ordine cifrata. In condizioni normali, se questa indicazione manca, l'utente può accedere senza indicare alcuna parola d'ordine.

    Se questo campo contiene un asterisco (*) l'utente non può accedere al sistema. Con questa tecnica è possibile impedire temporaneamente l'accesso, con la possibilità di ripristinarlo successivamente con la stessa parola d'ordine, togliendo semplicemente l'asterisco.

  3. uid

    È il numero identificativo dell'utente (User ID).

  4. gid

    È il numero identificativo del gruppo a cui appartiene l'utente (Group ID).

  5. dati_personali

    Di solito, questo campo contiene solo l'indicazione del nominativo completo dell'utente (nome e cognome), ma può contenere anche altre informazioni che di solito sono inserite attraverso chfn (130.2.6).

  6. directory_home

    La directory assegnata all'utente.

  7. shell

    La shell assegnata all'utente.

Segue la descrizione di alcuni esempi.

129.1.3   File «/etc/group»

È l'elenco dei gruppi di utenti. La struttura delle righe di questo file è molto semplice.

gruppo:parola_d'ordine_cifrata:gid:lista_di_utenti

Segue la descrizione dei campi.

  1. gruppo

    È il nome utilizzato per identificare il gruppo.

  2. parola_d'ordine_cifrata

    È la parola d'ordine cifrata. Di solito non viene utilizzata e di conseguenza non viene inserita. Se è presente una parola d'ordine, questa dovrebbe essere richiesta quando un utente tenta di cambiare gruppo attraverso newgrp (129.2.2).

  3. gid

    È il numero identificativo del gruppo.

  4. lista_di_utenti

    È la lista degli utenti che appartengono al gruppo, anche se questo non risulta dal file /etc/passwd. Si tratta di un elenco di nomi di utente separati da virgole.

Segue la descrizione di alcuni esempi.

129.1.4   File «/etc/shadow»

Il file /etc/shadow appare in quei sistemi in cui è attivata la gestione delle parole d'ordine oscurate (shadow password). Serve a contenere le parole d'ordine cifrate, togliendole dal file /etc/passwd. Facendo in questo modo, è possibile inibire la maggior parte dei permessi di accesso a questo file, proteggendo le parole d'ordine. Al contrario, non è possibile impedire l'accesso in lettura del file /etc/passwd che fornisce una quantità di informazioni sugli utenti, indispensabili a molti programmi.

Il problema è descritto nel capitolo 131.

129.1.5   File «/var/run/utmp»

Il file /var/run/utmp contiene l'elenco degli accessi in essere nel sistema. Non è un file di testo normale e per l'estrazione delle informazioni in esso contenute si usano dei programmi di servizio appositi. Tuttavia, è possibile che gli utenti presenti effettivamente nel sistema siano in numero maggiore, a causa del fatto che non tutti i programmi usano il metodo di registrazione fornito attraverso questo file.

Se il file non esiste, conviene crearlo manualmente in uno dei due modi seguenti:

cp /dev/null /var/run/utmp[Invio]

touch /var/run/utmp[Invio]

Solitamente, è la procedura di inizializzazione del sistema a prendersi cura di questo file, azzerandolo o ricreandolo, a seconda della necessità.

129.1.6   File «/var/log/wtmp»

Il file /var/log/wtmp ha una struttura analoga a quella di /var/run/utmp e serve per conservare la registrazione degli accessi e della loro conclusione (login-logout). Questo file non viene creato automaticamente; se manca, la conservazione delle registrazioni all'interno del sistema non viene effettuata. Viene aggiornato da Init e anche dal programma che si occupa di gestire la procedura di accesso al sistema (login).

Il formato di questo file non è quello di un file di testo normale, quindi non è leggibile o stampabile direttamente.

Se questo file non esiste, conviene crearlo manualmente in uno dei due modi seguenti:

cp /dev/null /var/log/wtmp[Invio]

touch /var/log/wtmp[Invio]

129.1.7   File «/etc/motd»

Il contenuto del file /etc/motd viene visualizzato da login al termine della procedura di accesso, prima dell'avvio della shell associata all'utente. Questo file contiene, o dovrebbe contenere, il cosiddetto messaggio del giorno (Message of the day).

129.1.8   File «/etc/nologin»

Se esiste il file /etc/nologin, login non accetta nuovi accessi al sistema, visualizzando il suo contenuto a ogni tentativo.

Se si desidera fermare il sistema è possibile creare questo file scrivendoci all'interno il motivo, o una breve spiegazione di ciò che sta avvenendo.

129.1.9   File «/etc/securetty»

Il file /etc/securetty contiene l'elenco dei terminali sicuri, cioè di quelli da cui si permette l'accesso all'utente root. I nomi dei terminali vengono indicati facendo riferimento ai file di dispositivo relativi, senza l'indicazione del prefisso /dev/. L'esempio seguente mostra un elenco di terminali che comprende la console vera e propria, le sei console virtuali standard, quattro terminali seriali e quattro pseudo-terminali che accedono dalla rete locale oppure dal sistema grafico X.

console
tty1
tty2
tty3
tty4
tty5
tty6
ttyS0
ttyS1
ttyS2
ttyS3
ttyp0
ttyp1
ttyp2
ttyp3

A seconda di come è organizzato il sistema di file di dispositivo, può essere necessario modificare di conseguenza questo file.

129.1.10   File «/var/mail/*»

Il file corrispondente al nome dell'utente, contenuto in /var/mail/ (oppure in /var/spool/mail/, a seconda dell'impostazione della distribuzione GNU), viene usato normalmente per accumulare i messaggi di posta elettronica a lui diretti.

Il programma login, dopo la visualizzazione del messaggio contenuto in /etc/motd, se trova che c'è posta per l'utente, visualizza un messaggio di avvertimento in tal senso.

La collocazione di questi file che rappresentano le caselle postali degli utenti, dipende dalla configurazione e dalla filosofia del sistema di gestione della posta elettronica. Generalmente si fa affidamento sul fatto che si utilizzi il solito Sendmail, il quale si avvale della directory /var/mail/, o /var/spool/mail/, per questo scopo. Se il sistema GNU che si utilizza è impostato diversamente, è probabile che il programma login sia stato compilato o configurato in modo da utilizzare un percorso differente per le caselle postali.

129.1.11   File «~/.hushlogin»

Se esiste il file .hushlogin all'interno della directory personale di un certo utente, quando quell'utente accede, login non visualizza alcun messaggio introduttivo.

129.1.12   File «/var/log/lastlog»

Il file /var/log/lastlog, se esiste, viene utilizzato da login per registrare gli ultimi accessi al sistema e per poter visualizzare la data e l'ora dell'ultimo accesso. Se questo file non esiste, conviene crearlo manualmente in uno dei due modi seguenti:

cp /dev/null /var/log/lastlog[Invio]

touch /var/log/lastlog[Invio]

129.2   Cambiamento di identità

Alcuni programmi consentono di ottenere i privilegi di un altro utente, come se si ripetesse la procedura di accesso. Questa possibilità rappresenta generalmente un problema di sicurezza. Per mezzo di questi programmi può capitare di riuscire a ottenere i privilegi dell'utente root anche quando si accede da un terminale che non viene considerato sicuro, pertanto non risulta incluso nell'elenco di /etc/securetty.

129.2.1   Utilizzo di «su»

Il programma su (1) permette a un utente di diventare temporaneamente un altro, avviando una shell con i privilegi dell'utente indicato (questo vale anche per il gruppo o i gruppi a cui questo appartiene). Se non viene indicato un utente, su sottintende root. Prima di attivare la nuova shell, viene richiesta la parola d'ordine associata all'utente selezionato, a meno che su sia stato eseguito da chi sta già accedendo come utente root.

su [opzioni] [utente]

L'opzione più importante di su è data dal trattino singolo (-), con il quale si fa in modo che la nuova shell venga avviata come shell di login. In questo modo, si ha di fronte l'ambiente normale dell'utente che si va a impersonare, come se si facesse un accesso standard.

Per terminare l'attività in veste di questo nuovo utente, basta concludere l'esecuzione della shell con il comando exit. Segue la descrizione di alcuni esempi.

Il programma su, per poter svolgere il suo compito, deve appartenere all'utente root e avere il bit SUID attivato (SUID-root). È in questo modo che un utente comune riesce a ottenere i privilegi di root o di un altro utente.

Il programma su viene usato frequentemente dall'utente root, o da un processo che ha già i privilegi dell'utente root, per diventare temporaneamente un utente comune. In tal caso, dal momento che il processo che avvia su ha già i privilegi di root, non c'è alcuna necessità della presenza del bit SUID attivo.

In generale, dal momento che su è molto importante per agevolare il lavoro dell'amministratore del sistema, se si temono problemi alla sicurezza, si può eliminare il bit SUID, per concedere praticamente il suo utilizzo solo all'utente root:

chmod u-s /bin/su[Invio]

Volendo calcare la mano, si possono togliere anche tutti i permessi per il gruppo proprietario e per gli altri utenti:

chmod go-rwx /bin/su[Invio]

Se si toglie la funzione SUID-root all'eseguibile su, si impedisce agli utenti comuni di elevarsi al livello di utente root. Questo fatto implica che, a meno di disporre di altri programmi che compiano funzioni simili, l'utente root può accedere solo nel modo «normale», pertanto si va così a confermare il rispetto del file /etc/securetty, che altrimenti potrebbe essere raggirato. Tuttavia, è perfettamente ammissibile la presenza di un file /etc/securetty limitato, assieme a un programma su con i permessi SUID-root, quando si vuole consentire un accesso solo a chi dispone di un'utenza normale, lasciando salva poi la possibilità di dimostrare di essere anche l'amministratore.

Fino a questo punto è stato mostrato l'utilizzo di su per avviare una shell interattiva, ma in generale questo programma può essere usato per avviare un certo processo elaborativo, al termine del quale si vuole che tutto ritorni come prima. In tal caso la sintassi va modificata come segue:

su [opzioni] [utente [argomenti]]

In pratica, gli argomenti che appaiono alla fine della riga di comando sono ciò che si vuole avviare. Tuttavia, si creano spesso delle complicazioni nel modo corretto di interpretare tali argomenti. Si vedano gli esempi seguenti, che in teoria dovrebbero produrre lo stesso effetto:

  1. su tizio ls -l[Invio]

  2. su tizio "ls -l"[Invio]

  3. su -c "ls -l" tizio[Invio]

In pratica, si vuole che il comando ls -l venga eseguito con i privilegi dell'utente tizio, quando originariamente si hanno quelli dell'amministratore. Delle tre forme, è sicuro il funzionamento solo dell'ultima, dove ci si affida all'opzione -c e di conseguenza il comando da eseguire è passato in forma di stringa. Naturalmente, sarebbe ammissibile scrivere quel comando anche nel modo seguente:

su tizio -c "ls -l"[Invio]

Inoltre, l'uso di questa forma, consente di scrivere comandi più complessi, come nell'esempio seguente:

su tizio -c "cd ; ls -l | less"[Invio]

129.2.2   Utilizzo di «newgrp»

Il programma newgrp (2) permette di cambiare il gruppo a cui appartiene l'utente. L'utente non cambia, la directory personale nemmeno, cambia solo il GID. Un utente può cambiare gruppo se nel file /etc/group sono diversi i gruppi a cui può appartenere l'utente. In alternativa, se il gruppo ha una parola d'ordine, l'utente può «entrare» nel gruppo solo se la conosce.

newgrp [gruppo]

Il problema della gestione dei gruppi, specialmente per ciò che riguarda le parole d'ordine, è descritto meglio nel capitolo 131.

129.3   Informazioni sugli accessi

Molti programmi permettono di avere informazioni sugli accessi e di conseguenza anche sugli utenti. In particolare sono importanti quelli che permettono di leggere il contenuto dei file /var/run/utmp e /var/log/wtmp il cui formato non è leggibile attraverso l'uso di un semplice cat.

In particolare, per quanto riguarda i programmi che analizzano il contenuto del file /var/log/wtmp, si può leggere il capitolo 133.

129.3.1   Utilizzo di «users»

Il programma users (3) visualizza i nomi degli utenti che accedono attualmente all'elaboratore. Se un utente ha attivato più sessioni in corso, il suo nome appare più volte nell'elenco. Se il comando viene avviato senza l'indicazione di un file, i dati visualizzati vengono estratti da /etc/utmp. Esiste comunque la possibilità di visualizzare attraverso users il contenuto di /etc/wtmp.

users [file]

129.3.2   Utilizzo di «w»

Il programma w (4) visualizza i nomi degli utenti che accedono attualmente e varie informazioni sulla loro attività; in particolare l'uso della CPU:

w [opzioni] [utente]

Attraverso le opzioni è possibile controllare in qualche modo le informazioni che vengono visualizzate, mentre se si indica il nome di un utente alla fine della riga di comando, si ottengono informazioni solo su quello. L'esempio seguente mostra cosa si può ottenere con w usato senza argomenti:

w[Invio]

 16:50:46 up 15 min,  5 users,  load average: 0,12, 0,14, 0,20
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
tizio    tty1     -                16:37   10:53   6.88s  6.75s /usr/bin/mc \
  \-P /tmp/mc4818-2783 tizio tty5 - 16:50 23.00s 1.17s 0.12s -sh tizio tty6 - 16:38 40.00s 0.28s 0.12s ssh \
  \root@172.21.1.1 root pts/2 172.21.1.2 16:46 45.00s 0.07s 0.07s -bash tizio pts/3 :0.0 16:50 0.00s 0.07s 0.01s w

Come si può osservare, la prima riga che si ottiene è equivalente a quanto genera il programma uptime, descritto nel capitolo 91.

Le colonne identificate dalle sigle JCPU e PCPU indicano il tempo di utilizzo della CPU; nel primo caso si tratta di tutti i processi, ancora attivi, anche se sullo sfondo, mentre nel secondo si tratta esclusivamente del tempo utilizzato dal processo indicato nella colonna finale (WHAT). La colonna IDLE indica il tempo di funzionamento complessivo, anche se inattivo, del processo indicato nella colonna WHAT.(5)

Per l'uso delle opzioni che qui non vengono descritte, si veda la pagina di manuale w(1).

129.3.3   Utilizzo di «who»

Il programma who (6) visualizza i nomi degli utenti che accedono attualmente e varie informazioni sulla loro attività. who trae normalmente le sue informazioni dal file /etc/utmp, se non ne viene indicato un altro negli argomenti (per esempio /etc/wtmp).

who [opzioni] [file] [am i]

Si veda il documento info who, oppure la pagina di manuale who(1).

129.3.4   Utilizzo di «pinky»

Il programma pinky (7) visualizza l'elenco degli utenti che utilizzano l'elaboratore, oppure visualizza informazioni dettagliate su utenti particolari. Il suo funzionamento è simile a quello di Finger (294.3):

pinky [opzioni] [utente]...

Attraverso le opzioni si può controllare la quantità di informazioni che si vogliono ottenere.

Tabella 129.8. Alcune opzioni.

Opzione Descrizione
-s
Produce un formato sintetico; questa modalità è predefinita.
-l
Questa opzione richiede l'indicazione di almeno un utente e serve a produrre informazioni dettagliate.

Segue la descrizione di alcuni esempi.

Si veda eventualmente la pagina di manuale pinky(1).

129.3.5   Utilizzo di «whoami»

Il programma whoami (8) visualizza il nome dell'utente associato con l'attuale UID efficace. È equivalente a id -un.

whoami

Il nominativo-utente associato al numero UID efficace è in pratica l'identità con cui si sta lavorando. Per esempio, dopo l'utilizzo di su per diventare utenti caio, il programma whoami restituisce esattamente il nome caio.

129.3.6   Utilizzo di «logname»

Il programma logname (9) emette il nome dell'utente, così come appare dal file /var/run/utmp.

logname

A titolo di esempio si può immaginare la situazione in cui l'utente tizio sia riuscito a ottenere i privilegi dell'utente root attraverso l'uso di su.

tizio$ su root[Invio]

Password: *******[Invio]

Quello che si dovrebbe ottenere con logname è il nome dell'utente che è stato usato per accedere inizialmente al sistema.

root# logname[Invio]

tizio

129.3.7   Utilizzo di «groups»

Il programma groups (10) visualizza i gruppi ai quali l'utente o gli utenti appartengono.

groups [utente...]

Il risultato è equivalente al comando seguente:

id -Gn [nome_utente]

129.3.8   Utilizzo di «id»

Il programma id visualizza il numero UID (User ID) e il numero GID (Group ID) reale ed efficace dell'utente selezionato o di quello corrente. (11)

id [opzioni] [utente]

Tabella 129.12. Alcune opzioni.

Opzione Descrizione
-u
--user
Emette solo il numero dell'utente (UID).
-g
--group
Emette solo il numero del gruppo (GID).
-G
--groups
Emette solo i numeri dei gruppi supplementari.
-n
--name
Emette il nome dell'utente, del gruppo o dei gruppi, a seconda che sia usato insieme a -u, -g o -G.
-r
--real
Emette i numeri UID o GID reali invece di quelli efficaci (ammesso che ci sia differenza). Si usa insieme a -u, -g o -G.

Usato senza argomenti, id fornisce l'identità dell'utente, il gruppo standard e l'elenco dei gruppi a cui l'utente è aggregato, come si vede dall'esempio seguente:

id[Invio]

uid=1001(tizio) gid=1001(tizio) gruppi=1001(tizio),6(disk),7(lp),\
  \24(cdrom),25(floppy),29(audio)

Le opzioni servono in pratica a limitare le informazioni che si desiderano avere; eventualmente si può consultare il documento info id, oppure la pagina di manuale id(1) per maggiori dettagli su questo programma.

Appunti di informatica libera 2007.02 --- Copyright © 2000-2007 Daniele Giacomini -- <daniele (ad) swlibero·org>


1) Shadow utilities   software libero con licenza speciale

2) Shadow utilities   software libero con licenza speciale

3) GNU core utilities   GNU GPL

4) Procps w   GNU GPL

5) Si può verificare facilmente che la colonna IDLE del comando w riporta il tempo di funzionamento complessivo; per farlo basta avviare in un terminale un programma che utilizza intensamente la CPU, come yes quando è ridiretto verso /dev/null, cronometrando il tempo e controllando ciò che riporta w.

6) GNU core utilities   GNU GPL

7) GNU core utilities   GNU GPL

8) GNU core utilities   GNU GPL

9) GNU core utilities   GNU GPL

10) GNU core utilities   GNU GPL

11) GNU core utilities   GNU GPL


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome controllo_degli_accessi.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico]

Valid ISO-HTML!

CSS validator!