xref: /aosp_15_r20/external/libcap/goapps/setid/setid.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1*2810ac1bSKiyoung Kim// Program setid demonstrates how the to use the cap and/or psx packages to
2*2810ac1bSKiyoung Kim// change the uid, gids of a program.
3*2810ac1bSKiyoung Kim//
4*2810ac1bSKiyoung Kim// A long writeup explaining how to use it in various different ways
5*2810ac1bSKiyoung Kim// is available:
6*2810ac1bSKiyoung Kim//
7*2810ac1bSKiyoung Kim//   https://sites.google.com/site/fullycapable/Home/using-go-to-set-uid-and-gids
8*2810ac1bSKiyoung Kimpackage main
9*2810ac1bSKiyoung Kim
10*2810ac1bSKiyoung Kimimport (
11*2810ac1bSKiyoung Kim	"flag"
12*2810ac1bSKiyoung Kim	"fmt"
13*2810ac1bSKiyoung Kim	"io/ioutil"
14*2810ac1bSKiyoung Kim	"log"
15*2810ac1bSKiyoung Kim	"strconv"
16*2810ac1bSKiyoung Kim	"strings"
17*2810ac1bSKiyoung Kim	"syscall"
18*2810ac1bSKiyoung Kim	"unsafe"
19*2810ac1bSKiyoung Kim
20*2810ac1bSKiyoung Kim	"kernel.org/pub/linux/libs/security/libcap/cap"
21*2810ac1bSKiyoung Kim	"kernel.org/pub/linux/libs/security/libcap/psx"
22*2810ac1bSKiyoung Kim)
23*2810ac1bSKiyoung Kim
24*2810ac1bSKiyoung Kimvar (
25*2810ac1bSKiyoung Kim	uid      = flag.Int("uid", -1, "specify a uid with a value other than (euid)")
26*2810ac1bSKiyoung Kim	gid      = flag.Int("gid", -1, "specify a gid with a value other than (egid)")
27*2810ac1bSKiyoung Kim	drop     = flag.Bool("drop", true, "drop privilege once IDs have been changed")
28*2810ac1bSKiyoung Kim	suppl    = flag.String("suppl", "", "comma separated list of groups")
29*2810ac1bSKiyoung Kim	withCaps = flag.Bool("caps", true, "raise capabilities to setuid/setgid")
30*2810ac1bSKiyoung Kim)
31*2810ac1bSKiyoung Kim
32*2810ac1bSKiyoung Kim// setIDWithCaps uses the cap.SetUID and cap.SetGroups functions.
33*2810ac1bSKiyoung Kimfunc setIDsWithCaps(setUID, setGID int, gids []int) {
34*2810ac1bSKiyoung Kim	if err := cap.SetGroups(setGID, gids...); err != nil {
35*2810ac1bSKiyoung Kim		log.Fatalf("group setting failed: %v", err)
36*2810ac1bSKiyoung Kim	}
37*2810ac1bSKiyoung Kim	if err := cap.SetUID(setUID); err != nil {
38*2810ac1bSKiyoung Kim		log.Fatalf("user setting failed: %v", err)
39*2810ac1bSKiyoung Kim	}
40*2810ac1bSKiyoung Kim}
41*2810ac1bSKiyoung Kim
42*2810ac1bSKiyoung Kimfunc main() {
43*2810ac1bSKiyoung Kim	flag.Parse()
44*2810ac1bSKiyoung Kim
45*2810ac1bSKiyoung Kim	showIDs("before", false, syscall.Getuid(), syscall.Getgid())
46*2810ac1bSKiyoung Kim
47*2810ac1bSKiyoung Kim	gids := splitToInts()
48*2810ac1bSKiyoung Kim	setGID := *gid
49*2810ac1bSKiyoung Kim	if *gid == -1 {
50*2810ac1bSKiyoung Kim		setGID = syscall.Getegid()
51*2810ac1bSKiyoung Kim	}
52*2810ac1bSKiyoung Kim	setUID := *uid
53*2810ac1bSKiyoung Kim	if *uid == -1 {
54*2810ac1bSKiyoung Kim		setUID = syscall.Getuid()
55*2810ac1bSKiyoung Kim	}
56*2810ac1bSKiyoung Kim
57*2810ac1bSKiyoung Kim	if *withCaps {
58*2810ac1bSKiyoung Kim		setIDsWithCaps(setUID, setGID, gids)
59*2810ac1bSKiyoung Kim	} else {
60*2810ac1bSKiyoung Kim		if _, _, err := psx.Syscall3(syscall.SYS_SETGID, uintptr(setGID), 0, 0); err != 0 {
61*2810ac1bSKiyoung Kim			log.Fatalf("failed to setgid(%d): %v", setGID, err)
62*2810ac1bSKiyoung Kim		}
63*2810ac1bSKiyoung Kim		if len(gids) != 0 {
64*2810ac1bSKiyoung Kim			gids32 := []int32{int32(setGID)}
65*2810ac1bSKiyoung Kim			for _, g := range gids {
66*2810ac1bSKiyoung Kim				gids32 = append(gids32, int32(g))
67*2810ac1bSKiyoung Kim			}
68*2810ac1bSKiyoung Kim			if _, _, err := psx.Syscall3(syscall.SYS_SETGROUPS, uintptr(unsafe.Pointer(&gids32[0])), 0, 0); err != 0 {
69*2810ac1bSKiyoung Kim				log.Fatalf("failed to setgroups(%d, %v): %v", setGID, gids32, err)
70*2810ac1bSKiyoung Kim			}
71*2810ac1bSKiyoung Kim		}
72*2810ac1bSKiyoung Kim		if _, _, err := psx.Syscall3(syscall.SYS_SETUID, uintptr(setUID), 0, 0); err != 0 {
73*2810ac1bSKiyoung Kim			log.Fatalf("failed to setgid(%d): %v", setUID, err)
74*2810ac1bSKiyoung Kim		}
75*2810ac1bSKiyoung Kim	}
76*2810ac1bSKiyoung Kim
77*2810ac1bSKiyoung Kim	if *drop {
78*2810ac1bSKiyoung Kim		if err := cap.NewSet().SetProc(); err != nil {
79*2810ac1bSKiyoung Kim			log.Fatalf("unable to drop privilege: %v", err)
80*2810ac1bSKiyoung Kim		}
81*2810ac1bSKiyoung Kim	}
82*2810ac1bSKiyoung Kim
83*2810ac1bSKiyoung Kim	showIDs("after", true, setUID, setGID)
84*2810ac1bSKiyoung Kim}
85*2810ac1bSKiyoung Kim
86*2810ac1bSKiyoung Kim// splitToInts parses a comma separated string to a slice of integers.
87*2810ac1bSKiyoung Kimfunc splitToInts() (ret []int) {
88*2810ac1bSKiyoung Kim	if *suppl == "" {
89*2810ac1bSKiyoung Kim		return
90*2810ac1bSKiyoung Kim	}
91*2810ac1bSKiyoung Kim	a := strings.Split(*suppl, ",")
92*2810ac1bSKiyoung Kim	for _, s := range a {
93*2810ac1bSKiyoung Kim		n, err := strconv.Atoi(s)
94*2810ac1bSKiyoung Kim		if err != nil {
95*2810ac1bSKiyoung Kim			log.Fatalf("bad supplementary group [%q]: %v", s, err)
96*2810ac1bSKiyoung Kim		}
97*2810ac1bSKiyoung Kim		ret = append(ret, n)
98*2810ac1bSKiyoung Kim	}
99*2810ac1bSKiyoung Kim	return
100*2810ac1bSKiyoung Kim}
101*2810ac1bSKiyoung Kim
102*2810ac1bSKiyoung Kim// dumpStatus explores the current process /proc/task/* status files
103*2810ac1bSKiyoung Kim// for matching values.
104*2810ac1bSKiyoung Kimfunc dumpStatus(testCase string, validate bool, filter, expect string) bool {
105*2810ac1bSKiyoung Kim	fmt.Printf("%s:\n", testCase)
106*2810ac1bSKiyoung Kim	var failed bool
107*2810ac1bSKiyoung Kim	pid := syscall.Getpid()
108*2810ac1bSKiyoung Kim	fs, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/task", pid))
109*2810ac1bSKiyoung Kim	if err != nil {
110*2810ac1bSKiyoung Kim		log.Fatal(err)
111*2810ac1bSKiyoung Kim	}
112*2810ac1bSKiyoung Kim	for _, f := range fs {
113*2810ac1bSKiyoung Kim		tf := fmt.Sprintf("/proc/%s/status", f.Name())
114*2810ac1bSKiyoung Kim		d, err := ioutil.ReadFile(tf)
115*2810ac1bSKiyoung Kim		if err != nil {
116*2810ac1bSKiyoung Kim			fmt.Println(tf, err)
117*2810ac1bSKiyoung Kim			failed = true
118*2810ac1bSKiyoung Kim			continue
119*2810ac1bSKiyoung Kim		}
120*2810ac1bSKiyoung Kim		lines := strings.Split(string(d), "\n")
121*2810ac1bSKiyoung Kim		for _, line := range lines {
122*2810ac1bSKiyoung Kim			if strings.HasPrefix(line, filter) {
123*2810ac1bSKiyoung Kim				fails := line != expect
124*2810ac1bSKiyoung Kim				failure := ""
125*2810ac1bSKiyoung Kim				if fails && validate {
126*2810ac1bSKiyoung Kim					failed = fails
127*2810ac1bSKiyoung Kim					failure = " (bad)"
128*2810ac1bSKiyoung Kim				}
129*2810ac1bSKiyoung Kim				fmt.Printf("%s %s%s\n", tf, line, failure)
130*2810ac1bSKiyoung Kim				break
131*2810ac1bSKiyoung Kim			}
132*2810ac1bSKiyoung Kim		}
133*2810ac1bSKiyoung Kim	}
134*2810ac1bSKiyoung Kim	return failed
135*2810ac1bSKiyoung Kim}
136*2810ac1bSKiyoung Kim
137*2810ac1bSKiyoung Kim// showIDs dumps the thread map out of the /proc/<proc>/tasks
138*2810ac1bSKiyoung Kim// filesystem to confirm that all of the threads associated with the
139*2810ac1bSKiyoung Kim// process have the same uid/gid values. Note, the code does not
140*2810ac1bSKiyoung Kim// attempt to validate the supplementary groups at present.
141*2810ac1bSKiyoung Kimfunc showIDs(test string, validate bool, wantUID, wantGID int) {
142*2810ac1bSKiyoung Kim	fmt.Printf("%s capability state: %q\n", test, cap.GetProc())
143*2810ac1bSKiyoung Kim
144*2810ac1bSKiyoung Kim	failed := dumpStatus(test+" gid", validate, "Gid:", fmt.Sprintf("Gid:\t%d\t%d\t%d\t%d", wantGID, wantGID, wantGID, wantGID))
145*2810ac1bSKiyoung Kim
146*2810ac1bSKiyoung Kim	failed = dumpStatus(test+" uid", validate, "Uid:", fmt.Sprintf("Uid:\t%d\t%d\t%d\t%d", wantUID, wantUID, wantUID, wantUID)) || failed
147*2810ac1bSKiyoung Kim
148*2810ac1bSKiyoung Kim	if validate && failed {
149*2810ac1bSKiyoung Kim		log.Fatal("did not observe desired *id state")
150*2810ac1bSKiyoung Kim	}
151*2810ac1bSKiyoung Kim}
152