2018-11-12 22:31:10 +00:00
package main
import (
"crypto/sha512"
"encoding/base64"
"encoding/binary"
"io"
"log"
2020-09-02 13:09:14 +00:00
"net/http"
2018-11-12 22:31:10 +00:00
"os"
"os/exec"
"time"
"gopkg.in/gomail.v2"
)
func ( l LDAPConn ) genToken ( dn string , previous bool ) string {
hour := time . Now ( )
// Generate the previous token?
if previous {
hour . Add ( time . Hour * - 1 )
}
b := make ( [ ] byte , binary . MaxVarintLen64 )
binary . PutVarint ( b , hour . Round ( time . Hour ) . Unix ( ) )
// Search the email address and current password
entries , err := l . GetEntry ( dn )
if err != nil {
log . Println ( "Unable to generate token:" , err )
return "#err"
}
email := ""
curpasswd := ""
for _ , e := range entries {
if e . Name == "mail" {
email += e . Values [ 0 ]
} else if e . Name == "userPassword" {
curpasswd += e . Values [ 0 ]
}
}
// Hash that
hash := sha512 . New ( )
hash . Write ( b )
hash . Write ( [ ] byte ( dn ) )
hash . Write ( [ ] byte ( email ) )
hash . Write ( [ ] byte ( curpasswd ) )
return base64 . StdEncoding . EncodeToString ( hash . Sum ( nil ) [ : ] )
}
func lostPassword ( w http . ResponseWriter , r * http . Request ) {
if r . Method != "POST" {
displayTmpl ( w , "lost.html" , map [ string ] interface { } { } )
return
}
// Connect to the LDAP server
conn , err := myLDAP . Connect ( )
if err != nil || conn == nil {
log . Println ( err )
displayTmplError ( w , http . StatusInternalServerError , "lost.html" , map [ string ] interface { } { "error" : err . Error ( ) } )
return
}
// Bind as service to perform the search
err = conn . ServiceBind ( )
if err != nil {
log . Println ( err )
displayTmplError ( w , http . StatusInternalServerError , "lost.html" , map [ string ] interface { } { "error" : err . Error ( ) } )
return
}
// Search the dn of the given user
2021-09-15 10:13:09 +00:00
dn , err := conn . SearchDN ( r . PostFormValue ( "login" ) , true )
2018-11-12 22:31:10 +00:00
if err != nil {
log . Println ( err )
displayTmplError ( w , http . StatusInternalServerError , "lost.html" , map [ string ] interface { } { "error" : err . Error ( ) } )
return
}
// Generate the token
token := conn . genToken ( dn , false )
// Search the email address
entries , err := conn . GetEntry ( dn )
if err != nil {
log . Println ( err )
displayTmplError ( w , http . StatusInternalServerError , "lost.html" , map [ string ] interface { } { "error" : err . Error ( ) } )
return
}
email := ""
cn := ""
for _ , e := range entries {
if e . Name == "mail" {
email = e . Values [ 0 ]
}
if e . Name == "cn" {
cn = e . Values [ 0 ]
}
}
if email == "" {
log . Println ( "Unable to find a valid adress for user " + dn )
displayTmplError ( w , http . StatusBadRequest , "lost.html" , map [ string ] interface { } { "error" : "We were unable to find a valid email address associated with your account. Please contact an administrator." } )
return
}
// Send the email
m := gomail . NewMessage ( )
2018-12-30 11:52:34 +00:00
m . SetHeader ( "From" , "noreply@nemunai.re" )
2018-11-12 22:31:10 +00:00
m . SetHeader ( "To" , email )
2018-12-30 11:52:34 +00:00
m . SetHeader ( "Subject" , "SSO nemunai.re: password recovery" )
2020-09-02 13:09:14 +00:00
m . SetBody ( "text/plain" , "Hello " + cn + "!\n\nSomeone, and we hope it's you, requested to reset your account password. \nIn order to continue, go to:\nhttps://ldap.nemunai.re/reset?l=" + r . PostFormValue ( "login" ) + "&t=" + token + "\n\nBest regards,\n-- \nnemunai.re SSO" )
2018-11-12 22:31:10 +00:00
2021-06-12 08:03:32 +00:00
var s gomail . Sender
if myLDAP . MailHost != "" {
d := gomail . NewDialer ( myLDAP . MailHost , myLDAP . MailPort , myLDAP . MailUser , myLDAP . MailPassword )
s , err = d . Dial ( )
2018-11-12 22:31:10 +00:00
if err != nil {
2021-06-12 08:03:32 +00:00
log . Println ( "Unable to connect to email server: " + err . Error ( ) )
displayTmplError ( w , http . StatusInternalServerError , "lost.html" , map [ string ] interface { } { "error" : "Unable to connect to email server: " + err . Error ( ) } )
return
2018-11-12 22:31:10 +00:00
}
2021-06-12 08:03:32 +00:00
} else {
// Using local sendmail: delegate to the local admin sys the responsability to transport the mail
s = gomail . SendFunc ( func ( from string , to [ ] string , msg io . WriterTo ) error {
cmd := exec . Command ( "sendmail" , "-t" )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
pw , err := cmd . StdinPipe ( )
if err != nil {
return err
}
2018-11-12 22:31:10 +00:00
2021-06-12 08:03:32 +00:00
err = cmd . Start ( )
2018-11-12 22:31:10 +00:00
if err != nil {
return err
}
2021-06-12 08:03:32 +00:00
var errs [ 3 ] error
_ , errs [ 0 ] = m . WriteTo ( pw )
errs [ 1 ] = pw . Close ( )
errs [ 2 ] = cmd . Wait ( )
for _ , err = range errs {
if err != nil {
return err
}
}
return nil
} )
}
2018-11-12 22:31:10 +00:00
if err := gomail . Send ( s , m ) ; err != nil {
log . Println ( "Unable to send email: " + err . Error ( ) )
displayTmplError ( w , http . StatusInternalServerError , "lost.html" , map [ string ] interface { } { "error" : "Unable to send email: " + err . Error ( ) } )
return
}
displayMsg ( w , "Password recovery email sent, check your inbox." , http . StatusOK )
}