package models import ( "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "log" "os" "path/filepath" "strings" "github.com/go-ldap/ldap" ) // Code relating to AD integration type LdapConfig struct { LdapBindAddress string LdapBaseDn string LdapCertFile string } var systemCA *x509.CertPool var CertLoaded bool var LdapEnabled bool func GetFilePath(path string) string { // Check for empty filename if len(path) == 0 { return "" } // check if filename exists if _, err := os.Stat(path); os.IsNotExist((err)) { log.Printf("File '%s' not found, searching in same directory as binary\n", path) // if not, check that it exists in the same directory as the currently executing binary ex, err2 := os.Executable() if err2 != nil { //log.Printf("Error determining binary path : '%s'", err) return "" } binaryPath := filepath.Dir(ex) path = filepath.Join(binaryPath, path) } return path } func LoadLdapCert() { var err error // Get a copy of the system defined CA's systemCA, err = x509.SystemCertPool() if err != nil { log.Printf("LoadLdapCert error getting system certificate pool : '%s'\n", err) return } // only try to load certificate from file if the command line argument was specified ldapCertFile := os.Getenv("LDAP_TRUST_CERT_FILE") if ldapCertFile == "" { log.Printf("LoadLdapCert no certificate specified\n") return } else { // Try to read the file cf, err := os.ReadFile(GetFilePath(ldapCertFile)) if err != nil { log.Printf("LoadLdapCert error opening LDAP certificate file '%s' : '%s'\n", ldapCertFile, err) return } // Get the certificate from the file cpb, _ := pem.Decode(cf) crt, err := x509.ParseCertificate(cpb.Bytes) //fmt.Printf("Loaded certificate with subject %s\n", crt.Subject) if err != nil { log.Printf("LoadLdapCert error processing LDAP certificate file '%s' : '%s'\n", ldapCertFile, err) return } // Add custom certificate to the system cert pool systemCA.AddCert(crt) CertLoaded = true } } func VerifyLdapCreds(username string, password string) bool { var ldaps *ldap.Conn var err error ldapServer := os.Getenv("LDAP_BIND_ADDRESS") if ldapServer == "" { log.Printf("VerifyLdapCreds no LDAP bind address supplied\n") return false } else { LdapEnabled = true } ldapBaseDn := os.Getenv("LDAP_BASE_DN") if ldapBaseDn == "" { log.Printf("VerifyLdapCreds no LDAP base DN supplied\n") return false } // Set up TLS to use our custom certificate authority passed in cli argument tlsConfig := &tls.Config{ RootCAs: systemCA, InsecureSkipVerify: true, } // Add port if not specified in .env file if !(strings.HasSuffix(ldapServer, ":636")) { ldapServer = fmt.Sprintf("%s:636", ldapServer) log.Printf("VerifyLdapCreds updated ldapServer string '%s'\n", ldapServer) } // try connecting to AD via TLS and our custom certificate authority ldaps, err = ldap.DialTLS("tcp", ldapServer, tlsConfig) if err != nil { log.Printf("VerifyLdapCreds error connecting to LDAP bind address '%s' : '%s'\n", ldapServer, err) return false } defer ldaps.Close() //ldaps.Debug = true // try to bind to AD log.Printf("Attempting LDAP bind with user '%s' and password '%s'\n", username, password) err = ldaps.Bind(username, password) if err != nil { if ldapErr, ok := err.(*ldap.Error); ok && ldapErr.ResultCode == ldap.LDAPResultInvalidCredentials { log.Printf("VerifyLdapCreds user credentials are incorrect : '%s'\n", err) return false } else { log.Printf("VerifyLdapCreds error binding to LDAP with supplied credentials : '%s'\n", err) return false } } else { log.Printf("VerifyLdapCreds successfully bound to LDAP\n") } log.Printf("Attempting LDAP search request from base DN '%s'\n", ldapBaseDn) searchReq := ldap.NewSearchRequest( ldapBaseDn, ldap.ScopeWholeSubtree, // you can also use ldap.ScopeWholeSubtree ldap.NeverDerefAliases, 0, 0, false, "(objectClass=*)", []string{}, nil, ) result, err := ldaps.Search(searchReq) if err != nil { log.Printf("VerifyLdapCreds search error : '%s'\n", err) return false } log.Printf("result: %v\n", result) return true }