fscan/plugins/services/ldap/connector.go
ZacharyZcR 4a3f281b6b refactor: 统一Plugins目录大小写为小写
- 将所有Plugins路径重命名为plugins
- 修复Git索引与实际文件系统大小写不一致问题
- 确保跨平台兼容性和路径一致性
2025-08-12 13:08:06 +08:00

124 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ldap
import (
"context"
"fmt"
"strings"
ldaplib "github.com/go-ldap/ldap/v3"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins/base"
)
// LDAPConnection LDAP连接包装器
type LDAPConnection struct {
client *ldaplib.Conn
target string
}
// LDAPConnector LDAP连接器实现
type LDAPConnector struct {
host string
port string
}
// NewLDAPConnector 创建LDAP连接器
func NewLDAPConnector() *LDAPConnector {
return &LDAPConnector{}
}
// Connect 连接到LDAP服务
func (c *LDAPConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
c.host = info.Host
c.port = info.Ports
target := fmt.Sprintf("%s:%s", c.host, c.port)
// 使用Context控制的TCP连接
conn, err := common.WrapperTcpWithContext(ctx, "tcp", target)
if err != nil {
return nil, fmt.Errorf("LDAP连接失败: %v", err)
}
// 创建LDAP连接
ldapConn := ldaplib.NewConn(conn, false)
go ldapConn.Start() // 在goroutine中启动连接处理
return &LDAPConnection{
client: ldapConn,
target: target,
}, nil
}
// Authenticate 认证
func (c *LDAPConnector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error {
ldapConn, ok := conn.(*LDAPConnection)
if !ok {
return fmt.Errorf("无效的连接类型")
}
// 在goroutine中进行认证操作支持Context取消
resultChan := make(chan error, 1)
go func() {
var err error
if cred.Username == "" && cred.Password == "" {
// 匿名绑定
err = ldapConn.client.UnauthenticatedBind("")
} else {
// 构建绑定DN - 支持多种常见格式优先管理员DN
bindDNs := []string{
fmt.Sprintf("cn=%s,dc=example,dc=com", cred.Username), // 管理员绑定格式
fmt.Sprintf("cn=%s,ou=users,dc=example,dc=com", cred.Username), // 用户绑定格式
fmt.Sprintf("uid=%s,ou=users,dc=example,dc=com", cred.Username),
fmt.Sprintf("uid=%s,dc=example,dc=com", cred.Username),
cred.Username, // 直接使用用户名作为DN
}
// 尝试不同的绑定DN格式
var bindErr error
for _, bindDN := range bindDNs {
bindErr = ldapConn.client.Bind(bindDN, cred.Password)
if bindErr == nil {
break
}
}
err = bindErr
}
// 绑定成功即表示认证成功,不需要额外搜索验证
// 因为某些LDAP配置下普通用户没有搜索权限
select {
case resultChan <- err:
case <-ctx.Done():
}
}()
// 等待认证结果或Context取消
select {
case err := <-resultChan:
if err != nil {
// 检查是否是认证失败
if strings.Contains(strings.ToLower(err.Error()), "bind") ||
strings.Contains(strings.ToLower(err.Error()), "authentication") ||
strings.Contains(strings.ToLower(err.Error()), "invalid credentials") ||
strings.Contains(strings.ToLower(err.Error()), "49") { // LDAP错误码49表示认证失败
return fmt.Errorf("LDAP认证失败")
}
return fmt.Errorf("LDAP操作失败: %v", err)
}
return nil
case <-ctx.Done():
return fmt.Errorf("LDAP认证超时: %v", ctx.Err())
}
}
// Close 关闭连接
func (c *LDAPConnector) Close(conn interface{}) error {
if ldapConn, ok := conn.(*LDAPConnection); ok && ldapConn.client != nil {
ldapConn.client.Close()
}
return nil
}