1// Copyright 2012 Google Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package main 16 17import ( 18 "fmt" 19 "log" 20 "net" 21 "os" 22 "path/filepath" 23 "syscall" 24) 25 26// A Socket is a wrapper around a Unix socket that verifies directory 27// permissions. 28type Socket struct { 29 Dir string 30} 31 32func defaultDir() string { 33 sockPath := ".git-credential-cache" 34 if home := os.Getenv("HOME"); home != "" { 35 return filepath.Join(home, sockPath) 36 } 37 log.Printf("socket: cannot find HOME path. using relative directory %q for socket", sockPath) 38 return sockPath 39} 40 41// DefaultSocket is a Socket in the $HOME/.git-credential-cache directory. 42var DefaultSocket = Socket{Dir: defaultDir()} 43 44// Listen announces the local network address of the unix socket. The 45// permissions on the socket directory are verified before attempting 46// the actual listen. 47func (s Socket) Listen() (net.Listener, error) { 48 network, addr := "unix", s.Path() 49 if err := s.mkdir(); err != nil { 50 return nil, &net.OpError{Op: "listen", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err} 51 } 52 return net.Listen(network, addr) 53} 54 55// Dial connects to the unix socket. The permissions on the socket directory 56// are verified before attempting the actual dial. 57func (s Socket) Dial() (net.Conn, error) { 58 network, addr := "unix", s.Path() 59 if err := s.checkPermissions(); err != nil { 60 return nil, &net.OpError{Op: "dial", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err} 61 } 62 return net.Dial(network, addr) 63} 64 65// Path returns the fully specified file name of the unix socket. 66func (s Socket) Path() string { 67 return filepath.Join(s.Dir, "persistent-https-proxy-socket") 68} 69 70func (s Socket) mkdir() error { 71 if err := s.checkPermissions(); err == nil { 72 return nil 73 } else if !os.IsNotExist(err) { 74 return err 75 } 76 if err := os.MkdirAll(s.Dir, 0700); err != nil { 77 return err 78 } 79 return s.checkPermissions() 80} 81 82func (s Socket) checkPermissions() error { 83 fi, err := os.Stat(s.Dir) 84 if err != nil { 85 return err 86 } 87 if !fi.IsDir() { 88 return fmt.Errorf("socket: got file, want directory for %q", s.Dir) 89 } 90 if fi.Mode().Perm() != 0700 { 91 return fmt.Errorf("socket: got perm %o, want 700 for %q", fi.Mode().Perm(), s.Dir) 92 } 93 if st := fi.Sys().(*syscall.Stat_t); int(st.Uid) != os.Getuid() { 94 return fmt.Errorf("socket: got uid %d, want %d for %q", st.Uid, os.Getuid(), s.Dir) 95 } 96 return nil 97}