File: server_ns.exp

package info (click to toggle)
systemtap 5.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 47,556 kB
  • sloc: cpp: 81,117; ansic: 54,933; xml: 49,795; exp: 43,595; sh: 11,526; python: 5,003; perl: 2,252; tcl: 1,312; makefile: 1,006; javascript: 149; lisp: 105; awk: 101; asm: 91; java: 70; sed: 16
file content (137 lines) | stat: -rw-r--r-- 5,499 bytes parent folder | download | duplicates (5)
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# When testing the systemtap server, we don't want to modify the real
# system configuration (like adding trusted signers). So, instead
# we'll use a mount namespace. This will allow us to mount a directory
# on top of the real system cert database directory and only processes
# that use this mount namespace will see our temporary system cert
# database directory. See unshare(1) and mount_namespaces(7) for more
# details.
#
# One note. Fedora 27 (the current Fedora at the time of this writeup)
# has the capability of creating persistent namespaces, which would
# simplify the following logic a bit. However, neither the RHEL6 or
# RHEL7 versions of 'unshare' support this. So, we'll do this the
# harder way.
#
# There are two main functions provided by this file:
# 'server_ns_as_root' and 'server_ns_as_user'. These allow the caller
# to run commands (as either 'root' or a specified user) in the custom
# mount namespace.
#
# Note that you have to be root to call these functions. Why? It might
# be possible to call them as non-root using 'sudo'. But, the quoting
# is really tricky now, and that would just complicate matters
# even further. So, we're taking the easy way out.

# Setup the server_ns configuration. This function is called by 
# 'server_ns_as_root' and 'server_ns_as_user' (so the setup is only
# done if those routines that need it are called).
proc server_ns_setup {} {
    global env systemtap_orig_cert_db_dir systemtap_new_cert_db_dir

    # We need to be root so that the temporary directory is owned by
    # root.
    set effective_uid [exec /usr/bin/id -u]
    if {$effective_uid != 0} {
	verbose -log "server_ns_setup: non-root user"
	return 1
    }

    # If we've already created the directory, just return.
    if {[info exists systemtap_orig_cert_db_dir]
	&& [string length $systemtap_orig_cert_db_dir]
	&& [info exists systemtap_new_cert_db_dir]
	&& [string length $systemtap_orig_cert_db_dir]} {
	return 0
    }

    # Create a new directory to bind mount on top of
    # ${SYSCONFDIR}/systemtap.
    if {! [info exists env(SYSCONFDIR)]
	|| [string length $env(SYSCONFDIR)] == 0} {
	verbose -log "server_ns_setup: no SYSCONFDIR!"
	return 1
    }

    # Note that the db path actually ends up under
    # SYSCONFDIR/systemtap/staprun, but for some reason stap doesn't
    # like us bind mounting that directory. So, we'll use the parent
    # directory.
    set systemtap_orig_cert_db_dir "$env(SYSCONFDIR)/systemtap"

    # Create a temporary cert dtatabase path. This directory must be
    # owned by root. We delete this when shutting down the server.
    if {[catch {exec mktemp -d} systemtap_new_cert_db_dir]} {
	verbose -log "server_ns_setup - Failed to create temporary cert db directory: $systemtap_new_cert_db_dir"
	unset systemtap_new_cert_db_dir
	return 1
    }
    verbose -log "server_ns_setup - orig cert db dir: $systemtap_orig_cert_db_dir"
    verbose -log "server_ns_setup - new cert db dir: $systemtap_new_cert_db_dir"
    return 0
}

# Clean up the server_ns configuration. This function is called by 
# 'shutdown_server' (and is careful not to do anyting if
# server_ns_setup hasn't been called).
proc server_ns_cleanup {} {
    global systemtap_new_cert_db_dir
    if {[info exists systemtap_new_cert_db_dir]
	&& [string length $systemtap_new_cert_db_dir]
	&& [file exists $systemtap_new_cert_db_dir]} {
	catch {exec rm -rf $systemtap_new_cert_db_dir}
	unset systemtap_new_cert_db_dir
    }
}

# server_ns_as_root: run 'command' in a custom mount
# namespace as root. Returns a list of [ RETURN_STATUS OUTPUT ].
proc server_ns_as_root { command } {
    global systemtap_orig_cert_db_dir systemtap_new_cert_db_dir

    if {[server_ns_setup]} { return [list 1 "server_ns_setup failure"] }

    # We're root (otherwise server_ns_setup would have failed). We're going
    # to call "unshare" to create a new mount namespace. This will
    # allow us to mount our temporary directory on top of the real
    # system cert db.
    set cmd {unshare --mount bash -c "mount --bind $systemtap_new_cert_db_dir $systemtap_orig_cert_db_dir && $command"}
    verbose -log "running: [subst $cmd]"
    set rc 0
    if {[catch {eval exec $cmd} output]} {
	# non-zero exit status, get it:
	if {[lindex $::errorCode 0] eq "CHILDSTATUS"} {
	    set rc [lindex $::errorCode 2]
	}
    }
    verbose -log "RC: $rc"
    verbose -log "$output"

    return [list $rc $output]
}

# server_ns_as_user: run 'command' in a custom mount namespace as user
# 'user'. Returns a list of [ RETURN_STATUS OUTPUT ].
proc server_ns_as_user { user command } {
    global systemtap_orig_cert_db_dir systemtap_new_cert_db_dir

    if {[server_ns_setup]} { return [list 1 "server_ns_setup failure"] }

    # We're root (otherwise server_ns_setup would have failed). We're going
    # to call "unshare" to create a new mount namespace. This will
    # allow us to mount our temporary directory on top of the real
    # system cert db. We're also going to run the caller's command as
    # the specified user (using 'su').
    set cmd {unshare --mount bash -c "mount --bind $systemtap_new_cert_db_dir $systemtap_orig_cert_db_dir && su -l -s /bin/sh $user -c '$command'"}
    verbose -log "running: [subst $cmd]"
    set rc 0
    if {[catch {eval exec $cmd} output]} {
	# non-zero exit status, get it:
	if {[lindex $::errorCode 0] eq "CHILDSTATUS"} {
	    set rc [lindex $::errorCode 2]
	}
    }
    verbose -log "RC: $rc"
    verbose -log "$output"

    return [list $rc $output]
}