package ldap import ( "crypto/tls" "fmt" "io" "io/fs" "os" "syscall" "tug/internal/platform/lockfile" ) const ( maxKeyBytes = 64 << 10 maxCertBytes = 1 << 20 ) var keyOpened = func() {} func loadKeyPair(certPath, keyPath string) (tls.Certificate, error) { keyPEM, err := readKey(keyPath) if err != nil { return tls.Certificate{}, err } certPEM, err := readCert(certPath) if err != nil { return tls.Certificate{}, err } pair, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { return tls.Certificate{}, fmt.Errorf("ldap: cannot load the LDAPS certificate %s and key %s", certPath, keyPath) } return pair, nil } func readCert(path string) ([]byte, error) { f, info, err := openRegular(path, "certificate") if err != nil { return nil, err } defer f.Close() if info.Size() > maxCertBytes { return nil, tooLarge("certificate", path, info.Mode(), maxCertBytes) } cert, err := io.ReadAll(io.LimitReader(f, maxCertBytes+1)) if err != nil { return nil, fmt.Errorf("ldap: cannot read the LDAPS certificate %s: %w", path, err) } if len(cert) > maxCertBytes { return nil, tooLarge("certificate", path, info.Mode(), maxCertBytes) } return cert, nil } func readKey(path string) ([]byte, error) { f, info, err := openRegular(path, "key") if err != nil { return nil, err } defer f.Close() mode := info.Mode() switch { case mode.Perm()&0o077 != 0: return nil, fmt.Errorf("ldap: the LDAPS key %s has mode %04o; it must grant no permission to group or others (0600 or 0400)", path, mode.Perm()) case info.Size() > maxKeyBytes: return nil, tooLarge("key", path, mode, maxKeyBytes) } keyOpened() key, err := io.ReadAll(io.LimitReader(f, maxKeyBytes+1)) if err != nil { return nil, fmt.Errorf("ldap: cannot read the LDAPS key %s: %w", path, err) } if len(key) > maxKeyBytes { return nil, tooLarge("key", path, mode, maxKeyBytes) } return key, nil } func openRegular(path, what string) (*os.File, fs.FileInfo, error) { f, err := os.OpenFile(path, os.O_RDONLY|syscall.O_NONBLOCK, 0) if err != nil { return nil, nil, fmt.Errorf("ldap: cannot open the LDAPS %s: %w", what, err) } info, err := f.Stat() if err != nil { f.Close() return nil, nil, fmt.Errorf("ldap: cannot inspect the LDAPS %s: %w", what, err) } if mode := info.Mode(); !mode.IsRegular() { f.Close() return nil, nil, fmt.Errorf("ldap: the LDAPS %s %s is a %s (mode %04o), not a regular file", what, path, lockfile.DescribeType(mode), mode.Perm()) } return f, info, nil } func tooLarge(what, path string, mode fs.FileMode, limit int) error { return fmt.Errorf("ldap: the LDAPS %s %s (mode %04o) is over the %d-byte limit", what, path, mode.Perm(), limit) }