104 lines
2.2 KiB
Go
104 lines
2.2 KiB
Go
//go:build linux || freebsd
|
|||
|
|
// +build linux freebsd
|
||
|
|
|
||
|
|
package auth
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bufio"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
// NetrcEntry holds credentials for a single machine entry in .netrc.
|
||
|
|
type NetrcEntry struct {
|
||
|
|
Machine string
|
||
|
|
Login string
|
||
|
|
Password string
|
||
|
|
}
|
||
|
|
|
||
|
|
// LoadNetrc reads ~/.netrc and returns entries keyed by machine name.
|
||
|
|
func LoadNetrc() (map[string]*NetrcEntry, error) {
|
||
|
|
home, err := os.UserHomeDir()
|
||
|
|
if err != nil {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
path := filepath.Join(home, ".netrc")
|
||
|
|
file, err := os.Open(path)
|
||
|
|
if err != nil {
|
||
|
|
if os.IsNotExist(err) {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
defer file.Close()
|
||
|
|
|
||
|
|
entries := make(map[string]*NetrcEntry)
|
||
|
|
var current *NetrcEntry
|
||
|
|
|
||
|
|
scanner := bufio.NewScanner(file)
|
||
|
|
for scanner.Scan() {
|
||
|
|
line := strings.TrimSpace(scanner.Text())
|
||
|
|
// Skip comments and empty lines
|
||
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
fields := strings.Fields(line)
|
||
|
|
|
||
|
|
// "default" is also valid but we treat it as a machine
|
||
|
|
if len(fields) >= 2 && (fields[0] == "machine" || fields[0] == "default") {
|
||
|
|
if current != nil && current.Machine != "" {
|
||
|
|
entries[current.Machine] = current
|
||
|
|
}
|
||
|
|
name := fields[1]
|
||
|
|
if fields[0] == "default" {
|
||
|
|
name = "default"
|
||
|
|
}
|
||
|
|
current = &NetrcEntry{Machine: name}
|
||
|
|
// Rest of line may have login/password
|
||
|
|
parseNetrcLine(fields[2:], current)
|
||
|
|
} else if current != nil {
|
||
|
|
parseNetrcLine(fields, current)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if current != nil && current.Machine != "" {
|
||
|
|
entries[current.Machine] = current
|
||
|
|
}
|
||
|
|
|
||
|
|
return entries, scanner.Err()
|
||
|
|
}
|
||
|
|
|
||
|
|
func parseNetrcLine(fields []string, entry *NetrcEntry) {
|
||
|
|
for i := 0; i < len(fields); i++ {
|
||
|
|
switch fields[i] {
|
||
|
|
case "login":
|
||
|
|
if i+1 < len(fields) {
|
||
|
|
entry.Login = fields[i+1]
|
||
|
|
i++
|
||
|
|
}
|
||
|
|
case "password":
|
||
|
|
if i+1 < len(fields) {
|
||
|
|
entry.Password = fields[i+1]
|
||
|
|
i++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// LookupNetrc returns credentials from .netrc for a given hostname.
|
||
|
|
func LookupNetrc(host string) (login, password string, found bool) {
|
||
|
|
entries, err := LoadNetrc()
|
||
|
|
if err != nil || entries == nil {
|
||
|
|
return "", "", false
|
||
|
|
}
|
||
|
|
// Exact match first
|
||
|
|
if e, ok := entries[host]; ok {
|
||
|
|
return e.Login, e.Password, true
|
||
|
|
}
|
||
|
|
// Default entry
|
||
|
|
if e, ok := entries["default"]; ok {
|
||
|
|
return e.Login, e.Password, true
|
||
|
|
}
|
||
|
|
return "", "", false
|
||
|
|
}
|