2.5 DIGEST-MD5 come meccanismo SASL

(RFC-2831)

 

Descriviamo qui come usare Digest come un meccanismo di autenticazione per il protocollo SASL.

Se il client non si è ancora autenticato al server, deve anzitutto effettuare l’autenticazione iniziale che descriviamo in questo paragrafo.

Passo Uno
Il server inizia mandando un challenge. I dati inclusi in questo challenge contengono alcune stringe strutturate secondo le regole della digest challenge. Abbiamo:

digest-challenge =


1#( realm | nonce | qop-options | stale | maxbuf | charset
algorithm | cipher-opts | auth-param )

 

realm = "realm" "=" <"> realm-value <">
realm-value = qdstr-val
nonce = "nonce" "=" <"> nonce-value <">
nonce-value = qdstr-val
qop-options = "qop" "=" <"> qop-list <">
qop-list = 1#qop-value
qop-value = "auth" | "auth-int" | "auth-conf" |
token
stale = "stale" "=" "true"
maxbuf = "maxbuf" "=" maxbuf-value
maxbuf-value = 1*DIGIT
charset = "charset" "=" "utf-8"
algorithm = "algorithm" "=" "md5-sess"
cipher-opts = "cipher" "=" <"> 1#cipher-value <">
cipher-value = "3des" | "des" | "rc4-40" | "rc4" |
"rc4-56" | token
auth-param = token "=" ( token | quoted-string )

 

Sinteticamente descriviamo il significato di ogni campo.

·         realm
E’ una stringa che dà la possibilità ad un utente di conoscere quale username e password utilizzare, nel caso che l’utente abbia differenti username e password per ogni server. Concettualmente,è il nome di un insieme di account che include l’account dell’utente che si sta autenticando.

·         nonce
È una stringa specificata dal server che deve essere differente ogni volta che una digest challenge è inviata come parte dell’autenticazione iniziale. Il contenuto di questa stringa dipende dall’implementazione del protocollo.

·         qop-options
È una stringa di uno o più token che indicano la qualità della protezione supportata dal server. Il valore auth indica l’autenticazione; il valore auth-int indica autenticazione con garanzia sull’integrità dei dati; il valore auth-conf indica autenticazione con integrità dei dati e crittografia. Il valore di default è auth.

·         stale
Questo valore non è usato nell’autenticazione iniziale.

·         maxbuf
Questo valore indica la dimensione del più grande buffer che il server può supportare nell’autenticazione auth-int e auth-conf.

·         charset
Questa direttiva, se presente, specifica se il server supporta lo schema di caratteri UTF-8 per la codifica dello username e della password.

·         algorithm
Questo valore è richiesto per la compatibilità col protocollo HTTP Digest, che supporta altri algoritmi.

·         cipher-opts
Contiene la lista delle modalità di cifratura che il server supporta. Questa lista è presente solo se è settato il valore auth-conf nel campo qop-options. I valori possibili sono: des, che indica l’algoritmo DES nella modalità CBC con una chiave a 56 bit; 3des, che indica l’algoritmo EDE, o del triplo DES, sempre in modalità CBC; rc4, rc4-40, rc4-56 che indica gli algoritmi RC4 rispettivamente con una chiave a 128, 40 e 56 bit.

·         auth-param
È un campo inutilizzato, previsto per scopi futuri.

 


Passo Due
A questo punto il client prende nota del digest challenge e risponde al server con una stringa strutturata e costruita secondo le regole del digest response definite come segue:

digest-response =

1#( username | realm | nonce | cnonce |
nonce-count | qop | digest-uri | response |
maxbuf | charset | cipher | authzid |auth-param )

 

username = "username" "=" <"> username-value <">
username-value = qdstr-val
cnonce = "cnonce" "=" <"> cnonce-value <">
cnonce-value = qdstr-val
nonce-count = "nc" "=" nc-value
nc-value = 8LHEX
qop = "qop" "=" qop-value
digest-uri = "digest-uri" "=" <"> digest-uri-value <">
digest-uri-value = serv-type "/" host [ "/" serv-name ]
serv-type = 1*ALPHA
host = 1*( ALPHA | DIGIT | "-" | "." )
serv-name = host
response = "response" "=" response-value
response-value = 32LHEX
LHEX = "0" | "1" | "2" | "3" |
"4" | "5" | "6" | "7" |
"8" | "9" | "a" | "b" |
"c" | "d" | "e" | "f"
cipher = "cipher" "=" cipher-value
authzid = "authzid" "=" <"> authzid-value <">
authzid-value = qdstr-val

 

