File: server_trust.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 (640 lines) | stat: -rw-r--r-- 18,789 bytes parent folder | download | duplicates (4)
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
# In this test, we are using arrays as sets. The key itself does not matter,
# only the value

set test "server_trust"
# exp_internal 1

if {! [installtest_p]} { untested "client"; return }
if {! [nss_p]} { untested "client (no nss)"; return }

# Note we have to be root (to get the namespace to work correctly).
set effective_uid [exec /usr/bin/id -u]
if {$effective_uid != 0} { untested "client (must be root)"; return }

# Let's start with a clean slate in terms of trust.
exec rm -fr $env(SYSTEMTAP_DIR)/ssl

# arr given as list (e.g. [array_has [array get myarr] elem])
proc array_has {arr elem} {

    array set foo $arr
    if {[array size foo] == 0} {
        return 0
    }

    foreach {key val} [array get foo] {
        if {[string equal $val $elem]} {
            return 1
        }
    }

    return 0
}

# Check if array1 is a subset of array2. Returns 1 if yes, otherwise 0.
# (Modified from http://wiki.tcl.tk/1032.)
proc array_in {array1 array2} {
    upvar 1 $array1 sub $array2 super

    if {![array exists sub]} {
        return -code error "$array1 is not an array"
    }
    if {![array exists super]} {
        return -code error "$array2 is not an array"
    }
    if {[array size sub] > [array size super]} {
        return 0
    }
    if {[array size sub] == 0} {
        return 1
    }

    foreach key [array names sub] {
        if {![array_has [array get super] $sub($key)]} {
            return 0
        }
    }
    return 1
}

# Check if array1 and array2 have all the same elements
proc array_equal {array1 array2} {
    upvar 1 $array1 foo $array2 bar
    return [expr [array_in foo bar] && [array_in bar foo]]
}

# Returns the union of the three arrays
proc array_union {array1 array2 array3} {
    upvar 1 $array1 foo $array2 bar $array3 baz
    array unset ::union

    if {![array exists foo]} {
        return -code error "$array1 is not an array"
    }
    if {![array exists bar]} {
        return -code error "$array2 is not an array"
    }
    if {![array exists baz]} {
        return -code error "$array3 is not an array"
    }

    set n 0
    foreach key [array names foo] {
        set ::union($n) $foo($key)
        incr n
    }
    foreach key [array names bar] {
        if {![array_has [array get ::union] $bar($key)]} {
            set ::union($n) $bar($key)
            incr n
        }
    }
    foreach key [array names baz] {
        if {![array_has [array get ::union] $baz($key)]} {
            set ::union($n) $baz($key)
            incr n
        }
    }
}

# Returns all the elements in array_new not in array_old
proc array_diff {array_new array_old} {
    upvar 1 $array_new anew $array_old aold
    array unset ::diff

    if {![array exists anew]} {
        return -code error "$array_new is not an array"
    }
    if {![array exists aold]} {
        return -code error "$array_old is not an array"
    }

    if {[array size anew] == 0} {
        verbose -log "array_diff $array_new size = 0"
    }
    foreach {index value} [array get anew] {
	verbose -log "array_diff $array_new index: $index, value: $value" 
    }
    if {[array size aold] == 0} {
        verbose -log "array_diff $array_old size = 0"
    }
    foreach {index value} [array get aold] {
	verbose -log "array_diff $array_old index: $index, value: $value" 
    }

    set n 0
    foreach key [array names anew] {
        if {![array_has [array get aold] $anew($key)]} {
            set ::diff($n) $anew($key)
            incr n
        }
    }
}

# Test the --list-servers option and return an array of the servers found.
proc list_servers { TEST_NAME SERVER_SPEC args } {
    global systemtap_http_server_spec
    set failed 0
    set n 0
    array unset ::servers
    set cmd [concat stap --list-servers=$SERVER_SPEC $args --use-http-server=$systemtap_http_server_spec]
    send_log "executing: $cmd\n"
    eval spawn $cmd
    expect {
	-timeout 150
	-re "^Systemtap Compile Server Status for '${SERVER_SPEC}'\r\n" {
	    exp_continue
	}
	-re {^No servers found\r\n} {
	}
	-re {^ host=[^\r]*\r\n} {
	    set ::servers($n) "$expect_out(0,string)"
	    incr n
	    exp_continue
	}
	-re {^No certificate found in database [^\r]*\r\n} {
	    exp_continue
	}
	-re {^[^\r]*\r\n} {
	    verbose -log "unexpected output: $expect_out(0,string)"
	    set failed 1
	}
	timeout { 
	    verbose -log "timeout"
	    kill -INT -[exp_pid] 2
	    set failed 1
	}
    }
    catch {close}; catch {wait}

    if {$failed != 0} {
	fail "$TEST_NAME"
    } else {
	pass "$TEST_NAME"
    }
}

