From 3ec58472dbd94b3e1cbec81e397a4ef4a13f8ece Mon Sep 17 00:00:00 2001 From: Maxime Lavigne Date: Thu, 8 Nov 2012 18:40:13 -0500 Subject: [PATCH] Modifications allowing /setup to be view on Windows When you go to /setup on Windows, it currently checks the "localhostAuthorized" function that checks your UserID with os.Getuid() and than check with the local and remote address to see if you are allow. In Windows os.Getuid will always equals -1 and the function default to unallowed. On darwin, it does check for uid but afterward only cares if the local and remote addresses are loopback addresses. So, I changed this function so that when the os.Getuid is not avalaible on the platform and returns -1, it does the same check as in darwin. I also modified the "isLocalhost" function to use the helper function "net.IsLoopback" instead of string matching. Since, I already had parsed addresses and had to call AddPairUserId, I checked if it wouldn't be simpler to pass net.Addr directly instead of strings and reconvert them afterward. It seemed after looking at all the code that called this function that it simplified calls quite a bit to do so. Finally, I modified "netutil.Localhost" for it to return the first IP it finds instead of the string representation of the first IP enclosed in square brackets. Since the square brackets around the IP are only necessary in a TCPAddr, it would be simplier and more robust for the user to directly print TCPAddr instead of appending this string to a post. Change-Id: Id79de6bebd6380f877074211c0d260782058765f --- pkg/auth/auth.go | 51 +++++++++++++------------- pkg/httputil/httputil.go | 2 +- pkg/netutil/ident.go | 72 +++++++++++++++++-------------------- server/camlistored/setup.go | 10 ++++-- 4 files changed, 68 insertions(+), 67 deletions(-) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 30d4f8bcf..4dc8c4232 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -19,6 +19,7 @@ package auth import ( "encoding/base64" "fmt" + "net" "net/http" "os" "regexp" @@ -38,7 +39,7 @@ type AuthMode interface { // IsAuthorized checks the credentials in req. IsAuthorized(req *http.Request) bool // AddAuthHeader inserts in req the credentials needed - // for a client to authenticate. + // for a client to authenticate. AddAuthHeader(req *http.Request) } @@ -159,30 +160,30 @@ type DevAuth struct { } func localhostAuthorized(req *http.Request) bool { - if uid := os.Getuid(); uid > 0 { - from := req.RemoteAddr - to := req.Host - if strings.HasPrefix(to, "localhost:") { - toPort := to[len("localhost:"):] - if strings.Contains(from, "[") { - to = "[::1]:" + toPort - } else { - to = "127.0.0.1:" + toPort - } - } + uid := os.Getuid() + from, err := netutil.HostPortToIP(req.RemoteAddr) + if err != nil { + fmt.Printf("auth: could not resolve the TCPAddr: %v", err) + } + to, err := netutil.HostPortToIP(req.Host) + if err != nil { + fmt.Printf("auth: could not resolve the TCPAddr: %v", err) + } - // TODO(bradfitz): netutil on OS X uses "lsof" to figure out - // ownership of tcp connections, but when fuse is mounted and a - // request is outstanding (for instance, a fuse request that's - // making a request to camlistored and landing in this code - // path), lsof then blocks forever waiting on a lock held by the - // VFS, leading to a deadlock. Instead, on darwin, just trust - // any localhost connection here, which is kinda lame, but - // whatever. Macs aren't very multi-user anyway. - if runtime.GOOS == "darwin" && isLocalhost(from) && isLocalhost(to) { - return true - } + // If our OS doesn't support uid. + // TODO(bradfitz): netutil on OS X uses "lsof" to figure out + // ownership of tcp connections, but when fuse is mounted and a + // request is outstanding (for instance, a fuse request that's + // making a request to camlistored and landing in this code + // path), lsof then blocks forever waiting on a lock held by the + // VFS, leading to a deadlock. Instead, on darwin, just trust + // any localhost connection here, which is kinda lame, but + // whatever. Macs aren't very multi-user anyway. + if uid == -1 || runtime.GOOS == "darwin" { + return from.IP.IsLoopback() && to.IP.IsLoopback() + } + if uid > 0 { owner, err := netutil.AddrPairUserid(from, to) if err == nil && owner == uid { return true @@ -191,8 +192,8 @@ func localhostAuthorized(req *http.Request) bool { return false } -func isLocalhost(addrPort string) bool { - return strings.HasPrefix(addrPort, "127.0.0.1:") || strings.HasPrefix(addrPort, "[::1]:") +func isLocalhost(addrPort net.IP) bool { + return addrPort.IsLoopback() } func LocalhostAuthorized(req *http.Request) bool { diff --git a/pkg/httputil/httputil.go b/pkg/httputil/httputil.go index e38a6e8ab..16f63dee8 100644 --- a/pkg/httputil/httputil.go +++ b/pkg/httputil/httputil.go @@ -89,7 +89,7 @@ func ReturnJSON(conn http.ResponseWriter, data interface{}) { return } - conn.Header().Set("Content-Length", strconv.Itoa(len(bytes) + 1)) + conn.Header().Set("Content-Length", strconv.Itoa(len(bytes)+1)) conn.Write(bytes) conn.Write([]byte("\n")) } diff --git a/pkg/netutil/ident.go b/pkg/netutil/ident.go index 731226fcf..14022e7fc 100644 --- a/pkg/netutil/ident.go +++ b/pkg/netutil/ident.go @@ -37,50 +37,50 @@ var ErrNotFound = errors.New("netutil: connection not found") // ConnUserid returns the uid that owns the given localhost connection. // The returned error is ErrNotFound if the connection wasn't found. func ConnUserid(conn net.Conn) (uid int, err error) { - return AddrPairUserid(conn.LocalAddr().String(), conn.RemoteAddr().String()) + return AddrPairUserid(conn.LocalAddr(), conn.RemoteAddr()) } -func splitIPPort(param, value string) (ip net.IP, port int, reterr error) { - addrs, ports, err := net.SplitHostPort(value) +// This fonction allows parsing of a TCPAddr without resolving names +// other than localhost. It will return an error instead of resolving. +func HostPortToIP(hostport string) (hostaddr *net.TCPAddr, err error) { + host, port, err := net.SplitHostPort(hostport) if err != nil { - reterr = fmt.Errorf("netutil: AddrPairUserid invalid %s value of %q: %v", param, value, err) - return + return nil, err } - ip = net.ParseIP(addrs) - if ip == nil { - reterr = fmt.Errorf("netutil: invalid %s IP %q", param, addrs) - return + iport, err := strconv.Atoi(port) + if err != nil || iport < 0 || iport > 0xFFFF { + return nil, fmt.Errorf("invalid port %s", iport) } - port, err = strconv.Atoi(ports) - if err != nil || port <= 0 || port > 65535 { - reterr = fmt.Errorf("netutil: invalid port %q", ports) - return + var addr net.IP + if host == "localhost" { + addr = net.IPv4(127, 0, 0, 1) + } else if addr = net.ParseIP(host); addr == nil { + return nil, fmt.Errorf("could not parse IP %s", host) } - return + + return &net.TCPAddr{IP: addr, Port: iport}, nil } // AddrPairUserid returns the local userid who owns the TCP connection // given by the local and remote ip:port (lipport and ripport, // respectively). Returns ErrNotFound for the error if the TCP connection // isn't found. -func AddrPairUserid(lipport, ripport string) (uid int, err error) { - lip, lport, err := splitIPPort("lipport", lipport) - if err != nil { - return -1, err +func AddrPairUserid(local, remote net.Addr) (uid int, err error) { + lAddr, lOk := local.(*net.TCPAddr) + rAddr, rOk := remote.(*net.TCPAddr) + if !(lOk && rOk) { + return -1, fmt.Errorf("netutil: Could not convert Addr to TCPAddr.") } - rip, rport, err := splitIPPort("ripport", ripport) - if err != nil { - return -1, err - } - localv4 := (lip.To4() != nil) - remotev4 := (rip.To4() != nil) + + localv4 := (lAddr.IP.To4() != nil) + remotev4 := (rAddr.IP.To4() != nil) if localv4 != remotev4 { return -1, fmt.Errorf("netutil: address pairs of different families; localv4=%v, remotev4=%v", localv4, remotev4) } if runtime.GOOS == "darwin" { - return uidFromDarwinLsof(lip, lport, rip, rport) + return uidFromDarwinLsof(lAddr.IP, lAddr.Port, rAddr.IP, rAddr.Port) } file := "/proc/net/tcp" @@ -92,7 +92,7 @@ func AddrPairUserid(lipport, ripport string) (uid int, err error) { return -1, fmt.Errorf("Error opening %s: %v", file, err) } defer f.Close() - return uidFromReader(lip, lport, rip, rport, f) + return uidFromReader(lAddr.IP, lAddr.Port, rAddr.IP, rAddr.Port, f) } func toLinuxIPv4Order(b []byte) []byte { @@ -212,20 +212,14 @@ func uidFromReader(lip net.IP, lport int, rip net.IP, rport int, r io.Reader) (u } // Localhost returns the first address found when -// doing a lookup on "localhost". It is surrounded -// by brackets if it contains a colon. -func Localhost() (string, error) { - var addr string - addrs, err := net.LookupHost("localhost") +// doing a lookup on "localhost". +func Localhost() (net.IP, error) { + ips, err := net.LookupIP("localhost") if err != nil { - return addr, err + return nil, err } - if len(addrs) < 1 { - return addr, errors.New("Host lookup for localhost returned no result") + if len(ips) < 1 { + return nil, errors.New("IP lookup for localhost returned no result") } - addr = addrs[0] - if strings.Contains(addr, ":") { - addr = "[" + addr + "]" - } - return addr, nil + return ips[0], nil } diff --git a/server/camlistored/setup.go b/server/camlistored/setup.go index 9d21d3308..e7a528535 100644 --- a/server/camlistored/setup.go +++ b/server/camlistored/setup.go @@ -18,6 +18,7 @@ package main import ( "fmt" + "net" "net/http" "syscall" @@ -31,8 +32,13 @@ func setupHome(rw http.ResponseWriter, req *http.Request) { if err != nil { httputil.ServerError(rw, req, err) } - ourAddr := fmt.Sprintf("%s:%d", localhostAddr, port) - uid, err := netutil.AddrPairUserid(req.RemoteAddr, ourAddr) + ourAddr := &net.TCPAddr{IP: localhostAddr, Port: port} + rAddr, err := net.ResolveTCPAddr("tcp", req.RemoteAddr) + if err != nil { + fmt.Printf("camlistored: unable to resolve RemoteAddr %q: %v", req.RemoteAddr, err) + return + } + uid, err := netutil.AddrPairUserid(rAddr, ourAddr) if err != nil { httputil.ServerError(rw, req, err) }