Descriviamo ora sinteticamente i campi della struttura:

·         username
È il valore dello username specificato nel campo realm, codificato secondo il valore della direttiva charset.

·         realm
Questo campo contiene l’account dell’utente.

·         nonce
È la stringa specificata dal server nel precedente digest challenge.

·         cnonce
È una stringa specificata dal cliente che deve essere diversa ad ogni digest response mandata a partire dalla procedura iniziale di autenticazione. Questo valore è utilizzato dal client e dal server per evitare attacchi plaintext. La sicurezza dell’implementazione dipende da una buona scelta di questo valore.

·         nonce-count
Questo valore è un contatore esadecimale del numero di richieste che il client ha inviato, inclusa la richiesta corrente. Ad esempio, nella prima richiesta spedita in risposta ad un dato valore di nonce, il client manda “nc = 00000001”.

·         qop
Indica quale “qualità di protezione” il client accetta. Se presente, appare solo la prima volta ed è una delle alternative del campo qop-options nello schema al passo precedente. In caso contrario è per default auth.

·         serv-type
Indica il tipo di servizio, ad esempio www indica i servizi web, ftp indica i servizi di trasferimento file, SMTP indica il servizio di mail delivery, ecc.

·         host
Questo campo contiene l’host name del DNS o l’indirizzo IP del servizio richiesto.

·         serv-name
Indica il nome del servizio.

·         digest-uri
Indica il nome principale del servizio al quale il client vuole connettersi, formato dai campi del serv-type, dell’host e del serv-name. Ad esempio, il servizio di FTP su ftp.example.com avrà come valore del digest-uri “ftp/ftp.example.com”.

·         response
È una stringa di 32 cifre esadecimali, che descriveremo più avanti. Questa stringa prova che l’utente conosce la password esatta per autenticarsi. Questa direttiva deve essere necessariamente presente la prima volta, altrimenti l’autenticazione fallisce.

·         maxbuf
Questo valore indica la dimensione massima del buffer di dati che il client è capace di gestire. Il valore di default è 65563. Questo campo, inoltre, può apparire solo una volta; se alle volte successive ci sono altri valori, il server può abortire la procedura di autenticazione.

·         charset
Questa direttiva, se presente, specifica se il server supporta lo schema di caratteri UTF-8 per la codifica dello username e della password.

·         LHEX
E’ un campo di 32 cifre esadecimali, dove i caratteri alfabetici devono essere necessariamente lower case, poiché l’algoritmo MD5 è case sensitive.

·         cipher
È l’algoritmo di cifratura scelto dal client. Questa direttiva deve essere presente solo se viene negoziata l’opzione auth-conf. Se viene richiesta e non è presente, l’autenticazione fallisce.

·         authzid
È un campo opzionale, che contiene l’ID per l’autorizzazione.

Ritorniamo al valore del campo response, formato da 32 cifre esadecimali con caratteri lower case. Mostriamo come il valore è calcolato.

response-value =

HEX( KD ( HEX(H(A1)), { nonce-value, ":" nc-value, ":", cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))


Se il campo authzid è presente, allora A1 è uguale a:

A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
":", nonce-value, ":", cnonce-value, ":", authzid-value }


Se il campo authzid non è presente, allora A1 è uguale a:

A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
":", nonce-value, ":", cnonce-value }


dove passwd = *OCTET


Se il valore del campo qop è auth, allora A2 è uguale a:

A2 = { "AUTHENTICATE:", digest-uri-value }

Se il valore del campo qop è auth-int oppure auth-conf allora A2 è:

A2 = { "AUTHENTICATE:", digest-uri-value,
":00000000000000000000000000000000" }

 


Passo Tre
Il server a questo punto riceve e verifica la digest response. Verifica anzitutto che il valore di nonce-count è “00000001” e manda un messaggio strutturato come segue:

response-auth = "rspauth" "=" response-value

dove il response-value è calcolato, come descritto al passo precedente, usando i valori mandati al passo due, eccetto che se il valore di qop è auth, allora A2 è:

A2 = { ":", digest-uri-value }

E se qop è auth-int oppure auth-conf allora A2 vale:

A2 = { ":", digest-uri-value, ":00000000000000000000000000000000" }