RELAZIONE TEAM: TheSorbaraProphecies Codello Fabrizio 75514 Crotti Fabio 75331 Olivotto Roberto 75517 Battellino Matteo 75364 Esercizio 3: Accesso Remoto Controllato Introduzione L'esercizio richiedeva di creare una pagina html che presentasse all'utente un interfaccia per l'autenticazione ad un servizio remoto di esecuzione comandi, di uno script cgi per verificare l'effettiva esistenza di un utente che voleva autenticarsi e di un file contenente gli utenti, e le relative password, con permesso ad accedere a tale sistema. Svolgimento Questo esercizio aveva quindi bisogno di 3 files: password.db ­ il database utenti (posizionato in /etc/password.db) rlogin.html ­ la pagina di login (/var/www/html/rlogin.html) rlogin.cgi – lo script contenente tutte le varie funzionalita' (/var/www/cgi­bin/rlogin.cgi) password.db Abbiamo innanzitutto creato il file/database degli utenti e delle password “password.db” che sarebbe stato usato come base, per il controllo dell'accesso degli utenti e posizionato nella cartella “/etc/”. Il file aveva una struttura del tipo: nomeutente1:password1 nomeutente2:password2 e cosi' via... . in questo modo siamo poi riusciti ad estrapolare le nostre informazioni nella via più facile. rlogin.html Abbiamo poi creato una semplice pagina in linguaggio html che mostrasse due campi di testo per l'inserimento dello username e della password dell'utente che voleva autenticarsi. Questi parametri venivano poi passati allo script cgi rlogin.cgi che avrebbe garantito delle funzionalita', descritte in seguito. rlogin.cgi Questo script in linguaggio bash (bourne against shell) era il “cuore” di tutto l'esercizio. Esso infatti aveva il compito di: controllare i parametri iniziali di username e password e di accettare/rifiutare la richiesta di autenticazione di un utente, andando a controllare nel file di database utenti, la sua effettiva esistenza. Generare una pagina in linguaggio html contenente: 1. una casella di testo per l'inserimento di un comando da eseguire ed un area di testo per la visualizzazione dell'output del relativo comando immesso, ed in più dei pulsanti per far partire l'esecuzione (richiamando lo stesso script passando un parametro contenente il comando), la cancellazione dei campi e per il logout dalla “sessione” dell utente; 2. soltanto un messaggio di errore con l'invito a controllare i dati immessi, in caso di login fallito. Ma vediamo più in dettaglio. Lo script e' stato scomposto in diversi “blocchi” di codice che potessero risolvere ognuna una parte del problema in modo separato: Estrazione dei parametri dalla query_string controllo dell'esistenza utente, leggendo il file/database e controllando ogni riga estrapolata conversione dei caratteri speciali ed esecuzione del comando costruzione della pagina html di risposta Estrapolazione dei parametri dalla query string Data la query_string del tipo: username=Utente&password=passworD... Attraverso due cicli for annidati e l'utilizzo della variabile “splittatrice” IFS e' stato possibile scomporre la query string ricevuta in input dallo script, in coppie del tipo coppia1&coppia2 e successivamente in coppie del tipo nomecampo=valore per poter così salvare i campi di cui avevamo bisogno. Controllo esistenza utente In questo punto dello script veniva letto il file/database degli utenti e venivano confrontati i campi username e password appena estrapolati dalla query, con le varie righe che erano state a loro volta “splittate” secondo il simbolo “:” per ottenere nome e pass degli utenti esistenti, vista la struttura del tipo username:pass . Sono stati allegati due metodi per fare cio': uno in versione più compatta e forse più leggibile, con un ciclo for, (questo metodo viene utilizzato di default) l'altra un po' più estesa e “macchinosa”, con un ciclo while (questo blocco e' stato commentato), ma con lo stesso obiettivo della precedente, in questo modo abbiamo verificato i due funzionamenti. Conversione dei caratteri hex Dato che il comando, preso dalla casella di testo e passato attraverso la query_string, era codificato in modo tale da contenere caratteri “speciali” (+ $ % £ ecc...) e' stato necessario riconvertirli da esadecimale per aver una migliore visualizzazione finale. Sono stati quindi convertiti i “+” in spazi e i valori hex del tipo “%XX” nei relativi simboli. Costruzione pagina Veniva infine generata la pagina html utilizzando semplicemente una stampa in output del codice html appropriato per generare il form necessario per dare la possibilita' all utente loggato di eseguire il comando desiderato. Ovvero una casella di testo dove inserire il comando, un area di testo dove mostrare il comando precedentemente inviato e l'output di esso, dei bottoni per l'esecuzione, per l'azzeramento dei campi o il logout dell utente. Nel caso l utente non fosse stato trovato nel database, sarebbe stato visualizzato un errore ed egli sarebbe stato rimandato alla pagina di login iniziale. Nota: Per mantenere la “sessione” dell utente vengono utilizzati due campi di tipo “hidden” per salvare le informazioni relative ad user e pass. Questo non e' sicuramente il metodo piu' sicuro per gestire un login, ma dato che non era stato precisato in nessun luogo su come procedere sul grado di “sicurezza” e date le simili alternative offerte da questo linguaggio, e' stato deciso di prendere questa via. (visionare il sorgente del file per una migliore comprensione dei vari punti) Note sull'installazione Per far funzionare correttamente il progetto: assicurarsi di avere i permessi di root posizionare il file password.db in /etc/ posizionare il file rlogin.html in /var/www/html posizionare infine il file rlogin.cgi in /var/www/cgi­bin rendere eseguibili gli ultimi 2 files nel caso non lo fossero da terminale lanciare “apachectl ­­start” per avviare apache. Sorgente dei files password.db Fabryz:w00t Guest:asd123 Gigi:latrottola rlogin.html <html> <head> <title>Login</title> </head> <body onLoad="form.username.focus();"> <div align="center"> <h4>Login form</h4> <form name="form" action="http://localhost/cgi-bin/rlogin.cgi" method="post"> Username: <input type="text" name="username"><br> Password: <input type="password" name="password"><br><br> <input type="Submit" value="Login"> <input type="Reset" value="Cancella"> </div> </body> </html> rlogin.cgi #!/bin/bash # Team: TheSorbaraProphecies # Esercizio 3 Accesso remoto controllato # #Gruppo: TheSorbaraProphecies #Universita' degli Studi di Udine #A.A. 2006/2007 # #Il software riportato di seguito come codice sorgente e' soggetto a #licenza RESL, esattamente nei termini indicati. # #RESTRICTED EDUCATIONAL SOFTWARE LICENSE (RESL) # #Ogni componente del gruppo puo' intervenire liberamente nella produzione #del software purche': #1. le modifiche siano concordate con gli altri componenti; #2. siano indicate esplicitamente le parti di software, eventualmente # acquisite da altri gruppi; #3. siano delineate chiaramente quali parti acquisite hanno subito # modifiche nel codice; #4. siano rispettate le condizioni cui deve sottostare l'acquisizione # di software libero; #5. non venga utilizzato software soggetto a copywright; #6. siano riportati provenienza e autori del software acquisito. # #Nei casi previsti e' necessario riportare esplicitamente le condizioni #di distribuzione del software acquisito. In particolare, se il software #e' sotto licenza GPL, questa deve essere indicata e riprodotta. #estrazione parametri quser="" qpass="" comando="" if [ "$REQUEST_METHOD" = POST ]; then QUERY_STRING=$(head --bytes="$CONTENT_LENGTH" ); fi IFSOLD=$IFS; IFS="&"; for argomento in $QUERY_STRING #splitta la stringa secondo coppia1&coppia2 do k=0; IFS="="; for elemento in $argomento #splitta secondo nome=valore do valore=""; if [ "$k" = 0 ]; then nome="$elemento" else valore="$elemento" fi ; let "k=$k+1"; done case $nome in "username") quser=$valore ;; "password") qpass=$valore ;; "comando") comando=$valore ;; esac IFS="&"; done IFS=$IFSOLD; #controllo esistenza esiste=false #modo1 for riga in `cat /etc/password.db` do user=${riga%:*} #salvo il campo username pass=${riga#*:} #salvo il campo della password if [ "$user" = "$quser" -a "$pass" = "$qpass" ]; then esiste=true; break fi done #modo2 #file=/etc/password.db #x=0 #ins=`wc -l $file` #numr=`expr "$ins" : '\([0-9]*\)'` #ricavo il numero di righe #while [ "$x" -lt "$numr" -a "$esiste" = false ] #do # let x=x+1 # riga=$(head -n $x $file | tail -n 1) # user=${riga%:*} # pass=${riga#*:} # if [ "$user" = "$quser" -a "$pass" = "$qpass" ]; then # esiste=true; # fi #done #conversione caratteri ed esecuzione comando comando=`echo "$comando" | sed 's/\+/ /g'` case $comando in *%[0-9a-fA-F][0-9a-fA-F]*) comando=$( printf "%b" "${comando//\%/\\x}" ) esac #eval "$comando=\$comando" output=`eval "$comando"` #costruzione pagina di risposta echo -e -n "Content-type: text/html\r\n\r\n"; echo -e "<html>\n"; echo -e "\n <head>\n <title>Accesso Remoto</title>\n </head>\n"; echo -e "<body onLoad=\"document.form.comando.focus();\">"; if [ "$esiste" = true ]; then #se l utente esiste realmente echo -e "\n <div align=\"center\">\n <h3>Accesso Remoto</h3><br>"; echo -e "\n <form name=\"form\" action=\"http://localhost/cgibin/$(basename $0)\" method=\"post\">"; echo -e " Inserire il comando:<br>\n"; echo -e " <textarea name=\"output\" cols=\"50\" rows=\"10\" readonly># $comando\n\n$output</textarea><br>\n"; echo -e " <input type=\"text\" name=\"comando\" size=\"53\" maxlength=\"80\" style=\"font-family: monospace;\"><br><br>\n"; echo -e " <input type=\"hidden\" name=\"username\" value=\"$quser\">"; echo -e " <input type=\"hidden\" name=\"password\" value=\"$qpass\">"; echo -e " <input type=\"Submit\" value=\"Esegui\"> "; echo -e " <input type=\"reset\" value=\"Cancella\"> "; echo -e " <input type=\"Button\" value=\"Logout\" onClick=\"window.location='http://localhost/rlogin.html';return false\">"; echo -e " </form>\n </div>\n"; #elif [ "$esiste" = true -a "$log" -eq 0 ]; then #echo -e "\n <div align=\"center\">\n successo!</h3><br>"; <h3>Sloggato con #echo -e " <a href=\"http://localhost/rlogin.html\">Torna alla pagina di Login -&gt;</a>\n </div>"; else echo -e "\n <div align=\"center\">\n errati!</h3><br>"; <h3>Nome utente e/o password echo -e "<font size=\"-1\">Controllare di aver immesso dei dati corretti<br>\ne di aver i permessi necessari per l'accesso al servizio</font><br><br>\n"; echo -e " <a href=\"javascript:history.back();\">&lt;- Torna indietro</a>\n </div>"; fi echo -e " </body>\n</html>"; Screenshots dell'esecuzione Verranno mostrate alcune schermate dell'esecuzione dell'esercizio. Qui vediamo il file password.db e la sua struttura usata per memorizzare le informazioni. La maschera di login che viene mostrata all'utente subito dopo aver aperto la prima pagina. Qui ovviamente l'utente inserirà i propri Username e Password necessari per l autenticazione. Nel caso in cui i dati immessi siano inesatti o non vengano trovati nel database degli utenti, verra' visualizzato il seguente messaggio di errore. Una volta autenticato, l'utente potrà inserire il comando desiderato nella casella di testo presente in basso, per eseguirlo dovrà premere il bottone “Esegui”. L'output del comando (se esistente) verra' poi inserito nell'area di testo posizionata in alto, insieme al comando precedentemente inserito, come se fosse un normale terminale. Ad esempio potrebbe inserire il comando “date”. Ed ecco che l'output viene successivamente mostrato nella relativa area di testo. Alla fine del lavoro, l'utente potrà effettuare il logout cliccando per l'appunto sull'apposito pulsante “logout”, e verra' quindi rimandato alla pagina iniziale di login. Conclusioni L'esercizio proposto e' stato un modo per farci affrontare le problematiche del lavoro di gruppo, abbiamo quindi avuto modo di imparare a suddividerci i compiti e di trovare strategie alternative, per far eseguire, nel linguaggio di programmazione utilizzato (bash), delle funzionalità che non sono integrate nativamente. Secondo noi sarebbe stato possibile migliorare tale esercizio, utilizzando magari il linguaggio PERL per lo script cgi nei blocchi di manipolazione stringhe e lettura file, e gestendo in modo migliore la sicurezza con cui vengono mantenuti i dati sensibili, durante la sessione di lavoro dell'utente. Ovviamente questo non era richiesto dalla consegna e ci siamo quindi adoperati per rendere efficiente l'esecuzione dell'esercizio nella sua forma di base.