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
|
;;; Guile-Git --- GNU Guile bindings of libgit2
;;; Copyright © 2019 Mathieu Othacehe <m.othacehe@gmail.com>
;;;
;;; This file is part of Guile-Git.
;;;
;;; Guile-Git 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.
;;;
;;; Guile-Git 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 Guile-Git. If not, see <http://www.gnu.org/licenses/>.
(define-module (tests ssh)
#:use-module (git auth)
#:use-module (tests helpers)
#:use-module ((srfi srfi-64) #:select (test-skip))
#:use-module (ice-9 popen)
#:use-module (ice-9 rdelim)
#:use-module (ice-9 regex)
#:export (sshd-available?
with-sshd-server
with-ssh-agent
make-client-ssh-auth))
(define sshd "@SSHD@")
(define %ssh-dir (path-join "@abs_top_builddir@" "/tests/.ssh"))
(define (in-ssh-directory . args)
(apply path-join %ssh-dir args))
(define (sshd-available?)
;; Return #t if sshd is available (it does not support
;; ‘--version’ or anything similar though).
(= 256 (parameterize ((current-error-port (%make-void-port "w")))
(system* sshd "--something-not-supported"))))
(define (start-sshd port)
(define (write-authorized-keys file)
(call-with-output-file file
(lambda (port)
;; We need to pass PATH so that git binary (git-upload-pack) can be
;; found from sshd.
(format port "environment=\"PATH=~a\" ~a"
(getenv "PATH")
(call-with-input-file (in-ssh-directory "id_rsa_client.pub")
read-string)))))
(define (write-sshd-conf conf authorized-keys)
(call-with-output-file conf
(lambda (port)
(format port "\
# Listen only on the loopback device.
ListenAddress localhost
AuthorizedKeysFile ~a
PidFile ~a
PermitUserEnvironment yes
# libssh2 and therefore libgit2 do not yet support
# sha2-based rsa algorithms.
# See upstream issue:
# https://github.com/libssh2/libssh2/issues/536
HostkeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
# Disable permission checks on auth files for the sake
# of isolated build environments.
StrictModes no~%"
authorized-keys
(in-ssh-directory "sshd_pid")))))
(let ((sshd-conf (in-ssh-directory "sshd.conf"))
(sshd-key (in-ssh-directory "id_rsa_server"))
(authorized-keys (in-ssh-directory "authorized_keys")))
(unsetenv "SSH_AUTH_SOCK")
(chmod sshd-key #o600)
(write-authorized-keys authorized-keys)
(write-sshd-conf sshd-conf authorized-keys)
(system* sshd "-p" (number->string port) "-f" sshd-conf "-h" sshd-key)))
(define (stop-sshd)
(define (read-pid port)
(string-trim-right (read-string port) #\newline))
(let ((pid
(call-with-input-file (in-ssh-directory "sshd_pid")
read-pid)))
(system* "kill" pid)))
(define (valid-user-shell?)
"Return true if the current user has a valid shell in /etc/passwd."
;; Note: This check is useful to account for Guix build environments, where
;; /etc/passwd refers to a non-existent shell for the build user. In that
;; case, it's impossible to log in over SSH as sshd blindly attempts to
;; execute the user shell.
(and=> (false-if-exception (getpwuid (getuid)))
(lambda (passwd)
(let ((shell (passwd:shell passwd)))
(access? shell (logior R_OK X_OK))))))
(define-syntax-rule (with-sshd-server port tests ...)
(let ((skip? (or (not (sshd-available?))
(not (valid-user-shell?)))))
(dynamic-wind
(lambda ()
(if skip?
(test-skip (length '(tests ...)))
(start-sshd port)))
(lambda ()
tests ...)
(lambda ()
(unless skip?
(stop-sshd))))))
(define %ssh-auth-sock-regexp
(make-regexp "SSH_AUTH_SOCK=(.*); export SSH_AUTH_SOCK;"))
(define %ssh-agent-pid-regexp
(make-regexp "SSH_AGENT_PID=(.*); export SSH_AGENT_PID;"))
(define (start-ssh-agent)
(let* ((p (open-input-pipe "ssh-agent -s"))
(ssh-auth-sock-data (read-line p))
(ssh-agent-pid-data (read-line p))
(sock
(let ((match (regexp-exec %ssh-auth-sock-regexp
ssh-auth-sock-data)))
(match:substring match 1)))
(pid (let ((match (regexp-exec %ssh-agent-pid-regexp
ssh-agent-pid-data)))
(match:substring match 1))))
(setenv "SSH_AUTH_SOCK" sock)
pid))
(define (ssh-agent-add-client-key)
(system* "ssh-add" (in-ssh-directory "id_rsa_client")))
(define-syntax-rule (with-ssh-agent body ...)
(let ((pid (start-ssh-agent)))
(dynamic-wind
(const #f)
(lambda ()
(ssh-agent-add-client-key)
body ...)
(lambda ()
(system* "kill" pid)
(unsetenv "SSH_AUTH_SOCK")))))
(define (make-client-ssh-auth)
(let ((pub-key (in-ssh-directory "id_rsa_client.pub"))
(pri-key (in-ssh-directory "id_rsa_client")))
(chmod pri-key #o600)
(chmod pub-key #o644)
(%make-auth-ssh-credentials
pub-key
pri-key)))
|