github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/systemd/dbus.go (about)

     1  package systemd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  
    10  	"github.com/hanks177/podman/v4/pkg/rootless"
    11  	"github.com/coreos/go-systemd/v22/dbus"
    12  	godbus "github.com/godbus/dbus/v5"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  // IsSystemdSessionValid checks if sessions is valid for provided rootless uid.
    17  func IsSystemdSessionValid(uid int) bool {
    18  	var conn *godbus.Conn
    19  	var err error
    20  	var object godbus.BusObject
    21  	var seat0Path godbus.ObjectPath
    22  	dbusDest := "org.freedesktop.login1"
    23  	dbusInterface := "org.freedesktop.login1.Manager"
    24  	dbusPath := "/org/freedesktop/login1"
    25  
    26  	if rootless.IsRootless() {
    27  		conn, err = GetLogindConnection(rootless.GetRootlessUID())
    28  		if err != nil {
    29  			// unable to fetch systemd object for logind
    30  			logrus.Debugf("systemd-logind: %s", err)
    31  			return false
    32  		}
    33  		object = conn.Object(dbusDest, godbus.ObjectPath(dbusPath))
    34  		if err := object.Call(dbusInterface+".GetSeat", 0, "seat0").Store(&seat0Path); err != nil {
    35  			// unable to get seat0 path.
    36  			logrus.Debugf("systemd-logind: %s", err)
    37  			return false
    38  		}
    39  		seat0Obj := conn.Object(dbusDest, seat0Path)
    40  		activeSession, err := seat0Obj.GetProperty(dbusDest + ".Seat.ActiveSession")
    41  		if err != nil {
    42  			// unable to get active sessions.
    43  			logrus.Debugf("systemd-logind: %s", err)
    44  			return false
    45  		}
    46  		activeSessionMap, ok := activeSession.Value().([]interface{})
    47  		if !ok || len(activeSessionMap) < 2 {
    48  			// unable to get active session map.
    49  			logrus.Debugf("systemd-logind: %s", err)
    50  			return false
    51  		}
    52  		activeSessionPath, ok := activeSessionMap[1].(godbus.ObjectPath)
    53  		if !ok {
    54  			// unable to fetch active session path.
    55  			logrus.Debugf("systemd-logind: %s", err)
    56  			return false
    57  		}
    58  		activeSessionObj := conn.Object(dbusDest, activeSessionPath)
    59  		sessionUser, err := activeSessionObj.GetProperty(dbusDest + ".Session.User")
    60  		if err != nil {
    61  			// unable to fetch session user from activeSession path.
    62  			logrus.Debugf("systemd-logind: %s", err)
    63  			return false
    64  		}
    65  		dbusUser, ok := sessionUser.Value().([]interface{})
    66  		if !ok {
    67  			// not a valid user.
    68  			return false
    69  		}
    70  		if len(dbusUser) < 2 {
    71  			// not a valid session user.
    72  			return false
    73  		}
    74  		activeUID, ok := dbusUser[0].(uint32)
    75  		if !ok {
    76  			return false
    77  		}
    78  		// active session found which belongs to following rootless user
    79  		if activeUID == uint32(uid) {
    80  			return true
    81  		}
    82  		return false
    83  	}
    84  	return true
    85  }
    86  
    87  // GetDbusConnection returns a user connection to D-BUS
    88  func GetLogindConnection(uid int) (*godbus.Conn, error) {
    89  	return dbusAuthConnectionLogind(uid)
    90  }
    91  
    92  func dbusAuthConnectionLogind(uid int) (*godbus.Conn, error) {
    93  	var conn *godbus.Conn
    94  	var err error
    95  	conn, err = godbus.SystemBusPrivate()
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	methods := []godbus.Auth{godbus.AuthExternal(strconv.Itoa(uid))}
   100  	if err = conn.Auth(methods); err != nil {
   101  		conn.Close()
   102  		return nil, err
   103  	}
   104  	err = conn.Hello()
   105  	if err != nil {
   106  		conn.Close()
   107  		return nil, err
   108  	}
   109  	return conn, nil
   110  }
   111  
   112  func dbusAuthRootlessConnection(createBus func(opts ...godbus.ConnOption) (*godbus.Conn, error)) (*godbus.Conn, error) {
   113  	conn, err := createBus()
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	methods := []godbus.Auth{godbus.AuthExternal(strconv.Itoa(rootless.GetRootlessUID()))}
   119  
   120  	err = conn.Auth(methods)
   121  	if err != nil {
   122  		conn.Close()
   123  		return nil, err
   124  	}
   125  
   126  	return conn, nil
   127  }
   128  
   129  func newRootlessConnection() (*dbus.Conn, error) {
   130  	return dbus.NewConnection(func() (*godbus.Conn, error) {
   131  		return dbusAuthRootlessConnection(func(opts ...godbus.ConnOption) (*godbus.Conn, error) {
   132  			path := filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "systemd/private")
   133  			return godbus.Dial(fmt.Sprintf("unix:path=%s", path))
   134  		})
   135  	})
   136  }
   137  
   138  // ConnectToDBUS returns a DBUS connection.  It works both as root and non-root
   139  // users.
   140  func ConnectToDBUS() (*dbus.Conn, error) {
   141  	if rootless.IsRootless() {
   142  		return newRootlessConnection()
   143  	}
   144  	return dbus.NewSystemdConnectionContext(context.Background())
   145  }