# Test the --list-servers option and return an array of the servers
# found in the custom namespace.
proc ns_list_servers { TEST_NAME SERVER_SPEC args } {
    global systemtap_http_server_spec
    set failed 0
    set n 0
    array unset ::servers
    set cmd "stap --list-servers=$SERVER_SPEC $args --use-http-server=$systemtap_http_server_spec"
    send_log "executing: $cmd\n"
    lassign [server_ns_as_root $cmd] rc output
    if {$rc == 0} {
	# The command succeeded. Search the output for the servers.
	foreach line [split $output \n] {
	    if {[regexp {^ host=(.*)$} $line match server]} {
		set ::servers($n) $server
		incr n
	    } elseif {[regexp "^Systemtap Compile Server Status for '${SERVER_SPEC}'$" $line]
		       || [regexp {^No servers found$} $line]
		       || [regexp {^No certificate found in database .*$} $line]} {
		# Ignore this output
	    } else {
		verbose -log "unexpected output: $line"
	    }
	}
	pass "$TEST_NAME"
    } else {
	fail "$TEST_NAME"
    }
}

array set existing_online_servers {}
array set existing_trusted_servers {}
# array set existing_signing_servers {}

# Now start our own server and make sure we can work with it.
if {[http_start_server] == 0} {
    pass "$test server start"
} else {
    untested "$test server start"
    return
}

# There may be existing trusted signers. Keep track of them.
list_servers "List existing signing servers" signer
array unset existing_signing_servers
array set existing_signing_servers [array get servers]

# Our server should now appear online, separate from the previously
# discovered online servers. Note that our server could generate
# serveral listings because it could appear at more than one ip
# address,
list_servers "List current online servers" online
array unset current_online_servers
array set current_online_servers [array get servers]

# array_diff will give us all the servers in current not in existing
array_diff current_online_servers existing_online_servers
array unset new_online_servers
array set new_online_servers [array get diff]

set test "New online servers"
if {[array size new_online_servers] > 0} {
    pass "$test"
} else {
    fail "$test"
}

# Our server should now be trusted, separate from the previously
# discovered trusted servers.
list_servers "List current trusted servers" online,trusted
array unset current_trusted_servers
array set current_trusted_servers [array get servers]

# array_diff will give us all the servers in current not in existing
array_diff current_trusted_servers existing_trusted_servers
array unset new_trusted_servers
array set new_trusted_servers [array get diff]

set test "New trusted servers"
if {[array size new_trusted_servers] > 0} {
    pass "$test"
} else {
    fail "$test"
}

# The new servers should automatically be trusted, so the
# new_trusted_servers array should be a subset of the
# new_online_servers array, but not necessarily vice-versa, since new
# servers may have come online independently of our testing.
set test "Verify new trusted server list"
if {[array_in new_trusted_servers new_online_servers]} {
    pass "$test"
} else {
    fail "$test"
}

# The newly trusted servers represent the server we just started.
array unset our_servers
array set our_servers [array get new_trusted_servers]

# The new servers should not be trusted as signers so there should be
# no new signing servers.
list_servers "List current signing servers" signer
array unset current_signing_servers
array set current_signing_servers [array get servers]

set test "No new signing servers"
if {[array_equal current_signing_servers existing_signing_servers]} {
    pass "$test"
} else {
    fail "$test"
}

# Revoke trust in our server. Specify the server by host name.
set test "Server has host name"
if {[array size our_servers] > 0} {
    if {[regexp {^ host=([^ ]*).*} $our_servers(0) match host_name]} {
	pass $test
    } else {
	fail $test
    }
} else {
    fail "$test no servers in list"
}

# Compile and run a simple hello test, selecting the server automatically
set test "Hello from server"
set rc [stap_run_batch $srcdir/systemtap.server/hello.stp --use-http-server=$systemtap_http_server_spec]
if {$rc == 0} { pass $test } else { fail $test }

set cmd [concat stap --trust-servers=ssl,revoke,no-prompt --use-http-server=$systemtap_http_server_spec]
send_log "executing: $cmd\n"
eval spawn $cmd
expect {
    -timeout 150
    -re {^.*\r\n} { exp_continue }
    timeout { 
	kill -INT -[exp_pid] 2
	set failed 1
    }
}
catch {close}; catch {wait}

# Our server should no longer be trusted.
list_servers "List current trusted servers after revocation by host name" trusted
array unset current_trusted_servers
array set current_trusted_servers [array get servers]

set test "No longer trusted after revocation by host name"
if {[array_equal current_trusted_servers existing_trusted_servers]} {
    pass "$test"
} else {
    fail "$test"
}

