-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathnxserver
executable file
·1515 lines (1384 loc) · 46.1 KB
/
nxserver
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/bash
# Free implementation of nxserver components
#
# To use nxserver add the user "nx"
# and use nxserver as default shell.
#
# Also make sure that hostkey based authentification works.
#
# Copyright (c) 2004 by Fabian Franz <[email protected]>.
# (c) 2008-23 by Dmitry Borisov <[email protected]>
#
# License: GNU GPL, version 2
shopt -s extglob
SHARED_CONFS="/usr/share/freenx-server"
. $SHARED_CONFS/nxfuncs
log() {
#args: <logstr>
[ "$NX_LOG_LEVEL" != "0" ] || return
[ -n "$1" ] && \
echo -n "[$(date "+%d.%m %T.%3N"): $$/$BASHPID] " >> "$NX_LOGFILE"
echo -e $@ >> "$NX_LOGFILE"
}
# Log in a way that is secure for passwords / cookies / ...
echo_secure() {
[ -n "$1" ] && \
echo -n "[$(date "+%d.%m %T.%3N")] " >> "$NX_LOGFILE"
local res=${@/password=+([^&])&/password=*&}
res=${res//password=\"+([^\"])\"/password=\"*\"}
echo "$res"
}
log_secure() {
#args: <loglevel> <logstr>
[ "$NX_LOG_LEVEL" != "0" ] || return
echo_secure $@ >> "$NX_LOGFILE"
}
log_tee() {
if [ "$NX_LOG_LEVEL" != "0" ]; then exec tee -a "$NX_LOGFILE"
else exec cat -
fi
}
log_error() {
if [ "$NX_LOG_LEVEL" != "0" ]; then exec tee -a "$NX_LOGFILE"
else exec cat -
fi
}
echo_x() { log "$@"; echo "$@"; }
############### PACKAGE session.bm #######################
#
# Library of session management functions
#
# Needed global vars: NX_SESS_DIR session_count session_count_user
# COMMAND_MD5SUM SESSION_USER_LIMIT SESSION_LIMIT SESSION_HISTORY
# user
# =================== sqlite3 stuff =====================
sqcols_sess="session_id, session, display, status, userip,\
rootless, type, user, screeninfo, geometry, host, shadowcookie,\
startTime, creationTime, endTime, agent_pid"
init_sess_db() {
local sess_cols="session_id TEXT PRIMARY KEY, session TEXT,\
display TEXT, status TEXT, userip TEXT, rootless INT, type TEXT,\
user TEXT, screeninfo TEXT, geometry TEXT, host TEXT,\
shadowcookie TEXT, startTime INT, creationTime INT, endTime INT,\
agent_pid TEXT"
local qstr="CREATE TABLE IF NOT EXISTS sessions.sess($sess_cols);"
qstr+="CREATE INDEX IF NOT EXISTS sessions.idx_status_user ON\
sess(status,user);"
q_dbe "$qstr"
}
#-------------------------------------------------------------------
session_list() {
#params: sessId ["format_times"]
#if you need to humane output put something in $2
local qc fls res r2="";
[ -n "$2" ] && { fls="session_id, session, display, status,\
userip, rootless, type, screeninfo, geometry, host, shadowcookie,\
datetime(startTime,'unixepoch','localtime') AS startTime,\
datetime(creationTime,'unixepoch','localtime') AS creationTime, user,\
datetime(endTime,'unixepoch','localtime') AS endTime";
} || { # original order of fields
fls=$sqcols_sess;
}
res=$(qa_dbe ".mode line sess\n" \
"SELECT $fls FROM sess WHERE session_id='$1' LIMIT 1;")
[ -n "$2" ] && { echo "$res"; return; }
local line k v;
while read line; do
k=$(trim "$(cutfn "$line" 0 '=')"); v=$(trim "$(cutfn "$line" 1 '=')") #"
r2+="$k=$v"$'\n'
done <<< "$res"
echo -n "$r2"
}
session_find_cmdstrs() {
#params: [sessid] [user] [display] [status="Running|Suspended"]
#ret: sessions command strings delimited by \n
local st wstr res;
st="Running|Suspended"; [ -n "$4" ] && st="$4"
wstr="WHERE $(str_eq_cond "status" "$st")"
[ -n "$1" ] && wstr+=" AND $(str_eq_cond "session_id" "$1")"
[ -n "$2" -a "$2" != "all" -a "$2" != ".*" ] && \
wstr+=" AND $(str_eq_cond "user" "$2")"
[ -n "$3" ] && wstr+=" AND $(str_eq_cond "display" "$3")"
res=$(qa_dbe ".mode line sess\n" \
"SELECT $sqcols_sess FROM sess $wstr ORDER BY startTime DESC;")
qtxt2cmdstrs "$res"
}
# Find all running session-filenames
session_find_all() { session_find_cmdstrs; }
# Find all running sessions of a id
session_find_id() { session_find_cmdstrs "$1"; }
# Finds out if a session belongs to a user
session_find_user() { session_find_cmdstrs "" "$1"; }
# Find all running sessions of a display
session_find_display() { session_find_cmdstrs "" "" "$1"; }
# Finds out if a session belongs to a user
session_find_id_user() {
#params: sessid [user]
local wstr c;
wstr="WHERE session_id='$1' AND status IN ('Running', 'Suspended')"
[ -n "$2" -a "$2" != "all" -a "$2" != ".*" ] && \
wstr+=" AND $(str_eq_cond "user" "$2")"
c=$(qa_dbe ".mode tabs sess\n" \
"SELECT count(session_id) FROM sess $wstr LIMIT 1;")
[ "$c" -gt "0" 2>/dev/null ] || return 1
return 0
}
# session_get <session_id>
session_get() { session_find_cmdstrs "$1"; }
# Get the first session, which can be resumed
session_get_user_suspended() {
#params: user status
#ret: session_id line[s] or empty
local wstr r a;
wstr="WHERE status='$2'AND user='$1'"
r=$(qa_dbe ".mode tabs sess\n" \
"SELECT count(session_id), session_id FROM sess $wstr ORDER BY startTime DESC LIMIT 1;")
a=($r); ((${#a[@]}==2)) && echo "${a[1]}"
}
session_count_user() {
#params: user [status]
# Count all sessions of a user
# and save it in session_count and session_count_user
local st wstr r u="" sc=""
session_count=0; session_count_user=0
st="Running|Suspended"; [ -n "$2" ] && st="$2"
wstr="WHERE $(str_eq_cond "status" "$st")"
if [ "$1" = ".*" -o "$1" = "all" ]; then
session_count=$(qa_dbe ".mode tabs sess\n" \
"SELECT count(*) FROM sess $wstr;") #"
session_count_user=$session_count
return 0
fi
r=$(qa_dbe ".mode tabs sess\n" \
"SELECT user,count(*) FROM sess $wstr GROUP BY user;") #"
while read u sc; do
[ -z "$sc" ] && continue
((session_count+=sc))
[ "$u" = "$1" ] && session_count_user=$sc
done <<< "$r"
return 0
}
session_user_acl_load() {
#arg: <user>
# load nx shadow acl for given user and store it to
# shadow_users shadow_oviews shadow_uauths variables
[ -n "$ugroups" ] || \
{ ugroups=$(groups $1); ugroups=$(trim "${ugroups#*:}"); }
local s ucond="'#$1','*${ugroups// /\',\'\*}','@all'"
local mode=".mode csv settings\n.separator '&'\n"
local qstr="SELECT value,val_type,val_depend FROM settings WHERE user IN ($ucond) \
AND key='@shadow@' ORDER BY user ASC, val_check DESC LIMIT 1;"
#log "$FUNCNAME: qstr='$qstr'" #debug
local ts=$(qa_dbe "$mode" "$qstr")
#log "$lp ts='$ts'" #debug
shadow_users=$(cutfn "$ts" 0 '&')
s=$(cutfn "$ts" 1 '&'); shadow_oviews=(${s//,/ })
s=$(cutfn "$ts" 2 '&'); shadow_uauths=(${s//,/ })
}
_session_list_user_suspended() {
# args: <user> <status,> [geometry] <type>
# use conf vars: COMMAND_MD5SUM
# use glob: user
local p pstrs pattern geom depth render udepth urender mode;
local options available session_id geo2 displays disp2;
local puser pshadowcookie pscreeninfo prootless pgeometry pstatus;
local ptype pdisplay psession_id psession
local shadow_all=""
[ "$4" = "shadow" ] && stringinstring ",all," ",$shadow_users," && shadow_all="1"
echo "NX> 127 Sessions list of user '$1' for reconnect:"
echo
if [ -z "$4" ]; then
echo "Display Type Session ID Options Depth Screensize Available Session Name"
echo "------- ---------------- -------------------------------- -------- ----- -------------- --------- ----------------------"
else
echo "Display Type Session ID Options Depth Screen Status Session Name"
echo "------- ---------------- -------------------------------- -------- ----- -------------- ----------- ------------------------------"
fi
pstrs=$(session_find_cmdstrs "" "$1" "" "$2")
while read p; do
[ -z "$p" ] && continue
puser=$(getparam "$p" user); pshadowcookie=$(getparam "$p" shadowcookie);
pscreeninfo=$(getparam "$p" screeninfo); prootless=$(getparam "$p" rootless)
pgeometry=$(getparam "$p" geometry); pstatus=$(getparam "$p" status)
ptype=$(getparam "$p" type); pdisplay=$(getparam "$p" display)
psession_id=$(getparam "$p" session_id); psession=$(getparam "$p" session)
if [ "$4" = "shadow" -a "$puser" != "$1" ]; then
[ "$ENABLE_SESSION_SHADOWING" = "1" ] || continue
[ -n "$NX_ACL_DIR" -a -z "$shadow_all" ] && {
#log "in nxacl present CHECK ,$puser, ; ,$shadow_users," #debug
stringinstring ",$puser," ",$shadow_users," || continue
}
fi
pattern='^([0-9]*x[0-9]*)x([0-9]*)\+?([^+]*)'
[[ $pscreeninfo =~ $pattern ]]
geom=${BASH_REMATCH[1]}; depth=${BASH_REMATCH[2]}; render=${BASH_REMATCH[3]}
[[ $3 =~ $pattern ]]
udepth=${BASH_REMATCH[2]}; urender=${BASH_REMATCH[3]}
mode="D"; [ "$prootless" = "1" ] && mode="-"
options="-"; stringinstring "fullscreen" "$3" && options="F"
[ "$pgeometry" = "fullscreen" ] || options="-"
[ "$urender" = "render" ] && options="${options}R${mode}--PSA"
[ "$urender" = "render" ] || options="${options}-${mode}--PSA"
[ "$udepth" = "$depth" -a "$urender" = "$render" ] && available=$pstatus
# FIXME: HACK !!! to keep compatibility with old snapshot version (Knoppix 3.6 based for example)
if [ -z "$4" -a "$available" != "N/A" ]; then
available="Yes"
fi
if [ "$4" = "shadow" ]; then
available=$pstatus
printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$pdisplay" "$ptype" "$psession_id" "$options" "$depth" "$geom" "$available" "$psession ($puser) (Shadowed)"
else
# only unix-* sessions can be resumed, but other session types can still be terminated
stringinstring "unix-" "$4" || available="N/A"
printf "%-7s %-16s %32s %8s %5s %-14s %-11s %s\n" "$pdisplay" "$ptype" "$psession_id" "$options" "$depth" "$geom" "$available" "$psession"
fi
done <<< "$pstrs"
echo ""
echo ""
session_count_user "$1"
if [ "$session_count" -ge "$SESSION_LIMIT" -o \
"$session_count_user" -ge "$SESSION_USER_LIMIT" ]; then
echo "NX> 147 Server capacity: reached for user: $1"
else
echo "NX> 148 Server capacity: not reached for user: $1"
fi
}
session_list_user_suspended() {
local slcd=$(_session_list_user_suspended "$@")
echo "$slcd" | log_tee
}
session_list_user() {
#params: user [status="Running|Suspended"]
local st wstr
echo -n "NX> 127 Sessions list"
if [ -n "$1" -a "$1" != "all" ]; then echo " of user '$1'"
else echo ":"
fi
echo
echo "Server Display Username Remote IP Session ID"
echo "------ ------- --------------- --------------- --------------------------------"
st="Running|Suspended"; [ -n "$2" ] && st="$2"
wstr="WHERE $(str_eq_cond "status" "$st")"
[ -n "$1" -a "$1" != "all" ] && wstr+=" AND $(str_eq_cond "user" "$1")"
qa_dbe ".mode tabs sess\n SELECT host,display,user,userip,session_id\
FROM sess $wstr ORDER BY startTime DESC;" # ! check order !
}
session_history() {
#params: user session_id
local wstr=""
echo "NX> 127 Session list:"
echo
echo "User Remote IP Status Start Stop "
echo "------- --------------- --------------- -------------- ---------------"
[ -n "$1" -a "$1" != "all" ] && wstr="$(str_eq_cond "user" "$1")"
[ -n "$wstr" -a -n "$2" ] && wstr+=" AND "
[ -n "$2" ] && wstr+=" session_id='$2'"
[ -n "$wstr" ] && wstr="WHERE $wstr"
qa_dbe ".mode tabs sess\n SELECT user,userip,status,strftime('%d.%m-%H:%M:%S',startTime,'unixepoch','localtime'),\
strftime('%d.%m-%H:%M:%S',endTime,'unixepoch','localtime')\
FROM sess $wstr ORDER BY endTime;"
}
# remove all sessions older than $SESSION_HISTORY seconds in failed/closed.
session_cleanup() {
local checkTime st wstr;
[ "$SESSION_HISTORY" -gt "-1" ] || return
let checkTime=$(date +%s)-$SESSION_HISTORY
st="Finished|Failed";
wstr="WHERE $(str_eq_cond "status" "$st") AND startTime < $checkTime"
q_dbe "DELETE FROM sess $wstr;"
}
session_list_all() { session_list_user "all"; }
session_add() {
#params: <col1,col2...> <val1&val2...>
q_row_ins "sess" "$1" "$2"
}
session_change() {
# <session_id> <col1,col2...> <val1&val2...>
q_rows_upd "sess" "session_id='$1'" "$2" "$3"
}
# session_running <session_id> (? now dublicated session_find_id_user ?)
# return: true if running, false if not
session_running() { session_find_id_user "$1"; }
session_close() {
#param: <session_id>
if [ "$SESSION_HISTORY" = "0" ] ; then
q_dbe "DELETE FROM sess WHERE session_id='$1';"
else
session_change "$1" "status,endTime" "Finished&$(date +%s)"
fi
}
session_fail() {
#param: <session_id>
if [ "$SESSION_HISTORY" = "0" ] ; then
q_dbe "DELETE FROM sess WHERE session_id='$1';"
else
session_change "$1" "status,endTime" "Failed&$(date +%s)"
fi
}
#
# end of library
#
msg2agentdisplay() {
#args: <dlg_type> <message> <[host]:display> <cookie> <agent_pid>
# [window_caption] [timeo=$AGENT_STARTUP_TIMEOUT]
# return exit code of nxdialog
local capt="FREENX server"; [ -n "$6" ] && capt=$6
local timeo=$AGENT_STARTUP_TIMEOUT; [ -n "$7" ] && timeo=$7
local rc=0
$COMMAND_XAUTH add "$3" MIT-MAGIC-COOKIE-1 "$4" &>/dev/null
DISPLAY="$3" $PATH_BIN/nxdialog --display "$3" --parent $5 \
--dialog "$1" --caption "$capt" --message "$2" &>/dev/null &
local dlg_pid=$!
if [ $timeo -gt 0 ]; then # make a simple watchdog for nxdialog
( sleep $timeo"s"
kill -0 $dlg_pid &>/dev/null && kill $dlg_pid &>/dev/null; ) &
fi
if stringinstring "$1" "ok|error|panic"; then
sleep 0.5s # don't remove xauthority cookie right now
else # wait for anwers only
wait $dlg_pid
rc=$?
fi
$COMMAND_XAUTH remove "$3" &>/dev/null
return $rc
}
#
# Main nxserver <-> nxclient communication module
#
# config variables in use:
# COMMAND_MD5SUM COMMAND_NETCAT
# COMMAND_SESSREG COMMAND_SSH COMMAND_XAUTH
# ENABLE_AUTORECONNECT
# ENABLE_LOAD_BALANCE_PREFERENCE ENABLE_LOG_FAILED_LOGINS
# ENABLE_SESSION_SHADOWING_AUTHORIZATION
# ENABLE_USESSION LOAD_BALANCE_SERVERS
# NX_ETC_DIR NX_HOME_DIR NX_LICENSE NX_LOGFILE NX_LOG_LEVEL
# NX_SESS_DIR NX_VERSION
# SERVER_NAME SESSION_HISTORY SESSION_LIMIT SESSION_USER_LIMIT
# declare global variables:
declare -g cmd in_params proto login_success login_method ugroups=""
declare -g server_mode preferred_host session_count session_count_user
declare -g user2 ENCRYPTION proxy_display server_host
declare -g oifs rc NODE_HOSTNAME user fl_send="1"
declare -g shadow_users shadow_oviews shadow_uauths shadowviewonly shadowauth
server_mode="0"; [ "$USER" = "nx" ] && server_mode="1"
if [ "$server_mode" = "1" ]; then
# Start!
open_dbe $$
attach_db "$sq_settings_fn" ro || {
echo "NX> 500 Error: Unable to attach db file $sq_settings_fn";
exit_proc 1;
}
set_vars_from_db
sess_bd="$NX_SESS_DIR/sessions.sq3"
attach_db $sess_bd && init_sess_db
log "-- NX SERVER START: $@ - ORIG_COMMAND=$SSH_ORIGINAL_COMMAND"
# Get the hostname out of SSH_ORIGINAL_COMMAND
preferred_host=$(rematchfn "host=([^&]*)" "$SSH_ORIGINAL_COMMAND") #"
echo_x "HELLO NXSERVER - Version $NX_VERSION $NX_LICENSE"
# Login stage
while true; do
echo_x -n "NX> 105 "
read cmd
[ "$cmd" = "" ] && cmd="quit" # FIXME?
echo_x "$cmd"
case "$cmd" in
quit|QUIT)
echo_x "Quit"; echo_x "NX> 999 Bye"; exit_proc 0;
;;
exit|EXIT)
echo_x "Exit"; echo_x "NX> 999 Bye"; exit_proc 0;
;;
bye|BYE)
echo_x "Bye"; echo_x "NX> 999 Bye"; exit_proc 0;
;;
hello*|HELLO*)
proto=$(rematchfn 'Version ([[:digit:][:punct:]]+)' "$cmd") #'
echo_x "NX> 134 Accepted protocol: $proto"
;;
"set auth_mode*"|"SET AUTH_MODE*")
if [ "$cmd" = "set auth_mode password" -o \
"$cmd" = "SET AUTH_MODE PASSWORD" ]; then
echo_x "Set auth_mode: password"
else
echo_x "NX> 500 ERROR: unknown auth mode ''"
fi
;;
login|LOGIN)
login_success="0"
echo_x -n "NX> 101 User: "; read user2; echo_x $user2;
echo_x -n "NX> 102 Password: ";
oifs="$IFS"; export IFS=$'\n';
read -r -s PASS; export IFS=$oifs; echo_x ""
log -n "Info: Auth method: "
# USER already logged in?
user=$user2
# SU based auth used ONLY
if [ "$login_success" = "0" ]; then
log -n "su "
LC_MESSAGES=C $COMMAND_SUDO -u $user -Svk -p "" \
&>/dev/null <<< "$PASS"
if [ $? -eq 0 ]; then login_success="1"; login_method="SU"; fi
fi
if [ "$login_success" = "1" ]; then
# Reread the config files (so that $USER.node.conf get sourced)
set_vars_from_db "" $user "only"
[ "$ENABLE_SESSION_SHADOWING" = "1" ] && session_user_acl_load $user
break
else
echo_x "NX> 404 ERROR: wrong password or login"
echo_x "NX> 999 Bye"
if [ "$ENABLE_LOG_FAILED_LOGINS" = "1" ]; then
logger -t nxserver -i -p auth.info \
"($(whoami)) Failed login for user=$user from IP=$(echo $SSH_CLIENT | awk '{print $1}')"
fi
exit_proc 1
fi
;;
esac
done
echo_x "NX> 103 Welcome to: $SERVER_NAME user: $user"
# remove old session infos from history
session_cleanup
#
# call it with: server_get_params $cmd # no ""!
#
server_get_params() {
shift;
local server_params=" $@"; server_params=${server_params// --/\&};
server_params=${server_params//\"/};
if [ "$server_params" = "" ]; then
echo_x -n "NX> 106 Parameters: "
read server_params2; server_params=${server_params2//%2B/+}
echo_x
fi
server_params=${server_params//%20/ };
server_params=${server_params//\&sessionid/\&session_id};
server_params=${server_params//\&id/\&session_id};
echo "$server_params"
}
server_nxnode_start() {
# use vars: session_id
# NODE_HOSTNAME NXNODE_TOSEND
local cmd="$1" user="$2"; shift; shift;
# Find NODE_HOSTNAME
#NODE_HOSTNAME=""
#CMDLINE="$@"; session_id=$(getparam session_id)
#CMDLINE=$(session_get "$session_id")
NODE_HOSTNAME=$host; [ -z "$NODE_HOSTNAME" ] && NODE_HOSTNAME="127.0.0.1"
export NODE_HOSTNAME
echo -e "$PASS\n$@" | \
$COMMAND_SUDO -u $user -HSik -p "" $PATH_BIN/nxnode "$cmd" 2>&1 | \
log_tee
}
server_add_usession() {
[ "$ENABLE_USESSION" = "1" ] || return
$COMMAND_SESSREG -l ":$display" -h "$userip" -a $user 2>&1 | \
log_error
}
server_remove_usession() {
[ "$ENABLE_USESSION" = "1" ] || return
$COMMAND_SESSREG -l ":$display" -h "$userip" -d $user 2>&1 | \
log_error
}
server_nxnode_echo() {
if [ "$server_channel" = "1" ]; then
echo -e "$@"
log "$FUNCNAME >&$server_channel: $@"
elif [ "$server_channel" = "2" ]; then
echo -e "$@" >&2
log "$FUNCNAME >&$server_channel: $@"
fi
}
server_nxnode_exit_func() {
log "$FUNCNAME: Info: Emergency-Shutting down due to kill signal ..."
session_fail $session_id
server_remove_usession
# remove lock file
[ -e "/tmp/.nX$display-lock" ] && rm -f /tmp/.nX$display-lock
exit_proc 1
}
server_nxserver_exit_func() {
log "$FUNCNAME: Info: Emergency-Shutting down!"
[ -n "$adm_pid" ] && kill -0 $adm_pid &>/dev/null \
&& kill $adm_pid &>/dev/null
exit_proc 1
}
server_nxnode_start_wait() {
# use uplevel: $server_wait_pid $user $session_id
local lp=" $FUNCNAME:";
local server_channel=1 kill_wait_pid=1 shadowcookie agent_pid
local fl_start="" to_send=""
open_dbe $BASHPID
[ "$1" = "--startsession" ] && fl_start="1";
if [ -n "$fl_start" ]; then
server_add_usession
# We need to stop sending things when a SIGPIPE arrives
trap "server_channel=0" SIGPIPE
trap server_nxnode_exit_func EXIT
fi
server_nxnode_start "$@" | while read cmd; do
case "$cmd" in
"NX> 700"*)
server_channel=0; to_send="$cmd"
;;
"NX> 706"*)
shadowcookie=$(cutfn "$cmd" 1 ':'); shadowcookie=${shadowcookie// /}
session_change "$session_id" "shadowcookie" "$shadowcookie"
to_send+="\n$cmd"
;;
"NX> 733"*)
agent_pid=$(cutfn "$cmd" 1 ':'); agent_pid=${agent_pid// /}
session_change "$session_id" "agent_pid" "$agent_pid"
;;
"NX> 70"*)
to_send+="\n$cmd"
;;
"NX> 710"*)
to_send+="\n$cmd"; server_channel=1;
server_nxnode_echo "$to_send";
server_channel=0;
to_send="";
kill -SIGUSR2 $$ 2>/dev/null
continue
;;
"NX> 1006"*|"NX> 1005"*|"NX> 1009"*)
case "$cmd" in
*running*)
[ "$kill_wait_pid" = "1" ] && kill -INT $server_wait_pid 2>/dev/null
kill_wait_pid=0
if [ "$server_channel" = "1" ]; then
server_channel=2
fi
log "$lp set status $session_id: Running"
session_change $session_id "status" "Running"
;;
*starting*)
log "$lp set status $session_id: Starting"
session_change $session_id "status" "Starting"
continue;
;;
*resuming*)
log "$lp set status $session_id: Resuming"
session_change $session_id "status" "Resuming"
continue;
;;
*closed*)
if [ -n "$fl_start" ]; then
log "$lp session_close $session_id"
session_close $session_id; break;
fi
;;
*suspended*)
if [ -n "$fl_start" ]; then
[ "$kill_wait_pid" = "1" ] && kill -INT $server_wait_pid 2>/dev/null
kill_wait_pid=0; log "$lp session suspend $session_id"
session_change $session_id "status,userip" "Suspended&-"
fi
;;
*suspending*)
if [ -n "$fl_start" ]; then
log "$lp set status $session_id: Suspending"
session_change $session_id "status" "Suspending"
# we need to stop sending to client as it will have already
# closed his side of the channel and this will lead to not
# closed sessions.
server_channel=0
fi
;;
*terminating*)
if [ -n "$fl_start" ]; then
log "$lp set status $session_id: Terminating"
session_change $session_id "status" "Terminating"
# we need to stop sending to client as it will have already
# closed his side of the channel and this will lead to not
# closed sessions.
server_channel=0
fi
;;
esac
;;
"NX> 1004"*)
[ "$kill_wait_pid" = "1" ] && {
kill -INT $server_wait_pid 2>/dev/null
kill_wait_pid=0
}
# This fail is correct here as somehow the
# monitor process might have died and we don't
# want the session to be resumed again.
session_fail $session_id
server_nxnode_echo "NX> 596 Session startup failed."
log "NX> 596 Session startup failed."
[ -z "$fl_start" ] && break; # on restore only?
;;
"NX> 1001"*)
if [ -z "$fl_start" ]; then
server_channel=0
log "$lp nxnode finished (1001) on display $display"
close_dbe $BASHPID; exit_proc 0
fi
;;
esac
case $cmd in "NX> "*) server_nxnode_echo $cmd;; esac
done
if [ "$1" = "--startsession" ]; then
trap - EXIT
trap - SIGPIPE
# Close it in case the session is still running
session_running $session_id && session_close $session_id
server_remove_usession
# remove lock file
[ -e "/tmp/.nX$display-lock" ] && rm -f /tmp/.nX$display-lock
fi
close_dbe $BASHPID
log "$lp $1 end on display $display"
}
server_check_session_count() {
session_count_user "$user"
if [ "$session_count" -ge "$SESSION_LIMIT" ]; then
echo_x "NX> 599 Reached the maximum number of concurrent sessions on this server."
echo_x "NX> 500 ERROR: Last operation failed."
return 1
fi
if [ "$session_count_user" -ge "$SESSION_USER_LIMIT" ]; then
echo_x "NX> 599 Server capacity: reached for user: $user"
echo_x "NX> 500 ERROR: Last operation failed."
return 1
fi
return 0
}
server_loadbalance_random() {
# Pick one based on "random" algorithm
local server_lb_hosts=( $LOAD_BALANCE_SERVERS )
local server_lb_nr_of_hosts=${#server_lb_hosts[@]}
let server_lb_nr=(RANDOM % server_lb_nr_of_host)
local server_lb_host=${server_lb_hosts[$server_lb_nr]}
echo $server_lb_host
}
# run in subshell!
server_loadbalance_round_robin() {
local server_lb_hosts=( $LOAD_BALANCE_SERVERS )
local server_lb_nr_of_hosts=${#server_lb_hosts[@]}
# Atomic incrementation:
# Enter critical section
# - Create .lock file
server_lb_lockfile=$(mktemp "$NX_SESS_DIR/round-robin.lock.XXXXXXXXX")
trap "rm -f $server_lb_lockfile" EXIT
i=0
while [ $i -lt 200 ]; do
# ln is an atomic operation
ln $server_lb_lockfile "$NX_SESS_DIR/round-robin.lock" && break
LC_MESSAGES=C sleep 0.01
((i++))
done
if [ $i -ge 200 ]; then
log "Load-Balancing: Round-Robin failed to gain lock file in 200 tries. Falling back to random."
server_loadbalance_random
return
fi
trap "rm -f \"$server_lb_lockfile\" \"$NX_SESS_DIR/round-robin.lock\"" EXIT
# Lock held
server_lb_nr=$(cat $NX_SESS_DIR/round-robin 2>/dev/null)
let server_lb_nr=(server_lb_nr+1)%server_lb_nr_of_hosts
echo $server_lb_nr >$NX_SESS_DIR/round-robin
# Exit critical section
rm -f "$server_lb_lockfile" "$NX_SESS_DIR/round-robin.lock"
trap - EXIT
server_lb_host=${server_lb_hosts[$server_lb_nr]}
echo $server_lb_host
}
server_loadbalance_load() {
local server_lb_max=0 server_lb_host="" server_lb_load;
export PATH_BIN
for i in $LOAD_BALANCE_SERVERS; do
server_lb_load=$($COMMAND_NXCHECKLOAD $i)
[ -z "$server_lb_load" ] && continue
if [ $server_lb_load -gt $server_lb_max ]; then
server_lb_max=$server_lb_load
server_lb_host=$i
fi
done
echo $server_lb_host
}
server_loadbalance() {
local server_host="127.0.0.1"
if [ -n "$LOAD_BALANCE_SERVERS" ]; then
server_host=""
if [ -n "$preferred_host" -a "$ENABLE_LOAD_BALANCE_PREFERENCE" = "1" ]; then
stringinstring " $preferred_host " " $LOAD_BALANCE_SERVERS " && \
server_host="$preferred_host"
fi
# Fallback if still empty
if [ -z "$server_host" ]; then
case "$LOAD_BALANCE_ALGORITHM" in
random)
server_host=$(server_loadbalance_random)
;;
round-robin)
server_host=$(server_loadbalance_round_robin)
;;
load)
server_host=$(server_loadbalance_load)
;;
esac
fi
[ -z "$server_host" ] && server_host="127.0.0.1"
[ -n "$server_host" ] && log "Info: Load-Balancing (if possible) to $server_host ..."
fi
echo "$server_host"
}
server_startrestore_session() {
local action="$1" params="$2" rc sess_lockfile
local agent_display samba_display cups_display media_display restore
local server_pid server_wait_pid new_params db_params nshu="-1" i s
params+="&clientproto=$proto&login_method=$login_method"
echo_x
if [ "$action" = "shadow" ]; then
shadowauth="0"; shadowviewonly="0"
action="start"; params=$(delparam "$params" "display");
params=$(delparam "$params" "session_id");
db_params=$(session_get "$in_session_id" 2>/dev/null)
set_vars_from_ampstr "$db_params" "db_"
encryption=$in_encryption
shadowdisplay=$in_display; shadowhost=$db_host;
shadowuser=$db_user; shadowcookie=$db_shadowcookie
[ "$shadowcookie" = "none" ] && shadowcookie=""
[ "$in_shadowviewonly" = "1" ] && shadowviewonly="1"
if [ -z "$shadowdisplay" ]; then
echo_x "NX> 596 Could not find shadowed session $session_id. Session failed."
exit_proc 1
fi
[ "$shadowhost" = "127.0.0.1" ] && shadowhost=""
if [ "$user" != "$shadowuser" -a \
"$ENABLE_SESSION_SHADOWING_AUTHORIZATION" = "1" ]; then
shadowauth="1"
[ -n "$shadow_users" ] && {
local shu=(${shadow_users//,/ })
for ((i=0; i<${#shu[@]}; i++)) {
[ "${shu[$i]}" = "$shadowuser" -o "${shu[$i]}" = "all" ] && {
nshu=$i; break;
}
}
}
((nshu>=0)) && {
[ "$shadowviewonly" = "0" ] && shadowviewonly=${shadow_oviews[$nshu]}
shadowauth=${shadow_uauths[$nshu]}
}
fi
if [ "$shadowauth" = "1" ]; then
# Ask for permission first:
echo_x "NX> 726 Asking user for authorization to attach to session"
msg2agentdisplay "yesno" \
"Do you want to allow $user to shadow your session?" \
$shadowhost:$shadowdisplay $shadowcookie $db_agent_pid \
"Authorization Request"
if [ "$?" != "1" ]; then
# User answered NO or time out
echo_x "NX> 596 Error: Authorization refused by user: $shadowuser."
exit_proc 1
fi
fi
params+="&shadowdisplay=$shadowdisplay&shadowhost=$shadowhost&\
shadowcookie=$shadowcookie&shadowuser=$shadowuser&\
shadowviewonly=$shadowviewonly&shadowauth=$shadowauth"
fi
[ "$action" = "start" ] && \
[ "$type" = "windows" -o "$type" = "vnc" ] && \
params=${params//&type=$type&/&type=$type'-helper'&}
# If we can't get the userip and SSHD_CHECK_IP is set to 1
# we bail out.
if [ -z "$SSH_CLIENT" -a -z "$SSH2_CLIENT" ]; then
if [ "$SSHD_CHECK_IP" = "1" ]; then
echo_x "NX> 596 Session startup failed. (Missing SSH_CLIENT environment variable)"
return 1
else
log "Warning: Failed to determine the client IP."
log "Warning: The SSH_CLIENT or SSH2_CLIENT variable was not provided by SSHD."
log "Warning: Please set SSHD_CHECK_IP=1 if you want to refuse the connection."
fi
fi
export ENCRYPTION=$encryption
if [ "$ENABLE_FORCE_ENCRYPTION" = "1" -a "$ENCRYPTION" != "1" ]; then
echo_x "NX> 596 Unencrypted sessions are not allowed."
exit_proc 1
fi
# check if there is a suspended session, which we could resume
if [ "$ENABLE_AUTORECONNECT" = "1" -a "$action" = "start" ]; then
restore=$(session_get_user_suspended "$user" "Suspended")
if [ -n "$restore" ]; then
session_id=$restore; action="resume"
fi
fi
# as only $SSH_CLIENT or $SSH2_CLIENT will be set, this should work
userip=$(rematchfn "($ip4_pattern)" "$SSH_CLIENT $SSH2_CLIENT") #"
[ -z "$userip" ] && userip="*"
if [ "$action" = "start" -o "$action" = "shadow" ]; then
server_check_session_count || exit_proc 1
# Possibly do loadbalancing
server_host=$(server_loadbalance)
# start nxnode
display=$DISPLAY_BASE
((sess_display_limit=DISPLAY_BASE+DISPLAY_LIMIT))
# stupid but working algo ...
while true; do
while [ -e /tmp/.X$display-lock -o \
-e "/tmp/.nX$display-lock" -o \
-e "/tmp/.X11-unix/X$display" ]; do
((display++))
done
# Check if there is already an agent running on that display on that host
((agent_display=display+6000))
if port_is_listening $agent_display "$server_host"; then
log "Warning: Stray nxagent without .nX$display-lock found on host:port $server_host:$agent_display."
((display++))
continue
fi
((proxy_display=display+4000))
if port_is_listening $proxy_display "$server_host"; then
log "Warning: nxagent proxy without .nX$display-lock found on host:port $server_host:$agent_display."
((display++))
continue
fi
# Now check for the other enabled services
((samba_display=display+3000))
if [ "$samba" = 1 ] && \
port_is_listening $samba_display "$server_host"; then
log "Warning: Skipping $server_host:$agent_display as samba port is not free."
((display++))
continue
fi
((media_display=display+7000))
if [ "$media" = 1 ] && \
port_is_listening $media_display "$server_host"; then
log "Warning: Skipping $server_host:$agent_display as media port is not free."
((display++))
continue
fi
((cups_display=display+9000))
if [ "$cups" = 1 ] && \
port_is_listening $cups_display "$server_host"; then
log "Warning: Skipping $server_host:$agent_display as cups port is not free."
((display++))
continue
fi
sess_lockfile=$(mktemp "/tmp/.nX$display-lock.XXXXXXXXX")
# ln is an atomic operation
ln "$sess_lockfile" "/tmp/.nX$display-lock" 2>/dev/null && break
done
rm -f "$sess_lockfile"
if [ "$display" -gt "$sess_display_limit" ]; then
echo_x "NX> 596 Error: Display limit exceeded. Please remove some files from /tmp/.X*-lock."
rm -f "/tmp/.nX$display-lock"
exit_proc 1
fi
session_id=$(echo $[$RANDOM*$RANDOM] | $COMMAND_MD5SUM)
session_id=${session_id%% *}; session_id=${session_id^^}
params+="&user=$user&userip=$userip&session_id=$session_id&\
display=$display&host=$server_host"
log_secure "$params"
# now update the session listing
local time_now=$(date +%s)
session_add "session_id,user,session,display,status,userip,rootless,\
type,screeninfo,geometry,host,shadowcookie,startTime,creationTime"\
"$session_id&$user&$session&$display&Running&$userip&$rootless&\
$type&$screeninfo&$geometry&$server_host&none&$time_now&$time_now"
else
[ -z "$(getparam "$params" "session_id")" ] && params+="&session_id=$session_id"
session_change "$session_id" "userip" "$userip"
db_params=$(session_get "$session_id");
display=$(getparam "$db_params" display);
host=$(getparam "$db_params" host); server_host=$host
[ -z "$server_host" ] && server_host="127.0.0.1"
status=$(getparam "$db_params" status)
params+="&host=$server_host&user=$user&userip=$userip&display=$display&status=$status"
if [ "$ENABLE_ADVANCED_SESSION_CONTROL" = "1" ]; then
case "$session" in
"add "*)
server_nxnode_start --applicationsession "$user" "$params"
echo_x "Quit"
echo_x "NX> 999 Quit"
exit_proc 1
;;
esac
fi