| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 | 
							- package httpd
 
- import (
 
- 	"bytes"
 
- 	"crypto/tls"
 
- 	"fmt"
 
- 	"net/http"
 
- 	"strconv"
 
- 	"strings"
 
- 	"github.com/drakkan/sftpgo/ldapauthserver/logger"
 
- 	"github.com/go-chi/chi/v5/middleware"
 
- 	"github.com/go-chi/render"
 
- 	"github.com/go-ldap/ldap/v3"
 
- 	"golang.org/x/crypto/ssh"
 
- )
 
- func getSFTPGoUser(entry *ldap.Entry, username string) (SFTPGoUser, error) {
 
- 	var err error
 
- 	var user SFTPGoUser
 
- 	uid := ldapConfig.DefaultUID
 
- 	gid := ldapConfig.DefaultGID
 
- 	status := 1
 
- 	if !ldapConfig.ForceDefaultUID {
 
- 		uid, err = strconv.Atoi(entry.GetAttributeValue(ldapConfig.GetUIDNumber()))
 
- 		if err != nil {
 
- 			return user, err
 
- 		}
 
- 	}
 
- 	if !ldapConfig.ForceDefaultGID {
 
- 		uid, err = strconv.Atoi(entry.GetAttributeValue(ldapConfig.GetGIDNumber()))
 
- 		if err != nil {
 
- 			return user, err
 
- 		}
 
- 	}
 
- 	sftpgoUser := SFTPGoUser{
 
- 		Username: username,
 
- 		HomeDir:  entry.GetAttributeValue(ldapConfig.GetHomeDirectory()),
 
- 		UID:      uid,
 
- 		GID:      gid,
 
- 		Status:   status,
 
- 	}
 
- 	sftpgoUser.Permissions = make(map[string][]string)
 
- 	sftpgoUser.Permissions["/"] = []string{"*"}
 
- 	return sftpgoUser, nil
 
- }
 
- func checkSFTPGoUserAuth(w http.ResponseWriter, r *http.Request) {
 
- 	r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
 
- 	var authReq externalAuthRequest
 
- 	err := render.DecodeJSON(r.Body, &authReq)
 
- 	if err != nil {
 
- 		logger.Warn(logSender, middleware.GetReqID(r.Context()), "error decoding auth request: %v", err)
 
- 		sendAPIResponse(w, r, err, "", http.StatusBadRequest)
 
- 		return
 
- 	}
 
- 	l, err := ldap.DialURL(ldapConfig.BindURL, ldap.DialWithTLSConfig(&tls.Config{
 
- 		InsecureSkipVerify: ldapConfig.InsecureSkipVerify,
 
- 		RootCAs:            rootCAs,
 
- 	}))
 
- 	if err != nil {
 
- 		logger.Warn(logSender, middleware.GetReqID(r.Context()), "error connecting to the LDAP server: %v", err)
 
- 		sendAPIResponse(w, r, err, "Error connecting to the LDAP server", http.StatusInternalServerError)
 
- 		return
 
- 	}
 
- 	defer l.Close()
 
- 	err = l.Bind(ldapConfig.BindUsername, ldapConfig.BindPassword)
 
- 	if err != nil {
 
- 		logger.Warn(logSender, middleware.GetReqID(r.Context()), "error binding to the LDAP server: %v", err)
 
- 		sendAPIResponse(w, r, err, "Error binding to the LDAP server", http.StatusInternalServerError)
 
- 		return
 
- 	}
 
- 	searchRequest := ldap.NewSearchRequest(
 
- 		ldapConfig.BaseDN,
 
- 		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
 
- 		strings.Replace(ldapConfig.SearchFilter, "%s", ldap.EscapeFilter(authReq.Username), 1),
 
- 		ldapConfig.SearchBaseAttrs,
 
- 		nil,
 
- 	)
 
- 	sr, err := l.Search(searchRequest)
 
- 	if err != nil {
 
- 		logger.Warn(logSender, middleware.GetReqID(r.Context()), "error searching LDAP user %q: %v", authReq.Username, err)
 
- 		sendAPIResponse(w, r, err, "Error searching LDAP user", http.StatusInternalServerError)
 
- 		return
 
- 	}
 
- 	if len(sr.Entries) != 1 {
 
- 		logger.Warn(logSender, middleware.GetReqID(r.Context()), "expected one user, found: %v", len(sr.Entries))
 
- 		sendAPIResponse(w, r, nil, fmt.Sprintf("Expected one user, found: %v", len(sr.Entries)), http.StatusNotFound)
 
- 		return
 
- 	}
 
- 	if len(authReq.PublicKey) > 0 {
 
- 		userKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(authReq.PublicKey))
 
- 		if err != nil {
 
- 			logger.Warn(logSender, middleware.GetReqID(r.Context()), "invalid public key for user %q: %v", authReq.Username, err)
 
- 			sendAPIResponse(w, r, err, "Invalid public key", http.StatusBadRequest)
 
- 			return
 
- 		}
 
- 		authOk := false
 
- 		for _, k := range sr.Entries[0].GetAttributeValues(ldapConfig.GetPublicKey()) {
 
- 			key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
 
- 			// we skip an invalid public key stored inside the LDAP server
 
- 			if err != nil {
 
- 				continue
 
- 			}
 
- 			if bytes.Equal(key.Marshal(), userKey.Marshal()) {
 
- 				authOk = true
 
- 				break
 
- 			}
 
- 		}
 
- 		if !authOk {
 
- 			logger.Warn(logSender, middleware.GetReqID(r.Context()), "public key authentication failed for user: %q", authReq.Username)
 
- 			sendAPIResponse(w, r, nil, "public key authentication failed", http.StatusForbidden)
 
- 			return
 
- 		}
 
- 	} else {
 
- 		// bind to the LDAP server with the user dn and the given password to check the password
 
- 		userdn := sr.Entries[0].DN
 
- 		err = l.Bind(userdn, authReq.Password)
 
- 		if err != nil {
 
- 			logger.Warn(logSender, middleware.GetReqID(r.Context()), "password authentication failed for user: %q", authReq.Username)
 
- 			sendAPIResponse(w, r, nil, "password authentication failed", http.StatusForbidden)
 
- 			return
 
- 		}
 
- 	}
 
- 	user, err := getSFTPGoUser(sr.Entries[0], authReq.Username)
 
- 	if err != nil {
 
- 		logger.Warn(logSender, middleware.GetReqID(r.Context()), "get user from LDAP entry failed for username %q: %v",
 
- 			authReq.Username, err)
 
- 		sendAPIResponse(w, r, err, "mapping LDAP user failed", http.StatusInternalServerError)
 
- 		return
 
- 	}
 
- 	render.JSON(w, r, user)
 
- }
 
 
  |