# Reinstate trust in our server. Specify the server by ip address.
# The default for --trusted servers is 'ssl'.
set test "Server has ip address"
if {[array size our_servers] > 0} {
    if {[regexp {^.*address=([^ ]*).*} $our_servers(0) match ip_address]} {
	pass $test
    } else {
	fail $test
    }
} else {
    fail "$test no servers in list"
}
set cmd [concat stap --trust-servers=no-prompt --use-http-server=$systemtap_http_server_spec]
send_log "executing: $cmd\n"
eval spawn $cmd
expect {
    -timeout 150
    -re {^.*\r\n} { exp_continue }
    timeout { 
	kill -INT -[exp_pid] 2
	set failed 1
    }
}
catch {close}; catch {wait}

# Our server should be trusted again, separate from the previously discovered
# trusted servers.
list_servers "List current trusted servers after reinstatement by ip address" online,trusted
array unset current_trusted_servers
array set current_trusted_servers [array get servers]

array_diff current_trusted_servers existing_trusted_servers
array unset new_trusted_servers
array set new_trusted_servers [array get diff]

set test "New trusted servers after reinstatement by ip address"
if {[array size new_trusted_servers] > 0} {
    pass "$test"
} else {
    fail "$test"
}

# The new_trusted_servers array should now match the our_servers
# array, since the our_servers array is a copy of the original
# new_trusted_servers array.
set test "New trusted servers matches after reinstatement by ip address"
if {[array size our_servers] > 0} {
    if {[array_equal new_trusted_servers our_servers]} {
	pass "$test"
    } else {
	fail "$test"
	verbose -log "new_trusted_servers:"
	foreach {index value} [array get new_trusted_servers] {
	    verbose -log "  index: $index, value: $value" 
	}
	verbose -log "our_servers:"
	foreach {index value} [array get our_servers] {
	    verbose -log "  index: $index, value: $value" 
	}
    }
} else {
	fail "$test no servers in list"
}


# Trust our server as a module signer. This must be done as root and
# in the namespace. Specify the server by certificate serial number.
set test "Server has certificate serial number"
if {[array size our_servers] > 0} {
    if {[regexp {^.*certinfo="([^ ]*)".*} $our_servers(0) match cert_info]} {
	pass $test
    } else {
	fail $test
    }
} else {
    fail "$test no servers in list"
}

# Grab the existing trusted signers in the namespace. There should be
# none (since we're starting with a clean slate).
set test "Namespace existing signer servers"
ns_list_servers "List namespace existing signing servers" signer
array unset ns_existing_signing_servers
array set ns_existing_signing_servers [array get servers]
if {[array size ns_existing_signing_servers] == 0} {
    pass $test
} else {
    fail $test
}

# We only want to change the system's trusted signer list in our mount
# namespace, not in the real system.
set test "Adding trusted signer (in a namespace)"
set cmd "stap --trust-servers=signer,no-prompt --use-http-server=$systemtap_http_server_spec"
lassign [server_ns_as_root $cmd] rc output
if {$rc == 0} {
    pass "$test"
} else {
    fail "$test"
}

# Our server should now be trusted as a signer (but only in our namespace).
ns_list_servers "List namespace current online signing servers" online,signer
array unset ns_current_signing_servers
array set ns_current_signing_servers [array get servers]

# The current list of signing servers in the namespace should have
# changed.
array_diff ns_current_signing_servers ns_existing_signing_servers
array unset ns_new_signing_servers
array set ns_new_signing_servers [array get diff]
set test "New namespace signing servers"
if {[array size ns_new_signing_servers] > 0} {
    pass "$test"
} else {
    fail "$test"
}

# The current list of signing servers shouldn't have changed in the
# real system (since the list of servers in the real system and in the
# namespace should be separate).
list_servers "List current online signing servers" online,signer
array unset current_signing_servers
array set current_signing_servers [array get servers]

array_diff current_signing_servers existing_signing_servers
array unset new_signing_servers
array set new_signing_servers [array get diff]
set test "New signing servers"
if {[array size new_signing_servers] == 0} {
    pass "$test"
} else {
    fail "$test"
}

set test "Server has port number"
if {[array size ns_new_signing_servers] > 0} {
    if {[regexp {^.*port=([^ ]*).*} $ns_new_signing_servers(0) match port_num]} {
	pass $test
    } else {
	fail $test
    }
} else {
    fail "$test no new signing servers"
}

