godap

package module
v0.0.0-...-460ba0c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2023 License: MIT Imports: 7 Imported by: 0

README

godap

A minimalistic LDAP server in Go

What is this thing?

This is the beginnings of LDAP serving functionality written in Go. It is not intended to be or become a full LDAP server, it's just minimal bind and simplistic search functionality. It aims to be enough to implement authentication-related LDAP services for use with Apache httpd or anything else that supports LDAP auth.

Theoretically more could be built out to approach the functionality of a full LDAP server. I don't have time for that stuff.

Why was this made?

Because the road to hell is paved with good intentions.

The short version of the story goes like this: I hate LDAP. I used to love it. But I loved it for all the wrong reasons. LDAP is supported as an authentication solution by many different pieces of software. Aside from its de jure standard status, its wide deployment cements it as a de facto standard as well.

However, just because it is a standard doesn't mean it is a great idea.

I'll admit that given its age LDAP has had a good run. I'm sure its authors carefully considered how to construct the protocol and chose ASN.1 and its encoding with all of wellest of well meaning intentions.

The trouble is that with today's Internet, LDAP is just a pain in the ass. You can't call it from your browser. It's not human readable or easy to debug. Tooling is often arcane and confusing. It's way more complicated than what is needed for most simple authentication-only uses. (Yes, I know there are many other uses than authentication - but it's often too complicated for those too.)

Likely owing to the complexity of the protocol, there seems to be virtually no easy to use library to implement the server side of the LDAP protocol that isn't tied in with some complete directory server system; and certainly not in a language as easy to "make it work" as Go.

So this means that if you are a web developer and you have a database table with usernames and (hopefully properly salted and hashed) passwords in it, and you have a third party application that supports LDAP authentication, you can't easily use your own user data source and just make it "speak LDAP" to this other application.

Well, this project provides the basic guts to make that work. Have a look at the test file for an example of what to do.

In a way, with this project I embrace LDAP. In the sense that a wrestler embraces the neck of his opponent to form a headlock, and the unruly brute is muscled into submission.

Dependencies

ASN.1+BER encoding and decoding is done with this: https://github.com/go-asn1-ber/asn1-ber

It works, it's cool.

Disclaimers

  1. This thing is still fairly rough. Haven't had time to polish it. But I wanted to get it up on Github in case anyone else was interested. I've been searching for something like this off and on for a while, finally got around to writing it.

  2. It's not impossible that in some places I'm violating pieces of the LDAP spec. My goal with this project was to get it so LDAP can be useful as an authentication mechanism on top of other non-directory data sources. It does that, I'm happy. Pull requests welcome to fix things that aren't perfect. License is MIT so feel free to do whatever you want with the code.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrNotASearchRequest = fmt.Errorf("not a search request")
View Source
var ErrSearchRequestTooComplex = fmt.Errorf("this search request is too complex to be parsed as a 'simple search'")
View Source
var LDAPDebug = false

lame, but simple - set to true when you want log output

Functions

func CheckPacket

func CheckPacket(p *ber.Packet, cl ber.Class, ty ber.Type, ta ber.Tag) error

func ExtractMessageId

func ExtractMessageId(p *ber.Packet) (int64, error)

func ForceInt64

func ForceInt64(v interface{}) int64

func IsUnbindRequest

func IsUnbindRequest(p *ber.Packet) bool

check if this is an unbind

func LDAPListenTLS

func LDAPListenTLS(listenAddr, certFile, keyFile string) (net.Listener, error)

func MakeLDAPSearchResultDonePacket

func MakeLDAPSearchResultDonePacket(msgid int64) *ber.Packet

func MakeLDAPSearchResultNoSuchObjectPacket

func MakeLDAPSearchResultNoSuchObjectPacket(msgid int64) *ber.Packet

Types

type LDAPBindFunc

type LDAPBindFunc func(binddn string, bindpw []byte) bool

function that checks simple auth credentials (username/password style)

type LDAPBindFuncHandler

type LDAPBindFuncHandler struct {
	LDAPBindFunc LDAPBindFunc
}

responds to bind requests

func (*LDAPBindFuncHandler) ServeLDAP

func (h *LDAPBindFuncHandler) ServeLDAP(ssn *LDAPSession, p *ber.Packet) []*ber.Packet

type LDAPRequestHandler

type LDAPRequestHandler interface {
	// read a packet and return one or more packets as a response
	// or nil/empty to indicate we don't want to handle this packet
	ServeLDAP(*LDAPSession, *ber.Packet) []*ber.Packet
}

processes a request, or not

type LDAPResultCodeHandler

type LDAPResultCodeHandler struct {
	ReplyTypeId int64 // the overall type of the response, e.g. 1 is BindResponse - it must be a response that is just a result code
	ResultCode  int64 // the result code, i.e. 0 is success, 49 is invalid credentials, etc.
}

func (*LDAPResultCodeHandler) ServeLDAP

func (h *LDAPResultCodeHandler) ServeLDAP(ssn *LDAPSession, p *ber.Packet) []*ber.Packet

type LDAPServer

type LDAPServer struct {
	Listener net.Listener
	Handlers []LDAPRequestHandler
}

Handles socket interaction and a chain of handlers

func (*LDAPServer) ListenAndServe

func (s *LDAPServer) ListenAndServe(addr string) error

listens and runs a plain (non-TLS) LDAP server on the address:port specified

func (*LDAPServer) Serve

func (s *LDAPServer) Serve() error

serves an ldap server on the listener specified in the LDAPServer struct

type LDAPSession

type LDAPSession struct {
	Attributes map[string]interface{}
}

something that the handlers can use to keep track of stuff across multiple requests in the same connection/session

type LDAPSimpleSearchFunc

type LDAPSimpleSearchFunc func(*LDAPSimpleSearchRequest) []*LDAPSimpleSearchResultEntry

a callback function to produce search results; should return nil to mean we chose not to attempt to search (i.e. this request is not for us); or return empty slice to mean 0 results (or slice with data for results)

type LDAPSimpleSearchFuncHandler

type LDAPSimpleSearchFuncHandler struct {
	LDAPSimpleSearchFunc LDAPSimpleSearchFunc
}

func (*LDAPSimpleSearchFuncHandler) ServeLDAP

func (h *LDAPSimpleSearchFuncHandler) ServeLDAP(ssn *LDAPSession, p *ber.Packet) []*ber.Packet

type LDAPSimpleSearchRequest

type LDAPSimpleSearchRequest struct {
	Packet       *ber.Packet
	BaseDN       string // DN under which to start searching
	Scope        int64  // baseObject(0), singleLevel(1), wholeSubtree(2)
	DerefAliases int64  // neverDerefAliases(0),derefInSearching(1),derefFindingBaseObj(2),derefAlways(3)
	SizeLimit    int64  // max number of results to return
	TimeLimit    int64  // max time in seconds to spend processing
	TypesOnly    bool   // if true client is expecting only type info
	FilterAttr   string // filter attribute name (assumed to be an equality match with just this one attribute)
	FilterValue  string // filter attribute value
}

a simplified ldap search request

func ParseLDAPSimpleSearchRequestPacket

func ParseLDAPSimpleSearchRequestPacket(p *ber.Packet) (*LDAPSimpleSearchRequest, error)

type LDAPSimpleSearchResultEntry

type LDAPSimpleSearchResultEntry struct {
	DN    string                 // DN of this search result
	Attrs map[string]interface{} // map of attributes
}

a simplified ldap search response

func (*LDAPSimpleSearchResultEntry) MakePacket

func (e *LDAPSimpleSearchResultEntry) MakePacket(msgid int64) *ber.Packet

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL