Skip to content
Commits on Source (32)
monkeysphere (0.44) unstable; urgency=medium
* Drop all direct use of perl (for now, we still ship
keytrans/openpgp2ssh/ssh2openpgp/pem2openpgp for others who
want it. It will be removed in a future version, though)
* Use gpg's --quick-* interface (Increase GnuPG dependency
to >= 2.1.17, where this interface was stabilized)
* Drop unused keytrans subcommands
* Avoid risky uses of chown
* monkeysphere-host import can now handle ed25519 host keys
* Avoid a shell invocation in agent-transfer
-- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Sun, 19 May 2019 19:03:04 -0400
monkeysphere (0.43) unstable; urgency=medium
* Depend on a modern version of GnuPG (>= 2.1.11) for --export-ssh-key
......
......@@ -73,7 +73,6 @@ install: all installman
install src/monkeysphere-authentication-keys-for-user $(DESTDIR)$(PREFIX)/share/monkeysphere
install -m 0644 src/share/common $(DESTDIR)$(PREFIX)/share/monkeysphere
install -m 0644 replaced/src/share/defaultenv $(DESTDIR)$(PREFIX)/share/monkeysphere
install -m 0755 src/share/checkperms $(DESTDIR)$(PREFIX)/share/monkeysphere
install -m 0755 src/share/keytrans $(DESTDIR)$(PREFIX)/share/monkeysphere
ln -sf ../share/monkeysphere/keytrans $(DESTDIR)$(PREFIX)/bin/pem2openpgp
ln -sf ../share/monkeysphere/keytrans $(DESTDIR)$(PREFIX)/bin/openpgp2ssh
......
......@@ -19,9 +19,10 @@ Dependencies
Monkeysphere depends on:
* GnuPG >= 2.1.11
* GnuPG >= 2.1.17
* find (POSIX or GNU should both work)
* Perl
* Perl's Crypt::OpenSSL::RSA module
* lockfile-progs or procmail's lockfile
* Bash
* OpenSSH's ssh-keygen utility (ideally >= 6.0)
* OpenSSH's ssh-keygen utility (>= 6.0)
* base64 (coreutils or fourmilab)
monkeysphere (0.44-1) experimental; urgency=medium
* new upstream version to experimental
* avoids use of perl, but still ships perl-based key translation
utilities for now
-- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Sun, 19 May 2019 19:06:45 -0400
monkeysphere (0.43-3) unstable; urgency=medium
* fix monkeysphere-host import-key (Closes: #909700)
......
From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
Date: Thu, 24 Jan 2019 15:33:32 -0500
Subject: Expose sshd logs when ssh test fails
(cherry picked from commit 27a86a05d74ca8f03e574d9776c2f0efd370ed17)
---
tests/basic | 1 +
1 file changed, 1 insertion(+)
diff --git a/tests/basic b/tests/basic
index 8815d69..5bf1149 100755
--- a/tests/basic
+++ b/tests/basic
@@ -87,6 +87,7 @@ ssh_test() {
return 0
else
echo "##### ssh connection test FAILED. returned: $RETURN, expected: $CODE"
+ cat "$TEMPDIR/sshd.log"
return 1
fi
}
From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
Date: Thu, 24 Jan 2019 16:57:26 -0500
Subject: Ensure that "make test-ed25519" works when no tty is present
(cherry picked from commit a8f4ac40bdf8d3a5331ca9b10e2c49a7af7d0e66)
---
tests/basic | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/basic b/tests/basic
index 5bf1149..d5c4692 100755
--- a/tests/basic
+++ b/tests/basic
@@ -344,7 +344,7 @@ if [ "$MONKEYSPHERE_TEST_USE_ED25519" = true ]; then
echo "### generating ed25519 key for testuser..."
# from the imported secret key
USER_FPR=8A4B353B4CBA6F30625498BAE00B5EEEBA79B482
- gpg --quick-add-key "$USER_FPR" ed25519 auth 2d
+ gpg --batch --no-tty --quick-add-key "$USER_FPR" ed25519 auth 2d
else
echo "### generating standard monkeysphere key for testuser..."
monkeysphere gen-subkey
From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
Date: Thu, 24 Jan 2019 15:53:55 -0500
Subject: Dump remaining jobs during test cleanup
See https://bugs.debian.org/920038 for more information about why this
additional debugging information might be useful.
(cherry picked from commit 318134aaa04befb6c1cf99b1b5ed21ec16ed33ff)
---
tests/common | 1 +
1 file changed, 1 insertion(+)
diff --git a/tests/common b/tests/common
index 727ad0f..0fc6c19 100644
--- a/tests/common
+++ b/tests/common
@@ -33,5 +33,6 @@ cleanup() {
kill "$SSHD_PID"
fi
+ jobs
wait
}
From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
Date: Fri, 10 May 2019 12:15:00 -0400
Subject: tests/basic: ensure functionality with output of standard OpenSSH
keygen
Our "fix" to https://bugs.debian.org/909700 in
d8fc9f284fc9a128a174b16ad19e866f1c00bc27 just avoided testing the
actual typical default output of ssh-keygen.
While this was fair to do in tests/keytrans, where it is exercised on
pem2openpgp (which is explicitly defined as only accepting PEM input),
this is inappropriate for testing monkeysphere in general.
So now, the test suite breaks again, but we need to provide a proper
fix.
---
tests/basic | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/basic b/tests/basic
index d5c4692..72a79a6 100755
--- a/tests/basic
+++ b/tests/basic
@@ -275,7 +275,7 @@ fi
echo
echo "##################################################"
echo "### import host key..."
-ssh-keygen -m PEM -b 3072 -t rsa -N '' -f "$TEMPDIR"/ssh_host_rsa_key
+ssh-keygen -b 3072 -t rsa -N '' -f "$TEMPDIR"/ssh_host_rsa_key
monkeysphere-host import-key "$TEMPDIR"/ssh_host_rsa_key ssh://testhost.example
echo
From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
Date: Fri, 10 May 2019 16:18:28 -0400
Subject: Use gpg's reworked --quick-* interface for adding/revoking uids
This interface stabilized in GnuPG 2.1.17, so we increase our
versioned dependency.
---
README | 2 +-
src/share/mh/add_name | 5 +----
src/share/mh/revoke_name | 10 +---------
3 files changed, 3 insertions(+), 14 deletions(-)
diff --git a/README b/README
index b47a9bf..33f5a0d 100644
--- a/README
+++ b/README
@@ -19,7 +19,7 @@ Dependencies
Monkeysphere depends on:
- * GnuPG >= 2.1.11
+ * GnuPG >= 2.1.17
* Perl
* Perl's Crypt::OpenSSL::RSA module
* lockfile-progs or procmail's lockfile
diff --git a/src/share/mh/add_name b/src/share/mh/add_name
index f37d9df..6357284 100644
--- a/src/share/mh/add_name
+++ b/src/share/mh/add_name
@@ -50,10 +50,7 @@ else
fi
# execute edit-key script
-if gpg_host --export-secret-keys "$keyID" | \
- PEM2OPENPGP_USAGE_FLAGS=authenticate \
- "$SYSSHAREDIR/keytrans" adduserid "$keyID" "$serviceName" \
- | gpg_host --import ; then
+if gpg_host --quick-add-uid "$keyID" "$serviceName" ; then
gpg_host --check-trustdb
diff --git a/src/share/mh/revoke_name b/src/share/mh/revoke_name
index d807ac1..4e8d666 100644
--- a/src/share/mh/revoke_name
+++ b/src/share/mh/revoke_name
@@ -46,15 +46,7 @@ else
fi
# actually revoke:
-
-# the gpg secring might not contain the host key we are trying to
-# revoke (let alone any selfsig over that host key), but the plain
-# --export won't contain the secret key. "keytrans revokeuserid"
-# needs access to both pieces, so we feed it both of them.
-
-if gpg_host --export-secret-keys "$keyID" \
- | "$SYSSHAREDIR/keytrans" revokeuserid "$keyID" "$serviceName" \
- | gpg_host --import ; then
+if gpg_host --quick-revoke-uid "$keyID" "$serviceName" ; then
gpg_host --check-trustdb
From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
Date: Fri, 10 May 2019 16:30:11 -0400
Subject: mh import-key: use ssh-add and gpg-agent for import (Closes:
#909700)
This directly fixes the problem that monkeysphere-host was failing to
import the style of SSH host key that was generated by default by
ssh-keygen.
As a side effect, we can now support all the host key algorithms that
are supported by both gpg-agent (in its ssh-agent implementation) and
ssh-add.
The lockfile and the dancing around the gpg-agent run in
$GNUPGHOME_HOST is a bit awkward. It would be nicer to use an
ephemeral GnuPG homedir as recommended in the "Unattended Usage"
section of the gnupg info pages, but to do that we'd need to be able
to extract the secret key, which is blocked by
https://dev.gnupg.org/T4490
---
man/man8/monkeysphere-host.8 | 7 ++--
src/share/mh/import_key | 82 ++++++++++++++++++++++++++++++++++++++++----
2 files changed, 79 insertions(+), 10 deletions(-)
diff --git a/man/man8/monkeysphere-host.8 b/man/man8/monkeysphere-host.8
index 3e37057..6ae30ed 100644
--- a/man/man8/monkeysphere-host.8
+++ b/man/man8/monkeysphere-host.8
@@ -27,9 +27,10 @@ be omitted, and \fBmonkeysphere\-host\fP will operate on it.
\fBmonkeysphere\-host\fP takes various subcommands:
.TP
.B import\-key FILE SCHEME://HOSTNAME[:PORT]
-Import a PEM\-encoded host secret key from file FILE. If FILE is
-`\-', then the key will be imported from stdin. Only RSA keys are
-supported at the moment. SCHEME://HOSTNAME[:PORT] is used to specify
+Import an SSH host secret key from file FILE. If FILE is
+`\-', then the key will be imported from stdin, and must be an
+RSA key in PEM\-encoded format.
+SCHEME://HOSTNAME[:PORT] is used to specify
the scheme (e.g. ssh or https), fully\-qualified hostname (and port)
used in the user ID of the new OpenPGP key (e.g. ssh://example.net or
https://www.example.net). If PORT is not specified, then no port is
diff --git a/src/share/mh/import_key b/src/share/mh/import_key
index 0f362b8..ebe2cc3 100644
--- a/src/share/mh/import_key
+++ b/src/share/mh/import_key
@@ -18,7 +18,7 @@ local serviceName="$2"
# check that key file specified
if [ -z "$keyFile" ] ; then
- failure "Must specify PEM-encoded key file to import, or specify '-' for stdin."
+ failure "Must specify key file to import, or specify '-' for PEM-encoded RSA key on stdin."
fi
# fail if hostname not specified
@@ -37,16 +37,84 @@ mkdir -p "${MHDATADIR}"
mkdir -p "${GNUPGHOME_HOST}"
chmod 700 "${GNUPGHOME_HOST}"
-# import pem-encoded key to an OpenPGP private key
+key_type_from_file() {
+ # translates from OpenSSH's pubkey format string to GnuPG's
+ # Key-Type parameter:
+ local keyType
+ if keyType=$(ssh-keygen -y -f "$keyFile" | awk '{ print $1 }'); then
+ case "$keyType" in
+ ssh-dss)
+ echo DSA
+ ;;
+ ecdsa-sha2-nistp256)
+ echo ECDSA
+ ;;
+ ssh-ed25519)
+ echo EDDSA
+ ;;
+ ssh-rsa)
+ echo RSA
+ ;;
+ *)
+ log error "unknown key type '$keyType' from file '$keyFile'"
+ return 1
+ ;;
+ esac
+ else
+ log error "ssh-keygen could not interpret '$keyFile'"
+ return 1
+ fi
+ return 0
+}
+
+
if [ "$keyFile" = '-' ] ; then
- log verbose "importing key from stdin..."
+ # import PEM-encoded RSA stdin to an OpenPGP private key
+ log verbose "importing PEM-encoded RSA key from stdin..."
PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$serviceName" \
| gpg_host --import
else
- log verbose "importing key from file '$keyFile'..."
- PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$serviceName" \
- <"$keyFile" \
- | gpg_host --import
+ # import some sort of file that OpenSSH's keygen can handle
+ if keyType=$(key_type_from_file "$keyFile"); then
+ # we lock to avoid concurrent interactions with gpg-agent and
+ # the sshcontrol file would be dubious
+ lock create "$GNUPGHOME_HOST/importlock"
+ if test -e "$GNUPGHOME_HOST/sshcontrol" && grep -q '^[0-9A-F]' "$GNUPGHOME_HOST/sshcontrol"; then
+ backupSshControl=$(mktemp "$GNUPGHOME_HOST/sshcontrol.XXXXXXXX")
+ log error "$GNUPGHOME_HOST/sshcontrol already contained a key, backing up to $backupSshControl"
+ mv -f "$GNUPGHOME_HOST/sshcontrol" "$backupSshControl"
+ fi
+
+ log verbose "importing $keyType key from file '$keyFile'..."
+ if ! { test -e "$GNUPGHOME_HOST/gpg-agent.conf" && grep -Fxq batch "$GNUPGHOME_HOST/gpg-agent.conf" ; }; then
+ echo batch >> "$GNUPGHOME_HOST/gpg-agent.conf"
+ GNUPGHOME="$GNUPGHOME_HOST" gpgconf --reload gpg-agent
+ GNUPGHOME="$GNUPGHOME_HOST" gpgconf --launch gpg-agent
+ fi
+ SSH_AUTH_SOCK=$(GNUPGHOME="$GNUPGHOME_HOST" gpgconf --list-dirs agent-ssh-socket) ssh-add "$keyFile"
+ if keyGrip=$(awk '/^[0-9A-F]/{print $1}' < "$GNUPGHOME_HOST/sshcontrol") &&
+ test -n "$keyGrip" && [ $(wc -l <<<"$keyGrip") -eq 1 ] ; then
+ gpg_host --batch --full-generate-key <<EOF
+Key-Type: $keyType
+Key-Grip: $keyGrip
+Key-Usage: auth
+Name-Real: $serviceName
+%no-protection
+%commit
+EOF
+ else
+ rm -f "$GNUPGHOME_HOST/sshcontrol"
+ lock remove "$GNUPGHOME_HOST/importlock"
+ failure "did not find a single keygrip in $GNUPGHOME_HOST/sshcontrol during import"
+ fi
+ rm -f "$GNUPGHOME_HOST/sshcontrol"
+ lock remove "$GNUPGHOME_HOST/importlock"
+ else
+ log error "falling back to pem2openpgp (which will probably still fail)..."
+ PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$serviceName" \
+ <"$keyFile" \
+ | gpg_host --import
+ fi
fi
# export to OpenPGP public key to file
0001-Expose-sshd-logs-when-ssh-test-fails.patch
0002-Ensure-that-make-test-ed25519-works-when-no-tty-is-p.patch
0003-Dump-remaining-jobs-during-test-cleanup.patch
0004-tests-basic-ensure-functionality-with-output-of-stan.patch
0005-Use-gpg-s-reworked-quick-interface-for-adding-revoki.patch
0006-mh-import-key-use-ssh-add-and-gpg-agent-for-import-C.patch
......@@ -10,6 +10,8 @@
# They are Copyright 2008-2009, and are all released under the GPL, version 3
# or later.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# update all keys from the key servers
monkeysphere-authentication refresh-keys
......
# example Monkeysphere cron job:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Hourly: update the per-user authorized_keys in /var based on
# ~/.monkeysphere/authorized_user_ids
......
......@@ -13,7 +13,7 @@
# This script uses bashisms
# It currently needs OpenSSL binaries to work properly
# It currently needs OpenSSL binaries, gpg, and gpgsm to work properly
# It assumes that the monkeysphere-host keyring is in
# /var/lib/monkeysphere/host (which it is on debian)
......@@ -67,23 +67,40 @@ EOF
}
gencertreq() {
keyid="$1"
local keyid="$1"
timestamp=$(gpg --fixed-list-mode --with-colons --list-keys "0x$keyid!" | grep ^pub: | cut -f6 -d:)
# It would be great to be able to do all of this with gpgsm
# directly. See https://dev.gnupg.org/T4503 for why we need to
# use OpenSSL for now.
san=''
primary=''
local timestamp=$(gpg --fixed-list-mode --with-colons --list-keys "0x$keyid!" | awk -F: '/^pub:/{ print $6 }')
local keygrip=$(gpg --fixed-list-mode --with-keygrip --with-colons --list-keys "0x$keyid!" | awk -F: '/^grp:/{ print $10 }')
local primary
# find all the $proto-using User IDs:
uids=$(gpg --fixed-list-mode --with-colons --list-keys "0x$keyid!" | \
local uids=$(gpg --fixed-list-mode --with-colons --list-keys \&"$keygrip" | \
grep '^uid:' | cut -f10 -d: | \
grep '^'"${proto}"'\\x3a//' | \
sed -r -e 's!^'"${proto}"'\\x3a//!!' -e 's!:[0-9]+$!!')
primary=$(printf "%s" "$uids" | head -n1)
# does gpgsm know about this key?
if ! gpgsm --with-colons --with-keygrip --list-keys \&"$keygrip" | grep -q ^grp: ; then
# ensure that gpgsm has a dummy self-signed cert; otherwise, it
# cannot emit the secret key in the necessary form.
local batchcmd="Key-Type: RSA
Key-Grip: $keygrip
Name-DN: CN=$primary (monkeysphere dummy self-signed)
Serial: $keygrip
%commit"
local dummycert="$(gpgsm --armor --batch --generate-key <<<"$batchcmd")"
gpgsm --import <<<"$dummycert"
fi
printf "Certificate Request for TLS WWW server %s\n[OpenPGP key %s]\n" "$primary" "$keyid"
openssl req -text -new \
-config <(get_openssl_config "$timestamp" "$uids") \
-key <(gpg --export-secret-key "$keyid" | openpgp2ssh "$keyid") \
-key <(gpgsm --armor --export-secret-key-raw \&"$keygrip") \
-subj "/CN=${primary}/"
}
......
......@@ -27,9 +27,10 @@ be omitted, and \fBmonkeysphere\-host\fP will operate on it.
\fBmonkeysphere\-host\fP takes various subcommands:
.TP
.B import\-key FILE SCHEME://HOSTNAME[:PORT]
Import a PEM\-encoded host secret key from file FILE. If FILE is
`\-', then the key will be imported from stdin. Only RSA keys are
supported at the moment. SCHEME://HOSTNAME[:PORT] is used to specify
Import an SSH host secret key from file FILE. If FILE is
`\-', then the key will be imported from stdin, and must be an
RSA key.
SCHEME://HOSTNAME[:PORT] is used to specify
the scheme (e.g. ssh or https), fully\-qualified hostname (and port)
used in the user ID of the new OpenPGP key (e.g. ssh://example.net or
https://www.example.net). If PORT is not specified, then no port is
......
......@@ -56,8 +56,7 @@ post-build {
# fix perl shebang line to point to macports perl install
exec sed -i .tmp -e "s|^#!/usr/bin/perl -T$|#!/opt/local/bin/perl -T|" \
${worksrcpath}/src/share/keytrans \
${worksrcpath}/src/share/checkperms
${worksrcpath}/src/share/keytrans
# remove leftover sed cruft
exec find ${worksrcpath} -name *.tmp -delete
......
......@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
#include <gcrypt.h>
#include <ctype.h>
......@@ -100,20 +101,66 @@ char* gpg_agent_sockname () {
FILE *f;
size_t bytecount, pos;
char buf[BUFSIZE];
int pipefd[2], wstatus;
pid_t pid, waited = 0;
f = popen("gpgconf --list-dirs | grep ^agent-socket: | cut -f2 -d:", "r");
if (!f)
if (pipe(pipefd)) {
fprintf (stderr, "Could not pipe (%d) %s\n", errno, strerror (errno));
return NULL;
}
pid = fork();
if (pid == 0) {
if (dup2 (pipefd[1], 1) == -1) {
fprintf (stderr, "failed to dup2 (%d) %s", errno, strerror (errno));
exit (1);
}
close (pipefd[0]);
/* FIXME: should we close other open file descriptors? gpgconf is
supposed to do that for us, but if we wanted to be defensive we
might want to do it here too. */
if (execlp ("gpgconf", "gpgconf", "--list-dirs", "agent-socket", NULL)) {
fprintf (stderr, "failed to execl (%d) %s", errno, strerror (errno));
exit (1);
}
}
close (pipefd[1]);
waited = waitpid (pid, &wstatus, 0);
if (waited != pid) {
fprintf (stderr, "waitpid failed (%d) %s\n", errno, strerror (errno));
close (pipefd[0]);
return NULL;
}
if (!WIFEXITED(wstatus)) {
fprintf (stderr, "'gpgconf --list-dirs agent-socket' did not exit cleanly!\n");
close (pipefd[0]);
return NULL;
}
if (WEXITSTATUS(wstatus)) {
fprintf (stderr, "'gpgconf --list-dirs agent-socket' exited with non-zero return code %d\n", WEXITSTATUS(wstatus));
close (pipefd[0]);
return NULL;
}
f = fdopen (pipefd[0], "r");
if (f == NULL) {
fprintf (stderr, "failed to get readable pipe (%d) %s\n", errno, strerror (errno));
close (pipefd[0]);
return NULL;
}
pos = 0;
while (!feof(f))
{
bytecount = fread(buf + pos, 1, sizeof(buf) - pos, f);
if (ferror(f))
if (ferror(f)) {
fclose (f);
return NULL;
}
pos += bytecount;
if (pos >= sizeof(buf)) /* too much data! */
if (pos >= sizeof(buf)) {/* too much data! */
fclose (f);
return NULL;
}
}
fclose (f);
buf[pos] = '\0';
return trim_and_unescape(buf);
}
......@@ -706,6 +753,10 @@ int main (int argc, const char* argv[]) {
return 1;
}
gpg_agent_socket = gpg_agent_sockname();
if (gpg_agent_socket == NULL) {
fprintf (stderr, "failed to get gpg-agent socket name!\n");
return 1;
}
/* launch gpg-agent if it is not already connected */
err = assuan_socket_connect (e.ctx, gpg_agent_socket,
......
......@@ -246,8 +246,7 @@ case $COMMAND in
;;
'import-subkey'|'import'|'i')
source "${MSHAREDIR}/import_subkey"
import_subkey "$@"
failure "import-subkey is not implemented yet. (see https://dev.gnupg.org/T4489)"
;;
'gen-subkey'|'g')
......@@ -279,7 +278,7 @@ case $COMMAND in
'sshfprs-for-userid')
CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}}
source "${MSHAREDIR}/keys_for_userid"
keys_for_userid "$@" | "$SYSSHAREDIR/keytrans" sshfpr
keys_for_userid "$@" | ssh-keygen -l -f - | awk '{ print $2 }'
;;
'keys-from-userid')
......
......@@ -131,11 +131,15 @@ check_service_name() {
[ -n "$name" ] || \
failure "You must supply a service name to check"
printf '%s' "$name" | perl -n -e '($str = $_) =~ s/\s//g ; exit !(lc($str) eq $_);' || \
[[ "$name" = "${name,,}" ]] || \
failure "Not a valid service name: '$name'
Service names should be canonicalized to all lower-case,
with no whitespace"
Service names should be canonicalized to all lower-case."
[[ "$name" = "${name//$' \n\r\t'/}" ]] || \
failure "Not a valid service name: '$name'
Service names should not contain whitespace."
[[ "$name" =~ ^[a-z0-9./:-]+$ ]] || \
failure "Not a valid service name: '$name'
......
#!/usr/bin/perl -T
# checkperms: ensure as best we can that a given file can only be
# modified by the given user (or the superuser, naturally). This
# means checking file ownership and permissions all the way back to
# the root directory. Pass the file by its absolute path.
# example invocation:
# checkperms dkg /home/dkg/.monkeysphere/authorized_user_ids
# return values: zero if we believe the file and path can only be
# modified by the user. non-zero otherwise.
# see StrictModes in sshd_config(5) (and its implementation in
# OpenSSH's secure_filename() in auth.c) for the initial
# inspiration/rationale for this code.
# Author:
# Daniel Kahn Gillmor <dkg@fifthhorseman.net>
# Started on: 2009-07-31 11:10:16-0400
# License: GPL v3 or later
use strict;
use Cwd qw(realpath); # found in debian in perl-base
use File::stat; # found in debian in perl-modules
use User::pwent; # found in debian in perl-modules
use Fcntl qw(:mode); # for S_IS* functions (in perl-base)
use File::Basename; # for dirname (in perl-modules)
my $username = shift;
my $path = shift;
defined($username) or die "You must pass a username and an absolute path.\n";
defined($path) or die "You must pass a username and an absolute path.\n";
my $pw = getpwnam($username) or die "no such user $username\n";
$path =~ m#^/# or die "path was not absolute (did not start with /)\n";
sub mslog {
my $level = shift;
# FIXME: check and compare the log level
if ($ENV{LOG_LEVEL} eq 'DEBUG') {
my $format = shift;
my $out = sprintf($format, @_);
$out =~ s/^/$ENV{LOG_PREFIX}/ ;
printf STDERR "%s", $out;
}
}
## return undef if permissions are OK. otherwise return an error string
sub permissions_ok {
my $user = shift;
my $path = shift;
# if we can't even stat the path, the permissions are not ok:
my $stat = lstat($path) or return "cannot stat '$path'";
while (S_ISLNK($stat->mode)) {
my $newpath = realpath($path) or return "cannot trace symlink '$path'";
mslog('DEBUG', "tracing link %s to %s\n", $path, $newpath);
$path = $newpath;
$stat = lstat($path) or return "cannot stat '$path'";
}
mslog('DEBUG', "checking '%s'\n", $path);
if (($stat->uid != $user->uid) &&
($stat->uid != 0)) {
return sprintf("improper ownership on '%s': owner ID %d is neither %s (ID %d) nor the superuser",
$path, $stat->uid, $user->name, $user->uid);
}
if ($stat->mode & S_IWGRP) {
return sprintf("improper group writability on '%s'", $path);
}
if ($stat->mode & S_IWOTH) {
return sprintf("improper other writability on '%s'", $path);
}
# see the rationalization in secure_filename() in auth.c in the
# OpenSSH sources for an explanation of this bailout (see also
# monkeysphere #675):
if ($path eq $user->dir) {
mslog('DEBUG', "stopping at %s's home directory '%s'\n", $user->name, $path);
return undef;
}
my $nextlevel = dirname($path);
if ($path eq $nextlevel) { # we bottom out at the root (/ in UNIX)
return undef;
}
return permissions_ok($user, $nextlevel);
}
my $err = permissions_ok($pw, $path);
if (defined($err)) {
printf(STDERR "%s%s\n", $ENV{LOG_PREFIX}, $err);
exit(1);
} else {
exit(0);
}