foreach privilege {"--unprivileged" "--privilege=stapusr" "--privilege=stapsys"} {
    # Compile a simple test using an unprivileged setting. This will
    # ask the server to check and sign the module. Specify the server
    # using host name and port.
    #
    # Notice we can use the server directly, and don't need to be in
    # the namespace. We'll need to be in the namespace when trying to
    # run the compiled module.
    set test "Compile module using server with $privilege"
    set failed 1
    set module_name ""
    set cmd [concat stap -p4 $privilege $srcdir/systemtap.server/hello.stp --use-http-server=$systemtap_http_server_spec]
    send_log "executing: $cmd\n"
    eval spawn $cmd
    expect {
	-timeout 150
	-re {^stap_[^ \t\r\n]*\.ko\r\n} {
	    set module_name [string trim "$expect_out(0,string)" \r\n]
	    set failed 0
	    exp_continue
	}
	-re {^.*\r\n} { exp_continue }
	timeout { 
	    kill -INT -[exp_pid] 2
	}
    }
    catch {close}; catch {wait}
    if {$failed == 0} {
	pass "$test"
    }
    send_log "'$module_name'\n"

    # Make sure that the module was returned
    set no_module 0
    set test "Module was created with $privilege"
    catch {exec /bin/ls $module_name $module_name.sgn} result
    send_log "$result\n"
    if {[file exists $module_name]} {
	pass "$test"
    } else {
	fail "$test"
	set no_module 1
    }

    # Make sure that the module was signed
    set no_signature 0
    set test "Module was signed with $privilege"
    if {[file exists $module_name.sgn]} {
	pass "$test"
    } else {
	fail "$test"
	set no_signature 1
    }

    # Make sure we can load the module. This will verify that the
    # signature is correct and trusted.  There must be a module to
    # load.
    #
    # Note that we must do this in the namespace, since only there can
    # the signature be verified.
    #
    # We're going to use the optional 'stapusr' and 'stapsys' users,
    # if they exist.
    set test "Load and run signed module when trusted with $privilege"
    set user ""
    if {$privilege == "--privilege=stapsys"} {
	if {$systemtap_stapsys_user_exists} {
	    set user "stapsys"
	} elseif {$systemtap_stapusr_user_exists} {
	    set user "stapusr"
	    setup_xfail *-*-*
	}
    } else {
	if {$systemtap_stapusr_user_exists} {
	    set user "stapusr"
	}
    }

    if {$no_module == 1 || $no_signature == 1 || [string length $user] == 0} {
	untested "$test"
    } else {
	set failed 1
	set module_path [exec pwd]/$module_name
	set cmd [concat staprun $module_path]
	lassign [server_ns_as_user $user $cmd] rc output
	if {$rc == 0} {
	    foreach line [split $output \n] {
		if {[regexp {^Hello From Server$} $line]} {
		    set failed 0
		}
	    }
	}
	if {$failed == 0} {
	    pass "$test"
	} else {
	    fail "$test"
	}
    }
}

# Revoke trust in our server as a module signer in the namespace.
# Specify the server by certificate serial number to ensure we're
# revoking trust in the correct server.
set test "Revoking trusted signer (in a namespace)"
if {[info exists cert_info]} {
    set cmd [concat stap --trust-servers=revoke,signer,no-prompt --use-http-server=$cert_info]
    lassign [server_ns_as_root $cmd] rc output
    if {$rc == 0} {
	pass "$test"
    } else {
	fail "$test"
    }
} else {
    fail "$test no cert_info"
}

# Our server should no longer be trusted as a signer in the namespace.
ns_list_servers "List namespace current signing servers after revocation " signer
array unset ns_current_signing_servers
array set ns_current_signing_servers [array get servers]

set test "No longer trusted as a signer after revocation"
    foreach {index value} [array get ns_current_signing_servers] {
	verbose -log "ns_current_signing_servers: $index, value: $value" 
    }
    foreach {index value} [array get ns_existing_signing_servers] {
	verbose -log "ns_existing_signing_servers: $index, value: $value" 
    }
if {[array_in ns_current_signing_servers ns_existing_signing_servers]} {
    pass "$test"
} else {
    fail "$test"
}

# Since our server is no longer a trusted signer, attempting to load
# and run the module now should fail (when run as 'stapsys' in the
# namespace). We have to use the 'stapsys' user since the last module
# compiled used "--privilege=stapsys".
set test "Load and run signed module when not trusted"
if {$no_module == 1 || !$systemtap_stapsys_user_exists} {
    untested "$test"
} else {
    setup_xfail *-*-*
    set failed 1
    set module_path [exec pwd]/$module_name
    set cmd [concat staprun $module_path]
    lassign [server_ns_as_user "stapsys" $cmd] rc output
    if {$rc == 0} {
	foreach line [split $output \n] {
	    if {[regexp {^Hello From Server$} $line]} {
		set failed 0
	    }
	}
    }
    if {$failed == 0} {
	pass "$test"
    } else {
	fail "$test"
    }
}

# Shutdown the server we started
http_shutdown_server