github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/daemon/ucrednet.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package daemon 21 22 import ( 23 "errors" 24 "fmt" 25 "net" 26 "regexp" 27 "strconv" 28 "sync" 29 sys "syscall" 30 ) 31 32 var errNoID = errors.New("no pid/uid found") 33 34 const ( 35 ucrednetNoProcess = int32(0) 36 ucrednetNobody = uint32((1 << 32) - 1) 37 ) 38 39 var raddrRegexp = regexp.MustCompile(`^pid=(\d+);uid=(\d+);socket=([^;]*);$`) 40 41 func ucrednetGet(remoteAddr string) (pid int32, uid uint32, socket string, err error) { 42 // NOTE treat remoteAddr at one point included a user-controlled 43 // string. In case that happens again by accident, treat it as tainted, 44 // and be very suspicious of it. 45 pid = ucrednetNoProcess 46 uid = ucrednetNobody 47 subs := raddrRegexp.FindStringSubmatch(remoteAddr) 48 if subs != nil { 49 if v, err := strconv.ParseInt(subs[1], 10, 32); err == nil { 50 pid = int32(v) 51 } 52 if v, err := strconv.ParseUint(subs[2], 10, 32); err == nil { 53 uid = uint32(v) 54 } 55 socket = subs[3] 56 } 57 if pid == ucrednetNoProcess || uid == ucrednetNobody { 58 err = errNoID 59 } 60 61 return pid, uid, socket, err 62 } 63 64 type ucrednet struct { 65 pid int32 66 uid uint32 67 socket string 68 } 69 70 func (un *ucrednet) String() string { 71 if un == nil { 72 return "pid=;uid=;socket=;" 73 } 74 return fmt.Sprintf("pid=%d;uid=%d;socket=%s;", un.pid, un.uid, un.socket) 75 } 76 77 type ucrednetAddr struct { 78 net.Addr 79 *ucrednet 80 } 81 82 func (wa *ucrednetAddr) String() string { 83 // NOTE we drop the original (user-supplied) net.Addr from the 84 // serialization entirely. We carry it this far so it helps debugging 85 // (via %#v logging), but from here on in it's not helpful. 86 return wa.ucrednet.String() 87 } 88 89 type ucrednetConn struct { 90 net.Conn 91 *ucrednet 92 } 93 94 func (wc *ucrednetConn) RemoteAddr() net.Addr { 95 return &ucrednetAddr{wc.Conn.RemoteAddr(), wc.ucrednet} 96 } 97 98 type ucrednetListener struct { 99 net.Listener 100 101 idempotClose sync.Once 102 closeErr error 103 } 104 105 var getUcred = sys.GetsockoptUcred 106 107 func (wl *ucrednetListener) Accept() (net.Conn, error) { 108 con, err := wl.Listener.Accept() 109 if err != nil { 110 return nil, err 111 } 112 113 var unet *ucrednet 114 if ucon, ok := con.(*net.UnixConn); ok { 115 f, err := ucon.File() 116 if err != nil { 117 return nil, err 118 } 119 // File() is a dup(); needs closing 120 defer f.Close() 121 122 ucred, err := getUcred(int(f.Fd()), sys.SOL_SOCKET, sys.SO_PEERCRED) 123 if err != nil { 124 return nil, err 125 } 126 127 unet = &ucrednet{ 128 pid: ucred.Pid, 129 uid: ucred.Uid, 130 socket: ucon.LocalAddr().String(), 131 } 132 } 133 134 return &ucrednetConn{con, unet}, nil 135 } 136 137 func (wl *ucrednetListener) Close() error { 138 wl.idempotClose.Do(func() { 139 wl.closeErr = wl.Listener.Close() 140 }) 141 return wl.closeErr 142 }