github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/misc/gpgagent/gpgagent.go (about) 1 // +build !appengine 2 3 /* 4 Copyright 2011 Google Inc. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 // Package gpgagent interacts with the local GPG Agent. 20 package gpgagent 21 22 import ( 23 "bufio" 24 "encoding/hex" 25 "errors" 26 "fmt" 27 28 "io" 29 "net" 30 "net/url" 31 "os" 32 "strings" 33 ) 34 35 // Conn is a connection to the GPG agent. 36 type Conn struct { 37 c io.ReadWriteCloser 38 br *bufio.Reader 39 } 40 41 var ( 42 ErrNoAgent = errors.New("GPG_AGENT_INFO not set in environment") 43 ErrNoData = errors.New("GPG_ERR_NO_DATA cache miss") 44 ErrCancel = errors.New("gpgagent: Cancel") 45 ) 46 47 // NewConn connects to the GPG Agent as described in the 48 // GPG_AGENT_INFO environment variable. 49 func NewConn() (*Conn, error) { 50 sp := strings.SplitN(os.Getenv("GPG_AGENT_INFO"), ":", 3) 51 if len(sp) == 0 || len(sp[0]) == 0 { 52 return nil, ErrNoAgent 53 } 54 addr := &net.UnixAddr{Net: "unix", Name: sp[0]} 55 uc, err := net.DialUnix("unix", nil, addr) 56 if err != nil { 57 return nil, err 58 } 59 br := bufio.NewReader(uc) 60 lineb, err := br.ReadSlice('\n') 61 if err != nil { 62 return nil, err 63 } 64 line := string(lineb) 65 if !strings.HasPrefix(line, "OK") { 66 return nil, fmt.Errorf("gpgagent: didn't get OK; got %q", line) 67 } 68 return &Conn{uc, br}, nil 69 } 70 71 func (c *Conn) Close() error { 72 c.br = nil 73 return c.c.Close() 74 } 75 76 // PassphraseRequest is a request to get a passphrase from the GPG 77 // Agent. 78 type PassphraseRequest struct { 79 CacheKey, Error, Prompt, Desc string 80 81 // If the option --no-ask is used and the passphrase is not in 82 // the cache the user will not be asked to enter a passphrase 83 // but the error code GPG_ERR_NO_DATA is returned. (ErrNoData) 84 NoAsk bool 85 } 86 87 func (c *Conn) RemoveFromCache(cacheKey string) error { 88 _, err := fmt.Fprintf(c.c, "CLEAR_PASSPHRASE %s\n", url.QueryEscape(cacheKey)) 89 if err != nil { 90 return err 91 } 92 lineb, err := c.br.ReadSlice('\n') 93 if err != nil { 94 return err 95 } 96 line := string(lineb) 97 if !strings.HasPrefix(line, "OK") { 98 return fmt.Errorf("gpgagent: CLEAR_PASSPHRASE returned %q", line) 99 } 100 return nil 101 } 102 103 func (c *Conn) GetPassphrase(pr *PassphraseRequest) (passphrase string, outerr error) { 104 defer func() { 105 if e, ok := recover().(string); ok { 106 passphrase = "" 107 outerr = errors.New(e) 108 } 109 }() 110 set := func(cmd string, val string) { 111 if val == "" { 112 return 113 } 114 _, err := fmt.Fprintf(c.c, "%s %s\n", cmd, val) 115 if err != nil { 116 panic("gpgagent: failed to send " + cmd) 117 } 118 line, _, err := c.br.ReadLine() 119 if err != nil { 120 panic("gpgagent: failed to read " + cmd) 121 } 122 if !strings.HasPrefix(string(line), "OK") { 123 panic("gpgagent: response to " + cmd + " was " + string(line)) 124 } 125 } 126 if d := os.Getenv("DISPLAY"); d != "" { 127 set("OPTION", "display="+d) 128 } 129 tty, err := os.Readlink("/proc/self/fd/0") 130 if err == nil { 131 set("OPTION", "ttyname="+tty) 132 } 133 set("OPTION", "ttytype="+os.Getenv("TERM")) 134 opts := "" 135 if pr.NoAsk { 136 opts += "--no-ask " 137 } 138 139 encOrX := func(s string) string { 140 if s == "" { 141 return "X" 142 } 143 return url.QueryEscape(s) 144 } 145 146 _, err = fmt.Fprintf(c.c, "GET_PASSPHRASE %s%s %s %s %s\n", 147 opts, 148 url.QueryEscape(pr.CacheKey), 149 encOrX(pr.Error), 150 encOrX(pr.Prompt), 151 encOrX(pr.Desc)) 152 if err != nil { 153 return "", err 154 } 155 lineb, err := c.br.ReadSlice('\n') 156 if err != nil { 157 return "", err 158 } 159 line := string(lineb) 160 if strings.HasPrefix(line, "OK ") { 161 decb, err := hex.DecodeString(line[3 : len(line)-1]) 162 if err != nil { 163 return "", err 164 } 165 return string(decb), nil 166 } 167 fields := strings.Split(line, " ") 168 if len(fields) >= 2 && fields[0] == "ERR" { 169 switch fields[1] { 170 case "67108922": 171 return "", ErrNoData 172 case "83886179": 173 return "", ErrCancel 174 } 175 } 176 return "", errors.New(line) 177 }