github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/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 var ucrednetGet = ucrednetGetImpl 42 43 func ucrednetGetImpl(remoteAddr string) (*ucrednet, error) { 44 // NOTE treat remoteAddr at one point included a user-controlled 45 // string. In case that happens again by accident, treat it as tainted, 46 // and be very suspicious of it. 47 u := &ucrednet{ 48 Pid: ucrednetNoProcess, 49 Uid: ucrednetNobody, 50 } 51 subs := raddrRegexp.FindStringSubmatch(remoteAddr) 52 if subs != nil { 53 if v, err := strconv.ParseInt(subs[1], 10, 32); err == nil { 54 u.Pid = int32(v) 55 } 56 if v, err := strconv.ParseUint(subs[2], 10, 32); err == nil { 57 u.Uid = uint32(v) 58 } 59 u.Socket = subs[3] 60 } 61 if u.Pid == ucrednetNoProcess || u.Uid == ucrednetNobody { 62 return nil, errNoID 63 } 64 65 return u, nil 66 } 67 68 type ucrednet struct { 69 Pid int32 70 Uid uint32 71 Socket string 72 } 73 74 func (un *ucrednet) String() string { 75 if un == nil { 76 return "pid=;uid=;socket=;" 77 } 78 return fmt.Sprintf("pid=%d;uid=%d;socket=%s;", un.Pid, un.Uid, un.Socket) 79 } 80 81 type ucrednetAddr struct { 82 net.Addr 83 *ucrednet 84 } 85 86 func (wa *ucrednetAddr) String() string { 87 // NOTE we drop the original (user-supplied) net.Addr from the 88 // serialization entirely. We carry it this far so it helps debugging 89 // (via %#v logging), but from here on in it's not helpful. 90 return wa.ucrednet.String() 91 } 92 93 type ucrednetConn struct { 94 net.Conn 95 *ucrednet 96 } 97 98 func (wc *ucrednetConn) RemoteAddr() net.Addr { 99 return &ucrednetAddr{wc.Conn.RemoteAddr(), wc.ucrednet} 100 } 101 102 type ucrednetListener struct { 103 net.Listener 104 105 idempotClose sync.Once 106 closeErr error 107 } 108 109 var getUcred = sys.GetsockoptUcred 110 111 func (wl *ucrednetListener) Accept() (net.Conn, error) { 112 con, err := wl.Listener.Accept() 113 if err != nil { 114 return nil, err 115 } 116 117 var unet *ucrednet 118 if ucon, ok := con.(*net.UnixConn); ok { 119 f, err := ucon.File() 120 if err != nil { 121 return nil, err 122 } 123 // File() is a dup(); needs closing 124 defer f.Close() 125 126 ucred, err := getUcred(int(f.Fd()), sys.SOL_SOCKET, sys.SO_PEERCRED) 127 if err != nil { 128 return nil, err 129 } 130 131 unet = &ucrednet{ 132 Pid: ucred.Pid, 133 Uid: ucred.Uid, 134 Socket: ucon.LocalAddr().String(), 135 } 136 } 137 138 return &ucrednetConn{con, unet}, nil 139 } 140 141 func (wl *ucrednetListener) Close() error { 142 wl.idempotClose.Do(func() { 143 wl.closeErr = wl.Listener.Close() 144 }) 145 return wl.closeErr 146 }