| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 | 
							- package main
 
- import (
 
- 	"bytes"
 
- 	"encoding/json"
 
- 	"fmt"
 
- 	"log"
 
- 	"log/syslog"
 
- 	"os"
 
- 	"strconv"
 
- 	"strings"
 
- 	"github.com/go-ldap/ldap/v3"
 
- 	"golang.org/x/crypto/ssh"
 
- )
 
- const (
 
- 	rootDN       = "dc=example,dc=com"
 
- 	bindUsername = "cn=sftpgo," + rootDN
 
- 	bindURL      = "ldap:///" // That is, the server on the default port of localhost.
 
- 	passwordFile = "/etc/sftpgo/admin-password.txt" // make this file readable only by the server
 
- 	publicDir    = "/var/www/webdav/public"
 
- )
 
- type userFilters struct {
 
- 	DeniedLoginMethods []string `json:"denied_login_methods,omitempty"`
 
- }
 
- type minimalSFTPGoUser struct {
 
- 	Status      int                 `json:"status,omitempty"`
 
- 	Username    string              `json:"username"`
 
- 	HomeDir     string              `json:"home_dir,omitempty"`
 
- 	UID         int                 `json:"uid,omitempty"`
 
- 	GID         int                 `json:"gid,omitempty"`
 
- 	Permissions map[string][]string `json:"permissions"`
 
- 	Filters     userFilters         `json:"filters"`
 
- }
 
- func exitError() {
 
- 	log.Printf("exitError\n")
 
- 	u := minimalSFTPGoUser{
 
- 		Username: "",
 
- 	}
 
- 	resp, _ := json.Marshal(u)
 
- 	fmt.Printf("%v\n", string(resp))
 
- 	os.Exit(1)
 
- }
 
- func printSuccessResponse(username, homeDir string, uid, gid int, permissions []string) {
 
- 	u := minimalSFTPGoUser{
 
- 		Username: username,
 
- 		HomeDir:  homeDir,
 
- 		UID:      uid,
 
- 		GID:      gid,
 
- 		Status:   1,
 
- 	}
 
- 	u.Permissions = make(map[string][]string)
 
- 	u.Permissions["/"] = permissions
 
- 	// uncomment the next line to require publickey+password authentication
 
- 	//u.Filters.DeniedLoginMethods = []string{"publickey", "password", "keyboard-interactive", "publickey+keyboard-interactive"}
 
- 	resp, _ := json.Marshal(u)
 
- 	log.Printf("%v\n", string(resp))
 
- 	fmt.Printf("%v\n", string(resp))
 
- 	os.Exit(0)
 
- }
 
- func main() {
 
- 	logWriter, err := syslog.New(syslog.LOG_NOTICE, "sftpgo")
 
- 	if err == nil {
 
- 		log.SetOutput(logWriter)
 
- 	}
 
- 	// get credentials from env vars
 
- 	username := os.Getenv("SFTPGO_AUTHD_USERNAME")
 
- 	password := os.Getenv("SFTPGO_AUTHD_PASSWORD")
 
- 	publickey := os.Getenv("SFTPGO_AUTHD_PUBLIC_KEY")
 
- 	if strings.ToLower(username) == "anonymous" {
 
- 		printSuccessResponse("anonymous", publicDir, 0, 0, []string{"list", "download"})
 
- 		return
 
- 	}
 
- 	l, err := ldap.DialURL(bindURL)
 
- 	if err != nil {
 
- 		log.Printf("DialURL: %s\n", err.Error())
 
- 		exitError()
 
- 	}
 
- 	defer l.Close()
 
- 	// bind to the ldap server with an account that can read users
 
- 	bindPassword, err := os.ReadFile(passwordFile)
 
- 	if err != nil {
 
- 		log.Printf("ReadFile(%s): %s\n", passwordFile, err.Error())
 
- 		exitError()
 
- 	}
 
- 	err = l.Bind(bindUsername, string(bindPassword))
 
- 	if err != nil {
 
- 		log.Printf("Bind(%s): %s\n", bindUsername, err.Error())
 
- 		exitError()
 
- 	}
 
- 	// search the user trying to login and fetch some attributes, this search string is tested against 389ds using the default configuration
 
- 	log.Printf("username=%s\n", username)
 
- 	searchFilter := fmt.Sprintf("(uid=%s)", ldap.EscapeFilter(username))
 
- 	searchRequest := ldap.NewSearchRequest(
 
- 		"ou=people," + rootDN,
 
- 		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
 
- 		searchFilter,
 
- 		[]string{"dn", "uid", "homeDirectory", "uidNumber", "gidNumber", "nsSshPublicKey"},
 
- 		nil,
 
- 	)
 
- 	sr, err := l.Search(searchRequest)
 
- 	if err != nil {
 
- 		log.Printf("Search(%s): %s\n", searchFilter, err.Error())
 
- 		exitError()
 
- 	}
 
- 	// we expect exactly one user
 
- 	if len(sr.Entries) != 1 {
 
- 		log.Printf("Search(%s): %d entries\n", searchFilter, len(sr.Entries))
 
- 		exitError()
 
- 	}
 
- 	if len(publickey) > 0 {
 
- 		// check public key
 
- 		userKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publickey))
 
- 		if err != nil {
 
- 			log.Printf("ParseAuthorizedKey(%s): %s\n", publickey, err.Error())
 
- 			exitError()
 
- 		}
 
- 		authOk := false
 
- 		for _, k := range sr.Entries[0].GetAttributeValues("nsSshPublicKey") {
 
- 			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 {
 
- 			log.Printf("publickey %s !authOk\n", publickey)
 
- 			exitError()
 
- 		}
 
- 	} else {
 
- 		// bind to the LDAP server with the user dn and the given password to check the password
 
- 		userdn := sr.Entries[0].DN
 
- 		// log.Printf("password=%s\n", password)
 
- 		err = l.Bind(userdn, password)
 
- 		if err != nil {
 
- 			log.Printf("Bind(%s): %s\n", userdn, err.Error())
 
- 			exitError()
 
- 		}
 
- 	}
 
- 	// People in the LDAP directory aren't necessarily Linux users;
 
- 	// so they might not have a uidNumber or gidNumber.
 
- 	uidNumber := sr.Entries[0].GetAttributeValue("uidNumber")
 
- 	uid, err := strconv.Atoi(uidNumber)
 
- 	if err != nil {
 
- 		//log.Printf("uid Atoi(%s) = %s\n", uidNumber, err.Error())
 
- 		uid = 0
 
- 	}
 
- 	gidNumber := sr.Entries[0].GetAttributeValue("gidNumber")
 
- 	gid, err := strconv.Atoi(gidNumber)
 
- 	if err != nil {
 
- 		//log.Printf("gid Atoi(%s) = %s\n", gidNumber, err.Error())
 
- 		gid = 0
 
- 	}
 
- 	homeDir := sr.Entries[0].GetAttributeValue("homeDirectory")
 
- 	if (len(homeDir) <= 0) {
 
- 		homeDir = publicDir // homeDir is a required attribute.
 
- 	}
 
- 	// return the authenticated user
 
- 	printSuccessResponse(sr.Entries[0].GetAttributeValue("uid"), homeDir, uid, gid, []string{"*"})
 
- }
 
 
  |