github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/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  }