Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added a means to prevent clients from running a search unless they authenticated previously. #37

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ import (
)

type client struct {
Numero int
srv *Server
rwc net.Conn
br *bufio.Reader
bw *bufio.Writer
chanOut chan *ldap.LDAPMessage
wg sync.WaitGroup
closing chan bool
requestList map[int]*Message
mutex sync.Mutex
writeDone chan bool
rawData []byte
Numero int
srv *Server
rwc net.Conn
br *bufio.Reader
bw *bufio.Writer
chanOut chan *ldap.LDAPMessage
wg sync.WaitGroup
closing chan bool
requestList map[int]*Message
mutex sync.Mutex
writeDone chan bool
rawData []byte
handler Handler
hasOwnHandler bool
}

func (c *client) GetConn() net.Conn {
Expand Down Expand Up @@ -238,7 +240,11 @@ func (c *client) ProcessRequestMessage(message *ldap.LDAPMessage) {
w.chanOut = c.chanOut
w.messageID = m.MessageID().Int()

c.srv.Handler.ServeLDAP(w, &m)
if c.handler != nil {
c.handler.ServeLDAP(w, &m)
} else {
c.srv.Handler.ServeLDAP(w, &m)
}
}

func (c *client) registerRequest(m *Message) {
Expand Down
124 changes: 124 additions & 0 deletions examples/handler_per_connection/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Listen to 10389 port for LDAP Request
// and route bind request to the handleBind func

// using a handler, one can pass data from one function to another
// use the result of bind in subsequent research requests

// this example accepts all bind requests for a dn starting with 'login' and rejects others
// that is for example "loginfoo" and "loginbar" will be accepted, "foobar" will be rejected.
package main

import (
"fmt"
"log"
"os"
"os/signal"
"strings"
"syscall"

ldap "github.com/vjeantet/ldapserver"
)

func main() {

//ldap logger
ldap.Logger = log.New(os.Stdout, "[server] ", log.LstdFlags)

//Create a new LDAP Server
server := ldap.NewServerWithHandlerSource(&myHandlerSource{})

// listen on 10389
go server.ListenAndServe("127.0.0.1:10389")

// When CTRL+C, SIGINT and SIGTERM signal occurs
// Then stop server gracefully
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
<-ch
close(ch)

server.Stop()
}

// this implements interface HandlerSource. this is called by ldap server on
// each new connetion to get a handler for this connection
type myHandlerSource struct {
}

func (*myHandlerSource) GetHandler() ldap.Handler {
routes := ldap.NewRouteMux()
mh := &myHandler{}
routes.Bind(mh.handleBind)
routes.Search(mh.handleSearch).Label("Search - Generic")
return routes
}

// the handlersource creates a new one of these for each connection
// thus data in here is unique "per connection"
type myHandler struct {
username string
was_bound bool
}

// handleBind return Success if login == mysql
func (mh *myHandler) handleBind(w ldap.ResponseWriter, m *ldap.Message) {
r := m.GetBindRequest()
res := ldap.NewBindResponse(ldap.LDAPResultSuccess)
mh.username = fmt.Sprintf("%s", r.Name())

if strings.HasPrefix(mh.username, "login") {
mh.was_bound = true
w.Write(res)
return
}
mh.was_bound = false
log.Printf("Bind failed User=%s, Pass=%s", string(r.Name()), string(r.AuthenticationSimple()))
res.SetResultCode(ldap.LDAPResultInvalidCredentials)
res.SetDiagnosticMessage("invalid credentials")
w.Write(res)
}

func (h *myHandler) handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
if !h.was_bound {
// if no user was authenticated, then don't search.
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultSuccess)
res.SetResultCode(ldap.LDAPResultInvalidCredentials)
w.Write(res)
return
}
r := m.GetSearchRequest()

log.Printf("----- Search request by %s-------\n", h.username)
log.Printf("Request BaseDn=%s\n", r.BaseObject())
log.Printf("Request Filter=%s\n", r.Filter())
log.Printf("Request FilterString=%s\n", r.FilterString())
log.Printf("Request Attributes=%s\n", r.Attributes())
log.Printf("Request TimeLimit=%d\n", r.TimeLimit().Int())

// Handle Stop Signal (server stop / client disconnected / Abandoned request....)
select {
case <-m.Done:
log.Print("Leaving handleSearch...")
return
default:
}

e := ldap.NewSearchResultEntry("cn=Valere JEANTET, " + string(r.BaseObject()))
e.AddAttribute("mail", "valere.jeantet@gmail.com", "mail@vjeantet.fr")
e.AddAttribute("company", "SODADI")
e.AddAttribute("department", "DSI/SEC")
e.AddAttribute("l", "Ferrieres en brie")
e.AddAttribute("mobile", "0612324567")
e.AddAttribute("telephoneNumber", "0612324567")
e.AddAttribute("cn", "Valère JEANTET")
w.Write(e)

e = ldap.NewSearchResultEntry("cn=Claire Thomas, " + string(r.BaseObject()))
e.AddAttribute("mail", "claire.thomas@gmail.com")
e.AddAttribute("cn", "Claire THOMAS")
w.Write(e)

res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultSuccess)
w.Write(res)

}
32 changes: 28 additions & 4 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import (
"time"
)

type HandlerSource interface {
GetHandler() Handler
}

// Server is an LDAP server.
type Server struct {
Listener net.Listener
Expand All @@ -21,19 +25,37 @@ type Server struct {

// Handler handles ldap message received from client
// it SHOULD "implement" RequestHandler interface
Handler Handler
Handler Handler
useHandlerSource bool
handlerSource HandlerSource
}

//NewServer return a LDAP Server
// NewServer return a LDAP Server
func NewServer() *Server {
return &Server{
chDone: make(chan bool),
}
}

// NewServer returns an LDAP Server, with a dedicated handler for each connection
// different to the "Handler", this allows one struct (object) for each connection.
// this is intented to pass information from one handle function to another, for
// example, if Bind() fails, a flag may be set in the source to decline subsequent searches
// (or limit them in scope).
func NewServerWithHandlerSource(hs HandlerSource) *Server {
return &Server{
handlerSource: hs,
useHandlerSource: true,
chDone: make(chan bool),
}
}

// Handle registers the handler for the server.
// If a handler already exists for pattern, Handle panics
func (s *Server) Handle(h Handler) {
if s.useHandlerSource {
panic("LDAP: attempt to register handler and a handlersource")
}
if s.Handler != nil {
panic("LDAP: multiple Handler registrations")
}
Expand All @@ -44,7 +66,6 @@ func (s *Server) Handle(h Handler) {
// calls Serve to handle requests on incoming connections. If
// s.Addr is blank, ":389" is used.
func (s *Server) ListenAndServe(addr string, options ...func(*Server)) error {

if addr == "" {
addr = ":389"
}
Expand All @@ -67,7 +88,7 @@ func (s *Server) ListenAndServe(addr string, options ...func(*Server)) error {
func (s *Server) serve() error {
defer s.Listener.Close()

if s.Handler == nil {
if s.Handler == nil && !s.useHandlerSource {
Logger.Panicln("No LDAP Request Handler defined")
}

Expand Down Expand Up @@ -122,6 +143,9 @@ func (s *Server) newClient(rwc net.Conn) (c *client, err error) {
br: bufio.NewReader(rwc),
bw: bufio.NewWriter(rwc),
}
if s.useHandlerSource {
c.handler = s.handlerSource.GetHandler()
}
return c, nil
}

Expand Down