[PATCH 0/1] [preview] Add a jami-daemon service.

OpenSubmitted by Maxim Cournoyer.
Details
2 participants
  • Maxim Cournoyer
  • Maxime Devos
Owner
unassigned
Severity
normal
M
M
Maxim Cournoyer wrote on 17 Apr 22:04 +0200
(address . guix-patches@gnu.org)(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)
20210417200414.18050-1-maxim.cournoyer@gmail.com
Hello,
This is an early version of a jami-daemon service that can be used to hostrendezvous points easily on servers, the conferencing feature of Jami.
It seems to work well in practice, but I've been struggling to fix a remainingtest failure with the stop action. It's proving difficult to pinpoint whatthe problem is.
Another thing that will need to be addressed is adding the documentation.
Thanks,
Maxim
Maxim Cournoyer (1): services: Add a service for the Jami daemon.
gnu/local.mk | 6 +- gnu/services/telephony.scm | 283 ++++++++++++++++++- gnu/tests/data/jami-dummy-account.dat | 391 ++++++++++++++++++++++++++ gnu/tests/telephony.scm | 202 +++++++++++++ 4 files changed, 878 insertions(+), 4 deletions(-) create mode 100644 gnu/tests/data/jami-dummy-account.dat create mode 100644 gnu/tests/telephony.scmb
-- 2.31.1
M
M
Maxim Cournoyer wrote on 17 Apr 22:06 +0200
[PATCH 1/1] services: Add a service for the Jami daemon.
(address . 47849@debbugs.gnu.org)(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)
20210417200617.18182-1-maxim.cournoyer@gmail.com
* gnu/services/telephony.scm (serialize-boolean)(serialize-string-list, string-list?): New variables.(maybe-string-list, jami-daemon-configuration): New syntax.(%jami-daemon-accounts): New variable.(jami-daemon-configuration->command-line-arguments): New procedure.(jami-dbus-session-activation): Likewise.(define-with-retries, define-send-dbus)(jami-daemon-shepherd-services, jami-daemon-service-type): New variables.* gnu/tests/data/jami-dummy-account.dat: New file.* gnu/tests/telephony.scm: New file.* gnu/local.mk (GNU_SYSTEM_MODULES): Register the new test file.(dist_patch_DATA): Register the new data file.--- gnu/local.mk | 6 +- gnu/services/telephony.scm | 283 ++++++++++++++++++- gnu/tests/data/jami-dummy-account.dat | 391 ++++++++++++++++++++++++++ gnu/tests/telephony.scm | 202 +++++++++++++ 4 files changed, 878 insertions(+), 4 deletions(-) create mode 100644 gnu/tests/data/jami-dummy-account.dat create mode 100644 gnu/tests/telephony.scm
Toggle diff (953 lines)diff --git a/gnu/local.mk b/gnu/local.mkindex 50b11a8ca2..4a412f5a69 100644--- a/gnu/local.mk+++ b/gnu/local.mk@@ -19,7 +19,7 @@ # Copyright © 2018 Amirouche Boubekki <amirouche@hypermove.net> # Copyright © 2018, 2019, 2020, 2021 Oleg Pykhalov <go.wigust@gmail.com> # Copyright © 2018 Stefan Stefanović <stefanx2ovic@gmail.com>-# Copyright © 2018, 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>+# Copyright © 2018, 2020, 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com> # Copyright © 2019, 2020 Guillaume Le Vaillant <glv@posteo.net> # Copyright © 2019, 2020 John Soo <jsoo1@asu.edu> # Copyright © 2019 Jonathan Brielmaier <jonathan.brielmaier@web.de>@@ -710,6 +710,7 @@ GNU_SYSTEM_MODULES = \ %D%/tests/security-token.scm \ %D%/tests/singularity.scm \ %D%/tests/ssh.scm \+ %D%/tests/telephony.scm \ %D%/tests/version-control.scm \ %D%/tests/virtualization.scm \ %D%/tests/web.scm@@ -1829,7 +1830,8 @@ dist_patch_DATA = \ %D%/packages/patches/ytnef-CVE-2021-3403.patch \ %D%/packages/patches/ytnef-CVE-2021-3404.patch \ %D%/packages/patches/zstd-CVE-2021-24031_CVE-2021-24032.patch \- %D%/packages/patches/zziplib-CVE-2018-16548.patch+ %D%/packages/patches/zziplib-CVE-2018-16548.patch \+ %D%/tests/data/jami-dummy-account.dat MISC_DISTRO_FILES = \ %D%/packages/ld-wrapper.indiff --git a/gnu/services/telephony.scm b/gnu/services/telephony.scmindex e1259cc2df..9e821f7286 100644--- a/gnu/services/telephony.scm+++ b/gnu/services/telephony.scm@@ -1,5 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2017 nee <nee-git@hidamari.blue>+;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com> ;;; ;;; This file is part of GNU Guix. ;;;@@ -17,16 +18,31 @@ ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. (define-module (gnu services telephony)- #:use-module (gnu services)+ #:use-module ((gnu services) #:hide (delete))+ #:use-module (gnu services configuration) #:use-module (gnu services shepherd) #:use-module (gnu system shadow) #:use-module (gnu packages admin)+ #:use-module (gnu packages glib)+ #:use-module (gnu packages jami) #:use-module (gnu packages telephony) #:use-module (guix records)+ #:use-module (guix modules)+ #:use-module (guix packages) #:use-module (guix gexp) #:use-module (srfi srfi-1) #:use-module (ice-9 match)- #:export (murmur-configuration+ #:export (jami-daemon-configuration+ jami-daemon-configuration-jami-daemon+ jami-daemon-configuration-dbus+ jami-daemon-configuration-enable-logging?+ jami-daemon-configuration-debug?+ jami-daemon-configuration-auto-answer?+ jami-daemon-configuration-account-archives++ jami-daemon-service-type++ murmur-configuration make-murmur-configuration murmur-configuration? murmur-configuration-package@@ -74,6 +90,269 @@ murmur-service-type)) ++ +;;;+;;; Jami daemon.+;;;++;;; XXX: These dummy definitions is because there's no way to disable the+;;; serialization code from define-configuration.+(define (serialize-boolean option value) "")+(define (serialize-string-list field-name val) "")++;;; Copied from (gnu services messaging).+(define (string-list? val)+ (and (list? val)+ (and-map (lambda (x)+ (or (computed-file? x) ;XXX: for tests+ (and (string? x) (not (string-index x #\,)))))+ val)))+(define-maybe string-list)++(define-configuration jami-daemon-configuration+ (jami-daemon+ (package libring)+ "The Jami daemon package to use.")+ (dbus+ (package dbus)+ "The D-Bus package to use to start the required D-Bus session.")+ (enable-logging?+ (boolean #true)+ "Whether to enable logging to syslog.")+ (debug?+ (boolean #false)+ "Whether to enable debug level messages.")+ (auto-answer?+ (boolean #false)+ "Whether to force automatic answer to incoming calls.")+ (account-archives+ (maybe-string-list 'disabled)+ "A list of Jami account archive (backup) file names to be (re-)provisioned+every time the Jami daemon service starts. These Jami account backups should+@emp{not} be encrypted and their file should be made readable only to the+@samp{jami} user (i.e., not in the store), to guard against leaking the secret+key material of the Jami accounts they contain. When providing this field,+the account directories under @file{/var/lib/jami/} are recreated every time+the service starts, ensuring a consistent state."))++(define %jami-daemon-accounts+ (list (user-group (name "jami") (system? #t))+ (user-account+ (name "jami")+ (group "jami")+ (system? #t)+ (comment "Jami daemon user")+ (home-directory "/var/lib/jami"))))++(define (jami-daemon-configuration->command-line-arguments config)+ "Derive the command line arguments to used to launch the Jami daemon from+CONFIG, a <jami-daemon-configuration> object."+ (match-record config <jami-daemon-configuration>+ (jami-daemon dbus enable-logging? debug? auto-answer?)+ `(,(file-append jami-daemon "/lib/ring/dring")+ "--persistent" ;stay alive after client quits+ ,@(if enable-logging?+ '() ;logs go to syslog by default+ (list "--console")) ;else stdout/stderr+ ,@(if debug?+ (list "--debug")+ '())+ ,@(if auto-answer?+ (list "--auto-answer")+ '()))))++(define (jami-dbus-session-activation config)+ "Create a directory to hold the Jami D-Bus session socket."+ (with-imported-modules (source-module-closure '((gnu build activation)))+ #~(begin+ (use-modules (gnu build activation))+ (let ((user (getpwnam "jami")))+ (mkdir-p/perms "/var/run/jami" user #o700)))))++;; Local definitions to expand in source form in G-exps.+(define define-with-retries+ '(define-syntax-rule (with-retries n delay body ...)+ "Retry the code in BODY up to N times until it returns #t,+else #f. A delay of DELAY seconds is inserted before each retry."+ (let loop ((attempts 0))+ (if (< attempts n)+ (or (begin+ body ...) ;return #t on success+ (begin+ (sleep delay) ;else wait and retry+ (loop (+ 1 attempts))))+ #f)))) ;maximum number of attempts reached++(define define-send-dbus+ '(define (send-dbus dbus-send interface method . arguments)+ "Print the response and return #t on success, else #f."+ (let* ((command `(,dbus-send+ "--bus=unix:path=/var/run/jami/bus"+ "--print-reply"+ "--dest=cx.ring.Ring"+ "/cx/ring/Ring/ConfigurationManager" ;object path+ ,(string-append interface "." method)+ ,@arguments))+ (pid (fork+exec-command command+ #:user "jami"+ #:group "jami")))+ (zero? (cdr (waitpid pid))))))++(define (jami-daemon-shepherd-services config)+ "Return a <shepherd-service> running the Jami daemon."+ (let* ((jami-daemon (jami-daemon-configuration-jami-daemon config))+ (dbus (jami-daemon-configuration-dbus config))+ (dbus-daemon (file-append dbus "/bin/dbus-daemon"))+ (dbus-send (file-append dbus "/bin/dbus-send"))+ (accounts (jami-daemon-configuration-account-archives config))+ (declarative-mode? (not (eq? 'disabled accounts))))++ (list (shepherd-service+ (documentation "Run a D-Bus session for the Jami daemon.")+ (provision '(jami-daemon-dbus-session))+ ;; The requirement on dbus-system is to ensure other required+ ;; activation for D-Bus, such as a /etc/machine-id file.+ (requirement '(dbus-system syslogd))+ (start+ #~(lambda args+ #$define-with-retries++ (define pid (fork+exec-command+ (list #$dbus-daemon "--session"+ "--address=unix:path=/var/run/jami/bus"+ "--nofork" "--syslog-only" "--nopidfile")+ #:user "jami"+ #:group "jami"+ #:environment-variables+ ;; This is so that the cx.ring.Ring service D-Bus+ ;; definition is found by dbus-send.+ (list (string-append "XDG_DATA_DIRS="+ #$jami-daemon "/share"))))++ ;; XXX: This manual synchronization probably wouldn't be+ ;; needed if we were using a PID file, but providing it via a+ ;; customized config file with the <pidfile> would not+ ;; override the one inherited from the base config of D-Bus.+ (let ((sock (socket PF_UNIX SOCK_STREAM 0)))+ (with-retries 20 1 (catch 'system-error+ (lambda ()+ (connect sock AF_UNIX+ "/var/run/jami/bus")+ (close-port sock)+ #t)+ (lambda args+ #f))))++ pid))+ (stop #~(make-kill-destructor)))++ (shepherd-service+ (documentation "Run the Jami daemon.")+ (provision '(jami-daemon dring))+ (requirement '(jami-daemon-dbus-session))+ (modules `((ice-9 ftw)+ (srfi srfi-1)+ (srfi srfi-26)+ ,@%default-modules))+ (start+ #~(lambda args+ #$define-with-retries+ #$define-send-dbus++ (when #$declarative-mode?+ ;; Clear the Jami configuration and accounts, to enforce the+ ;; declared state.+ (catch #t+ (lambda ()+ (delete-file-recursively "/var/lib/jami/.cache/jami")+ (delete-file-recursively "/var/lib/jami/.config/jami")+ (delete-file-recursively "/var/lib/jami/.local/share/jami")+ (delete-file-recursively "/var/lib/jami/accounts"))+ (lambda args+ #t))+ ;; Copy the Jami accounts from somewhere readable by root to+ ;; a place only the jami user can read.+ (let* ((accounts-dir "/var/lib/jami/accounts/")+ (pwd (getpwnam "jami"))+ (user (passwd:uid pwd))+ (group (passwd:gid pwd)))+ (mkdir-p accounts-dir)+ (chown accounts-dir user group)+ (for-each (lambda (f)+ (let ((dest (string-append accounts-dir+ (basename f))))+ (copy-file f dest)+ (chown dest user group)))+ '#$accounts)))++ ;; Start the daemon.+ (define daemon-pid+ (fork+exec-command+ '#$(jami-daemon-configuration->command-line-arguments config)+ #:user "jami"+ #:group "jami"+ #:environment-variables+ (list (string-append "DBUS_SESSION_BUS_ADDRESS="+ "unix:path=/var/run/jami/bus"))))++ ;; Wait until it's reachable via D-Bus.+ (with-retries 20 1 (send-dbus #$dbus-send+ "org.freedesktop.DBus.Peer"+ "Ping"))++ ;; Provision the accounts.+ (when #$declarative-mode?+ (or (every identity+ (map (lambda (archive)+ (send-dbus+ #$dbus-send+ "cx.ring.Ring.ConfigurationManager"+ "addAccount"+ (string-append+ "dict:string:string:Account.archivePath,"+ archive+ ",Account.type,RING")))+ (map (cut string-append+ "/var/lib/jami/accounts/" <>)+ (scandir "/var/lib/jami/accounts/"+ (lambda (f)+ (not (member f '("." ".."))))))))+ (format (current-error-port)+ "error: failed provisioning the jami accounts")))++ ;; Finally, return the PID of the dring process.+ daemon-pid))+ (stop+ #~(lambda (pid . args)+ (kill pid SIGTERM)+ ;; Wait for the process to exit; this prevents overlapping+ ;; processes when issuing 'herd restart'.+ (waitpid pid)+ #f))))))++(define jami-daemon-service-type+ (service-type+ (name 'jami-daemon)+ (default-value (jami-daemon-configuration))+ (extensions+ (list (service-extension shepherd-root-service-type+ jami-daemon-shepherd-services)+ (service-extension account-service-type+ (const %jami-daemon-accounts))+ (service-extension activation-service-type+ jami-dbus-session-activation)))+ (description "Run the Jami daemon (@command{dring}). This service is+geared toward the use case of hosting Jami rendezvous points over a headless+server. If you use Jami on your local machine, you may prefer to setup a user+Shepherd service for it instead; this way, the daemon will be shared via your+normal user D-Bus session bus.")))++ +;;;+;;; Murmur.+;;;+ ;; https://github.com/mumble-voip/mumble/blob/master/scripts/murmur.ini (define-record-type* <murmur-configuration> murmur-configurationdiff --git a/gnu/tests/data/jami-dummy-account.dat b/gnu/tests/data/jami-dummy-account.datnew file mode 100644index 0000000000..026890052d--- /dev/null+++ b/gnu/tests/data/jami-dummy-account.dat@@ -0,0 +1,391 @@+;;; JSON extracted from an actual Jami account and processed with+;;; Emacs/guile-json.+(define %jami-account-content-sexp+ '(("RINGCAKEY" . "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRd0lCQURBTkJna3F\+oa2lHOXcwQkFRRUZBQVNDQ1Mwd2dna3BBZ0VBQW9JQ0FRQzBxWUozSkYvTzhQRGEKRnUwRnpRcHBCaD\+gybGJMdURrNTlVU0I0MUJSaS9kdDZGV1BRN29YOVpsY25vNGZzM2dmUHQ0dU1hRVBkVFBGKwowbGN2Q\+jc2cytQTEFlcjlOZGpVQzQ2ZXp0UnNiNE9aQXc4ZUk1M3EwSU04QWJFd0o0ZjllLzBmQUFueHgrK3Qw\+CmZDeGV1YTBUaVBqVHBpZVJMNmpwQkd5UGI2Qk5pU2ViTkZCNzJOMTBnbzI4REVQYlhkNE9CNkN1blZ\+5RGZKSU0KOC9PRy8rMndNamI4WkRwT3JrYy93U2ZHbnQyZXA3U0xKSkgwOFIzV1FKWklsSndrcGdLTH\+FRakVwWFNpclN4dAozSkdtdXdBdE9LaXFFTXh1R043elV3ZlNINEtHdkRaUFNkZklZVXJ3eEp4aDJZZ\+3lobG5RRC9SSnhRN3d4YlJBCjFhMUZVV0FzbDhLODk5cEtESk1GL09VOWZMRUx6QlViblpaRDRmSlg1\+NmcyTlluUnJobS94NG9FbFk0MFNYMUUKcHYzb01hNnZrVGN5RjJnUFhKL2FkUVJoS0dFaGRjaHBpeDl\+5UVphaDFCUFBGYW5jNzBMcjhOaDZJeHFNQ1hiMQozMG9vWHpWZmZNMVFOd28rL3hzRnBlRkRqUTAxQ0\+9pdWZocitKREcyc0txb0o0V0JwYVhubWI1YXVrVWUvV1RKCjAxVmRyaEkvSVExd3V4QzNMMnpac3dVU\+1NTaDk0aXg1M0hpU3pWbkI5UkxmaVhZUUVCcFEyNHVoRTdiYlo0bm0KZTczZC9zenpPTXMzYUt3OWtW\+a2VLMTVtYWhSVWZjdEdhSVQxTkhGWUNYYXByaWExakdNdVpmSk1pSUtZUzNidQpMbUhZckF6dEptNDZ\+0aHpjdnN3NHlhMnFoa2xUUlFJREFRQUJBb0lDQVFDaHZaUm85KzZ5aFhFTHZ6U3FXZHcxCkZGOERibG\+hIMmhVWkNuV0kxMDM5SmdyRkxMczFSU1krSzg1aFZYMk9hV1VTNk44TmNCYzUyL1hrdFltS09HUFQKM\+WZqMnE2M3pPcDNSSFdGNWVPMXhNeExRN3JZSDhqMGZZTFFTUytKemdwb3ZRVnJLSXkrb21JSSt3aUN6\+R1laRApGQUM0ODJzL0J5MHdtRjVjdC9JTEdIeVY3ZXNVUlo1Vi9iL0ltQzUwQ1lDUWpQR2xBb3JkeUx\+1MHp2NjZZUXc2CkQycTg0VHAyVUg3SExEVmhFNytUbDg4Q04xWll0VGtpSkthbkNpMFVmbStPKzJFM0\+5HM01hajk1aDl2NktqYkoKUlkxeTNDRTVmQmkyUFNLbVVzRjN3SzdhbzJDRks5MTgybmlxL2FaNm5WO\+Xc3NmVrRjhEOWUvS1pqUE5ZT0xkaApFczBSL2laV3RpbUx2RHdXQWNWNFNnSFFjNXJvNU9yOEFUS1ZK\+VmlzZGFuWXkvdUhmVXZWN3U5cDVLK2E4SHU2CllabW13ZTh4bnF5M3V2M0VabE9LY20zTnZvWllVMnJ\+HUUFQQW1sWWQ4WlRsZGxPa1JCSGxxYzllMmJuSnNTQW8KNUhhS0N3aDJsWmZpalVGNXFrMXNQcm1kN3\+BlMld2VVV3QmVuSjJnS1ZoTE5VVGtHWmtTWGhzNlV3WWRRMjVtRQppQzl6WjhXNkQ2OXBvb0lsTTVXT\+01ySEs0Rng1ck9vT05kUHQ4NEk1bTI3cnpnbXM4QnJXVUlGLytZZjJ0bGdmClBIR0V4c3ZCK3JRQk52\+WHU3dXoxcVdFTlJTL2YwR2E3dVF4ZW5sZ2dubHc5M1pNOW1GWXpXb1RpdWFmdnphTnAKWEsrTEVrV2F\+RYUs1Q0VaNEhmUlhBUUtDQVFFQTUyK240OUxQODlyQkR2bFdsTkxNanJqTDdSb0xyQ3FVZGpQWQpyT1\+hZS3ZkTkxyS2NTc1hNdkY4YW5HQng1UG5oVDZGY05ic2dzQ3BUUXowMThZYmcrbUUxQno5ZTdFNTJGZ\+i9NCk9BbWZqSllXUnZueUtiNnB3SGlvOHlXUXlVVk1zZU1CcmpvWk1kNkpPZEZ0K2JITHBWOS9iSkdR\+a3NTRE04WTkKbWxGQUlUL0gyNTh1K1ZKTWsrT0prU28zZmJQSk5Ja1Q2WVBKVmNaSnZTRGI5QU83WDF\+lendCOXVRL0FEblZ6YwpSQkJOUVZaTStZS2ZNWFJBdmFuWnlmWFFwaUxCQW8rRVRPSHJCR1dDRUhtSF\+RCaEZIMTkyamtxNlcrTStvS0R1Cm1xMitMc2hZWTVFc2NpL2hPOVZjK2FCM0hhaGliME03M092MHFNc\+WZoTncyU1BncHdRS0NBUUVBeDlaR1gxQnQKL3MwdGtNcGV1QWhlWjFqTklnZmFEY0RRTWlmU0k4QjRx\+WUhiL1hOREQ5NjFQME9zMDdCN2wzNE1iZ3U2QlNwcwpXdSt0Y1hjSFlqQVJUc0Qzd0pSaVRIb0RSQzR\+YYkxEa2pHRUVCVzRKbFVqZTA1QWZrU0QrdkZSMkJwZStxQlBLCm5yb3Mwd1BWL3RXa3MzY2VFOUlBTV\+pWWDhQeFA3RVNXbitVZDJEWkZhcVFLb0JybHZXRXhxelpYUEJSVjhoS3QKcFBqWnFkZXFQLzhUZTBtS\+zh5MEVreXVXOWhFdGZ2Sm5HWXhMNStrSG9xd2hQVk1tODZ5YlZNVHRQYWJTdCtPUwp0WHhJTE9RMWRN\+QkFabzRxSnNkUUZJcTJnSHA5WFYwa2ZNUms1ajdJT1Q4c2Z6TlpKVkRNK1k5VHVlSGJXSnduCnZsWld\+VZ2NVZTlBaWhRS0NBUUVBbFJaK2h1ckUvNGdLR2dWUld5bTRrTEJHM2dTTFJHdGhuQXVtSnlzaFoveE\+wKZ2l1Wk55bll5L2hRQWpDMjdoUnlxb04rRFRid3hjdGVPOUJ3c1poNzBZOVJROHYwOERGVExMVE43O\+E56UG5OcApBbXY5TGhzZTYxaFBMZU1qTkNVcVZPV3hyWFRMeWk1YkpCM2Z4SnhlWGJmNU5BMUpudUpz\+eXF1SC82TWJ0cytKCmhkY3p3WFRjMCtBZVBKOS9nOENQZXdKYkMzRFVBQ2R1VlNHWHo4ZWZxcm1xbDd\+jbnB5ZzBpK2pJRkNpVU8rVEcKVFcxeDg3KzUvUFF2MGtSQ0Z1UUloZ2ZCNkcwWW9vcHBrUWRZdXhKZl\+pPaHdUUldpbTVMMlF5K294WWZySGVQOQozSlltbGFCMmJiN3kxL1FoQjcvek9VMk1nTEtYdHl4Z09ve\+EpoQlFwZ1FLQ0FRQkIwSUE4dy9CMkNuMEhRcDhQClhUSTZOelRZRUYzd1NhQkg1SFdBOE5MTWdNaERJ\+TUxsWnlPcVFrK1pLSGFMM2llWjFxTGRNS3VmQjNESC9idWcKeXRQb2JBVXNsN0lJSGVjVmZWaVpvMml\+pRXhHUCtEMlB2UUFtRFVGWU90V3FrT2FPSlV2VmJ5ODhOM1NyeW9lZgo5aHpZUGxMWmxFQWNGR055S3\+FibjJXOENHaU5LSWhXYW1Zd21UclY3T1pkeUcrTi9GZk40Vms1NkZyc1pCTDQ5CmRYU2xGZ045TTBaZ\+WNleTEvZEpPRE9lSHNuME5VK0gvNFZEUk1hR1NmelpwSkxJOXE4T2FiSWpVM0ttb24wQTcKdzFWeWNU\+L1FwYlBxRUFVckt5dytvMzV3MlAyaUZ1czZiMlBvUUxFTGFTRVl6K3R6UEw5UTM1ejNRdGdMQytuagp\+IUmxCQW9JQkFES0Q4NGhrYkphczlIQ00zanNNSU9kb213ZEMvcktxVUxKNHFWZU5QR0xDY2VpNEdocn\+dlQnNICnNoN0hibFlZSDN2U2Y3RW1iZEVCb2xlWVJsaUx5ZzJDQU1ZOGpNK0lpUEwydk1yM2Y3SzZPT\+mU0UkVPUFJSZkcKWlJDcTh4a0ZPQlg1SUJUbkVCV3QzdVdyb1NGY2x4RTdUa2I4VkUyVzExTG9ZNlkw\+TUNPaHdxN21xYXRUVnNrawpTRDNySmkrTFR6a2Y4OEx1bjZZNjdiaFNOTWpKZkFaUXNQc0FTRkJBUTJ\+rQnE5alRLZGVuaU4yYTJIbm0xNCtrCnJDeU9ZVE14Q2hQbWNpS25pVy9MWnFUL0U1dlNRUGdBVzc0dT\+VLazJoSjRBajNjRW9NVEwxSytZbStWYWh2U0cKTi8xOFdYQ1JRQkg1d0p2eXJYczBtT29GQlRnTWg4d\+z0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=")+ ("ringAccountKey" . "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRUUlCQURBTk\+Jna3Foa2lHOXcwQkFRRUZBQVNDQ1Nzd2dna25BZ0VBQW9JQ0FRRDNCdDRnOUVUdk9EVnYKM3hWV0ZlS\+1Nnbk5uVEF3S2dYa3IrQ1FhdU8vTGZWM01RenNSNHliL3hoaWhhb1Z2c2FtZ3ZRU1haL0M1R3I5QQpE\+YlAxbHNHclRCK1pXMC9uMXVEb3hmVWdZRWY3SGtVanJtdVFjUGlFWGlUVkNiY002U0NzdVNrMnRxdE1\+BNjBNClRacVo2LzAvbkEvblR5TnFNaUJNSmFRN0NUb2xOTTh2Z2tvd0tBRW14OGpJUG9YZEttMnd6MD\+Z6SnhwU1d4WjUKc0FBTkdSSHU0b2ZXNWJiMERvamtnRzFBYUJ6Nm9uSmdKK1JFSWV1UkpNQWFHYmtzW\+TQ4Z1Z1b21BVTU0UFNvUApFb3psVGdHd1k0cnhJTGE0V1Y0RVZMVExrR240ZTYrRUYrSjIvRURBUFdN\+bXREdFpUeURCc2t5bStLaDRUT2xPCjdnN3JlUUhLdDd1R21YU0RnbzVKZ2hOOHNVRFUxL1Z2YmFFcUZ\+tTDJrZWNGOVlVZmNsUWRGY1ZncmIrMkh5R3AKRVc0b3RkZjlYYzhOMWxrbGk1dFBqRGZuQ1U3OHB6QT\+BxQmV1SWhZTnF1VjhGSm1NemhXeVFDbE1MUEFYbXFVdwpWYlY0MWduM0NkektuUlVhZXFONXlzOG5KQ\+mRJNDBleWlYRUlvU0VKcFpyT1Z5ck1icnNCQzltaVFpZUFhSnlBClFvcjFaRGlwMkpZNUFza2phUGQ0\+NGk0MGNkeFpob1RhNnpzako5UjZScllFYjhWTmZQemFLaElwSXJSd2NpbCsKWjFrbUUwSE1kY21ITXR\+hbWZhK0l6WUR4dDV6OGVOZys1RGpVVG1MRkdyQUVWRW5hNDcxdllxYnk5UCs1T1cvNQpSdkxtUER2Tj\+dlUURpTlZENlBYRFk5OTY4bTZaaHdJREFRQUJBb0lDQURjOEkrTCtlNE41OEFqcHV0MmEyeVNqCllxY\+VFUSWowMW1GTWhOWXMwQUdTTUswQncyMkdleXZwNFl3R1EzdnNIOSsvSkEydXdoYkJzazNpUW9FQmlx\+Q0EKenZmOWdPcDRFNlk0elV6RitwSmQvRnUwSG4wWHBab0Rhdnp2eFN4djNFeUN3b0puYWZuL1FHeGw\+xZEhoQUtsKwpmZGZjekRCc3NPZ1Y2cGtBd1MyY2wwOHFOT2g3cVhaQWFkYk1sQ1lWM0owU1hhaVZiNz\+lHZXNvTzNwUVBMUUZiClNjQjFjT2sxYnNxWkpOU244d0xmMis5QVBEdzMwWEtNNHg5eTdRTE42Q3oxQ\+WpvcFJLQ0NIS3R1SEc4UmVETTIKcnRTbjJmTnltQ0VqeDZGVTB6MHFldDV3Y01UbU5weEZuYXdEMU5s\+dFpnZXBsSllwTjVKZXNEUmo2cFlnWXBPKwo5UDU2cEdtZVNTVzVxcHRoWFFLQmFsdy8wWXp2YUlYdHp\+hTnZyNUJzRFNrWkU3cldzODIvQmFzUE1RckFmOGpLClZFMU9pSzcrVllUVTRFVWVoZ0FZOXdzNjFqYk\+lSSEpQbW9VQXpkWHcyL0d0Vk9JUTFwUFJnOXNYN0JaMmUyV1YKdmd5aThPUEJxRWtwblBiMkU5Z0d1U\+25rY01OeWFVbUl0c3pFandadW14dnZrUW5HanlTb3pjY2R3dnNQNnBJagpoN0g5VUNQTHdOM0N5N1lp\+UmliSlZBWlNjZkF6QzhubXNLODQrTzJUZHBzTXk0SEZDMmM4dlZiclpteTVkWC9qCk1ESnBzS25JWlZ\+JMmpXSzRpRS82aUdIWVdoY3JvWnYvVEJ0YW1SQUxTWDVOYkhhWTI0bXVRSG5yMldtaytld3EKbHRGbC\+90bXgyVkpWUitMZ0JCUGhBb0lCQVFENEI0MjQyVTgvbkJ4d2RzelhCdWxBOVFTa0I5Zktud0RlRkV3S\+wp3Nks0eU14YVdRU04ycjRxRDhLcW93OVZVMzRYdkRWbFh0RUlDaVh3Q0hZdW5IL3g3cXNSdEVzbHJM\+aWg4UHRPClpDSU8zUml4RmlIQXFlQUh2YXF0NVhXdndaREx6WnV2THRJOTdINkZ1QjYxck5qMnhxdlR\+IN05pUmp2S0R0WXgKR1VtNURoQ094cm9tR0NkWHRnWHJGaS9WRU1TSmpQMkM3OTNrN1pTNmNZL0Nkc1\+RqWEsrZ1UzeWM3OC9kN1pYbwpKMGg2WFlSdmhlQW9Bd2dkVTl4MWtYL2ZmY2tZK1hUcFRwV2xXWmtlU\+ytsejBpQkRnUlJzMm45OFZDeTZDRmRZCldsZXZaZy9SWXZ6dzlKdWFVcXArOHpHbHNXR2xuOEhtZW5X\+Q1luSHJnNFBxQnRkL0FvSUJBUUQrOXhDL1N5ZGIKVWZxMUJHYy95YktZc3RLb3A1azB0d3M0SlUwTzN\+aT1U3MlZ5YTdLT2lTemdPSzVnL2QzckhMMXR2dHViTDBWNAo1dEF4a1AzSkVYbmxZZU5IVkpROTc2RH\+NGS3Ztc0FGL2JJdVBsdFBRT1dyM3g1eW1RU3lCOTBUczV0dFdWMTVQCktYYVNnMTZidDhwNS9MeERkZ\+ng2c1YzN1o5RDFRd21EQllreVFIcWQ4clljTm9ad1M5ZnI3UTZhN1ZNSDVtT3IKbEF5dzBCYVdZQk9k\+bjFGd0pVV3NlRlpmTy9vNUVqZk9Hd0xMR1hiOEVmQ1hqdlRYcUNHLzNrT1JvN1NkOWY1eQowVjIxMmt\+YVVNINHNDbFB0SmwzeHpaMWJxQ2RMVDNITWNLUTk5UGFFVFppQnNXQ1lOcXg1c0Q0RGhoMzdZQ2hKCm\+hlN3VUM1E0MElINUFvSUJBREN1VXR1b0UweloyQjhld2grbUpKdnlPMEh5cENFSnlrTE1Xd3gxejNkV\+E9nQzEKbmhZMWk4TjNxbTZSYUk0SHdDVHFkTlI3b3ExZ1NJZnZNVHIrem9IdXBUYnBXeUorM3hJeDJU\+Rk9wL3lnMnByUApURHFqWE94SUJycnc0WU5vaTRIa3poeTVKTnl3a1RpdnBaOWsySVMvQTdTQmNWVGx\+raENianVDK0pPRWthSTJOClpiWGFZY1p1WElVQ3FzcTM2c3RRbCtWZUxRQWt2VjlHc0wrclRnT09Dbz\+UrTkdRZEVZQnVoRkMzZlJzL1JhSVoKOWFBRTBFL3BTTWp1a05tTnQ2Mm1NSk1tTUdydXhnWFRRblBRR\+npNSW43aXB2Z0hxQjRsUDM4emdsbnMvbmZVcgo1NWRuZXk3ejhMRFFETHVIc0RHd3hINzNKQjgrTVR2\+WGFVbkNwQU1DZ2dFQVNBSGxBL0dvdXR6TFRvWmcxcDRUClI1YnhjZHBycFh5d3VYbW5hclJmY3VldG9\+nUVNtTGpiS0xRNVk0RXZSTENJTzA5MDNENGNnOG5FTU10L01XTXoKSnZwZll3emJGU2J4THR1anRQSX\+VhaHR3eXV2UkJIVEM1aG5FL3h0WEE1bWZLTDBHWXpzbmtubm1WL2lzSnBSZwpwZFVnSW5sWEJodkRyR\+FlreUsvWEp0N1FZWlhlUzI5NXlUd0krZndoamlzVVBlTWEyUmRUUE9rQ01JbUVaNUhZCjJHSmZjS25H\+SkxDVHpDKzNPcGtQazdFRE4vTUlMS2F3YVUxaGp1cVlKWVVUVmpXQzFEM2VUL1ViWHptM0VQNHMKVEN\+uYWpCYVMzN0N2YVd4ek5JektXZS9TSXdGbEFmYWNSTHlneUR4Z3Q3bHp1akVObEtvU2xya3h3ckpEND\+Z2WAptUUtDQVFBcTVQWWxSQjgvNnFiWWt1OTA0NUZRdXk2QWtlYXBaMW0raS9SQzZtbFRvUXB6NDlPU\+Gs4ZGx5YjVtCndvSVhpaEo2V05jN1RsWlRYMnpQTTRBS0M5VFNBUWJrWDg5bjEyU2VDSUlHbXVINnk0\+TjZiY2lxZjVVcSsvc0IKcHJKeFRNYlRSUFpqS0VVd1N0SFg1MUQ1bi9sQnZERGY3Y2VEZytsYlE0RjR\+KMTlPd09oZ1lGcjFheGQvNXd2VgpURjNoVlQwbFZGN2RyRC9iMHZOcmxnbUNjbEk4UDg1a2dkRUhZbG\+ZtTFoxeXJIMkNXVy9SS0lsWk9ZdFVuNFNpCkp5a2VlNDROWElXU3ovalRBdFRta3VQTzRvUjF5d3dRc\+jdhUTF5a3hRVm9rVm5vY2xqU0tyQlk4R294a0I0eDIKUDNrb3F1UnkvcUd3QzBnN1o4ZjBTQjNQZVZt\+eQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==")+ ("ringAccountCert" . "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZXakNDQTBLZ\+0F3SUJBZ0lJRm1tNmZuaWRndEl3RFFZSktvWklodmNOQVFFTUJRQXdUREVRTUE0R0ExVUUKQXhNSFNt\+RnRhU0JEUVRFNE1EWUdDZ21TSm9tVDhpeGtBUUVUS0RjNVpqSTJaVFZpWTJNeU9EWXlPREppT0dFMwp\+PRFF6TUdOak1EWXpNakV4T1RFNFkyWm1PVGd3SGhjTk1qRXdOREUyTVRjek1qRXdXaGNOTXpFd05ERT\+BNVGN6Ck1qRXdXakJKTVEwd0N3WURWUVFERXdSS1lXMXBNVGd3TmdZS0NaSW1pWlB5TEdRQkFSTW9aa\+k16TkRWbU1qYzMKTldSa1ptVXdOMkUwWWpCa09UVmtZV1ZoTVRFeFpERTFabUpqTVRFNU9UQ0NBaUl3\+RFFZSktvWklodmNOQVFFQgpCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFQY0czaUQwUk84NE5XL2ZGVllWNHB\+LQ2MyZE1EQXFCZVN2NEpCcTQ3OHQ5ClhjeERPeEhqSnYvR0dLRnFoVyt4cWFDOUJKZG44TGthdjBBTn\+MvV1d3YXRNSDVsYlQrZlc0T2pGOVNCZ1Ivc2UKUlNPdWE1QncrSVJlSk5VSnR3enBJS3k1S1RhMnEwd\+0RyUXhObXBuci9UK2NEK2RQSTJveUlFd2xwRHNKT2lVMAp6eStDU2pBb0FTYkh5TWcraGQwcWJiRFBU\+ck1uR2xKYkZubXdBQTBaRWU3aWg5Ymx0dlFPaU9TQWJVQm9IUHFpCmNtQW41RVFoNjVFa3dCb1p1U3h\+qanlCVzZpWUJUbmc5S2c4U2pPVk9BYkJqaXZFZ3RyaFpYZ1JVdE11UWFmaDcKcjRRWDRuYjhRTUE5WX\+lhME8xbFBJTUd5VEtiNHFIaE02VTd1RHV0NUFjcTN1NGFaZElPQ2prbUNFM3l4UU5UWAo5Vzl0b1NvV\+1l2YVI1d1gxaFI5eVZCMFZ4V0N0djdZZklha1JiaWkxMS8xZHp3M1dXU1dMbTArTU4rY0pUdnluCk1E\+U29GNjRpRmcycTVYd1VtWXpPRmJKQUtVd3M4QmVhcFRCVnRYaldDZmNKM01xZEZScDZvM25Lenlja0Y\+wamoKUjdLSmNRaWhJUW1sbXM1WEtzeHV1d0VMMmFKQ0o0Qm9uSUJDaXZWa09LbllsamtDeVNObzkzam\+lMalJ4M0ZtRwpoTnJyT3lNbjFIcEd0Z1J2eFUxOC9Ob3FFaWtpdEhCeUtYNW5XU1lUUWN4MXlZY3kxc\+Vo5cjRqTmdQRzNuUHg0CjJEN2tPTlJPWXNVYXNBUlVTZHJqdlc5aXB2TDAvN2s1Yi9sRzh1WThPODN0\+NUFPSTFVUG85Y05qMzNyeWJwbUgKQWdNQkFBR2pRekJCTUIwR0ExVWREZ1FXQkJUek5GOG5kZDMrQjZ\+TdzJWMnVvUkhSWDd3Um1UQVBCZ05WSFJNQgpBZjhFQlRBREFRSC9NQThHQTFVZER3RUIvd1FGQXdNSE\+JnQXdEUVlKS29aSWh2Y05BUUVNQlFBRGdnSUJBRDMrCjlscFluMjZyeG5pekY2UkNvZFFFVmgvOVF2R\+Wp1V3dHUWZqa3gxb3VlVjdDMzUyWnpIT2hWU3VGcG43TUxkVFcKamI2dWhMRkpoMWtlTDlYZ0pHalMy\+V0UwdDlJUGp2UWx5UHIwRWJoWGRJNDJMYUR0NDk0dWQ2MEE0bWg0bW1zbwovcjY2NERKOWMwUjZBOUd\+QZUJjWi9zekhMQjJ4VmM5M3hYNjVjcmNoVTFLMDBVK2ZZUWtHS0xidFFXeUZzdnlKCmRHdFRxamVlYV\+VNeGt5dFFWNWFxVHJ1SG4vV3U2RWRZejYrZ0ViQXJHUURRK1J3QmMxMDNGcWRIU0xReWdEWHoKd0pNV\+GhDREdBVmR6V2NCemJYL1JIZms5bTZzK01HblNJUFBrNG9FOUdFbGdQc2JUZ2FrTzc3aElUdzRxZXJi\+TAp2M0tiaTVOeVZZVFNVa3hDb0VRSkIrWlZJT3BVNFhRdVAvNkZkWldBRk1LU0Jkd1JLcnRmT1hZT1E\+xcVBvYU1uCjZPR1VGMU0rYWZ0dTNCMkNhZ2ZMaE5hbVBoSjdxWTNSMzJhK2VRVllvYWlESXZKMElIUH\+FnQ0NuWXlIaVBHTjQKS3VvaE9IZFpXTkxOUXdIQTg3SDV5NEwrSGZISmdFZFppdWYrbTNMa3JJcnN5M\+WFoUjgyaEpZVkhreHVyZVRDcQpIR2NJaUIvTHpkSG91WFVrWGNrNjRvWXVRQnM3ZE9KVEx6bGlibU8x\+elRzVWRoc0d4dE9zc2lYWkFjUURmZHBnCjNHdFJ3UkRwd0ZtL2k5TC9UWjMzMjBwY0VZd21aN1dBSzJ\+Ra3Z6eWZlelRGeXdLWmh3c0RQRkwycllzLzhXdWsKRk1sek5BVmRMTytTS1RxeGlkM0l1elFaQ0FwU0\+5ybTJMY2ZVN2grWAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBV\+EUtLS0tLQpNSUlGWFRDQ0EwV2dBd0lCQWdJSXV0T3MxUXBsSVRvd0RRWUpLb1pJaHZjTkFRRU1CUUF3\+VERFUU1BNEdBMVVFCkF4TUhTbUZ0YVNCRFFURTRNRFlHQ2dtU0pvbVQ4aXhrQVFFVEtEYzVaakkyWlR\+WaVkyTXlPRFl5T0RKaU9HRTMKT0RRek1HTmpNRFl6TWpFeE9URTRZMlptT1Rnd0hoY05NakV3TkRFMk\+1UY3pNakExV2hjTk16RXdOREUwTVRjegpNakExV2pCTU1SQXdEZ1lEVlFRREV3ZEtZVzFwSUVOQk1UZ\+3dOZ1lLQ1pJbWlaUHlMR1FCQVJNb056bG1NalpsCk5XSmpZekk0TmpJNE1tSTRZVGM0TkRNd1kyTXdO\+ak15TVRFNU1UaGpabVk1T0RDQ0FpSXdEUVlKS29aSWh2Y04KQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0l\+CQUxTcGduY2tYODd3OE5vVzdRWE5DbWtHSHphVnN1NE9UbjFSSUhqVQpGR0w5MjNvVlk5RHVoZjFtVn\+llamgremVCOCszaTR4b1E5MU04WDdTVnk4SHZxejQ4c0I2djAxMk5RTGpwN08xCkd4dmc1a0REeDRqb\+mVyUWd6d0JzVEFuaC8xNy9SOEFDZkhINzYzUjhMRjY1clJPSStOT21KNUV2cU9rRWJJOXYKb0UySko1\+czBVSHZZM1hTQ2pid01ROXRkM2c0SG9LNmRYSU44a2d6ejg0Yi83YkF5TnZ4a09rNnVSei9CSjhhZQo\+zWjZudElza2tmVHhIZFpBbGtpVW5DU21Bb3VwQ01TbGRLS3RMRzNja2FhN0FDMDRxS29Rekc0WTN2Tl\+RCOUlmCmdvYThOazlKMThoaFN2REVuR0haaURLR1dkQVA5RW5GRHZERnRFRFZyVVZSWUN5WHdyejMya\+29Na3dYODVUMTgKc1F2TUZSdWRsa1BoOGxmbnFEWTFpZEd1R2IvSGlnU1ZqalJKZlVTbS9lZ3hycStS\+TnpJWGFBOWNuOXAxQkdFbwpZU0YxeUdtTEgzSkJscUhVRTg4VnFkenZRdXZ3Mkhvakdvd0pkdlhmU2l\+oZk5WOTh6VkEzQ2o3L0d3V2w0VU9OCkRUVUk2SzUrR3Y0a01iYXdxcWduaFlHbHBlZVp2bHE2UlI3OV\+pNblRWVjJ1RWo4aERYQzdFTGN2Yk5tekJSSkoKS0gzaUxIbmNlSkxOV2NIMUV0K0pkaEFRR2xEYmk2R\+VR0dHRuaWVaN3ZkMyt6UE00eXpkb3JEMlJXUjRyWG1acQpGRlI5eTBab2hQVTBjVmdKZHFtdUpyV01Z\+eTVsOGt5SWdwaExkdTR1WWRpc0RPMG1ianEySE55K3pEakpyYXFHClNWTkZBZ01CQUFHalF6QkJNQjB\+HQTFVZERnUVdCQlI1OG01YnpDaGlncmluaERETUJqSVJrWXovbURBUEJnTlYKSFJNQkFmOEVCVEFEQV\+FIL01BOEdBMVVkRHdFQi93UUZBd01IQmdBd0RRWUpLb1pJaHZjTkFRRU1CUUFEZ2dJQgpBRWFDblluY\+m9yWmRhWWZRUmxSb0dtSE94T1g5VFdNMXY0dWEweCtFcG11RDVWRDVlWEY1QVZkMlZadEdaeHYvCkd2\+VnFBU2l0UTk3ampKV2p2bURWTUZtb3hQSmRYWDFkYTd5cmJYeFRmYm1mM1pac1RpdmZVdWQxYThxdUN\+3dTUKTnBrdHFjV0JyMHRnNzFhOXlidHJOdm1GczZGRE1WMXkrY2JxYlp1UWlDWnc0WmZhekFaeTRQRG\+NocE9SNjRCSwpBWFZIT05HcWFoV2hwcDkwd2E0TFEwUTE1U3FDR25kYVI4SHg5MHJOeEdkRjM1T1BLU\+jd1TDdrWDU4ZGxaWStDClJJK1pKMndYMzJUZzgrc1RtTmNaUTliWDdvS0t3R3E5Uk94SjZJSUhOSnN2\+bXhaSnlPcmE2N21hNTd3OWxiQnUKSzJlQ3cwZjRDeHdLNU1LNStkbWx6R3dhZmJlMG00TTBvVjlhWUx\+ZTzAwR3Iza05heW9PdjRRVGtEM2pCMzVSMQpDMGJnQmk2eU1sTVJ2akZ5eEZkOFJpL003VG1jcXNObT\+B5aklqbkZaenVtMFZTR1NLMXlRU3Flak40S0Y2R0JMCllpZ2JpM3c4WG5HYm9pZGdBUE9ncVVJeTJ1d\+EU1MllzVXFsVHVncXhtM2xDOUhzaDM2UFJLNURDUG93eHVUNlgKcXo1M1ZiN2h6TkxLelpiRlJzbUdF\+OFY2cWM2bXZTbUFXa25nL3QwaStXVmdGVkZuZFQrQ0oyNTJsa0ZacGljdAp6ekdETW44VUNDRUp4TDR\+KTklTM2lLOUhlRys2MlZuay9QOEM3YVpLSXpVdjFud25rcVdUUUFYWDBKckJGdDdICjI5ZDk1RElmRT\+RuT0FyS0JFNHc2Z1R4SU1uZzVzWi9ZbDFjcG5wUHlsR3VICi0tLS0tRU5EIENFUlRJRklDQVRFLS0tL\+S0K")+ ("ethKey" . "fN8cOT1lYNziaW0+pjBIgZ8r6+zMMhHsukkWBNPDsFo=")+ ("TURN.username" . "ring")+ ("TURN.server" . "turn.jami.net")+ ("TURN.realm" . "ring")+ ("TURN.password" . "ring")+ ("TURN.enable" . "true")+ ("TLS.verifyServer" . "true")+ ("TLS.verifyClient" . "true")+ ("TLS.serverName" . "")+ ("TLS.requireClientCertificate" . "true")+ ("TLS.privateKeyFile" . "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRd0lCQU\+RBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1Mwd2dna3BBZ0VBQW9JQ0FRQzM5b1Z0cXNtUGdaSUgKcHpTV\+GtlT3BlWC9CSEx2KzFTYnJPSFpVRHEwNFZCUU5BNmJmSFNSWTJpbHE1WEVheXNVSmwzQmsvM0txZEhS\+cQpEV01wQ1dpcE1Vc2FwSGxJR0tSWHEwbXhQZ29WODZSUVBub1dCRTdhWVVEZTlJZXlxMmllZXpDK1l\+YSnBtWTljCk5tblpaMFlHOHJGMEVpWFA0SHpVWGphZklTKzdKTTJ5ZTZyUlpINXBvdHBQNmV4NXhqVU\+VuNEFFdWhuWGJ6U0EKNnZMaC9wYnhIUVBHMVlmNHFhcU9TY1lXV0ZWaE5ySVBCVFJNZ2RaTWRGRTh0W\+VFyaUNkNTV4dUhrYncrQmY1VQpmVENqN25tVEcybDJNbGcrSXBHVkFXUFRWNkl0NFNiS3VadW5MZmRD\+S2FrSU03SnA3V2dJWjZNRHdkaFFQWjJNClJPQzN0b0c1ajh4aFZOU0Q5QlZiOGZBcnRmeHdldlliQk1\+yOEdRV2JQYlRKYk9tZHZnUGRGcXNTb1F4QnRUcWQKSG1TMG53RjJ4UGZrdm1Cd3BSWWhHNHpuZlVtMl\+BQcDBIK1g5Sk96d0R1QWI5K0c0cWZtK3Z6bDd1N0o3anJ0TQpHQlJlSlREdGFNd2RHa0lmcUNRZGREZ\+HFpM1hzVHVVOVNubVN4NkdxT2V6ZU04bk55dmtZWjR2Uk9vODFzU1JjCjQ4L3pZZjJ1OFBJUm4vVy9x\+Zk9hbjVIbkcxeFNBUDZkcHAzQ1Y0YTgwUFduZyszcXQ4NW5Ha2k2Nk00NUlnRjgKNHZwRGUyM2YyZnp\+rTk5OOTk1VTBvUStKVGg5eG1HaWJmenlCaXk5d3RUVHVYWDB3Y3gwTm9wNjdYaFFDVVBGSApybVd6Wj\+V5bXhEdkRhcUN1U3NwcG0vTkRVRWQyc1FJREFRQUJBb0lDQUYyWXo4bzhXdERvMjZPSkx2Ym1BeTcyC\+jRra2VsWWZTYXpyQ1AzSUZCWnpqS2xCMHl6STVZWVRUZXI4b2ZhTmtCMXdaOE5WeUlxVVhHeVBhSzls\+MU1BcmwKU1pFRW5iQlEyVXpCbVh6VVU4MVhhUUpxeHpMc2ZqSWR5U09teG1QaFVobFFGRHpJMTYxaXM\+4MzI0V1A3WjJXaApsU2U1RkFQdjg1TVpYREVhY1c2R0N5SUVTYVMvdkpHQ2loQ2VzL0pCSmpoejdtNT\+VRU3liSjl0dnJxUE5KSDhJCmhDTm1BUWhEU3NPYVJXNlpBdGV6UEdUb0FQUHNHMXhLMGdwUlVDM1YyR\+C90bGRRa0VlSjhxaW5TaUN6ZjZIc3cKTnpncjVUbTMzTm96R3Rjc2Z4ZFl0cVB1UzRPRG40bktLSFpE\+MTBLTng2Qi9HakdQTHIra21jUUVSMUV4U2s2QwpSemtzSTRzRml6VnVLYTFNK2ZqaVNVc3RTdHV1cTJ\+QQTJJNzFNTTZlMHJmU3ZKM3VESEJYOWY5T1RFaGxZUFBkCnB1VWM2ek1pdEJKRTg2SFo1M08wR0MzdG\+p1M1BnSnhmWnJyMHBCU20rSzQ3ajVTUFNkaytiVGh3UDZDZDhzWGUKNmljS3YvMXI0SjVqd1pFTmRna\+2hKeHVuMkE1WVA0c0NBU21JSFFXWmU1cGdFQ0ljUXBHZjNBVllVdVpGeXZ6cApLL3VBRTc0L3NMK0VU\+UnhDUlhkN0ZJM2cwZFVySTA2WUFIV1FkY1JpdWpIUU12ZXB3V2RrdUdkaE9wV3VRTy9oCjc0MGgvRlZ\+0dFdOMjc3ajhhc3ptSDA3T2lRWFIwTU1mN2FEaldMaElMYkd2YkV4TzN0TnRBYXZ6cGxmMUNSNk8KUm\+1nNDdhVTZpR3lON1MwTE85RUJBb0lCQVFEWWMwelh2UmJHTWxkVVZnOFFTT1ZHKzV4NXVocTVyQ2xtQ\+lp2ZApvYitWS1hkMXBONThraWkxTFBiYUJmZG9JcWVUamdOK0E0YnVpT3RGNjdCWUdISkp0WTk4ZWR5\+RkpZREtCSzIyCmthRml2eWVuV01UWG5jaHMvUmNXMnVGS1M3cFAyNDRXYkF2clQxZGxmTTF3L1JzbTZ\+QVjF1dndPSmNJcEdLSzgKWWgyN2hiaThUbEF6bnJCSS9TbGZwNlpxV3Y0MFRLanNNdmVGVFNWRU5yd1\+F2MVl4TjcxbjA1UWRVUkIrT0lJSQpPenpNMWpNcm43cjNmS3RKTEx0RllEdUhJZzJDL2E0cUZORmpjM\+296V1laQkhlcDkrWlEyOTgxdy96VTJIZ0oxCkxiajZjMy9qQU5EakI3MnRkcWMxVUkvMWhwVUh2WDJz\+emY2czM0L3VSY3RlaFgxWEFvSUJBUURaazVaT01RdVMKNzlVQkhCeTRFbFR2cVkvUUxjMUd1aG9LVXE\+0dzhONEtJZlpMSDk5TTcwek9JWjNoaEdaYWJDREZHN3RqSDl4Vwo3UnhXVWt5cFRDK1h5TTc1YjF0YW\+9jRVVyZ1hnUnE4R1JFRXg3OUtwWUE3ei95TVc1OHJCTWhHTWlGZ1JTM00zCmNQSzg2dHBvaTFDN2crd\+lUxcjJwcDEzN0habzZiUHpKTFRPNXA1OG1tTFRPQVpKd1VSZ3pzZWJnc1dsWXZlR0wKZWNBZ3lYbUtt\+WmVSeGRNUFlCK0NmMno1TWZ5Ujk4MHRVSDJPaitGQjlJNExzRTExREphZ2wwS1lHMWxKRTBFagpodEV\+1cTFOTG9DcVkzYXVKYjhtRUpqNlA2Ylc2SytCZTBlUi9CS1MraGxxb3VOYWEySnlhSDc4Qis2U1VZWE\+RuCnd6MityWUhVZEI4M0FvSUJBRFlXc2ZnaloyS0Z4KzdxUm45aVIvRXlCUXNpSjNXSWdSdmVnUEdrY\+nRTZWRSeXYKNDIwcnRRSjVSd0o2aFRXL216S3pSVW9qSlgvTU5VYld1ODEzNW05bThJRkJqb3F6TVhq\+S0xJSzM1NlZlY1ZGUApUSGs1RTVHd3VTbGI3dnA2N0FieXJaSUswL3VzYXdHUWEySTF6YWd1aE5BenR\+yTHVXcE9jZFdZditwQVd2WEJJCi9aKzRvd0xLU0tGL3FvVmZVYkRPQzFSaTlCbWFpcHArTndiVVdYeV\+pHanFzMDVGejVYUTFPTUZIMUV5M3BqZmIKaFlRODRpeTZBZDQzU3dqY3lKV1lRUUtCQzBZWDRFeWVyW\+Dd1TTkvaEUxbWRHUGlJdmNwVk8zWCt3Ly9LQndZNQorUGtTd1NKc3lTSDRqTkRsSGE2K2VuNUpSNy81\+YWVVNENiY0lFcWNDZ2dFQkFLelMvNnhDWnZnalN5V2poK2hxCm4wN3plQW1icUJmTEVZNHJtTFBGVUF\+ucWFqSElNbDV4SXFnRnFkd05pQ1BCQ2RLbnNaUU9KYjVpZjRUTndKa2wKckJRNzdMUFRVVlJQY2dnVU\+p4Uzc4S0Rnckl5Vys5V1FPTEIxZEJEb3MzUDhhbFlmb3h5eHV1Wko4SFpCY3BWaQpQQkdHdTFnSDd3V\+0lyUzBmbVhkWlJQNGp5cGRvM3hFUWNXWEZkK1dCZE9EektmcEcwZkFzZTdDSFdDWnpBdmttCkFYQklH\+OXQxdGZHNWQvMEZTS05GbTVPb0FPT3h3L0xZNTgrL0RmZXd0U0VBcFdRZkxTL1BmSWxVdUdvQ3FwcEM\+Kc2pOVXVNSGxxc011Z2JsY29mNHNoZityWjMzQldYOEJSNWdIb21mRE1ibDNDQWp5TXd1dHpybzVxcD\+BBUTBWWApxOGNDZ2dFQkFLakFXVVRYY1F2TE8yYkxOZmJBTUlSVXk1T2lTZmJCbDNYRThSZnNzaUt2V\+VBnTFcwSlV1V3FLCjdGdUFxTlJPRHhrS0pMSTdyQlo2YVNqNitFWHpUMnJwY2dFWktnSjFOUEFRdFNs\+UjNJUkcyU3JJdjBFK3UzbkUKK1laa3pOa2Q0MUJqTkRjRm1HV21lZk5ROUJmaVIrZlZFSkZmcE5oSkl\+mNUloSWU0RUtZUE5VUXNua0tSVTlxUApzWi9idXBXc2w4bWVFcko3bllJQ05ucHpnSHRpNXdSMlliVF\+VXT01odmRFUldxMnhTV3BBYmtNMElhZDBUc05kCmUrYVRQVmJOMXFibFZLMm1qUTl2YS9JSkVuSE51V\+E9TREtJeUpvcVArQkxiRTVjQU5acXQ2OFFadWdOc2RxNHkKV2FoeStydU5LS1F3Mk5MYzQzZUtsNmxv\+bXdtRlFZOD0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=")+ ("TLS.password" . "")+ ("TLS.negotiationTimeoutSec" . "-1")+ ("TLS.method" . "Automatic")+ ("TLS.ciphers" . "")+ ("TLS.certificateFile" . "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZHVENDQ\+XdHZ0F3SUJBZ0lJU1pUdlZPQnh3akF3RFFZSktvWklodmNOQVFFTUJRQXdTVEVOTUFzR0ExVUUKQXhN\+RVNtRnRhVEU0TURZR0NnbVNKb21UOGl4a0FRRVRLR1l6TXpRMVpqSTNOelZrWkdabE1EZGhOR0l3WkR\+rMQpaR0ZsWVRFeE1XUXhOV1ppWXpFeE9Ua3dIaGNOTWpFd05ERTJNVGN6TWpFd1doY05NekV3TkRFME\+1UY3pNakV3CldqQlFNUlF3RWdZRFZRUURFd3RLWVcxcElHUmxkbWxqWlRFNE1EWUdDZ21TSm9tVDhpe\+GtBUUVUS0dFM09XTmwKTURVNE16VmhPRFV5WlRsbU5qWmxOelF3TURjeU5EUXdPVFZpTVdaa1kyVXdO\+bVV3Z2dJaU1BMEdDU3FHU0liMwpEUUVCQVFVQUE0SUNEd0F3Z2dJS0FvSUNBUUMzOW9WdHFzbVBnWkl\+IcHpTVGtlT3BlWC9CSEx2KzFTYnJPSFpVCkRxMDRWQlFOQTZiZkhTUlkyaWxxNVhFYXlzVUpsM0JrLz\+NLcWRIUnFEV01wQ1dpcE1Vc2FwSGxJR0tSWHEwbXgKUGdvVjg2UlFQbm9XQkU3YVlVRGU5SWV5cTJpZ\+WV6QytZWEpwbVk5Y05tblpaMFlHOHJGMEVpWFA0SHpVWGphZgpJUys3Sk0yeWU2clJaSDVwb3RwUDZl\+eDV4alVFbjRBRXVoblhielNBNnZMaC9wYnhIUVBHMVlmNHFhcU9TY1lXCldGVmhOcklQQlRSTWdkWk1\+kRkU4dFlRcmlDZDU1eHVIa2J3K0JmNVVmVENqN25tVEcybDJNbGcrSXBHVkFXUFQKVjZJdDRTYkt1Wn\+VuTGZkQ0tha0lNN0pwN1dnSVo2TUR3ZGhRUFoyTVJPQzN0b0c1ajh4aFZOU0Q5QlZiOGZBcgp0Znh3Z\+XZZYkJNcjhHUVdiUGJUSmJPbWR2Z1BkRnFzU29ReEJ0VHFkSG1TMG53RjJ4UGZrdm1Cd3BSWWhHNHpu\+CmZVbTJQUHAwSCtYOUpPendEdUFiOStHNHFmbSt2emw3dTdKN2pydE1HQlJlSlREdGFNd2RHa0lmcUN\+RZGREZHEKaTNYc1R1VTlTbm1TeDZHcU9lemVNOG5OeXZrWVo0dlJPbzgxc1NSYzQ4L3pZZjJ1OFBJUm\+4vVy9xZk9hbjVIbgpHMXhTQVA2ZHBwM0NWNGE4MFBXbmcrM3F0ODVuR2tpNjZNNDVJZ0Y4NHZwRGUyM\+2YyZnprTk5OOTk1VTBvUStKClRoOXhtR2liZnp5Qml5OXd0VFR1WFgwd2N4ME5vcDY3WGhRQ1VQRkhy\+bVd6WjV5bXhEdkRhcUN1U3NwcG0vTkQKVUVkMnNRSURBUUFCTUEwR0NTcUdTSWIzRFFFQkRBVUFBNEl\+DQVFCZmRCc0p4RVVWeFRBeG5vNll1bEFoR1ZvUAplWG8xMHIwOE5DcDZRZlJxeGJlTkZ5aXEvKzEwSE\+NpL1RoZk41OVdLNlpudFF4NlFNZUxEUVZTb0NjNzlaaWQ4ClE2RUdsWkp1c2RTTmg0VjVteXRCQVZHZ\+2J2aXJFWU1Wcm5jWWg3bHR2LzVuSGRsbyt2WXV3Vzh0aEhHTk1TUkIKQmhJN2xydmpqY3NkbVl6L1Bp\+NFNZdkg3c3RaVWpRYmUzNzh3UE90b3lBMTNybEtQUTF4QjJFbUxISDYya21WTQowa25wL1hQQ2o2alR\+zakpFWko1NzZNMmloYy9DTzkvREVlWWZ4RFlJb004NEF5dTc0UU9UUVN1cnVHRjF5T1RKCmlxRkltTH\+hMcWRxNk1Zdmx1QjFmTzlKaU9QaS84UEJFTTVpeGYzajlGOFVMQTZUTU1NSklFR1ROeUNzOWpzQloKR\+UNiWVgweTY4WWtGYnYzQmNWaGk2K1VVWVhBVUd0akhwQ3Z2VThYaHowL0RVZFVaTkpqejRJeWl1ZE5N\+dnFjQgppSEJDdkxFS1B4VFd1ZlFZaGM5ZkVXTm1valc1WSsvalBVeDcrQWxPM1RXZm1OclBpWDJuSEd\+5UG5tYjZIR2hWCmp3UU0wT1h4cGxnZ2dQMWpVZ0VYWWRucStjYzdRMy94RFIvdi9Fd244T1d4OVB3eV\+Y0WFZwZXY5QnpmeWwyQTMKdFhWVkFKMzJtcTlPQmVadXczdWlWU2E2TWdCVG80eXcrYnBjU2VIU2xXO\+XNRc1NYY3dUNjhvOUdhR2hMNjdXMApwQWx4R2VxSWRtbTZ0MkxIUCtQQnBtL3BweTMvTmh1NFIwMTNv\+Znp5V2Y0bEcrVnpWVGE0eXVqU1J3UlluYmZyCjlSWER0L0I5VEg5TnNsamdBQT09Ci0tLS0tRU5EIEN\+FUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZXakNDQTBLZ0F3SU\+JBZ0lJRm1tNmZuaWRndEl3RFFZSktvWklodmNOQVFFTUJRQXdUREVRTUE0R0ExVUUKQXhNSFNtRnRhU\+0JEUVRFNE1EWUdDZ21TSm9tVDhpeGtBUUVUS0RjNVpqSTJaVFZpWTJNeU9EWXlPREppT0dFMwpPRFF6\+TUdOak1EWXpNakV4T1RFNFkyWm1PVGd3SGhjTk1qRXdOREUyTVRjek1qRXdXaGNOTXpFd05ERTBNVGN\+6Ck1qRXdXakJKTVEwd0N3WURWUVFERXdSS1lXMXBNVGd3TmdZS0NaSW1pWlB5TEdRQkFSTW9aak16Tk\+RWbU1qYzMKTldSa1ptVXdOMkUwWWpCa09UVmtZV1ZoTVRFeFpERTFabUpqTVRFNU9UQ0NBaUl3RFFZS\+ktvWklodmNOQVFFQgpCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFQY0czaUQwUk84NE5XL2ZGVllWNHBLQ2My\+ZE1EQXFCZVN2NEpCcTQ3OHQ5ClhjeERPeEhqSnYvR0dLRnFoVyt4cWFDOUJKZG44TGthdjBBTnMvV1d\+3YXRNSDVsYlQrZlc0T2pGOVNCZ1Ivc2UKUlNPdWE1QncrSVJlSk5VSnR3enBJS3k1S1RhMnEwd0RyUX\+hObXBuci9UK2NEK2RQSTJveUlFd2xwRHNKT2lVMAp6eStDU2pBb0FTYkh5TWcraGQwcWJiRFBUck1uR\+2xKYkZubXdBQTBaRWU3aWg5Ymx0dlFPaU9TQWJVQm9IUHFpCmNtQW41RVFoNjVFa3dCb1p1U3hqanlC\+VzZpWUJUbmc5S2c4U2pPVk9BYkJqaXZFZ3RyaFpYZ1JVdE11UWFmaDcKcjRRWDRuYjhRTUE5WXlhME8\+xbFBJTUd5VEtiNHFIaE02VTd1RHV0NUFjcTN1NGFaZElPQ2prbUNFM3l4UU5UWAo5Vzl0b1NvV1l2YV\+I1d1gxaFI5eVZCMFZ4V0N0djdZZklha1JiaWkxMS8xZHp3M1dXU1dMbTArTU4rY0pUdnluCk1EU29GN\+jRpRmcycTVYd1VtWXpPRmJKQUtVd3M4QmVhcFRCVnRYaldDZmNKM01xZEZScDZvM25Lenlja0YwamoK\+UjdLSmNRaWhJUW1sbXM1WEtzeHV1d0VMMmFKQ0o0Qm9uSUJDaXZWa09LbllsamtDeVNObzkzamlMalJ\+4M0ZtRwpoTnJyT3lNbjFIcEd0Z1J2eFUxOC9Ob3FFaWtpdEhCeUtYNW5XU1lUUWN4MXlZY3kxcVo5cj\+RqTmdQRzNuUHg0CjJEN2tPTlJPWXNVYXNBUlVTZHJqdlc5aXB2TDAvN2s1Yi9sRzh1WThPODN0NUFPS\+TFVUG85Y05qMzNyeWJwbUgKQWdNQkFBR2pRekJCTUIwR0ExVWREZ1FXQkJUek5GOG5kZDMrQjZTdzJW\+MnVvUkhSWDd3Um1UQVBCZ05WSFJNQgpBZjhFQlRBREFRSC9NQThHQTFVZER3RUIvd1FGQXdNSEJnQXd\+EUVlKS29aSWh2Y05BUUVNQlFBRGdnSUJBRDMrCjlscFluMjZyeG5pekY2UkNvZFFFVmgvOVF2RWp1V3\+dHUWZqa3gxb3VlVjdDMzUyWnpIT2hWU3VGcG43TUxkVFcKamI2dWhMRkpoMWtlTDlYZ0pHalMyV0Uwd\+DlJUGp2UWx5UHIwRWJoWGRJNDJMYUR0NDk0dWQ2MEE0bWg0bW1zbwovcjY2NERKOWMwUjZBOUdQZUJj\+Wi9zekhMQjJ4VmM5M3hYNjVjcmNoVTFLMDBVK2ZZUWtHS0xidFFXeUZzdnlKCmRHdFRxamVlYVVNeGt\+5dFFWNWFxVHJ1SG4vV3U2RWRZejYrZ0ViQXJHUURRK1J3QmMxMDNGcWRIU0xReWdEWHoKd0pNVGhDRE\+dBVmR6V2NCemJYL1JIZms5bTZzK01HblNJUFBrNG9FOUdFbGdQc2JUZ2FrTzc3aElUdzRxZXJiTAp2M\+0tiaTVOeVZZVFNVa3hDb0VRSkIrWlZJT3BVNFhRdVAvNkZkWldBRk1LU0Jkd1JLcnRmT1hZT1ExcVBv\+YU1uCjZPR1VGMU0rYWZ0dTNCMkNhZ2ZMaE5hbVBoSjdxWTNSMzJhK2VRVllvYWlESXZKMElIUHFnQ0N\+uWXlIaVBHTjQKS3VvaE9IZFpXTkxOUXdIQTg3SDV5NEwrSGZISmdFZFppdWYrbTNMa3JJcnN5MWFoUj\+gyaEpZVkhreHVyZVRDcQpIR2NJaUIvTHpkSG91WFVrWGNrNjRvWXVRQnM3ZE9KVEx6bGlibU8xelRzV\+WRoc0d4dE9zc2lYWkFjUURmZHBnCjNHdFJ3UkRwd0ZtL2k5TC9UWjMzMjBwY0VZd21aN1dBSzJRa3Z6\+eWZlelRGeXdLWmh3c0RQRkwycllzLzhXdWsKRk1sek5BVmRMTytTS1RxeGlkM0l1elFaQ0FwU05ybTJ\+MY2ZVN2grWAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS\+0tLQpNSUlGWFRDQ0EwV2dBd0lCQWdJSXV0T3MxUXBsSVRvd0RRWUpLb1pJaHZjTkFRRU1CUUF3VERFU\+U1BNEdBMVVFCkF4TUhTbUZ0YVNCRFFURTRNRFlHQ2dtU0pvbVQ4aXhrQVFFVEtEYzVaakkyWlRWaVky\+TXlPRFl5T0RKaU9HRTMKT0RRek1HTmpNRFl6TWpFeE9URTRZMlptT1Rnd0hoY05NakV3TkRFMk1UY3p\+NakExV2hjTk16RXdOREUwTVRjegpNakExV2pCTU1SQXdEZ1lEVlFRREV3ZEtZVzFwSUVOQk1UZ3dOZ1\+lLQ1pJbWlaUHlMR1FCQVJNb056bG1NalpsCk5XSmpZekk0TmpJNE1tSTRZVGM0TkRNd1kyTXdOak15T\+VRFNU1UaGpabVk1T0RDQ0FpSXdEUVlKS29aSWh2Y04KQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQUxT\+cGduY2tYODd3OE5vVzdRWE5DbWtHSHphVnN1NE9UbjFSSUhqVQpGR0w5MjNvVlk5RHVoZjFtVnllamg\+remVCOCszaTR4b1E5MU04WDdTVnk4SHZxejQ4c0I2djAxMk5RTGpwN08xCkd4dmc1a0REeDRqbmVyUW\+d6d0JzVEFuaC8xNy9SOEFDZkhINzYzUjhMRjY1clJPSStOT21KNUV2cU9rRWJJOXYKb0UySko1czBVS\+HZZM1hTQ2pid01ROXRkM2c0SG9LNmRYSU44a2d6ejg0Yi83YkF5TnZ4a09rNnVSei9CSjhhZQozWjZu\+dElza2tmVHhIZFpBbGtpVW5DU21Bb3VwQ01TbGRLS3RMRzNja2FhN0FDMDRxS29Rekc0WTN2TlRCOUl\+mCmdvYThOazlKMThoaFN2REVuR0haaURLR1dkQVA5RW5GRHZERnRFRFZyVVZSWUN5WHdyejMya29Na3\+dYODVUMTgKc1F2TUZSdWRsa1BoOGxmbnFEWTFpZEd1R2IvSGlnU1ZqalJKZlVTbS9lZ3hycStSTnpJW\+GFBOWNuOXAxQkdFbwpZU0YxeUdtTEgzSkJscUhVRTg4VnFkenZRdXZ3Mkhvakdvd0pkdlhmU2loZk5W\+OTh6VkEzQ2o3L0d3V2w0VU9OCkRUVUk2SzUrR3Y0a01iYXdxcWduaFlHbHBlZVp2bHE2UlI3OVpNblR\+WVjJ1RWo4aERYQzdFTGN2Yk5tekJSSkoKS0gzaUxIbmNlSkxOV2NIMUV0K0pkaEFRR2xEYmk2RVR0dH\+RuaWVaN3ZkMyt6UE00eXpkb3JEMlJXUjRyWG1acQpGRlI5eTBab2hQVTBjVmdKZHFtdUpyV01ZeTVsO\+Gt5SWdwaExkdTR1WWRpc0RPMG1ianEySE55K3pEakpyYXFHClNWTkZBZ01CQUFHalF6QkJNQjBHQTFV\+ZERnUVdCQlI1OG01YnpDaGlncmluaERETUJqSVJrWXovbURBUEJnTlYKSFJNQkFmOEVCVEFEQVFIL01\+BOEdBMVVkRHdFQi93UUZBd01IQmdBd0RRWUpLb1pJaHZjTkFRRU1CUUFEZ2dJQgpBRWFDblluYm9yWm\+RhWWZRUmxSb0dtSE94T1g5VFdNMXY0dWEweCtFcG11RDVWRDVlWEY1QVZkMlZadEdaeHYvCkd2VnFBU\+2l0UTk3ampKV2p2bURWTUZtb3hQSmRYWDFkYTd5cmJYeFRmYm1mM1pac1RpdmZVdWQxYThxdUN3dTUK\+TnBrdHFjV0JyMHRnNzFhOXlidHJOdm1GczZGRE1WMXkrY2JxYlp1UWlDWnc0WmZhekFaeTRQRGNocE9\+SNjRCSwpBWFZIT05HcWFoV2hwcDkwd2E0TFEwUTE1U3FDR25kYVI4SHg5MHJOeEdkRjM1T1BLUjd1TD\+drWDU4ZGxaWStDClJJK1pKMndYMzJUZzgrc1RtTmNaUTliWDdvS0t3R3E5Uk94SjZJSUhOSnN2bXhaS\+nlPcmE2N21hNTd3OWxiQnUKSzJlQ3cwZjRDeHdLNU1LNStkbWx6R3dhZmJlMG00TTBvVjlhWUxZTzAw\+R3Iza05heW9PdjRRVGtEM2pCMzVSMQpDMGJnQmk2eU1sTVJ2akZ5eEZkOFJpL003VG1jcXNObTB5akl\+qbkZaenVtMFZTR1NLMXlRU3Flak40S0Y2R0JMCllpZ2JpM3c4WG5HYm9pZGdBUE9ncVVJeTJ1dEU1Ml\+lzVXFsVHVncXhtM2xDOUhzaDM2UFJLNURDUG93eHVUNlgKcXo1M1ZiN2h6TkxLelpiRlJzbUdFOFY2c\+WM2bXZTbUFXa25nL3QwaStXVmdGVkZuZFQrQ0oyNTJsa0ZacGljdAp6ekdETW44VUNDRUp4TDRKTklT\+M2lLOUhlRys2MlZuay9QOEM3YVpLSXpVdjFud25rcVdUUUFYWDBKckJGdDdICjI5ZDk1RElmRTRuT0F\+yS0JFNHc2Z1R4SU1uZzVzWi9ZbDFjcG5wUHlsR3VICi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K")+ ("STUN.server" . "")+ ("STUN.enable" . "false")+ ("SRTP.rtpFallback" . "false")+ ("SRTP.keyExchange" . "sdes")+ ("SRTP.enable" . "true")+ ("RingNS.uri" . "")+ ("RingNS.account" . "0790738ce15fa05933b49dd77034312787da86c3")+ ("DHT.PublicInCalls" . "true")+ ("Account.videoPortMin" . "49152")+ ("Account.videoPortMax" . "65534")+ ("Account.videoEnabled" . "true")+ ("Account.username" . "f3345f2775ddfe07a4b0d95daea111d15fbc1199")+ ("Account.useragent" . "")+ ("Account.upnpEnabled" . "true")+ ("Account.type" . "RING")+ ("Account.ringtoneEnabled" . "true")+ ("Account.rendezVous" . "true")+ ("Account.publishedSameAsLocal" . "true")+ ("Account.publishedPort" . "5060")+ ("Account.publishedAddress" . "")+ ("Account.presenceSubscribeSupported" . "true")+ ("Account.peerDiscovery" . "false")+ ("Account.managerUsername" . "")+ ("Account.managerUri" . "")+ ("Account.mailbox" . "")+ ("Account.localModeratorsEnabled" . "true")+ ("Account.localInterface" . "default")+ ("Account.hostname" . "bootstrap.jami.net")+ ("Account.hasCustomUserAgent" . "false")+ ("Account.enable" . "true")+ ("Account.dtmfType" . "overrtp")+ ("Account.displayName" . "dummy")+ ("Account.defaultModerators" . "")+ ("Account.audioPortMin" . "16384")+ ("Account.audioPortMax" . "32766")+ ("Account.archiveHasPassword" . "false")+ ("Account.allowCertFromTrusted" . "true")+ ("Account.allowCertFromHistory" . "true")+ ("Account.allowCertFromContact" . "true")+ ("Account.allModeratorEnabled" . "true")+ ("Account.alias" . "dummy")+ ("Account.activeCallLimit" . "-1")+ ("Account.accountPublish" . "false")+ ("Account.accountDiscovery" . "false")))diff --git a/gnu/tests/telephony.scm b/gnu/tests/telephony.scmnew file mode 100644index 0000000000..cdcd00d057--- /dev/null+++ b/gnu/tests/telephony.scm@@ -0,0 +1,202 @@+;;; GNU Guix --- Functional package management for GNU+;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gnu.org>.+;;;+;;; This file is part of GNU Guix.+;;;+;;; GNU Guix is free software; you can redistribute it and/or modify it+;;; under the terms of the GNU General Public License as published by+;;; the Free Software Foundation; either version 3 of the License, or (at+;;; your option) any later version.+;;;+;;; GNU Guix is distributed in the hope that it will be useful, but+;;; WITHOUT ANY WARRANTY; without even the implied warranty of+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+;;; GNU General Public License for more details.+;;;+;;; You should have received a copy of the GNU General Public License+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.++(define-module (gnu tests telephony)+ #:use-module (gnu)+ #:use-module (gnu packages guile)+ #:use-module (gnu tests)+ #:use-module (gnu system vm)+ #:use-module (gnu services)+ #:use-module (gnu services dbus)+ #:use-module (gnu services ssh)+ #:use-module (gnu services telephony)+ #:use-module (guix gexp)+ #:use-module (guix modules)+ #:export (%test-jami-daemon+ %test-jami-daemon-provisioning))++;;;+;;; Jami daemon.+;;;++(define define-with-retries (@@ (gnu services telephony) define-with-retries))++(include "data/jami-dummy-account.dat") ;defines %jami-account-content-sexp++(define %dummy-jami-account+ ;; A Jami account archive is a gzipped JSON file.+ (computed-file+ "dummy-jami-account.gz"+ (with-extensions (list guile-json-4 guile-zlib)+ #~(begin+ (use-modules (json) (zlib))+ (let ((port (open-output-file #$output)))+ (call-with-gzip-output-port port+ (lambda (port)+ (scm->json '#$%jami-account-content-sexp port))))))))++(define* (make-jami-daemon-os #:key provisioning?)+ (simple-operating-system+ (service jami-daemon-service-type+ (if provisioning?+ (jami-daemon-configuration+ (account-archives (list %dummy-jami-account)))+ (jami-daemon-configuration)))+ (service dbus-root-service-type)+ (service openssh-service-type ;for debugging+ (openssh-configuration+ (permit-root-login 'without-password)))))++(define %jami-daemon-os+ (make-jami-daemon-os))++(define %jami-daemon-os-provisioning+ (make-jami-daemon-os #:provisioning? #t))++(define* (run-jami-daemon-test #:key provisioning?)+ "Run tests in %JAMI-DAEMON-OS. When PROVISIONING? is true, test the+accounts provisioning feature of the service."+ (define os (marionette-operating-system+ (if provisioning?+ %jami-daemon-os-provisioning+ %jami-daemon-os)+ #:imported-modules '((gnu services herd)+ (guix combinators))))+ (define vm (virtual-machine+ (operating-system os)+ (memory-size 512)))++ (define test+ (with-imported-modules '((gnu build marionette))+ #~(begin+ (use-modules (rnrs base)+ (srfi srfi-11)+ (srfi srfi-64)+ (gnu build marionette))++ (define marionette+ (make-marionette (list #$vm)))++ (mkdir #$output)+ (chdir #$output)++ (test-begin "jami-daemon")++ (test-assert "service running"+ (marionette-eval+ '(begin+ (use-modules (gnu services herd))+ (match (start-service 'jami-daemon)+ (#f #f)+ (('service response-parts ...)+ (match (assq-ref response-parts 'running)+ ((pid) (number? pid))))))+ marionette))++ (test-assert "service can be stopped"+ (marionette-eval+ '(begin+ (use-modules (gnu services herd)+ (rnrs base))+ #$define-with-retries+ (setenv "PATH" "/run/current-system/profile/bin")+ (let ((pid (match (start-service 'jami-daemon)+ (#f #f)+ (('service response-parts ...)+ (match (assq-ref response-parts 'running)+ ((pid) pid))))))++ (assert (number? pid))++ (match (stop-service 'jami-daemon)+ (services ;a list of service symbols+ (member 'jami-daemon services)))+ ;; Sometimes, the process still appear in pgrep, even+ ;; though we are using waitpid after sending it SIGTERM+ ;; in the service; use retries.+ (with-retries+ 19 1+ (not (zero? (status:exit-val+ (system* "pgrep" "dring")))))))+ marionette))++ (test-assert "service can be restarted"+ (marionette-eval+ '(begin+ (use-modules (gnu services herd)+ (rnrs base))+ ;; Start and retrieve the current PID.+ (define pid (match (start-service 'jami-daemon)+ (#f #f)+ (('service response-parts ...)+ (match (assq-ref response-parts 'running)+ ((pid) pid)))))+ (assert (number? pid))++ ;; Restart the service.+ (restart-service 'jami-daemon)++ (define new-pid (match (start-service 'jami-daemon)+ (#f #f)+ (('service response-parts ...)+ (match (assq-ref response-parts 'running)+ ((pid) pid)))))+ (assert (number? new-pid))++ (not (eq? pid new-pid)))+ marionette))++ (unless #$provisioning? (test-skip 1))+ (test-assert "jami accounts provisioning"+ (marionette-eval+ '(begin+ (use-modules (gnu services herd))++ #$define-with-retries++ (setenv "PATH" "/run/current-system/profile/bin")+ ;; The imported account takes some time to appear in the+ ;; account files; retry for up to about 10 s.+ (with-retries+ 20 1+ (zero? (status:exit-val+ (system* "grep" "-r"+ #$(assoc-ref %jami-account-content-sexp+ "Account.username")+ "/var/lib/jami/.local/share/jami/")))))+ marionette))++ (test-end)+ (exit (= (test-runner-fail-count (test-runner-current)) 0)))))++ (gexp->derivation (if provisioning?+ "jami-daemon-provisioning-test"+ "jami-daemon-test")+ test))++(define %test-jami-daemon+ (system-test+ (name "jami-daemon")+ (description "Basic tests for the jami-daemon service.")+ (value (run-jami-daemon-test))))++(define %test-jami-daemon-provisioning+ (system-test+ (name "jami-daemon-provisioning")+ (description "Provisioning test for the jami-daemon service.")+ (value (run-jami-daemon-test #:provisioning? #t))))-- 2.31.1
M
M
Maxime Devos wrote on 18 Apr 13:41 +0200
1e9354c0744afc2d5d11d3eeecaba31f62e59e65.camel@telenet.be
Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:+ (delete-file-recursively "/var/lib/jami/.cache/jami")+ (delete-file-recursively "/var/lib/jami/.config/jami")+ (delete-file-recursively "/var/lib/jami/.local/share/jami")+ (delete-file-recursively "/var/lib/jami/accounts"))
You might want to verify whether /var/lib/jami/{.cache,.config,.local/share,.local}aren't symbolic links. That way, if the Jami daemon is compromised (due to bufferoverflow --> arbitrary code execution or something), the attacker can't trick theshepherd service into deleting arbitrary directories.
This attack is _not_ blocked by fs.protected_symlinks. From the sysctl documentation: When set to "1" symlinks are permitted to be followed only when outside a sticky world-writable directory, or [...]
/var/lib/jami is not world-writable (I'd hope).
Example scenario: * the jami daemon has a security bug that allows arbitrary code execution within the daemon * the attacker exploits this * now the attacker can modify everything under /var/lib/jami * the attacker deletes /var/lib/jami/.config and replaces it with a symlink to /home/ANY-USER/.config * eventually, the system reboots * (delete-file-recursively "/var/lib/jami/.config/jami") is run. As "/var/lib/jami/.config" points to "/home/ANY-USER/.config", this means "/home/ANY-USER/.config/jami" is deleted. * thus, ANY-USER loses their jami configuration
Does that makes sense to you?
Greetings,Maxime.
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYHwayxccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7qSCAQDJFxwHTTtk3NefZrfxoYn7wiXUbKKWE4wsy018Jz0yPAD/c/j0Ag79AkvEljYjkUgJSS4QjNo7MRPHVgK0cUSR7wE==pUai-----END PGP SIGNATURE-----

M
M
Maxime Devos wrote on 18 Apr 13:47 +0200
8f42495246e684121605e7cef2462a068130d5cc.camel@telenet.be
Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:
Toggle quote (10 lines)> + ;; Start the daemon.> + (define daemon-pid> + (fork+exec-command> + '#$(jami-daemon-configuration->command-line-arguments config)> + #:user "jami"> + #:group "jami"> + #:environment-variables> + (list (string-append "DBUS_SESSION_BUS_ADDRESS="> + "unix:path=/var/run/jami/bus"))))
It would be nice if this could be run in a containerthat only has access to the relevant parts of the file system(and not, say, /run/setuid-programs). See, e.g., gnu/build/linux-container.scm.
Greetings,Maxime.
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYHwcNBccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7hx2AQDtvj1EkLuPKUa7vjy9nYW3l80/mtgi+KNSxLp9hPMxtgD+JkkjJ8LUfJEgkI24ezKYQyhVyVvonAVA8hPNH2byCgs==7Vh7-----END PGP SIGNATURE-----

M
M
Maxim Cournoyer wrote on 19 Apr 14:07 +0200
(name . Maxime Devos)(address . maximedevos@telenet.be)(address . 47849@debbugs.gnu.org)
87czuqiiki.fsf@gmail.com
Hi Maxime!
Maxime Devos <maximedevos@telenet.be> writes:
Toggle quote (11 lines)> Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:> + (delete-file-recursively "/var/lib/jami/.cache/jami")> + (delete-file-recursively "/var/lib/jami/.config/jami")> + (delete-file-recursively "/var/lib/jami/.local/share/jami")> + (delete-file-recursively "/var/lib/jami/accounts"))>> You might want to verify whether /var/lib/jami/{.cache,.config,.local/share,.local}> aren't symbolic links. That way, if the Jami daemon is compromised (due to buffer> overflow --> arbitrary code execution or something), the attacker can't trick the> shepherd service into deleting arbitrary directories.
It would only be able to delete directories that are world writablethough, right? Seems the opportunity to cause damage is limited, butit's a simple check to add, so I'll do it. What about if the daemon wasrun in a container (your suggestion in a following email, to which Iagree would be a good thing)? It would prevent this kind of attack,right?
Toggle quote (6 lines)> This attack is _not_ blocked by fs.protected_symlinks. From the sysctl documentation:> When set to "1" symlinks are permitted to be followed only when outside> a sticky world-writable directory, or [...]>> /var/lib/jami is not world-writable (I'd hope).
No, it's only readable/writable by the 'jami' user of the service:
$ sudo ls -ald /var/lib/jamidrwx------ 1 jami jami 80 Apr 19 00:38 /var/lib/jami
Toggle quote (13 lines)> Example scenario:> * the jami daemon has a security bug that allows arbitrary code execution> within the daemon> * the attacker exploits this> * now the attacker can modify everything under /var/lib/jami> * the attacker deletes /var/lib/jami/.config and replaces it with a symlink> to /home/ANY-USER/.config> * eventually, the system reboots> * (delete-file-recursively "/var/lib/jami/.config/jami") is run.> As "/var/lib/jami/.config" points to "/home/ANY-USER/.config",> this means "/home/ANY-USER/.config/jami" is deleted.> * thus, ANY-USER loses their jami configuration
The cleanup code is run as the 'jami' user, so I don't think it'd beable to touch anything under /home/ANY-OTHER-USER/, unless they manuallyloosened permissions on their home directory (shooting themselves in thefoot).
Toggle quote (2 lines)> Does that makes sense to you?
It does! Thanks for explaining.
Maxim
M
M
Maxim Cournoyer wrote on 19 Apr 14:08 +0200
(name . Maxime Devos)(address . maximedevos@telenet.be)(address . 47849@debbugs.gnu.org)
878s5eiiil.fsf@gmail.com
Hi again,
Maxime Devos <maximedevos@telenet.be> writes:
Toggle quote (15 lines)> Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:>> + ;; Start the daemon.>> + (define daemon-pid>> + (fork+exec-command>> + '#$(jami-daemon-configuration->command-line-arguments config)>> + #:user "jami">> + #:group "jami">> + #:environment-variables>> + (list (string-append "DBUS_SESSION_BUS_ADDRESS=">> + "unix:path=/var/run/jami/bus"))))>> It would be nice if this could be run in a container> that only has access to the relevant parts of the file system> (and not, say, /run/setuid-programs). See, e.g., gnu/build/linux-container.scm.
I agree! The opendht service is already doing so, so it should not betoo difficult I believe. I'll try looking into it, after the 1.3.0release is done.
Thank you!
Maxim
M
M
Maxime Devos wrote on 19 Apr 16:41 +0200
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 47849@debbugs.gnu.org)
9fc5f3f454a77ccf2a8e09fa602755166c03abf3.camel@telenet.be
Maxim Cournoyer schreef op ma 19-04-2021 om 08:07 [-0400]:
Toggle quote (17 lines)> Hi Maxime!> > Maxime Devos <maximedevos@telenet.be> writes:> > > Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:> > + [...]> > + (delete-file-recursively "/var/lib/jami/accounts"))> > > > You might want to verify whether /var/lib/jami/{.cache,.config,.local/share,.local}> > aren't symbolic links. That way, if the Jami daemon is compromised (due to buffer> > overflow --> arbitrary code execution or something), the attacker can't trick the> > shepherd service into deleting arbitrary directories.> > It would only be able to delete directories that are world writable> though, right? Seems the opportunity to cause damage is limited, but> it's a simple check to add, so I'll do it.
Let's step through the relevant code of the shepherd service.
(shepherd-service (documentation "Run the Jami daemon.") [blabla] (start #~(lambda args [other stuff] (when [blabla, and a 'catch' form] (delete-file-recursively "/var/lib/jami/.cache/jami") [etcetera]) (let* (([blabla]) (user (passwd:uid [blabla jami user])) (group [likewise])) [blabla] (chown accounts-dir user group))) ;; Start the daemon (define daemon-pid (fork+exec-command [blabla Jami cmdline arguments] #:user "jami" #:group "jami" [blabla])) [blabla])) (stop [blabla]))
Remember that the shepherd daemon is run as root (and thereforehas read-write-execute access to everything). The 'start' procedure(and 'stop' procedure) are run _within_ the shepherd daemon. Thus, the'start' gexp is run as root.
As the start procedure didn't change the uid/gid from root to somethingelse, (delete-file-recursively "/var/lib/jami/.cache/jami") is run asroot. IIUC, root user can read/write anything, ignoring things like "user"and "group". World-writability is not required.
Toggle quote (5 lines)> What about if the daemon was> run in a container (your suggestion in a following email, to which I> agree would be a good thing)? It would prevent this kind of attack,> right?
I don't see how that would help. It is the _shepherd daemon_ (that runsas root) that runs (delete-file-recursively ...), not the attacker (fromwithin the compromised jami-daemon process). Perhaps this is cleared upby my previous response? If not, please walk me through the attack scenarioyou had in mind that would be thwarted by running the jami-daemon in a container.
Toggle quote (15 lines)> > Example scenario:> > * the jami daemon has a security bug that allows arbitrary code execution> > within the daemon> > * the attacker exploits this> > * now the attacker can modify everything under /var/lib/jami> > * the attacker deletes /var/lib/jami/.config and replaces it with a symlink> > to /home/ANY-USER/.config> > * eventually, the system reboots> > * (delete-file-recursively "/var/lib/jami/.config/jami") is run.> > As "/var/lib/jami/.config" points to "/home/ANY-USER/.config",> > this means "/home/ANY-USER/.config/jami" is deleted.> > * thus, ANY-USER loses their jami configuration> > The cleanup code is run as the 'jami' user,
Err, the cleanup code is run from within the shepherd service, outside thefork+exec-command. The shepherd service is run _within_ the shepherd daemon,which runs as *root*. Only in the fork+exec-command, code is run as the 'jami'user; the cleanup code is run as 'root'.
Toggle quote (3 lines)> so I don't think it'd be able to touch anything under /home/ANY-OTHER-USER/,> unless they manually loosened permissions on their home directory (shooting> themselves in the foot).
See my comment above.
Greetings,Maxime.
-----BEGIN PGP SIGNATURE-----
iI0EABYKADUWIQTB8z7iDFKP233XAR9J4+4iGRcl7gUCYH2WkBccbWF4aW1lZGV2b3NAdGVsZW5ldC5iZQAKCRBJ4+4iGRcl7mCIAP9jTTa/0eekxqBmhORKYGL12rVgE1ZdgcCXyh36NjjXPQEA/m3pbrue/O1BDrlXO1zgfJoxFMF/6JGQcKS77cbjXwY==1rqa-----END PGP SIGNATURE-----

M
M
Maxim Cournoyer wrote on 19 Apr 17:42 +0200
(name . Maxime Devos)(address . maximedevos@telenet.be)(address . 47849@debbugs.gnu.org)
87r1j6gu22.fsf@gmail.com
Hi Maxime,
Maxime Devos <maximedevos@telenet.be> writes:
Toggle quote (45 lines)> Maxim Cournoyer schreef op ma 19-04-2021 om 08:07 [-0400]:>> Hi Maxime!>> >> Maxime Devos <maximedevos@telenet.be> writes:>> >> > Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:>> > + [...]>> > + (delete-file-recursively "/var/lib/jami/accounts"))>> > >> > You might want to verify whether /var/lib/jami/{.cache,.config,.local/share,.local}>> > aren't symbolic links. That way, if the Jami daemon is compromised (due to buffer>> > overflow --> arbitrary code execution or something), the attacker can't trick the>> > shepherd service into deleting arbitrary directories.>> >> It would only be able to delete directories that are world writable>> though, right? Seems the opportunity to cause damage is limited, but>> it's a simple check to add, so I'll do it.>> Let's step through the relevant code of the shepherd service.>> (shepherd-service> (documentation "Run the Jami daemon.")> [blabla]> (start> #~(lambda args> [other stuff]> (when [blabla, and a 'catch' form]> (delete-file-recursively "/var/lib/jami/.cache/jami")> [etcetera])> (let* (([blabla])> (user (passwd:uid [blabla jami user]))> (group [likewise]))> [blabla] (chown accounts-dir user group)))> ;; Start the daemon> (define daemon-pid> (fork+exec-command [blabla Jami cmdline arguments]> #:user "jami" #:group "jami" [blabla]))> [blabla]))> (stop [blabla])) >> Remember that the shepherd daemon is run as root (and therefore> has read-write-execute access to everything). The 'start' procedure> (and 'stop' procedure) are run _within_ the shepherd daemon. Thus, the> 'start' gexp is run as root.
Ah yes, looking the service definition it's obvious. Sorry for missingthat earlier :-).
Toggle quote (15 lines)> As the start procedure didn't change the uid/gid from root to something> else, (delete-file-recursively "/var/lib/jami/.cache/jami") is run as> root. IIUC, root user can read/write anything, ignoring things like "user"> and "group". World-writability is not required.>>> What about if the daemon was>> run in a container (your suggestion in a following email, to which I>> agree would be a good thing)? It would prevent this kind of attack,>> right?>> I don't see how that would help. It is the _shepherd daemon_ (that runs> as root) that runs (delete-file-recursively ...), not the attacker (from> within the compromised jami-daemon process). Perhaps this is cleared up> by my previous response?
Indeed! Thanks for taking the time to make it clear for me!
I'll address this in a v2 patch series, hopefully resolving the issueswith the daemon starting in double the first time the system isreconfigured (something to do with d-bus autospawning I think -- perhapsthe D-Bus ping method is enough to spawn the process if it was not yetup and running).
It'll take me some time to get to it; probably after the v1.3.0 isreleased.
Thank you!
Maxim
?