github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/x11/xauth.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 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 x11 21 22 import ( 23 "encoding/binary" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "os" 28 ) 29 30 // See https://cgit.freedesktop.org/xorg/lib/libXau/tree/AuRead.c and 31 // https://cgit.freedesktop.org/xorg/lib/libXau/tree/include/X11/Xauth.h 32 // for details about the actual file format. 33 type xauth struct { 34 Family uint16 35 Address []byte 36 Number []byte 37 Name []byte 38 Data []byte 39 } 40 41 func readChunk(r io.Reader) ([]byte, error) { 42 // A chunk consists of a length encoded by two bytes (so max 64K) 43 // and additional data which is the real value of the item we're 44 // reading here from the file. 45 46 b := [2]byte{} 47 if _, err := io.ReadFull(r, b[:]); err != nil { 48 return nil, err 49 } 50 51 size := int(binary.BigEndian.Uint16(b[:])) 52 chunk := make([]byte, size) 53 if _, err := io.ReadFull(r, chunk); err != nil { 54 return nil, err 55 } 56 57 return chunk, nil 58 } 59 60 func (xa *xauth) readFromFile(r io.Reader) error { 61 b := [2]byte{} 62 if _, err := io.ReadFull(r, b[:]); err != nil { 63 return err 64 } 65 // The family field consists of two bytes 66 xa.Family = binary.BigEndian.Uint16(b[:]) 67 68 var err error 69 70 if xa.Address, err = readChunk(r); err != nil { 71 return err 72 } 73 74 if xa.Number, err = readChunk(r); err != nil { 75 return err 76 } 77 78 if xa.Name, err = readChunk(r); err != nil { 79 return err 80 } 81 82 if xa.Data, err = readChunk(r); err != nil { 83 return err 84 } 85 86 return nil 87 } 88 89 // ValidateXauthority validates a given Xauthority file. The file is valid 90 // if it can be parsed and contains at least one cookie. 91 func ValidateXauthorityFile(path string) error { 92 f, err := os.Open(path) 93 if err != nil { 94 return err 95 } 96 defer f.Close() 97 return ValidateXauthority(f) 98 } 99 100 // ValidateXauthority validates a given Xauthority file. The file is valid 101 // if it can be parsed and contains at least one cookie. 102 func ValidateXauthority(r io.Reader) error { 103 cookies := 0 104 for { 105 xa := &xauth{} 106 err := xa.readFromFile(r) 107 if err == io.EOF { 108 break 109 } else if err != nil { 110 return err 111 } 112 cookies++ 113 } 114 115 if cookies <= 0 { 116 return fmt.Errorf("Xauthority file is invalid") 117 } 118 119 return nil 120 } 121 122 // MockXauthority will create a fake xauthority file and place it 123 // on a temporary path which is returned as result. 124 func MockXauthority(cookies int) (string, error) { 125 f, err := ioutil.TempFile("", "xauth") 126 if err != nil { 127 return "", err 128 } 129 defer f.Close() 130 for n := 0; n < cookies; n++ { 131 data := []byte{ 132 // Family 133 0x01, 0x00, 134 // Address 135 0x00, 0x04, 0x73, 0x6e, 0x61, 0x70, 136 // Number 137 0x00, 0x01, 0xff, 138 // Name 139 0x00, 0x05, 0x73, 0x6e, 0x61, 0x70, 0x64, 140 // Data 141 0x00, 0x01, 0xff, 142 } 143 m, err := f.Write(data) 144 if err != nil { 145 return "", err 146 } else if m != len(data) { 147 return "", fmt.Errorf("Could write cookie") 148 } 149 } 150 return f.Name(), nil 151 }