summaryrefslogtreecommitdiff
path: root/cmd/podman/user.go
blob: 3e2e308c501d43375c9a8158866c12b322cebc9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package main

// #include <sys/types.h>
// #include <grp.h>
// #include <pwd.h>
// #include <stdlib.h>
// #include <stdio.h>
// #include <string.h>
// typedef FILE * pFILE;
import "C"

import (
	"fmt"
	"os/user"
	"path/filepath"
	"sync"
	"syscall"
	"unsafe"

	"github.com/pkg/errors"
)

func fopenContainerFile(rootdir, filename string) (C.pFILE, error) {
	var st, lst syscall.Stat_t

	ctrfile := filepath.Join(rootdir, filename)
	cctrfile := C.CString(ctrfile)
	defer C.free(unsafe.Pointer(cctrfile))
	mode := C.CString("r")
	defer C.free(unsafe.Pointer(mode))
	f, err := C.fopen(cctrfile, mode)
	if f == nil || err != nil {
		return nil, errors.Wrapf(err, "error opening %q", ctrfile)
	}
	if err = syscall.Fstat(int(C.fileno(f)), &st); err != nil {
		return nil, errors.Wrapf(err, "fstat(%q)", ctrfile)
	}
	if err = syscall.Lstat(ctrfile, &lst); err != nil {
		return nil, errors.Wrapf(err, "lstat(%q)", ctrfile)
	}
	if st.Dev != lst.Dev || st.Ino != lst.Ino {
		return nil, errors.Errorf("%q is not a regular file", ctrfile)
	}
	return f, nil
}

var (
	lookupUser, lookupGroup sync.Mutex
)

func lookupUserInContainer(rootdir, username string) (uint64, uint64, error) {
	name := C.CString(username)
	defer C.free(unsafe.Pointer(name))

	f, err := fopenContainerFile(rootdir, "/etc/passwd")
	if err != nil {
		return 0, 0, err
	}
	defer C.fclose(f)

	lookupUser.Lock()
	defer lookupUser.Unlock()

	pwd := C.fgetpwent(f)
	for pwd != nil {
		if C.strcmp(pwd.pw_name, name) != 0 {
			pwd = C.fgetpwent(f)
			continue
		}
		return uint64(pwd.pw_uid), uint64(pwd.pw_gid), nil
	}

	return 0, 0, user.UnknownUserError(fmt.Sprintf("error looking up user %q", username))
}

func lookupGroupForUIDInContainer(rootdir string, userid uint64) (string, uint64, error) {
	f, err := fopenContainerFile(rootdir, "/etc/passwd")
	if err != nil {
		return "", 0, err
	}
	defer C.fclose(f)

	lookupUser.Lock()
	defer lookupUser.Unlock()

	pwd := C.fgetpwent(f)
	for pwd != nil {
		if uint64(pwd.pw_uid) != userid {
			pwd = C.fgetpwent(f)
			continue
		}
		return C.GoString(pwd.pw_name), uint64(pwd.pw_gid), nil
	}

	return "", 0, user.UnknownUserError(fmt.Sprintf("error looking up user with UID %d", userid))
}

func lookupGroupInContainer(rootdir, groupname string) (uint64, error) {
	name := C.CString(groupname)
	defer C.free(unsafe.Pointer(name))

	f, err := fopenContainerFile(rootdir, "/etc/group")
	if err != nil {
		return 0, err
	}
	defer C.fclose(f)

	lookupGroup.Lock()
	defer lookupGroup.Unlock()

	grp := C.fgetgrent(f)
	for grp != nil {
		if C.strcmp(grp.gr_name, name) != 0 {
			grp = C.fgetgrent(f)
			continue
		}
		return uint64(grp.gr_gid), nil
	}

	return 0, user.UnknownGroupError(fmt.Sprintf("error looking up group %q", groupname))
}