diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index f24afccb405a..39ed914994c1 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -244,6 +244,7 @@
postsrsd = 220;
opendkim = 221;
dspam = 222;
+ gale = 223;
# When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
@@ -465,6 +466,7 @@
postsrsd = 220;
opendkim = 221;
dspam = 222;
+ gale = 223;
# When adding a gid, make sure it doesn't match an existing
# uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index b2f08feb1082..d9e8c2da5b32 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -301,6 +301,7 @@
./services/networking/firewall.nix
./services/networking/flashpolicyd.nix
./services/networking/freenet.nix
+ ./services/networking/gale.nix
./services/networking/gateone.nix
./services/networking/git-daemon.nix
./services/networking/gnunet.nix
diff --git a/nixos/modules/services/networking/gale.nix b/nixos/modules/services/networking/gale.nix
new file mode 100644
index 000000000000..3a5d9bd63c7b
--- /dev/null
+++ b/nixos/modules/services/networking/gale.nix
@@ -0,0 +1,182 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.gale;
+ # we convert the path to a string to avoid it being copied to the nix store,
+ # otherwise users could read the private key as all files in the store are
+ # world-readable
+ keyPath = toString cfg.keyPath;
+ # ...but we refer to the pubkey file using a path so that we can ensure the
+ # config gets rebuilt if the public key changes (we can assume the private key
+ # will never change without the public key having changed)
+ gpubFile = cfg.keyPath + "/${cfg.domain}.gpub";
+ home = "/var/lib/gale";
+ keysPrepared = cfg.keyPath != null && lib.pathExists cfg.keyPath;
+in
+{
+ options = {
+ services.gale = {
+ enable = mkEnableOption "the Gale messaging daemon";
+
+ user = mkOption {
+ default = "gale";
+ type = types.str;
+ description = "Username for the Gale daemon.";
+ };
+
+ group = mkOption {
+ default = "gale";
+ type = types.str;
+ description = "Group name for the Gale daemon.";
+ };
+
+ setuidWrapper = mkOption {
+ default = null;
+ description = "Configuration for the Gale gksign setuid wrapper.";
+ };
+
+ domain = mkOption {
+ default = "";
+ type = types.str;
+ description = "Domain name for the Gale system.";
+ };
+
+ keyPath = mkOption {
+ default = null;
+ type = types.nullOr types.path;
+ description = ''
+ Directory containing the key pair for this Gale domain. The expected
+ filename will be taken from the domain option with ".gpri" and ".gpub"
+ appended.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = ''
+ Additional text to be added to /etc/gale/conf.
+ '';
+ };
+ };
+ };
+
+ config = mkMerge [
+ (mkIf cfg.enable {
+ assertions = [{
+ assertion = cfg.domain != "";
+ message = "A domain must be set for Gale.";
+ }];
+
+ warnings = mkIf (!keysPrepared) [
+ "You must run gale-install in order to generate a domain key."
+ ];
+
+ system.activationScripts.gale = mkIf cfg.enable (
+ stringAfter [ "users" "groups" ] ''
+ chmod -R 755 ${home}
+ mkdir -m 0777 -p ${home}/auth/cache
+ mkdir -m 1777 -p ${home}/auth/local # GALE_DOMAIN.gpub
+ mkdir -m 0700 -p ${home}/auth/private # ROOT.gpub
+ mkdir -m 0755 -p ${home}/auth/trusted # ROOT
+ mkdir -m 0700 -p ${home}/.gale
+ mkdir -m 0700 -p ${home}/.gale/auth
+ mkdir -m 0700 -p ${home}/.gale/auth/private # GALE_DOMAIN.gpri
+
+ ln -sf ${pkgs.gale}/etc/gale/auth/trusted/ROOT "${home}/auth/trusted/ROOT"
+ chown -R ${cfg.user}:${cfg.group} ${home}
+ ''
+ );
+
+ environment = {
+ etc = {
+ "gale/auth".source = home + "/auth"; # symlink /var/lib/gale/auth
+ "gale/conf".text = ''
+ GALE_USER ${cfg.user}
+ GALE_DOMAIN ${cfg.domain}
+ ${cfg.extraConfig}
+ '';
+ };
+
+ systemPackages = [ pkgs.gale ];
+ };
+
+ users.extraUsers = [{
+ name = cfg.user;
+ description = "Gale daemon";
+ uid = config.ids.uids.gale;
+ group = cfg.group;
+ home = home;
+ createHome = true;
+ }];
+
+ users.extraGroups = [{
+ name = cfg.group;
+ gid = config.ids.gids.gale;
+ }];
+ })
+ (mkIf (cfg.enable && keysPrepared) {
+ assertions = [
+ {
+ assertion = cfg.keyPath != null
+ && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpub");
+ message = "Couldn't find a Gale public key for ${cfg.domain}.";
+ }
+ {
+ assertion = cfg.keyPath != null
+ && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpri");
+ message = "Couldn't find a Gale private key for ${cfg.domain}.";
+ }
+ ];
+
+ services.gale.setuidWrapper = {
+ program = "gksign";
+ source = "${pkgs.gale}/bin/gksign";
+ owner = cfg.user;
+ group = cfg.group;
+ setuid = true;
+ setgid = false;
+ };
+
+ security.setuidOwners = [ cfg.setuidWrapper ];
+
+ systemd.services.gale-galed = {
+ description = "Gale messaging daemon";
+ wantedBy = [ "multi-user.target" ];
+ wants = [ "gale-gdomain.service" ];
+ after = [ "network.target" ];
+
+ preStart = ''
+ install -m 0640 ${keyPath}/${cfg.domain}.gpri "${home}/.gale/auth/private/"
+ install -m 0644 ${gpubFile} "${home}/.gale/auth/private/${cfg.domain}.gpub"
+ install -m 0644 ${gpubFile} "${home}/auth/local/${cfg.domain}.gpub"
+ chown -R ${cfg.user}:${cfg.group} ${home}
+ '';
+
+ serviceConfig = {
+ Type = "forking";
+ ExecStart = "@${pkgs.gale}/bin/galed galed";
+ User = cfg.user;
+ Group = cfg.group;
+ PermissionsStartOnly = true;
+ };
+ };
+
+ systemd.services.gale-gdomain = {
+ description = "Gale AKD daemon";
+ wantedBy = [ "multi-user.target" ];
+ requires = [ "gale-galed.service" ];
+ after = [ "gale-galed.service" ];
+
+ serviceConfig = {
+ Type = "forking";
+ ExecStart = "@${pkgs.gale}/bin/gdomain gdomain";
+ User = cfg.user;
+ Group = cfg.group;
+ };
+ };
+ })
+ ];
+}
diff --git a/pkgs/applications/networking/instant-messengers/gale/default.nix b/pkgs/applications/networking/instant-messengers/gale/default.nix
new file mode 100644
index 000000000000..65f6cab6e81c
--- /dev/null
+++ b/pkgs/applications/networking/instant-messengers/gale/default.nix
@@ -0,0 +1,31 @@
+{ stdenv, fetchFromGitHub, adns, boehmgc, openssl, automake, m4, autoconf
+, libtool, pkgconfig }:
+
+stdenv.mkDerivation {
+ name = "gale-1.1happy";
+
+ src = fetchFromGitHub {
+ owner = "grawity";
+ repo = "gale";
+ rev = "b34a67288e8bd6f0b51b60abb704858172a3665c";
+ sha256 = "19mcisxxqx70m059rqwv7wpmp94fgyckzjwywpmdqd7iwvppnsqf";
+ };
+
+ nativeBuildInputs = [ m4 libtool automake autoconf ];
+ buildInputs = [ boehmgc openssl adns pkgconfig ];
+
+ patches = [ ./gale-install.in.patch ];
+
+ preConfigure = ''
+ substituteInPlace configure.ac --replace \$\{sysconfdir\} /etc
+ ./bootstrap
+ '';
+ configureArgs = [ "--sysconfdir=/etc" ];
+
+ meta = with stdenv.lib; {
+ homepage = "http://gale.org/";
+ description = "chat/messaging system (server and client)";
+ platforms = platforms.all;
+ license = licenses.gpl2Plus;
+ };
+}
diff --git a/pkgs/applications/networking/instant-messengers/gale/gale-install.in.patch b/pkgs/applications/networking/instant-messengers/gale/gale-install.in.patch
new file mode 100644
index 000000000000..f9c3e3c55922
--- /dev/null
+++ b/pkgs/applications/networking/instant-messengers/gale/gale-install.in.patch
@@ -0,0 +1,339 @@
+diff --git a/gale-install.in b/gale-install.in
+index 50e8ad8..eec0ed2 100644
+--- a/gale-install.in
++++ b/gale-install.in
+@@ -29,22 +29,78 @@ testkey_stdin() {
+ gkinfo -x 2>/dev/null | qgrep "^Public key: <$1>"
+ }
+
+-if [ -n "$GALE_SYS_DIR" ]; then
+- SYS_DIR="$GALE_SYS_DIR"
+-elif [ -n "$sysconfdir" ]; then
+- SYS_DIR="$sysconfdir/gale"
++INST_SYS_DIR="$sysconfdir/gale"
++
++if [ `id -u` -eq 0 ]; then
++ is_root=yes
++ SYS_DIR=/etc/gale
++else
++ is_root=no
++ SYS_DIR="$HOME/.gale"
++fi
++
++if [ -f /etc/NIXOS ]; then
++ is_nixos=yes
++else
++ is_nixos=no
++fi
++
++if [ -u /var/setuid-wrappers/gksign ]; then
++ cat < "$CONF" <> "$CONF" <> "$CONF" << EOM
++ cat > "$CONF" </dev/null`"
+-[ -x "$readlink" ] && gksignlink="`"$readlink" "$gksign" 2>/dev/null`"
+-[ -f "$gksignlink" ] && gksign="$gksignlink"
+-
+-echo ""
+-if copy chown "$GALE_USER" "$gksign" ; then
+- :
+-else
+- echo "*** We need to chown $GALE_USER '$gksign'."
+- echo " Please run this script as a user that can do so,"
+- echo " or do so yourself and re-run this script."
+- exit 1
++ fi
+ fi
+-run chmod 4755 "$gksign"
+
+-# -----------------------------------------------------------------------------
+-# create a domain, if necessary
++if [ $is_root = no ]; then
++ GALE_SYS_DIR="$SYS_DIR"
++ export GALE_SYS_DIR
+
+-echo ""
+-if test -u "$gksign" || copy chmod u+s "$gksign" ; then
+- :
++ testkey "$GALE_DOMAIN" && exit 0
++ echo "*** You lack a signed key for your domain, \"$GALE_DOMAIN\"."
++ GALE="$SYS_DIR"
+ else
+- echo "*** We need to chmod u+s '$gksign'."
+- echo " Please run this script as a user that can do so,"
+- echo " or do so yourself and re-run this script."
+- exit 1
+-fi
+-
+-testkey "$GALE_DOMAIN" && exit 0
+-echo "*** You lack a signed key for your domain, \"$GALE_DOMAIN\"."
+-
+-if [ "x$GALE_USER" != "x$USER" ]; then
+-cat <