From 4d12729aa4026229e4e118b924cc3b1c75ca214b Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 17 Jun 2016 20:09:33 -0400 Subject: write setuid, move things around --- .gitignore | 17 ++- .gitmodules | 16 +- Makefile | 59 +++++--- bin/common.rb.in | 44 ++++++ bin/meta-cat | 22 +++ bin/meta-check | 60 ++++++++ bin/meta-normalize-stdio | 190 ++++++++++++++++++++++++ bin/nshd-tester.c | 168 +++++++++++++++++++++ bin/pacman-make-keyring | 167 +++++++++++++++++++++ bin/pgp-list-keyids | 37 +++++ bin/postfix-generate-virtual-map | 34 +++++ bin/setuid.c | 108 ++++++++++++++ bin/ssh-list-authorized-keys | 41 +++++ bin/uid-map | 24 +++ build-aux/Makefile.once.head/20-golang.mk | 3 +- go/cmd-nshd/.gitignore | 1 + go/cmd-nshd/main.go.in | 34 +++++ go/gopkg.in/yaml.v2 | 1 + go/lukeshu.com/git/go/libgnulinux.git | 1 + go/lukeshu.com/git/go/libnslcd.git | 1 + go/lukeshu.com/git/go/libsystemd.git | 1 + go/parabola_hackers/.gitignore | 2 + go/parabola_hackers/nslcd_backend/db_config.go | 40 +++++ go/parabola_hackers/nslcd_backend/db_group.go | 141 ++++++++++++++++++ go/parabola_hackers/nslcd_backend/db_pam.go | 167 +++++++++++++++++++++ go/parabola_hackers/nslcd_backend/db_passwd.go | 82 ++++++++++ go/parabola_hackers/nslcd_backend/db_shadow.go | 78 ++++++++++ go/parabola_hackers/nslcd_backend/hackers.go | 122 +++++++++++++++ go/parabola_hackers/nslcd_backend/util.go | 58 ++++++++ go/parabola_hackers/passwords.go.in | 97 ++++++++++++ go/parabola_hackers/users.go.in | 141 ++++++++++++++++++ go/parabola_hackers/util.go | 47 ++++++ scripts/common.rb.in | 44 ------ scripts/meta-cat | 22 --- scripts/meta-check | 60 -------- scripts/meta-normalize-stdio | 190 ------------------------ scripts/pacman-make-keyring | 167 --------------------- scripts/pgp-list-keyids | 37 ----- scripts/postfix-generate-virtual-map | 34 ----- scripts/ssh-list-authorized-keys | 41 ----- scripts/uid-map | 24 --- src/cmd-nshd/.gitignore | 1 - src/cmd-nshd/main.go.in | 32 ---- src/gopkg.in/yaml.v2 | 1 - src/lukeshu.com/git/go/libgnulinux.git | 1 - src/lukeshu.com/git/go/libnslcd.git | 1 - src/lukeshu.com/git/go/libsystemd.git | 1 - src/parabola_hackers/.gitignore | 2 - src/parabola_hackers/nslcd_backend/db_config.go | 40 ----- src/parabola_hackers/nslcd_backend/db_group.go | 141 ------------------ src/parabola_hackers/nslcd_backend/db_pam.go | 167 --------------------- src/parabola_hackers/nslcd_backend/db_passwd.go | 82 ---------- src/parabola_hackers/nslcd_backend/db_shadow.go | 78 ---------- src/parabola_hackers/nslcd_backend/hackers.go | 122 --------------- src/parabola_hackers/nslcd_backend/util.go | 58 -------- src/parabola_hackers/passwords.go.in | 94 ------------ src/parabola_hackers/users.go.in | 141 ------------------ src/parabola_hackers/util.go | 47 ------ test/.gitignore | 1 - test/runner.c | 168 --------------------- 60 files changed, 1963 insertions(+), 1838 deletions(-) create mode 100644 bin/common.rb.in create mode 100755 bin/meta-cat create mode 100755 bin/meta-check create mode 100755 bin/meta-normalize-stdio create mode 100644 bin/nshd-tester.c create mode 100755 bin/pacman-make-keyring create mode 100755 bin/pgp-list-keyids create mode 100755 bin/postfix-generate-virtual-map create mode 100644 bin/setuid.c create mode 100755 bin/ssh-list-authorized-keys create mode 100755 bin/uid-map create mode 100644 go/cmd-nshd/.gitignore create mode 100644 go/cmd-nshd/main.go.in create mode 160000 go/gopkg.in/yaml.v2 create mode 160000 go/lukeshu.com/git/go/libgnulinux.git create mode 160000 go/lukeshu.com/git/go/libnslcd.git create mode 160000 go/lukeshu.com/git/go/libsystemd.git create mode 100644 go/parabola_hackers/.gitignore create mode 100644 go/parabola_hackers/nslcd_backend/db_config.go create mode 100644 go/parabola_hackers/nslcd_backend/db_group.go create mode 100644 go/parabola_hackers/nslcd_backend/db_pam.go create mode 100644 go/parabola_hackers/nslcd_backend/db_passwd.go create mode 100644 go/parabola_hackers/nslcd_backend/db_shadow.go create mode 100644 go/parabola_hackers/nslcd_backend/hackers.go create mode 100644 go/parabola_hackers/nslcd_backend/util.go create mode 100644 go/parabola_hackers/passwords.go.in create mode 100644 go/parabola_hackers/users.go.in create mode 100644 go/parabola_hackers/util.go delete mode 100644 scripts/common.rb.in delete mode 100755 scripts/meta-cat delete mode 100755 scripts/meta-check delete mode 100755 scripts/meta-normalize-stdio delete mode 100755 scripts/pacman-make-keyring delete mode 100755 scripts/pgp-list-keyids delete mode 100755 scripts/postfix-generate-virtual-map delete mode 100755 scripts/ssh-list-authorized-keys delete mode 100755 scripts/uid-map delete mode 100644 src/cmd-nshd/.gitignore delete mode 100644 src/cmd-nshd/main.go.in delete mode 160000 src/gopkg.in/yaml.v2 delete mode 160000 src/lukeshu.com/git/go/libgnulinux.git delete mode 160000 src/lukeshu.com/git/go/libnslcd.git delete mode 160000 src/lukeshu.com/git/go/libsystemd.git delete mode 100644 src/parabola_hackers/.gitignore delete mode 100644 src/parabola_hackers/nslcd_backend/db_config.go delete mode 100644 src/parabola_hackers/nslcd_backend/db_group.go delete mode 100644 src/parabola_hackers/nslcd_backend/db_pam.go delete mode 100644 src/parabola_hackers/nslcd_backend/db_passwd.go delete mode 100644 src/parabola_hackers/nslcd_backend/db_shadow.go delete mode 100644 src/parabola_hackers/nslcd_backend/hackers.go delete mode 100644 src/parabola_hackers/nslcd_backend/util.go delete mode 100644 src/parabola_hackers/passwords.go.in delete mode 100644 src/parabola_hackers/users.go.in delete mode 100644 src/parabola_hackers/util.go delete mode 100644 test/.gitignore delete mode 100644 test/runner.c diff --git a/.gitignore b/.gitignore index b128030..a75421c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,16 @@ -/pkg -/bin/ +/LICENSE.*.txt +/bin/common.rb +/bin/nshd +/bin/nshd-tester +/bin/setuid +/nshd.service +/nshd.sysusers + +/.gopath .var.* .tmp.* *.o *~ - -/nshd.service -/nshd.sysusers -/scripts/common.rb -/LICENSE.*.txt - parabola-keyring-*.tar.gz /config.mk diff --git a/.gitmodules b/.gitmodules index 5e18a55..fcf63f4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,12 @@ -[submodule "src/gopkg.in/yaml.v2"] - path = src/gopkg.in/yaml.v2 +[submodule "go/gopkg.in/yaml.v2"] + path = go/gopkg.in/yaml.v2 url = https://gopkg.in/yaml.v2/ -[submodule "src/lukeshu.com/git/go/libgnulinux.git"] - path = src/lukeshu.com/git/go/libgnulinux.git +[submodule "go/lukeshu.com/git/go/libgnulinux.git"] + path = go/lukeshu.com/git/go/libgnulinux.git url = https://lukeshu.com/git/go/libgnulinux.git/ -[submodule "src/lukeshu.com/git/go/libnslcd.git"] - path = src/lukeshu.com/git/go/libnslcd.git +[submodule "go/lukeshu.com/git/go/libnslcd.git"] + path = go/lukeshu.com/git/go/libnslcd.git url = https://lukeshu.com/git/go/libnslcd.git/ -[submodule "src/lukeshu.com/git/go/libsystemd.git"] - path = src/lukeshu.com/git/go/libsystemd.git +[submodule "go/lukeshu.com/git/go/libsystemd.git"] + path = go/lukeshu.com/git/go/libsystemd.git url = https://lukeshu.com/git/go/libsystemd.git/ diff --git a/Makefile b/Makefile index 4ddef29..3a546fe 100644 --- a/Makefile +++ b/Makefile @@ -34,14 +34,15 @@ CGO_CPPFLAGS = $(CPPFLAGS) -U_FORTIFY_SOURCE CGO_CFLAGS = $(CFLAGS) -O0 -Wno-unused-parameter CGO_ENABLED = 1 -at.subdirs += src/lukeshu.com/git/go/libnslcd.git/proto +at.subdirs += go/lukeshu.com/git/go/libnslcd.git/proto -scripts = $(filter-out common.rb common.rb.in,$(notdir $(wildcard $(srcdir)/scripts/*))) common.rb +programs = setuid nshd-tester nshd common.rb +scripts = $(filter-out %.c %.o $(programs) common.rb common.rb.in,$(notdir $(wildcard $(srcdir)/bin/*))) std.gen_files += LICENSE.lgpl-2.1.txt LICENSE.gpl-2.txt LICENSE.apache-2.0.txt -std.out_files += bin/cmd-nshd nshd.service nshd.sysusers scripts/common.rb test/runner -std.sys_files += $(addprefix $(bindir)/,nshd $(scripts)) $(systemunitdir)/nshd.socket $(systemunitdir)/nshd.service $(sysusersdir)/nshd.conf $(conf_file) $(shadow_file) -std.clean_files += test/*.o pkg/ .tmp* .var* $(_out) +std.out_files += $(addprefix bin/,$(programs)) nshd.service nshd.sysusers +std.sys_files += $(addprefix $(bindir)/,$(programs) $(scripts)) $(systemunitdir)/nshd.socket $(systemunitdir)/nshd.service $(sysusersdir)/nshd.conf $(conf_file) $(shadow_file) +std.clean_files += bin/*.o .gopath/ .tmp* .var* $(_out) $(srcdir)/LICENSE.lgpl-2.1.txt: $(NET) curl https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt > $@ @@ -52,39 +53,44 @@ $(srcdir)/LICENSE.apache-2.0.txt: $(NET) $(srcdir)/LICENSE.wtfpl-2.txt: $(NET) curl http://www.wtfpl.net/txt/copying/ > $@ -_gen += src/lukeshu.com/git/go/libnslcd.git/proto/server/interface_backend.go -_gen += src/lukeshu.com/git/go/libnslcd.git/proto/server/func_handlerequest.go -_gen += src/lukeshu.com/git/go/libnslcd.git/proto/server/type_nilbackend.go -_out += src/parabola_hackers/users.go -_out += src/parabola_hackers/passwords.go -_out += src/cmd-nshd/main.go -$(outdir)/bin/%-nshd: $(call golang.src,$(srcdir)) $(_gen) $(_out) - $(call golang.install,$(topsrcdir),cmd-nshd) +_gen += go/lukeshu.com/git/go/libnslcd.git/proto/server/interface_backend.go +_gen += go/lukeshu.com/git/go/libnslcd.git/proto/server/func_handlerequest.go +_gen += go/lukeshu.com/git/go/libnslcd.git/proto/server/type_nilbackend.go +_out += go/parabola_hackers/users.go +_out += go/parabola_hackers/passwords.go +_out += go/cmd-nshd/main.go +$(outdir)/bin/nshd: \ +$(outdir)/bin/%: $(outdir)/.gopath/bin/cmd-% + cp -T $< $@ +$(outdir)/.gopath/src: $(golang.var) + rm -rf -- $(@D) + mkdir $(@D) + ln -sr $(topsrcdir)/go $@ + touch $@ +$(outdir)/.gopath/bin/cmd-nshd: $(call golang.src,$(outdir)/.gopath) $(_gen) $(_out) + $(call golang.install,$(topoutdir)/.gopath,cmd-nshd) $(outdir)/%.o: $(srcdir)/%.c $(var)CC $(var)CPPFLAGS $(var)CFLAGS $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(filter-out $(var)%,$^) +$(outdir)/bin/nshd-tester $(outdir)/bin/setuid: \ $(outdir)/%: $(outdir)/%.o $(var)CC $(var)LDFLAGS $(CC) $(LDFLAGS) -o $@ $(filter-out $(var)%,$^) $(outdir)/%: $(srcdir)/%.in < $< sed $(foreach v,$(patsubst $(var)%,%,$(filter $(var)%,$^)), -e 's|@$v@|$($v)|g' ) > $@ +$(outdir)/bin/setuid: -ldl $(outdir)/nshd.service: $(var)user $(var)bindir $(outdir)/nshd.sysusers: $(var)user -$(outdir)/scripts/common.rb: $(var)conf_file -$(outdir)/src/cmd-nshd/main.go: $(var)conf_file -$(outdir)/src/parabola_hackers/users.go: $(var)bindir -$(outdir)/src/parabola_hackers/passwords.go: $(var)shadow_file +$(outdir)/bin/common.rb: $(var)conf_file +$(outdir)/go/cmd-nshd/main.go: $(var)conf_file +$(outdir)/go/parabola_hackers/users.go: $(var)bindir +$(outdir)/go/parabola_hackers/passwords.go: $(var)shadow_file -$(DESTDIR)$(bindir)/%: $(outdir)/bin/cmd-% - $(NORMAL_INSTALL) - install -TDm755 $< $@ -$(DESTDIR)$(bindir)/%: $(srcdir)/scripts/% +# Patterns +$(DESTDIR)$(bindir)/%: $(outdir)/bin/% $(NORMAL_INSTALL) install -TDm755 $< $@ -$(DESTDIR)$(bindir)/common.rb: $(srcdir)/scripts/common.rb - $(NORMAL_INSTALL) - install -TDm644 $< $@ $(DESTDIR)$(systemunitdir)/%.socket: $(outdir)/%.socket $(NORMAL_INSTALL) install -TDm644 $< $@ @@ -94,6 +100,11 @@ $(DESTDIR)$(systemunitdir)/%.service: $(outdir)/%.service $(DESTDIR)$(sysusersdir)/%.conf: $(outdir)/%.sysusers $(NORMAL_INSTALL) install -TDm644 $< $@ + +# Specific files +$(DESTDIR)$(bindir)/common.rb: $(srcdir)/bin/common.rb + $(NORMAL_INSTALL) + install -TDm644 $< $@ $(DESTDIR)$(conf_file): $(srcdir)/parabola-hackers.yml $(NORMAL_INSTALL) install -TDm644 $< $@ diff --git a/bin/common.rb.in b/bin/common.rb.in new file mode 100644 index 0000000..7c457b8 --- /dev/null +++ b/bin/common.rb.in @@ -0,0 +1,44 @@ +# Copyright 2016 Luke Shumaker . +# +# This 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 2 of +# the License, or (at your option) any later version. +# +# This software 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 this manual; if not, see +# . + +require 'yaml' + +def cfg + if @cfg.nil? + @cfg = YAML::load(open("@conf_file@")) + if ENV['PARABOLA_HACKERS_YAMLDIR'] + @cfg["yamldir"] = ENV['PARABOLA_HACKERS_YAMLDIR'] + end + end + return @cfg +end + +def load_user_yaml(filename) + user = YAML::load(open(filename)) + groups = user["groups"] || [] + user["groups"] = groups.concat((groups & cfg["groupgroups"].keys).map{|g|cfg["groupgroups"][g]}.flatten) + return user +end + +def load_all_users + users = {} + Dir.glob("#{cfg["yamldir"]}/*.yml").map{|filename| + uid = File.basename(filename).sub(/^([0-9]*)\.yml$/, "\\1").to_i + user = load_user_yaml(filename) + users[uid] = user + } + return users +end diff --git a/bin/meta-cat b/bin/meta-cat new file mode 100755 index 0000000..5e7097e --- /dev/null +++ b/bin/meta-cat @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby +# Usage: meta-cat + +# Copyright 2016 Luke Shumaker . +# +# This 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 2 of +# the License, or (at your option) any later version. +# +# This software 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 this manual; if not, see +# . + +load "#{File.dirname(__FILE__)}/common.rb" + +print load_all_users.to_yaml diff --git a/bin/meta-check b/bin/meta-check new file mode 100755 index 0000000..4add9d3 --- /dev/null +++ b/bin/meta-check @@ -0,0 +1,60 @@ +#!/bin/bash +# Copyright 2014, 2016 Luke Shumaker . +# Copyright 2015 Márcio Alexandre Silva Delgado . +# +# This 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 2 of +# the License, or (at your option) any later version. +# +# This software 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 this manual; if not, see +# . + +. libremessages + +mydir="$(dirname "$0")" +PATH="$mydir:$PATH" + +check-yaml() { + file=$1 + msg 'Inspecting %q' "$file" + norm=$(mktemp --tmpdir) + trap "rm -f -- $(printf '%q' "$norm")" RETURN + meta-normalize-stdio < "$file" > "$norm" || return $? + colordiff -u "$file" "$norm" || return $? +} + +main() { + declare -i ret=0 + + yamldir="$(ruby -e "load '$mydir/common.rb'; print cfg['yamldir']")" + + # Check the user YAML files + for file in "$yamldir"/*.yml; do + check-yaml "$file" || ret=$? + done + + msg 'Checking for duplicate usernames' + dups=($(sed -n 's/^username: //p' -- "$yamldir"/*.yml| sort | uniq -d)) + if (( ${#dups[@]} )); then + error 'Duplicate usernames:' + plain '%s' "${dups[@]}" + ret=1 + fi + + msg 'Checking PGP keys' + if pgp-list-keyids | grep -Ev '^(trusted|secondary|revoked)/[a-z][a-z0-9-]* [0-9A-F]{40}$'; then + error 'Bad pgp keys ^^^' + ret=1 + fi + + return $ret +} + +main "$@" diff --git a/bin/meta-normalize-stdio b/bin/meta-normalize-stdio new file mode 100755 index 0000000..a7ca381 --- /dev/null +++ b/bin/meta-normalize-stdio @@ -0,0 +1,190 @@ +#!/usr/bin/env ruby +# coding: utf-8 + +# Copyright 2014, 2016 Luke Shumaker . +# Copyright 2015 Márcio Alexandre Silva Delgado . +# +# This 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 2 of +# the License, or (at your option) any later version. +# +# This software 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 this manual; if not, see +# . + +# First we define a bunch of code-generators, then at the end is a +# very neat and readable definition of the format of the YAML files. + +require 'yaml' + +def error(msg) + $stderr.puts "ERROR: #{msg}" + @err = 1 +end + +def warning(msg) + $stderr.puts "WARNING: #{msg}" +end + + +# Generic validators/formatters + +def semiordered_list(cnt, validator) + lambda {|name,ary| + if ary.class != Array + error "`#{name}' must be a list" + else + ary.each_index{|i| ary[i] = validator.call("#{name}[#{i}]", ary[i])} + ary = ary.first(cnt).concat(ary.last(ary.count-cnt).sort) + end + ary + } +end + +def unordered_list(validator) + semiordered_list(0, validator) +end + +def _unknown(map_name, key) + error "Unknown item: #{map_name}[#{key.inspect}]" + 0 +end +def unordered_map1(validator) + lambda {|name,hash| + if hash.class != Hash + error "`#{name}' must be a map" + else + order = Hash[[*validator.keys.map.with_index]] + hash = Hash[hash.sort_by{|k,v| order[k] || _unknown(name,k) }] + hash.keys.each{|k| + if validator[k] + hash[k] = validator[k].call("#{name}[#{k.inspect}]", hash[k]) + end + } + end + hash + } +end + +def unordered_map2(key_validator, val_validator) + lambda {|name,hash| + if hash.class != Hash + error "`#{name}' must be a map" + else + hash = Hash[hash.sort_by{|k,v| k}] + hash.keys.each{|k| + key_validator.call("#{name} key #{k.inspect}", k) + hash[k] = val_validator.call("#{name}[#{k.inspect}]", hash[k]) + } + end + hash + } +end + +string = lambda {|name,str| + if str.class != String + error "`#{name}' must be a string" + else + str + end +} + +# Regular Expression String +def restring(re) + lambda {|name,str| + if str.class != String + error "`#{name}' must be a string" + else + unless re =~ str + error "`#{name}' does not match #{re.inspect}: #{str}" + end + str + end + } +end + + +# Specific validators/formatters + +year = lambda {|name, num| + if num.class != Fixnum + error "`#{name}' must be a year" + else + if (num < 1900 || num > 3000) + error "`#{name}' is a number, but doesn't look like a year" + end + num + end +} + +# This regex is taken from http://www.w3.org/TR/html5/forms.html#valid-e-mail-address +_email_regex = /^[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ +email_list = lambda {|name, ary| + if ary.class != Array + error "`#{name}' must be a list" + elsif not ary.empty? + preserve = 1 + if ary.first.end_with?("@parabola.nu") and ary.count >= 2 + preserve = 2 + end + ary = semiordered_list(preserve, restring(_email_regex)).call(name, ary) + end + ary +} + +shell = lambda {|name, sh| + if sh.class != String + error "`#{name}' must be a string" + else + @valid_shells ||= open("/etc/shells").read.split("\n") + .find_all{|line| /^[^\#]/ =~ line} + .push("/usr/bin/nologin") + unless @valid_shells.include?(sh) + warning "shell not listed in /etc/shells: #{sh}" + end + end + sh +} + + +# The format of the YAML files + +format = unordered_map1( + { + "username" => restring(/^[a-z][a-z0-9-]*$/), + "fullname" => string, + "email" => email_list, + "groups" => semiordered_list(1, string), + "pgp_keyid" => restring(/^[0-9A-F]{40}$/), + "pgp_revoked_keyids" => unordered_list(restring(/^[0-9A-F]{40}$/)), + "ssh_keys" => unordered_map2(string, string), + "shell" => shell, + "extra" => unordered_map1( + { + "alias" => string, + "other_contact" => string, + "roles" => string, + "website" => string, + "occupation" => string, + "yob" => year, + "location" => string, + "languages" => string, + "interests" => string, + "favorite_distros" => string, + }) + }) + + + +@err = 0 +user = format.call("user", YAML::load(STDIN)) +if @err != 0 + exit @err +end +print user.to_yaml diff --git a/bin/nshd-tester.c b/bin/nshd-tester.c new file mode 100644 index 0000000..110819d --- /dev/null +++ b/bin/nshd-tester.c @@ -0,0 +1,168 @@ +/* Copyright (C) 2015 Luke Shumaker + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _(s) s + +const char *xgetenv(const char *name, const char *unset) { + const char *val = getenv(name); + if (!val) + val = unset; + return val; +} + +char *xasprintf(const char *format, ...) { + va_list arg; + int len; + char *str = NULL; + + va_start(arg, format); + len = vasprintf(&str, format, arg); + va_end(arg); + + if (len < 0) + error(EXIT_FAILURE, errno, _("Could not allocate memory in vasprintf")); + + return str; +} + +#define xasprintfa(...) (__extension__ ({ char *heap = xasprintf(__VA_ARGS__); char *stack = strdupa(heap); free(heap); stack; })) + +int pid = -1; +void +sigchld_handler(int sig __attribute__((__unused__))) { + int status; + pid = waitpid(pid, &status, WNOHANG); + int exited = WEXITSTATUS(status); + error(exited, 0, "%ld exited with status %d", (long)pid, exited); + exit(0); +} + +union addr { + struct sockaddr gen; + struct sockaddr_un un; +}; + +int new_unix_sock(const char *filename, int type) { + union addr addr; + addr.un.sun_family = AF_UNIX; + strcpy(addr.un.sun_path, filename); + + int sock = socket(AF_UNIX, type, 0); + if (sock < 0) + error(EXIT_FAILURE, errno, "socket(%d, %d)", AF_UNIX, type); + unlink(filename); + if (bind(sock, &addr.gen, sizeof(addr))) + error(EXIT_FAILURE, errno, "bind(%d, sockaddr(\"%s\"))", sock, filename); + switch (type) { + case SOCK_STREAM: + case SOCK_SEQPACKET: + if (listen(sock, 5)) + error(EXIT_FAILURE, errno, "listen(%d /* \"%s\" */, %d)", sock, filename, 5); + break; + case SOCK_DGRAM: + break; + default: + error(EXIT_FAILURE, errno, "new_unix_sock: Unrecognized type: %d", type); + } + return sock; +} + +char *cmdname = "nshd_runner"; +const char *notify_sockname = "notify.sock"; +const char *nslcd_sockname = "nslcd.sock"; +void cleanup(void) { + if (nslcd_sockname) + unlink(nslcd_sockname); + if (notify_sockname) + unlink(notify_sockname); + fprintf(stderr, "%s: Exiting\n", cmdname); +} + +int main(int argc, char *argv[]) { + cmdname = argv[0]; + if (argc != 2) { + error(2, 0, _("Usage: %s NSHD_PROGRAM"), argv[0]); + } + + atexit(&cleanup); + int nslcd_sock = new_unix_sock(nslcd_sockname , SOCK_STREAM); + int notify_sock = new_unix_sock(notify_sockname, SOCK_DGRAM ); + + struct sigaction act; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + act.sa_handler = sigchld_handler; + if (sigaction(SIGCHLD, &act, 0)) + error(EXIT_FAILURE, errno, "sigaction"); + + + pid = fork(); + switch (pid) { + case -1: + error(EXIT_FAILURE, errno, "fork"); + case 0: + close(notify_sock); + dup2(nslcd_sock, 3); + if (nslcd_sock != 3) + close(nslcd_sock); + pid = getpid(); + setenv("LISTEN_PID", xasprintfa("%ld", (long)pid), 1); + setenv("LISTEN_FDS", "1", 1); + setenv("NOTIFY_SOCKET", notify_sockname, 1); + execl(argv[1], argv[1], NULL); + error(EXIT_FAILURE, errno, "execl"); + } + + while (1) { + union addr client_addr; + socklen_t client_size; + char buf[4097]; + ssize_t bytes_read = recvfrom(notify_sock, buf, sizeof(buf)-1, 0, &client_addr.gen, &client_size); + if (bytes_read < 1) + error(EXIT_FAILURE, errno, "recvfrom"); + if (buf[bytes_read-1] != '\n') { + buf[bytes_read] = '\n'; + bytes_read++; + } + ssize_t bytes_written = 0; + while (bytes_written < bytes_read) { + ssize_t n = write(2, &(buf[bytes_written]), bytes_read-bytes_written); + if (n < 0) { + bytes_written = -1; + break; + } + bytes_written += n; + } + if (bytes_written < 0) + error(EXIT_FAILURE, errno, "write"); + } + error(EXIT_FAILURE, 0, "not reached"); + return EXIT_FAILURE; +} diff --git a/bin/pacman-make-keyring b/bin/pacman-make-keyring new file mode 100755 index 0000000..702ea69 --- /dev/null +++ b/bin/pacman-make-keyring @@ -0,0 +1,167 @@ +#!/usr/bin/make -rRf +# Usage: pacman-make-keyring V=$(date -u +%Y%m%d) + +# Copyright 2014, 2016 Luke Shumaker . +# +# This 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 2 of +# the License, or (at your option) any later version. +# +# This software 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 this manual; if not, see +# . + +ifeq ($(origin V),undefined) +$(info Usage: pacman-make-keyring V=$$(date -u +%Y%m%d)) +$(error You must set V= on the command line) +endif + +bin := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) +yamldir := $(shell ruby -e "load '$(bin)/common.rb'; print cfg['yamldir']") +cachedir := $(shell ruby -e "load '$(bin)/common.rb'; print cfg['keyring_cachedir']") + +outputdir = $(cachedir)/$(KEYRING_NAME)-keyring-$(V) +KEYRING_NAME = parabola + +all: $(KEYRING_NAME)-keyring-$(V).tar.gz +.PHONY: all + +export SHELL = /bin/bash -o pipefail +.PHONY: FORCE +.SECONDARY: +.DELETE_ON_ERROR: + +dirs = \ + $(outputdir) \ + $(cachedir) \ + $(cachedir)/gpghome \ + $(cachedir)/keys/trusted \ + $(cachedir)/keys/secondary \ + $(cachedir)/keys/revoked + +$(dirs): + mkdir -p $@ + +$(cachedir)/var.%: FORCE | $(cachedir) + @$(file >$(@D)/tmp.$(@F),$($*)) + @sed -i 's|^|#|' $(@D)/tmp.$(@F) + @if cmp -s $(@D)/tmp.$(@F) $@; then \ + rm -f $(@D)/tmp.$(@F) || :; \ + else \ + mv -f $(@D)/tmp.$(@F) $@; \ + fi +-include $(wildcard $(cachedir)/var.*) +$(cachedir)/txt.%: $(cachedir)/var.% + sed 's|^#||' < $< > $@ +var=$(cachedir)/var. + +keyring-files = \ + $(outputdir)/Makefile \ + $(outputdir)/${KEYRING_NAME}.gpg \ + $(outputdir)/${KEYRING_NAME}-trusted \ + $(outputdir)/${KEYRING_NAME}-revoked + +$(KEYRING_NAME)-keyring-$(V).tar.gz: %.tar.gz: $(keyring-files) + bsdtar --format=ustar -cf - -C $(cachedir) $(addprefix $*/,$(notdir $^)) | gzip -9 > $@ + +define Makefile.in +V=@V@ + +prefix = /usr/local +PREFIX = $$(prefix) + +install: + install -dm755 $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ + install -m0644 @KEYRING_NAME@{.gpg,-trusted,-revoked} $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ + +uninstall: + rm -f $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/@KEYRING_NAME@{.gpg,-trusted,-revoked} + rmdir -p --ignore-fail-on-non-empty $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ + +.PHONY: install uninstall +endef + +$(outputdir)/Makefile: $(cachedir)/txt.Makefile.in $(var)V $(var)KEYRING_NAME | $(outputdir) + sed $(foreach v,$(patsubst $(var)%,%,$(filter $(var)%,$^)), -e 's|@$v@|$($v)|' ) < $< > $@ + + +users := $(sort $(shell find $(yamldir))) $(var)users + +# Assemble the list of .asc files needed to generate the keyring +$(cachedir)/deps.mk: ${users} $(var)outputdir $(var)cachedir $(var)KEYRING_NAME| $(cachedir) + { \ + echo $(outputdir)/${KEYRING_NAME}.gpg: $$($(bin)/pgp-list-keyids | sed -r 's|(\S+) .*|$$(cachedir)/keys/\1.asc|') && \ + echo $(cachedir)/stamp.ownertrust: $$($(bin)/pgp-list-keyids | sed -rn 's|^(trusted/\S+) .*|$$(cachedir)/keys/\1.asc|p') && \ + $(bin)/pgp-list-keyids | sed -rn 's|^trusted/(\S+) (.*)|keyid.\1 = \2|p' && \ + $(bin)/uid-map | sed 's|.*|trusted:&\nsecondary:&\nrevoked:&|' | sed -r 's|(.*):(.*):(.*)|$$(cachedir)/keys/\1/\3.asc: $$(yamldir)/\2.yml|' && \ + :; }> $@ +-include $(cachedir)/deps.mk + +# The remainder of file is mostly just a translation of the shell +# script `update-keys`. +# +# https://git.archlinux.org/archlinux-keyring.git/tree/update-keys + +export LANG=C + +KEYSERVER = hkp://pool.sks-keyservers.net + +GPG = gpg --quiet --batch --no-tty --no-permission-warning --keyserver ${KEYSERVER} --homedir $(cachedir)/gpghome + +define gpg-init +%echo Generating Parabola Keyring keychain master key... +Key-Type: RSA +Key-Length: 1024 +Key-Usage: sign +Name-Real: Parabola Keyring Keychain Master Key +Name-Email: parabola-keyring@localhost +Expire-Date: 0 +%no-protection +%commit +%echo Done +endef +$(cachedir)/stamp.gpg-init: $(cachedir)/txt.gpg-init $(var)GPG | $(cachedir)/gpghome + ${GPG} --gen-key < $< + touch $@ + +# The appropriate ${uid}.yml file is added as a dependency to +# ${username}.yml by deps.mk +keyid=$(keyid.$(patsubst %.asc,%,$(notdir $@))) + +# In 'update-keys', this is the 'master-keyids' loop +$(outputdir)/${KEYRING_NAME}-trusted: ${users} | $(outputdir) + $(bin)/pgp-list-keyids | sed -rn 's|^trusted/\S+ (\S+)|\1:4:|p' > $@ +$(cachedir)/keys/trusted/%.asc : $(cachedir)/stamp.gpg-init | $(cachedir)/keys/trusted + ${GPG} --recv-keys ${keyid} &>/dev/null + printf 'minimize\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} + printf 'y\ny\n' | ${GPG} --command-fd 0 --lsign-key ${keyid} &>/dev/null + ${GPG} --armor --no-emit-version --export ${keyid} > $@ + +$(cachedir)/stamp.ownertrust: $(outputdir)/${KEYRING_NAME}-trusted $(cachedir)/deps.mk + ${GPG} --import-ownertrust < $< 2>/dev/null + touch $@ + +# In 'update-keys', this is the 'packager-keyids' loop +$(cachedir)/keys/secondary/%.asc: $(cachedir)/stamp.ownertrust | $(cachedir)/keys/secondary + ${GPG} --recv-keys ${keyid} &>/dev/null + printf 'clean\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} + ${GPG} --list-keys --with-colons ${keyid} 2>/dev/null | grep -q '^pub:f:' # make sure it is trusted + ${GPG} --armor --no-emit-version --export ${keyid} > $@ + +# In 'update-keys', this is the 'packager-revoked-keyids' loop +$(outputdir)/${KEYRING_NAME}-revoked: ${users} | $(outputdir) + $(bin)/pgp-list-keyids | sed -rn 's|^revoked/\S+ ||p' > $@ +$(cachedir)/keys/revoked/%.asc : $(cachedir)/stamp.ownertrust | $(cachedir)/keys/revoked + ${GPG} --recv-keys ${keyid} &>/dev/null + printf 'clean\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} + ! ${GPG} --list-keys --with-colons ${keyid} 2>/dev/null | grep -q '^pub:f:' # make sure it isn't trusted + ${GPG} --armor --no-emit-version --export ${keyid} > $@ + +$(outputdir)/${KEYRING_NAME}.gpg: $(cachedir)/deps.mk | $(outputdir) + cat $(filter %.asc,$^) > $@ diff --git a/bin/pgp-list-keyids b/bin/pgp-list-keyids new file mode 100755 index 0000000..749cb7b --- /dev/null +++ b/bin/pgp-list-keyids @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby +# Usage: pgp-list-keyids + +# Copyright 2014, 2016 Luke Shumaker . +# +# This 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 2 of +# the License, or (at your option) any later version. +# +# This software 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 this manual; if not, see +# . + +load "#{File.dirname(__FILE__)}/common.rb" + +load_all_users.each do |uid,user| + if user["groups"] + if user["groups"].include?("keyring-trusted") + puts "trusted/#{user["username"]} #{user["pgp_keyid"]}" + elsif user["groups"].include?("keyring-secondary") + puts "secondary/#{user["username"]} #{user["pgp_keyid"]}" + elsif user["pgp_keyid"] + #puts "revoked/#{user["username"]} #{user["pgp_keyid"]}" + end + end + if user["pgp_revoked_keyids"] + user["pgp_revoked_keyids"].each do |keyid| + puts "revoked/#{user["username"]} #{keyid}" + end + end +end diff --git a/bin/postfix-generate-virtual-map b/bin/postfix-generate-virtual-map new file mode 100755 index 0000000..f2fb8ec --- /dev/null +++ b/bin/postfix-generate-virtual-map @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +# Usage: postfix-show-virtual-map > ${file} && postmap hash:${file} + +# Copyright 2014, 2016 Luke Shumaker . +# +# This 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 2 of +# the License, or (at your option) any later version. +# +# This software 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 this manual; if not, see +# . + +load "#{File.dirname(__FILE__)}/common.rb" + +users = load_all_users.values.find_all{|u|u["groups"].include?("email")} + +users.each do |user| + if user["email"] and user["email"].length > 0 + if user["email"][0] =~ /.*@parabola.nu$/ + if user["email"].length > 1 + puts "#{user["username"]}@parabola.nu #{user["email"][1]}" + end + else + puts "#{user["username"]}@parabola.nu #{user["email"][0]}" + end + end +end diff --git a/bin/setuid.c b/bin/setuid.c new file mode 100644 index 0000000..7ae1105 --- /dev/null +++ b/bin/setuid.c @@ -0,0 +1,108 @@ +/* + Copyright (C) 2006 West Consulting + Copyright (C) 2006-2015 Arthur de Jong + Copyright (C) 2015-2016 Luke Shumaker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include /* for dlopen(3), dlsym(3), and dlerror(3) */ +#include /* for errno */ +#include /* for getpwnam(3) */ +#include /* for printf(3) and fprintf(3) */ +#include /* for strerror(3) */ +#include /* for 'struct passwd' and 'struct group' */ +#include /* for SD_{WARNING,DEBUG} */ +#include /* for setuid(3), setgid(3), and dup2(3) */ + +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 +#define EXIT_INVALIDARGUMENT 2 +#define EXIT_NOPERMISSION 4 + +const char *nss_module_soname = "libnss_ldap.so.2"; +const char *nss_module_sym_version = "_nss_ldap_version"; +const char *nss_module_sym_enablelookups = "_nss_ldap_enablelookups"; + +static void disable_nss_module(void) { + char *err; + + dlerror(); + void *handle = dlopen(nss_module_soname, RTLD_LAZY | RTLD_NODELETE); + err = dlerror(); + if (handle == NULL) { + fprintf(stderr, SD_WARNING "NSS module %s not loaded: %s", nss_module_soname, err); + return; + } + + dlerror(); + char **version_info = dlsym(handle, nss_module_sym_version); + err = dlerror(); + if ((version_info != NULL) && (err == NULL)) { + fprintf(stderr, SD_DEBUG "NSS module %s version %s %s", nss_module_soname, + version_info[0], + version_info[1]); + } else { + fprintf(stderr, SD_WARNING "NSS module %s version missing: %s", nss_module_soname, err); + } + + dlerror(); + int *enable_flag = dlsym(handle, nss_module_sym_enablelookups); + err = dlerror(); + if ((enable_flag == NULL) || (err != NULL)) { + fprintf(stderr, SD_WARNING "Unable to disable NSS ldap module for nslcd process: %s", err); + dlclose(handle); + return; + } + *enable_flag = 0; + dlclose(handle); +} + +void usage(char *cmd) { + printf("Usage: %s USERNAME COMMAND...\n", cmd); + printf("A simple setuid(3) wrapper that runs with the `ldap' NSS module disabled\n"); +} + +int main(int argc, char *argv[]) { + if (argc < 3) { + dup2(2, 1); + usage(argv[0]); + return EXIT_INVALIDARGUMENT; + } + + disable_nss_module(); + + struct passwd *passwd = getpwnam(argv[1]); + if (passwd == NULL) { + fprintf(stderr, SD_ERR "Could not look up user: %s", argv[1]); + return EXIT_FAILURE; + } + + if (setgid(passwd->pw_gid) != 0) { + fprintf(stderr, SD_ERR "Could not setgid(%lu): %s", + (unsigned long int)passwd->pw_gid, strerror(errno)); + return EXIT_NOPERMISSION; + } + if (setuid(passwd->pw_uid) != 0) { + fprintf(stderr, SD_ERR "Could not setuid(%lu): %s", + (unsigned long int)passwd->pw_gid, strerror(errno)); + return EXIT_NOPERMISSION; + } + + execvp(argv[2], &argv[2]); + fprintf(stderr, SD_ERR "Could not exec: %s", strerror(errno)); + return EXIT_FAILURE; +} diff --git a/bin/ssh-list-authorized-keys b/bin/ssh-list-authorized-keys new file mode 100755 index 0000000..5364ac2 --- /dev/null +++ b/bin/ssh-list-authorized-keys @@ -0,0 +1,41 @@ +#!/usr/bin/env ruby +# Usage: ssh-list-authorized-keys [username] + +# Copyright 2014, 2016 Luke Shumaker . +# +# This 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 2 of +# the License, or (at your option) any later version. +# +# This software 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 this manual; if not, see +# . + +load "#{File.dirname(__FILE__)}/common.rb" + +all_users = load_all_users.values + +groupnames = ARGV & cfg["ssh_pseudo_users"] +usernames = ARGV & all_users.map{|u|u["username"]} + +users = all_users.find_all{|u| + # [ username was listed ] or [ the user is in a listed group ] + usernames.include?(u["username"]) or not (u["groups"] & groupnames).empty? +} + +# Buffer the output to avoid EPIPE when the reader hangs up early +output="" +users.each do |user| + if user["ssh_keys"] + user["ssh_keys"].each do |addr,key| + output+="#{key} #{user["fullname"]} (#{user["username"]}) <#{addr}>\n" + end + end +end +print output diff --git a/bin/uid-map b/bin/uid-map new file mode 100755 index 0000000..e759c30 --- /dev/null +++ b/bin/uid-map @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby +# Usage: uid-map + +# Copyright 2016 Luke Shumaker . +# +# This 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 2 of +# the License, or (at your option) any later version. +# +# This software 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 this manual; if not, see +# . + +load "#{File.dirname(__FILE__)}/common.rb" + +load_all_users.each do |uid,user| + puts "#{uid}:#{user["username"]}" +end diff --git a/build-aux/Makefile.once.head/20-golang.mk b/build-aux/Makefile.once.head/20-golang.mk index 8d10a47..8e3835e 100644 --- a/build-aux/Makefile.once.head/20-golang.mk +++ b/build-aux/Makefile.once.head/20-golang.mk @@ -21,7 +21,8 @@ $(foreach v,$(_golang.cgo_variables),$(eval $v ?=)) export $(_golang.cgo_variables) _golang.src_cmd = find -L $1/src -name '.*' -prune -o \( -type f \( -false $(foreach e,go c s S cc cpp cxx h hh hpp hxx,-o -name '*.$e') \) -o -type d \) -print -golang.src = $(shell $(_golang.src_cmd)) $(addprefix $(var),$(_golang.cgo_variables)) +golang.src = $1/src $(shell $(_golang.src_cmd) 2>/dev/null) +golang.var = $(addprefix $(var),$(_golang.cgo_variables)) define golang.install GOPATH='$(abspath $1)' go install $(golang.FLAGS) $2 diff --git a/go/cmd-nshd/.gitignore b/go/cmd-nshd/.gitignore new file mode 100644 index 0000000..00870e2 --- /dev/null +++ b/go/cmd-nshd/.gitignore @@ -0,0 +1 @@ +/main.go diff --git a/go/cmd-nshd/main.go.in b/go/cmd-nshd/main.go.in new file mode 100644 index 0000000..b1b7108 --- /dev/null +++ b/go/cmd-nshd/main.go.in @@ -0,0 +1,34 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +// Command nshd is an implementation of nslcd that talks to hackers.git instead of LDAP. +package main + +import ( + "os" + hackers_nslcd_backend "parabola_hackers/nslcd_backend" + + nslcd_systemd "lukeshu.com/git/go/libnslcd.git/systemd" + "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" +) + +func main() { + logger.Debug("starting") + backend := &hackers_nslcd_backend.Hackers{ + CfgFilename: "@conf_file@", + } + os.Exit(int(nslcd_systemd.Main(backend))) +} diff --git a/go/gopkg.in/yaml.v2 b/go/gopkg.in/yaml.v2 new file mode 160000 index 0000000..f7716cb --- /dev/null +++ b/go/gopkg.in/yaml.v2 @@ -0,0 +1 @@ +Subproject commit f7716cbe52baa25d2e9b0d0da546fcf909fc16b4 diff --git a/go/lukeshu.com/git/go/libgnulinux.git b/go/lukeshu.com/git/go/libgnulinux.git new file mode 160000 index 0000000..d8c4fd9 --- /dev/null +++ b/go/lukeshu.com/git/go/libgnulinux.git @@ -0,0 +1 @@ +Subproject commit d8c4fd9aef9137b04e4311a1f50024ab88d4c6e3 diff --git a/go/lukeshu.com/git/go/libnslcd.git b/go/lukeshu.com/git/go/libnslcd.git new file mode 160000 index 0000000..132cb9e --- /dev/null +++ b/go/lukeshu.com/git/go/libnslcd.git @@ -0,0 +1 @@ +Subproject commit 132cb9ec42cc4fb6c4a20c49422413ab0a62ef6e diff --git a/go/lukeshu.com/git/go/libsystemd.git b/go/lukeshu.com/git/go/libsystemd.git new file mode 160000 index 0000000..89efdfb --- /dev/null +++ b/go/lukeshu.com/git/go/libsystemd.git @@ -0,0 +1 @@ +Subproject commit 89efdfbee5f9a22f9dd1083f7a383daba54d4f12 diff --git a/go/parabola_hackers/.gitignore b/go/parabola_hackers/.gitignore new file mode 100644 index 0000000..3be3f08 --- /dev/null +++ b/go/parabola_hackers/.gitignore @@ -0,0 +1,2 @@ +/users.go +/passwords.go diff --git a/go/parabola_hackers/nslcd_backend/db_config.go b/go/parabola_hackers/nslcd_backend/db_config.go new file mode 100644 index 0000000..e78643b --- /dev/null +++ b/go/parabola_hackers/nslcd_backend/db_config.go @@ -0,0 +1,40 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +package hackers_nslcd_backend + +import ( + s "syscall" + + p "lukeshu.com/git/go/libnslcd.git/proto" +) + +func (o *Hackers) Config_Get(cred s.Ucred, req p.Request_Config_Get) <-chan p.Config { + o.lock.RLock() + ret := make(chan p.Config) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + switch req.Key { + case p.NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE: + if o.cfg.Pam_password_prohibit_message != "" { + ret <- p.Config{Value: o.cfg.Pam_password_prohibit_message} + } + } + }() + return ret +} diff --git a/go/parabola_hackers/nslcd_backend/db_group.go b/go/parabola_hackers/nslcd_backend/db_group.go new file mode 100644 index 0000000..18e54b1 --- /dev/null +++ b/go/parabola_hackers/nslcd_backend/db_group.go @@ -0,0 +1,141 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +package hackers_nslcd_backend + +import ( + "parabola_hackers" + s "syscall" + + p "lukeshu.com/git/go/libnslcd.git/proto" +) + +func (o *Hackers) groupByName(name string, users bool) p.Group { + members_set, found := o.groups[name] + if !found { + return p.Group{ID: -1} + } + gid := name2gid(name) + if gid < 0 { + return p.Group{ID: -1} + } + var members_list []string + if users { + members_list = parabola_hackers.Set2list(members_set) + } else { + members_list = make([]string, 0) + } + return p.Group{ + Name: name, + PwHash: "x", + ID: gid, + Members: members_list, + } +} + +func (o *Hackers) groupByGid(gid int32, users bool) p.Group { + name, found := gid2name(gid) + if !found { + return p.Group{ID: -1} + } + members_set, found := o.groups[name] + if !found { + return p.Group{ID: -1} + } + var members_list []string + if users { + members_list = parabola_hackers.Set2list(members_set) + } else { + members_list = make([]string, 0) + } + return p.Group{ + Name: name, + PwHash: "x", + ID: gid, + Members: members_list, + } +} + +func (o *Hackers) Group_ByName(cred s.Ucred, req p.Request_Group_ByName) <-chan p.Group { + o.lock.RLock() + ret := make(chan p.Group) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + group := o.groupByName(req.Name, true) + if group.ID < 0 { + return + } + ret <- group + }() + return ret +} + +func (o *Hackers) Group_ByGid(cred s.Ucred, req p.Request_Group_ByGid) <-chan p.Group { + o.lock.RLock() + ret := make(chan p.Group) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + group := o.groupByGid(req.Gid, true) + if group.ID < 0 { + return + } + ret <- group + }() + return ret +} + +// note that the BYMEMBER call returns an empty members list +func (o *Hackers) Group_ByMember(cred s.Ucred, req p.Request_Group_ByMember) <-chan p.Group { + o.lock.RLock() + ret := make(chan p.Group) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + uid := o.name2uid(req.Member) + if uid < 0 { + return + } + for _, name := range o.users[uid].Groups { + group := o.groupByName(name, false) + if group.ID >= 0 { + ret <- group + } + } + }() + return ret +} + +func (o *Hackers) Group_All(cred s.Ucred, req p.Request_Group_All) <-chan p.Group { + o.lock.RLock() + ret := make(chan p.Group) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + for name, _ := range o.groups { + group := o.groupByName(name, true) + if group.ID >= 0 { + ret <- group + } + } + }() + return ret +} diff --git a/go/parabola_hackers/nslcd_backend/db_pam.go b/go/parabola_hackers/nslcd_backend/db_pam.go new file mode 100644 index 0000000..3374170 --- /dev/null +++ b/go/parabola_hackers/nslcd_backend/db_pam.go @@ -0,0 +1,167 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +package hackers_nslcd_backend + +import ( + "fmt" + "parabola_hackers" + s "syscall" + + "lukeshu.com/git/go/libgnulinux.git/crypt" + p "lukeshu.com/git/go/libnslcd.git/proto" + "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" +) + +func checkPassword(password string, hash string) bool { + return crypt.Crypt(password, hash) == hash +} + +func hashPassword(newPassword string, oldHash string) string { + salt := oldHash + if salt == "!" { + str, err := parabola_hackers.RandomString(crypt.SaltAlphabet, 8) + if err != nil { + logger.Err("Could not generate a random string") + str = "" + } + salt = "$6$" + str + "$" + } + return crypt.Crypt(newPassword, salt) +} + +func (o *Hackers) PAM_Authentication(cred s.Ucred, req p.Request_PAM_Authentication) <-chan p.PAM_Authentication { + o.lock.RLock() + ret := make(chan p.PAM_Authentication) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + uid := o.name2uid(req.UserName) + if uid < 0 { + return + } + + user := o.users[uid] + obj := p.PAM_Authentication{ + AuthenticationResult: p.NSLCD_PAM_AUTH_ERR, + UserName: "", + AuthorizationResult: p.NSLCD_PAM_AUTH_ERR, + AuthorizationError: "", + } + if checkPassword(req.Password, user.Passwd.PwHash) { + obj.AuthenticationResult = p.NSLCD_PAM_SUCCESS + obj.AuthorizationResult = obj.AuthenticationResult + obj.UserName = user.Passwd.Name + } + ret <- obj + }() + return ret +} + +func (o *Hackers) PAM_Authorization(cred s.Ucred, req p.Request_PAM_Authorization) <-chan p.PAM_Authorization { + o.lock.RLock() + ret := make(chan p.PAM_Authorization) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + uid := o.name2uid(req.UserName) + if uid < 0 { + return + } + ret <- p.PAM_Authorization{ + Result: p.NSLCD_PAM_SUCCESS, + Error: "", + } + }() + return ret +} + +const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + +func (o *Hackers) PAM_SessionOpen(cred s.Ucred, req p.Request_PAM_SessionOpen) <-chan p.PAM_SessionOpen { + ret := make(chan p.PAM_SessionOpen) + go func() { + defer close(ret) + + sessionid, err := parabola_hackers.RandomString(alphabet, 24) + if err != nil { + return + } + ret <- p.PAM_SessionOpen{SessionID: sessionid} + }() + return ret +} + +func (o *Hackers) PAM_SessionClose(cred s.Ucred, req p.Request_PAM_SessionClose) <-chan p.PAM_SessionClose { + ret := make(chan p.PAM_SessionClose) + go close(ret) + return ret +} + +func (o *Hackers) PAM_PwMod(cred s.Ucred, req p.Request_PAM_PwMod) <-chan p.PAM_PwMod { + ret := make(chan p.PAM_PwMod) + o.lock.Lock() + go func() { + defer close(ret) + defer o.lock.Unlock() + + uid := o.name2uid(req.UserName) + if uid < 0 { + return + } + user := o.users[uid] + + // Check the OldPassword + if req.AsRoot == 1 { + if !checkPassword(req.OldPassword, user.Passwd.PwHash) { + ret <- p.PAM_PwMod{ + Result: p.NSLCD_PAM_PERM_DENIED, + Error: fmt.Sprintf("password change failed: %s", "Old password did not match"), + } + return + } + } + + // Update the PwHash in memory + user.Passwd.PwHash = hashPassword(req.NewPassword, user.Passwd.PwHash) + if user.Passwd.PwHash == "" { + logger.Err("Password hashing failed") + return + } + + // Update the PwHash on disk + passwords := make(map[string]string, len(o.users)) + for _, ouser := range o.users { + passwords[ouser.Passwd.Name] = ouser.Passwd.PwHash + } + passwords[user.Passwd.Name] = user.Passwd.PwHash + err := parabola_hackers.SaveAllPasswords(passwords) + if err != nil { + logger.Err("Writing passwords to disk: %v", err) + return + } + + // Ok, we're done, commit the changes + o.users[uid] = user + ret <- p.PAM_PwMod{ + Result: p.NSLCD_PAM_SUCCESS, + Error: "", + } + }() + return ret +} diff --git a/go/parabola_hackers/nslcd_backend/db_passwd.go b/go/parabola_hackers/nslcd_backend/db_passwd.go new file mode 100644 index 0000000..3f32ddd --- /dev/null +++ b/go/parabola_hackers/nslcd_backend/db_passwd.go @@ -0,0 +1,82 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +package hackers_nslcd_backend + +import ( + s "syscall" + + p "lukeshu.com/git/go/libnslcd.git/proto" +) + +/* Note that the output password hash value should be one of: + - no password set, allow login without password + ! - used to prevent logins + x - "valid" encrypted password that does not match any valid password + often used to indicate that the password is defined elsewhere + other - encrypted password, in crypt(3) format */ + +func (o *Hackers) Passwd_ByName(cred s.Ucred, req p.Request_Passwd_ByName) <-chan p.Passwd { + o.lock.RLock() + ret := make(chan p.Passwd) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + uid := o.name2uid(req.Name) + if uid < 0 { + return + } + passwd := o.users[uid].Passwd + passwd.PwHash = "x" // only put actual hashes in the Shadow DB + ret <- passwd + }() + return ret +} + +func (o *Hackers) Passwd_ByUID(cred s.Ucred, req p.Request_Passwd_ByUID) <-chan p.Passwd { + o.lock.RLock() + ret := make(chan p.Passwd) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + user, found := o.users[req.UID] + if !found { + return + } + passwd := user.Passwd + passwd.PwHash = "x" // only put actual hashes in the Shadow DB + ret <- passwd + }() + return ret +} + +func (o *Hackers) Passwd_All(cred s.Ucred, req p.Request_Passwd_All) <-chan p.Passwd { + o.lock.RLock() + ret := make(chan p.Passwd) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + for _, user := range o.users { + passwd := user.Passwd + passwd.PwHash = "x" // only put actual hashes in the Shadow DB + ret <- passwd + } + }() + return ret +} diff --git a/go/parabola_hackers/nslcd_backend/db_shadow.go b/go/parabola_hackers/nslcd_backend/db_shadow.go new file mode 100644 index 0000000..abfff28 --- /dev/null +++ b/go/parabola_hackers/nslcd_backend/db_shadow.go @@ -0,0 +1,78 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +package hackers_nslcd_backend + +import ( + s "syscall" + + p "lukeshu.com/git/go/libnslcd.git/proto" +) + +func (o *Hackers) Shadow_ByName(cred s.Ucred, req p.Request_Shadow_ByName) <-chan p.Shadow { + o.lock.RLock() + ret := make(chan p.Shadow) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + if cred.Uid != 0 { + return + } + uid := o.name2uid(req.Name) + user := o.users[uid] + ret <- p.Shadow{ + Name: user.Passwd.Name, + PwHash: user.Passwd.PwHash, + LastChangeDate: -1, + MinDays: -1, + MaxDays: -1, + WarnDays: -1, + InactDays: -1, + ExpireDate: -1, + Flag: -1, + } + }() + return ret +} + +func (o *Hackers) Shadow_All(cred s.Ucred, req p.Request_Shadow_All) <-chan p.Shadow { + o.lock.RLock() + ret := make(chan p.Shadow) + go func() { + defer o.lock.RUnlock() + defer close(ret) + + if cred.Uid != 0 { + return + } + + for _, user := range o.users { + ret <- p.Shadow{ + Name: user.Passwd.Name, + PwHash: user.Passwd.PwHash, + LastChangeDate: -1, + MinDays: -1, + MaxDays: -1, + WarnDays: -1, + InactDays: -1, + ExpireDate: -1, + Flag: -1, + } + } + }() + return ret +} diff --git a/go/parabola_hackers/nslcd_backend/hackers.go b/go/parabola_hackers/nslcd_backend/hackers.go new file mode 100644 index 0000000..bb03862 --- /dev/null +++ b/go/parabola_hackers/nslcd_backend/hackers.go @@ -0,0 +1,122 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +// Package hackers_nslcd_backend is an nslcd_server Backend that +// speaks to hackers.git. +package hackers_nslcd_backend + +import ( + "parabola_hackers" + "sync" + + nslcd_server "lukeshu.com/git/go/libnslcd.git/proto/server" + nslcd_systemd "lukeshu.com/git/go/libnslcd.git/systemd" + "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" +) + +type config struct { + Pam_password_prohibit_message string +} + +type Hackers struct { + nslcd_server.NilBackend + lock sync.RWMutex + + CfgFilename string + + cfg config + users map[int32]parabola_hackers.User + groups map[string]map[string]bool +} + +var _ nslcd_systemd.Backend = &Hackers{} +var _ nslcd_server.Backend = &Hackers{} + +func (o *Hackers) Init() error { + logger.Debug("hackers.git: CfgFilename = %v", o.CfgFilename) + err := o.Reload() + if err != nil { + logger.Err("hackers.git: Could not initialize: %v", err) + return err + } + return nil +} + +func (o *Hackers) Close() { + logger.Info("hackers.git: Closing session") + o.lock.Lock() + defer o.lock.Unlock() + + o.users = make(map[int32]parabola_hackers.User, 0) + o.groups = make(map[string]map[string]bool) +} + +func (o *Hackers) Reload() error { + logger.Info("hackers.git: Loading session") + o.lock.Lock() + defer o.lock.Unlock() + + var err error + o.cfg, err = parse_config(o.CfgFilename) + if err != nil { + return err + } + logger.Info("hackers.git: pam_password_prohibit_message: %#v", o.cfg.Pam_password_prohibit_message) + + logger.Debug("hackers.git: Parsing user data") + o.users, err = parabola_hackers.LoadAllUsers() + if err != nil { + return err + } + + passwords, err := parabola_hackers.LoadAllPasswords() + if err != nil { + return err + } + + o.groups = make(map[string]map[string]bool) + for uid, user := range o.users { + user.Passwd.GID = usersGid + hash, hasHash := passwords[user.Passwd.Name] + if !hasHash { + hash = "!" + } + user.Passwd.PwHash = hash + o.users[uid] = user + for _, groupname := range user.Groups { + o.add_user_to_group(user.Passwd.Name, groupname) + } + } + return nil +} + +func (o *Hackers) name2uid(name string) int32 { + for uid, data := range o.users { + if data.Passwd.Name == name { + return uid + } + } + return -1 +} + +func (o *Hackers) add_user_to_group(username string, groupname string) { + group, found := o.groups[groupname] + if !found { + group = make(map[string]bool) + o.groups[groupname] = group + } + group[username] = true +} diff --git a/go/parabola_hackers/nslcd_backend/util.go b/go/parabola_hackers/nslcd_backend/util.go new file mode 100644 index 0000000..4fb28f3 --- /dev/null +++ b/go/parabola_hackers/nslcd_backend/util.go @@ -0,0 +1,58 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +package hackers_nslcd_backend + +import ( + "io/ioutil" + "os" + + yaml "gopkg.in/yaml.v2" + "lukeshu.com/git/go/libgnulinux.git/getgr" +) + +func name2gid(name string) int32 { + gr, err := getgr.ByName(name) + if gr == nil || err != nil { + return -1 + } else { + return int32(gr.Gid) + } +} + +func gid2name(gid int32) (string, bool) { + gr, err := getgr.ByGid(gid) + if gr == nil || err != nil { + return "", false + } else { + return gr.Name, true + } +} + +var usersGid = name2gid("users") + +func parse_config(filename string) (cfg config, err error) { + file, err := os.Open(filename) + if err != nil { + return + } + contents, err := ioutil.ReadAll(file) + if err != nil { + return + } + err = yaml.Unmarshal(contents, &cfg) + return +} diff --git a/go/parabola_hackers/passwords.go.in b/go/parabola_hackers/passwords.go.in new file mode 100644 index 0000000..b9f93d9 --- /dev/null +++ b/go/parabola_hackers/passwords.go.in @@ -0,0 +1,97 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +package parabola_hackers + +import ( + "fmt" + "io/ioutil" + "os" + "sort" + "strings" + + "lukeshu.com/git/go/libgnulinux.git/crypt" + "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" +) + +/* Note that the password hash value should be one of: + - no password set, allow login without password + ! - used to prevent logins + x - "valid" encrypted password that does not match any valid password + often used to indicate that the password is defined elsewhere + other - encrypted password, in crypt(3) format */ + +const shadow_file = "@shadow_file@" + +func LoadAllPasswords() (map[string]string, error) { + file, err := os.Open(shadow_file) + if err != nil { + return nil, err + } + contents, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + lines := strings.Split(string(contents), "\n") + passwords := make(map[string]string, len(lines)) + for i, line := range lines { + if line == "" { + continue + } + cols := strings.SplitN(line, ":", 2) + if len(cols) != 2 { + logger.Err("hackers.git %s:%d: malformed line", shadow_file, i+1) + continue + } + username := cols[0] + hash := cols[1] + if hash != "!" && !crypt.SaltOk(hash) { + hash = "!" + logger.Err("%s:%d: malformed hash for user: %s", shadow_file, i+1, username) + } + passwords[username] = hash + } + return passwords, nil +} + +func SaveAllPasswords(passwords map[string]string) error { + usernames := make([]string, len(passwords)) + i := 0 + for username, _ := range passwords { + usernames[i] = username + i++ + } + sort.Strings(usernames) + + file, err := os.OpenFile(shadow_file+"-", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) + if err != nil { + return err + } + + for _, username := range usernames { + fmt.Fprintf(file, "%s:%s\n", username, passwords[username]) + } + err = file.Sync() + if err != nil { + return err + } + err = file.Close() + if err != nil { + return err + } + + return os.Rename(shadow_file+"-", shadow_file) +} diff --git a/go/parabola_hackers/users.go.in b/go/parabola_hackers/users.go.in new file mode 100644 index 0000000..aeda069 --- /dev/null +++ b/go/parabola_hackers/users.go.in @@ -0,0 +1,141 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +package parabola_hackers + +import ( + "fmt" + "os/exec" + + yaml "gopkg.in/yaml.v2" + nslcd_proto "lukeshu.com/git/go/libnslcd.git/proto" + "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" +) + +/* Note that the password hash value should be one of: + - no password set, allow login without password + ! - used to prevent logins + x - "valid" encrypted password that does not match any valid password + often used to indicate that the password is defined elsewhere + other - encrypted password, in crypt(3) format */ + +type User struct { + Passwd nslcd_proto.Passwd + Groups []string +} + +func LoadAllUsers() (users map[int32]User, err error) { + contents, err := exec.Command("@bindir@/meta-cat").Output() + if err != nil { + return + } + + var _data interface{} + err = yaml.Unmarshal(contents, &_data) + if err != nil { + return + } + + data, isMap := _data.(map[interface{}]interface{}) + errs := []string{} + if !isMap { + errs = append(errs, "root node is not a map") + } else { + users = make(map[int32]User, len(data)) + for _uid, _user := range data { + uid, isInt := _uid.(int) + if !isInt { + errs = append(errs, fmt.Sprintf("UID is not an int: %T ( %#v )", _uid, _uid)) + continue + } + user, _err := parseUser(_user) + if _err != nil { + errs = append(errs, fmt.Sprintf("Could not parse data for UID %d: %v", uid, _err)) + continue + } + user.Passwd.UID = int32(uid) + logger.Debug("hackers.git: -> User %d(%s) parsed", user.Passwd.UID, user.Passwd.Name) + users[user.Passwd.UID] = user + } + } + if len(errs) > 0 { + users = nil + err = &yaml.TypeError{Errors: errs} + } + return +} + +func parseUser(_data interface{}) (ret User, err error) { + data, isMap := _data.(map[interface{}]interface{}) + errs := []string{} + if !isMap { + errs = append(errs, "root node is not a map") + } else { + if iface, isSet := data["username"]; !isSet { + errs = append(errs, "\"username\" is not set") + } else if str, isTyp := iface.(string); !isTyp { + errs = append(errs, "\"username\" is not a string") + } else { + ret.Passwd.Name = str + ret.Passwd.HomeDir = "/home/" + str + } + + if iface, isSet := data["fullname"]; !isSet { + errs = append(errs, "\"fullname\" is not set") + } else if str, isTyp := iface.(string); !isTyp { + errs = append(errs, "\"fullname\" is not a string") + } else { + ret.Passwd.GECOS = str + } + + if iface, isSet := data["shell"]; !isSet { + errs = append(errs, "\"shell\" is not set") + } else if str, isTyp := iface.(string); !isTyp { + errs = append(errs, "\"shell\" is not a string") + } else { + ret.Passwd.Shell = str + } + + if iface, isSet := data["groups"]; !isSet { + ret.Groups = make([]string, 0) + } else if ary, isTyp := iface.([]interface{}); !isTyp { + errs = append(errs, "\"groups\" is not an array") + } else { + groups := make(map[string]bool, len(ary)) + e := false + for _, iface := range ary { + if str, isTyp := iface.(string); !isTyp { + errs = append(errs, "\"group\" item is not an array") + e = true + break + } else { + groups[str] = true + } + } + if !e { + ret.Groups = Set2list(groups) + } + } + } + if len(errs) > 0 { + err = &yaml.TypeError{Errors: errs} + } + + ret.Passwd.PwHash = "x" // look in shadow for the password hash + ret.Passwd.GID = -1 + + return +} diff --git a/go/parabola_hackers/util.go b/go/parabola_hackers/util.go new file mode 100644 index 0000000..9a241db --- /dev/null +++ b/go/parabola_hackers/util.go @@ -0,0 +1,47 @@ +// Copyright 2015-2016 Luke Shumaker . +// +// This 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 2 of +// the License, or (at your option) any later version. +// +// This software 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 this manual; if not, see +// . + +package parabola_hackers + +import ( + "crypto/rand" + "math/big" +) + +func RandomString(alphabet string, n uint) (str string, err error) { + var alphabet_len = big.NewInt(int64(len(alphabet))) + var bigint *big.Int + _str := make([]byte, n) + for i := 0; i < len(_str); i++ { + bigint, err = rand.Int(rand.Reader, alphabet_len) + if err != nil { + return + } + _str[i] = alphabet[bigint.Int64()] + } + str = string(_str[:]) + return +} + +func Set2list(set map[string]bool) []string { + list := make([]string, len(set)) + i := uint(0) + for item, _ := range set { + list[i] = item + i++ + } + return list +} diff --git a/scripts/common.rb.in b/scripts/common.rb.in deleted file mode 100644 index 7c457b8..0000000 --- a/scripts/common.rb.in +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2016 Luke Shumaker . -# -# This 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 2 of -# the License, or (at your option) any later version. -# -# This software 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 this manual; if not, see -# . - -require 'yaml' - -def cfg - if @cfg.nil? - @cfg = YAML::load(open("@conf_file@")) - if ENV['PARABOLA_HACKERS_YAMLDIR'] - @cfg["yamldir"] = ENV['PARABOLA_HACKERS_YAMLDIR'] - end - end - return @cfg -end - -def load_user_yaml(filename) - user = YAML::load(open(filename)) - groups = user["groups"] || [] - user["groups"] = groups.concat((groups & cfg["groupgroups"].keys).map{|g|cfg["groupgroups"][g]}.flatten) - return user -end - -def load_all_users - users = {} - Dir.glob("#{cfg["yamldir"]}/*.yml").map{|filename| - uid = File.basename(filename).sub(/^([0-9]*)\.yml$/, "\\1").to_i - user = load_user_yaml(filename) - users[uid] = user - } - return users -end diff --git a/scripts/meta-cat b/scripts/meta-cat deleted file mode 100755 index 5e7097e..0000000 --- a/scripts/meta-cat +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env ruby -# Usage: meta-cat - -# Copyright 2016 Luke Shumaker . -# -# This 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 2 of -# the License, or (at your option) any later version. -# -# This software 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 this manual; if not, see -# . - -load "#{File.dirname(__FILE__)}/common.rb" - -print load_all_users.to_yaml diff --git a/scripts/meta-check b/scripts/meta-check deleted file mode 100755 index 4add9d3..0000000 --- a/scripts/meta-check +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -# Copyright 2014, 2016 Luke Shumaker . -# Copyright 2015 Márcio Alexandre Silva Delgado . -# -# This 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 2 of -# the License, or (at your option) any later version. -# -# This software 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 this manual; if not, see -# . - -. libremessages - -mydir="$(dirname "$0")" -PATH="$mydir:$PATH" - -check-yaml() { - file=$1 - msg 'Inspecting %q' "$file" - norm=$(mktemp --tmpdir) - trap "rm -f -- $(printf '%q' "$norm")" RETURN - meta-normalize-stdio < "$file" > "$norm" || return $? - colordiff -u "$file" "$norm" || return $? -} - -main() { - declare -i ret=0 - - yamldir="$(ruby -e "load '$mydir/common.rb'; print cfg['yamldir']")" - - # Check the user YAML files - for file in "$yamldir"/*.yml; do - check-yaml "$file" || ret=$? - done - - msg 'Checking for duplicate usernames' - dups=($(sed -n 's/^username: //p' -- "$yamldir"/*.yml| sort | uniq -d)) - if (( ${#dups[@]} )); then - error 'Duplicate usernames:' - plain '%s' "${dups[@]}" - ret=1 - fi - - msg 'Checking PGP keys' - if pgp-list-keyids | grep -Ev '^(trusted|secondary|revoked)/[a-z][a-z0-9-]* [0-9A-F]{40}$'; then - error 'Bad pgp keys ^^^' - ret=1 - fi - - return $ret -} - -main "$@" diff --git a/scripts/meta-normalize-stdio b/scripts/meta-normalize-stdio deleted file mode 100755 index a7ca381..0000000 --- a/scripts/meta-normalize-stdio +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env ruby -# coding: utf-8 - -# Copyright 2014, 2016 Luke Shumaker . -# Copyright 2015 Márcio Alexandre Silva Delgado . -# -# This 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 2 of -# the License, or (at your option) any later version. -# -# This software 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 this manual; if not, see -# . - -# First we define a bunch of code-generators, then at the end is a -# very neat and readable definition of the format of the YAML files. - -require 'yaml' - -def error(msg) - $stderr.puts "ERROR: #{msg}" - @err = 1 -end - -def warning(msg) - $stderr.puts "WARNING: #{msg}" -end - - -# Generic validators/formatters - -def semiordered_list(cnt, validator) - lambda {|name,ary| - if ary.class != Array - error "`#{name}' must be a list" - else - ary.each_index{|i| ary[i] = validator.call("#{name}[#{i}]", ary[i])} - ary = ary.first(cnt).concat(ary.last(ary.count-cnt).sort) - end - ary - } -end - -def unordered_list(validator) - semiordered_list(0, validator) -end - -def _unknown(map_name, key) - error "Unknown item: #{map_name}[#{key.inspect}]" - 0 -end -def unordered_map1(validator) - lambda {|name,hash| - if hash.class != Hash - error "`#{name}' must be a map" - else - order = Hash[[*validator.keys.map.with_index]] - hash = Hash[hash.sort_by{|k,v| order[k] || _unknown(name,k) }] - hash.keys.each{|k| - if validator[k] - hash[k] = validator[k].call("#{name}[#{k.inspect}]", hash[k]) - end - } - end - hash - } -end - -def unordered_map2(key_validator, val_validator) - lambda {|name,hash| - if hash.class != Hash - error "`#{name}' must be a map" - else - hash = Hash[hash.sort_by{|k,v| k}] - hash.keys.each{|k| - key_validator.call("#{name} key #{k.inspect}", k) - hash[k] = val_validator.call("#{name}[#{k.inspect}]", hash[k]) - } - end - hash - } -end - -string = lambda {|name,str| - if str.class != String - error "`#{name}' must be a string" - else - str - end -} - -# Regular Expression String -def restring(re) - lambda {|name,str| - if str.class != String - error "`#{name}' must be a string" - else - unless re =~ str - error "`#{name}' does not match #{re.inspect}: #{str}" - end - str - end - } -end - - -# Specific validators/formatters - -year = lambda {|name, num| - if num.class != Fixnum - error "`#{name}' must be a year" - else - if (num < 1900 || num > 3000) - error "`#{name}' is a number, but doesn't look like a year" - end - num - end -} - -# This regex is taken from http://www.w3.org/TR/html5/forms.html#valid-e-mail-address -_email_regex = /^[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ -email_list = lambda {|name, ary| - if ary.class != Array - error "`#{name}' must be a list" - elsif not ary.empty? - preserve = 1 - if ary.first.end_with?("@parabola.nu") and ary.count >= 2 - preserve = 2 - end - ary = semiordered_list(preserve, restring(_email_regex)).call(name, ary) - end - ary -} - -shell = lambda {|name, sh| - if sh.class != String - error "`#{name}' must be a string" - else - @valid_shells ||= open("/etc/shells").read.split("\n") - .find_all{|line| /^[^\#]/ =~ line} - .push("/usr/bin/nologin") - unless @valid_shells.include?(sh) - warning "shell not listed in /etc/shells: #{sh}" - end - end - sh -} - - -# The format of the YAML files - -format = unordered_map1( - { - "username" => restring(/^[a-z][a-z0-9-]*$/), - "fullname" => string, - "email" => email_list, - "groups" => semiordered_list(1, string), - "pgp_keyid" => restring(/^[0-9A-F]{40}$/), - "pgp_revoked_keyids" => unordered_list(restring(/^[0-9A-F]{40}$/)), - "ssh_keys" => unordered_map2(string, string), - "shell" => shell, - "extra" => unordered_map1( - { - "alias" => string, - "other_contact" => string, - "roles" => string, - "website" => string, - "occupation" => string, - "yob" => year, - "location" => string, - "languages" => string, - "interests" => string, - "favorite_distros" => string, - }) - }) - - - -@err = 0 -user = format.call("user", YAML::load(STDIN)) -if @err != 0 - exit @err -end -print user.to_yaml diff --git a/scripts/pacman-make-keyring b/scripts/pacman-make-keyring deleted file mode 100755 index 702ea69..0000000 --- a/scripts/pacman-make-keyring +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/make -rRf -# Usage: pacman-make-keyring V=$(date -u +%Y%m%d) - -# Copyright 2014, 2016 Luke Shumaker . -# -# This 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 2 of -# the License, or (at your option) any later version. -# -# This software 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 this manual; if not, see -# . - -ifeq ($(origin V),undefined) -$(info Usage: pacman-make-keyring V=$$(date -u +%Y%m%d)) -$(error You must set V= on the command line) -endif - -bin := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) -yamldir := $(shell ruby -e "load '$(bin)/common.rb'; print cfg['yamldir']") -cachedir := $(shell ruby -e "load '$(bin)/common.rb'; print cfg['keyring_cachedir']") - -outputdir = $(cachedir)/$(KEYRING_NAME)-keyring-$(V) -KEYRING_NAME = parabola - -all: $(KEYRING_NAME)-keyring-$(V).tar.gz -.PHONY: all - -export SHELL = /bin/bash -o pipefail -.PHONY: FORCE -.SECONDARY: -.DELETE_ON_ERROR: - -dirs = \ - $(outputdir) \ - $(cachedir) \ - $(cachedir)/gpghome \ - $(cachedir)/keys/trusted \ - $(cachedir)/keys/secondary \ - $(cachedir)/keys/revoked - -$(dirs): - mkdir -p $@ - -$(cachedir)/var.%: FORCE | $(cachedir) - @$(file >$(@D)/tmp.$(@F),$($*)) - @sed -i 's|^|#|' $(@D)/tmp.$(@F) - @if cmp -s $(@D)/tmp.$(@F) $@; then \ - rm -f $(@D)/tmp.$(@F) || :; \ - else \ - mv -f $(@D)/tmp.$(@F) $@; \ - fi --include $(wildcard $(cachedir)/var.*) -$(cachedir)/txt.%: $(cachedir)/var.% - sed 's|^#||' < $< > $@ -var=$(cachedir)/var. - -keyring-files = \ - $(outputdir)/Makefile \ - $(outputdir)/${KEYRING_NAME}.gpg \ - $(outputdir)/${KEYRING_NAME}-trusted \ - $(outputdir)/${KEYRING_NAME}-revoked - -$(KEYRING_NAME)-keyring-$(V).tar.gz: %.tar.gz: $(keyring-files) - bsdtar --format=ustar -cf - -C $(cachedir) $(addprefix $*/,$(notdir $^)) | gzip -9 > $@ - -define Makefile.in -V=@V@ - -prefix = /usr/local -PREFIX = $$(prefix) - -install: - install -dm755 $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ - install -m0644 @KEYRING_NAME@{.gpg,-trusted,-revoked} $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ - -uninstall: - rm -f $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/@KEYRING_NAME@{.gpg,-trusted,-revoked} - rmdir -p --ignore-fail-on-non-empty $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ - -.PHONY: install uninstall -endef - -$(outputdir)/Makefile: $(cachedir)/txt.Makefile.in $(var)V $(var)KEYRING_NAME | $(outputdir) - sed $(foreach v,$(patsubst $(var)%,%,$(filter $(var)%,$^)), -e 's|@$v@|$($v)|' ) < $< > $@ - - -users := $(sort $(shell find $(yamldir))) $(var)users - -# Assemble the list of .asc files needed to generate the keyring -$(cachedir)/deps.mk: ${users} $(var)outputdir $(var)cachedir $(var)KEYRING_NAME| $(cachedir) - { \ - echo $(outputdir)/${KEYRING_NAME}.gpg: $$($(bin)/pgp-list-keyids | sed -r 's|(\S+) .*|$$(cachedir)/keys/\1.asc|') && \ - echo $(cachedir)/stamp.ownertrust: $$($(bin)/pgp-list-keyids | sed -rn 's|^(trusted/\S+) .*|$$(cachedir)/keys/\1.asc|p') && \ - $(bin)/pgp-list-keyids | sed -rn 's|^trusted/(\S+) (.*)|keyid.\1 = \2|p' && \ - $(bin)/uid-map | sed 's|.*|trusted:&\nsecondary:&\nrevoked:&|' | sed -r 's|(.*):(.*):(.*)|$$(cachedir)/keys/\1/\3.asc: $$(yamldir)/\2.yml|' && \ - :; }> $@ --include $(cachedir)/deps.mk - -# The remainder of file is mostly just a translation of the shell -# script `update-keys`. -# -# https://git.archlinux.org/archlinux-keyring.git/tree/update-keys - -export LANG=C - -KEYSERVER = hkp://pool.sks-keyservers.net - -GPG = gpg --quiet --batch --no-tty --no-permission-warning --keyserver ${KEYSERVER} --homedir $(cachedir)/gpghome - -define gpg-init -%echo Generating Parabola Keyring keychain master key... -Key-Type: RSA -Key-Length: 1024 -Key-Usage: sign -Name-Real: Parabola Keyring Keychain Master Key -Name-Email: parabola-keyring@localhost -Expire-Date: 0 -%no-protection -%commit -%echo Done -endef -$(cachedir)/stamp.gpg-init: $(cachedir)/txt.gpg-init $(var)GPG | $(cachedir)/gpghome - ${GPG} --gen-key < $< - touch $@ - -# The appropriate ${uid}.yml file is added as a dependency to -# ${username}.yml by deps.mk -keyid=$(keyid.$(patsubst %.asc,%,$(notdir $@))) - -# In 'update-keys', this is the 'master-keyids' loop -$(outputdir)/${KEYRING_NAME}-trusted: ${users} | $(outputdir) - $(bin)/pgp-list-keyids | sed -rn 's|^trusted/\S+ (\S+)|\1:4:|p' > $@ -$(cachedir)/keys/trusted/%.asc : $(cachedir)/stamp.gpg-init | $(cachedir)/keys/trusted - ${GPG} --recv-keys ${keyid} &>/dev/null - printf 'minimize\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} - printf 'y\ny\n' | ${GPG} --command-fd 0 --lsign-key ${keyid} &>/dev/null - ${GPG} --armor --no-emit-version --export ${keyid} > $@ - -$(cachedir)/stamp.ownertrust: $(outputdir)/${KEYRING_NAME}-trusted $(cachedir)/deps.mk - ${GPG} --import-ownertrust < $< 2>/dev/null - touch $@ - -# In 'update-keys', this is the 'packager-keyids' loop -$(cachedir)/keys/secondary/%.asc: $(cachedir)/stamp.ownertrust | $(cachedir)/keys/secondary - ${GPG} --recv-keys ${keyid} &>/dev/null - printf 'clean\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} - ${GPG} --list-keys --with-colons ${keyid} 2>/dev/null | grep -q '^pub:f:' # make sure it is trusted - ${GPG} --armor --no-emit-version --export ${keyid} > $@ - -# In 'update-keys', this is the 'packager-revoked-keyids' loop -$(outputdir)/${KEYRING_NAME}-revoked: ${users} | $(outputdir) - $(bin)/pgp-list-keyids | sed -rn 's|^revoked/\S+ ||p' > $@ -$(cachedir)/keys/revoked/%.asc : $(cachedir)/stamp.ownertrust | $(cachedir)/keys/revoked - ${GPG} --recv-keys ${keyid} &>/dev/null - printf 'clean\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} - ! ${GPG} --list-keys --with-colons ${keyid} 2>/dev/null | grep -q '^pub:f:' # make sure it isn't trusted - ${GPG} --armor --no-emit-version --export ${keyid} > $@ - -$(outputdir)/${KEYRING_NAME}.gpg: $(cachedir)/deps.mk | $(outputdir) - cat $(filter %.asc,$^) > $@ diff --git a/scripts/pgp-list-keyids b/scripts/pgp-list-keyids deleted file mode 100755 index 749cb7b..0000000 --- a/scripts/pgp-list-keyids +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env ruby -# Usage: pgp-list-keyids - -# Copyright 2014, 2016 Luke Shumaker . -# -# This 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 2 of -# the License, or (at your option) any later version. -# -# This software 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 this manual; if not, see -# . - -load "#{File.dirname(__FILE__)}/common.rb" - -load_all_users.each do |uid,user| - if user["groups"] - if user["groups"].include?("keyring-trusted") - puts "trusted/#{user["username"]} #{user["pgp_keyid"]}" - elsif user["groups"].include?("keyring-secondary") - puts "secondary/#{user["username"]} #{user["pgp_keyid"]}" - elsif user["pgp_keyid"] - #puts "revoked/#{user["username"]} #{user["pgp_keyid"]}" - end - end - if user["pgp_revoked_keyids"] - user["pgp_revoked_keyids"].each do |keyid| - puts "revoked/#{user["username"]} #{keyid}" - end - end -end diff --git a/scripts/postfix-generate-virtual-map b/scripts/postfix-generate-virtual-map deleted file mode 100755 index f2fb8ec..0000000 --- a/scripts/postfix-generate-virtual-map +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env ruby -# Usage: postfix-show-virtual-map > ${file} && postmap hash:${file} - -# Copyright 2014, 2016 Luke Shumaker . -# -# This 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 2 of -# the License, or (at your option) any later version. -# -# This software 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 this manual; if not, see -# . - -load "#{File.dirname(__FILE__)}/common.rb" - -users = load_all_users.values.find_all{|u|u["groups"].include?("email")} - -users.each do |user| - if user["email"] and user["email"].length > 0 - if user["email"][0] =~ /.*@parabola.nu$/ - if user["email"].length > 1 - puts "#{user["username"]}@parabola.nu #{user["email"][1]}" - end - else - puts "#{user["username"]}@parabola.nu #{user["email"][0]}" - end - end -end diff --git a/scripts/ssh-list-authorized-keys b/scripts/ssh-list-authorized-keys deleted file mode 100755 index 5364ac2..0000000 --- a/scripts/ssh-list-authorized-keys +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env ruby -# Usage: ssh-list-authorized-keys [username] - -# Copyright 2014, 2016 Luke Shumaker . -# -# This 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 2 of -# the License, or (at your option) any later version. -# -# This software 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 this manual; if not, see -# . - -load "#{File.dirname(__FILE__)}/common.rb" - -all_users = load_all_users.values - -groupnames = ARGV & cfg["ssh_pseudo_users"] -usernames = ARGV & all_users.map{|u|u["username"]} - -users = all_users.find_all{|u| - # [ username was listed ] or [ the user is in a listed group ] - usernames.include?(u["username"]) or not (u["groups"] & groupnames).empty? -} - -# Buffer the output to avoid EPIPE when the reader hangs up early -output="" -users.each do |user| - if user["ssh_keys"] - user["ssh_keys"].each do |addr,key| - output+="#{key} #{user["fullname"]} (#{user["username"]}) <#{addr}>\n" - end - end -end -print output diff --git a/scripts/uid-map b/scripts/uid-map deleted file mode 100755 index e759c30..0000000 --- a/scripts/uid-map +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env ruby -# Usage: uid-map - -# Copyright 2016 Luke Shumaker . -# -# This 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 2 of -# the License, or (at your option) any later version. -# -# This software 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 this manual; if not, see -# . - -load "#{File.dirname(__FILE__)}/common.rb" - -load_all_users.each do |uid,user| - puts "#{uid}:#{user["username"]}" -end diff --git a/src/cmd-nshd/.gitignore b/src/cmd-nshd/.gitignore deleted file mode 100644 index 00870e2..0000000 --- a/src/cmd-nshd/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/main.go diff --git a/src/cmd-nshd/main.go.in b/src/cmd-nshd/main.go.in deleted file mode 100644 index b8c3e71..0000000 --- a/src/cmd-nshd/main.go.in +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -// Command nshd is an implementation of nslcd that talks to hackers.git instead of LDAP. -package main - -import ( - "os" - hackers_nslcd_backend "parabola_hackers/nslcd_backend" - - nslcd_systemd "lukeshu.com/git/go/libnslcd.git/systemd" -) - -func main() { - backend := &hackers_nslcd_backend.Hackers{ - CfgFilename: "@conf_file@", - } - os.Exit(int(nslcd_systemd.Main(backend))) -} diff --git a/src/gopkg.in/yaml.v2 b/src/gopkg.in/yaml.v2 deleted file mode 160000 index f7716cb..0000000 --- a/src/gopkg.in/yaml.v2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f7716cbe52baa25d2e9b0d0da546fcf909fc16b4 diff --git a/src/lukeshu.com/git/go/libgnulinux.git b/src/lukeshu.com/git/go/libgnulinux.git deleted file mode 160000 index d8c4fd9..0000000 --- a/src/lukeshu.com/git/go/libgnulinux.git +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d8c4fd9aef9137b04e4311a1f50024ab88d4c6e3 diff --git a/src/lukeshu.com/git/go/libnslcd.git b/src/lukeshu.com/git/go/libnslcd.git deleted file mode 160000 index 99adee2..0000000 --- a/src/lukeshu.com/git/go/libnslcd.git +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 99adee24d96f27f08fecc0a56b3c26c688045290 diff --git a/src/lukeshu.com/git/go/libsystemd.git b/src/lukeshu.com/git/go/libsystemd.git deleted file mode 160000 index 89efdfb..0000000 --- a/src/lukeshu.com/git/go/libsystemd.git +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 89efdfbee5f9a22f9dd1083f7a383daba54d4f12 diff --git a/src/parabola_hackers/.gitignore b/src/parabola_hackers/.gitignore deleted file mode 100644 index 3be3f08..0000000 --- a/src/parabola_hackers/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/users.go -/passwords.go diff --git a/src/parabola_hackers/nslcd_backend/db_config.go b/src/parabola_hackers/nslcd_backend/db_config.go deleted file mode 100644 index e78643b..0000000 --- a/src/parabola_hackers/nslcd_backend/db_config.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -package hackers_nslcd_backend - -import ( - s "syscall" - - p "lukeshu.com/git/go/libnslcd.git/proto" -) - -func (o *Hackers) Config_Get(cred s.Ucred, req p.Request_Config_Get) <-chan p.Config { - o.lock.RLock() - ret := make(chan p.Config) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - switch req.Key { - case p.NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE: - if o.cfg.Pam_password_prohibit_message != "" { - ret <- p.Config{Value: o.cfg.Pam_password_prohibit_message} - } - } - }() - return ret -} diff --git a/src/parabola_hackers/nslcd_backend/db_group.go b/src/parabola_hackers/nslcd_backend/db_group.go deleted file mode 100644 index 18e54b1..0000000 --- a/src/parabola_hackers/nslcd_backend/db_group.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -package hackers_nslcd_backend - -import ( - "parabola_hackers" - s "syscall" - - p "lukeshu.com/git/go/libnslcd.git/proto" -) - -func (o *Hackers) groupByName(name string, users bool) p.Group { - members_set, found := o.groups[name] - if !found { - return p.Group{ID: -1} - } - gid := name2gid(name) - if gid < 0 { - return p.Group{ID: -1} - } - var members_list []string - if users { - members_list = parabola_hackers.Set2list(members_set) - } else { - members_list = make([]string, 0) - } - return p.Group{ - Name: name, - PwHash: "x", - ID: gid, - Members: members_list, - } -} - -func (o *Hackers) groupByGid(gid int32, users bool) p.Group { - name, found := gid2name(gid) - if !found { - return p.Group{ID: -1} - } - members_set, found := o.groups[name] - if !found { - return p.Group{ID: -1} - } - var members_list []string - if users { - members_list = parabola_hackers.Set2list(members_set) - } else { - members_list = make([]string, 0) - } - return p.Group{ - Name: name, - PwHash: "x", - ID: gid, - Members: members_list, - } -} - -func (o *Hackers) Group_ByName(cred s.Ucred, req p.Request_Group_ByName) <-chan p.Group { - o.lock.RLock() - ret := make(chan p.Group) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - group := o.groupByName(req.Name, true) - if group.ID < 0 { - return - } - ret <- group - }() - return ret -} - -func (o *Hackers) Group_ByGid(cred s.Ucred, req p.Request_Group_ByGid) <-chan p.Group { - o.lock.RLock() - ret := make(chan p.Group) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - group := o.groupByGid(req.Gid, true) - if group.ID < 0 { - return - } - ret <- group - }() - return ret -} - -// note that the BYMEMBER call returns an empty members list -func (o *Hackers) Group_ByMember(cred s.Ucred, req p.Request_Group_ByMember) <-chan p.Group { - o.lock.RLock() - ret := make(chan p.Group) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - uid := o.name2uid(req.Member) - if uid < 0 { - return - } - for _, name := range o.users[uid].Groups { - group := o.groupByName(name, false) - if group.ID >= 0 { - ret <- group - } - } - }() - return ret -} - -func (o *Hackers) Group_All(cred s.Ucred, req p.Request_Group_All) <-chan p.Group { - o.lock.RLock() - ret := make(chan p.Group) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - for name, _ := range o.groups { - group := o.groupByName(name, true) - if group.ID >= 0 { - ret <- group - } - } - }() - return ret -} diff --git a/src/parabola_hackers/nslcd_backend/db_pam.go b/src/parabola_hackers/nslcd_backend/db_pam.go deleted file mode 100644 index 3374170..0000000 --- a/src/parabola_hackers/nslcd_backend/db_pam.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -package hackers_nslcd_backend - -import ( - "fmt" - "parabola_hackers" - s "syscall" - - "lukeshu.com/git/go/libgnulinux.git/crypt" - p "lukeshu.com/git/go/libnslcd.git/proto" - "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" -) - -func checkPassword(password string, hash string) bool { - return crypt.Crypt(password, hash) == hash -} - -func hashPassword(newPassword string, oldHash string) string { - salt := oldHash - if salt == "!" { - str, err := parabola_hackers.RandomString(crypt.SaltAlphabet, 8) - if err != nil { - logger.Err("Could not generate a random string") - str = "" - } - salt = "$6$" + str + "$" - } - return crypt.Crypt(newPassword, salt) -} - -func (o *Hackers) PAM_Authentication(cred s.Ucred, req p.Request_PAM_Authentication) <-chan p.PAM_Authentication { - o.lock.RLock() - ret := make(chan p.PAM_Authentication) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - uid := o.name2uid(req.UserName) - if uid < 0 { - return - } - - user := o.users[uid] - obj := p.PAM_Authentication{ - AuthenticationResult: p.NSLCD_PAM_AUTH_ERR, - UserName: "", - AuthorizationResult: p.NSLCD_PAM_AUTH_ERR, - AuthorizationError: "", - } - if checkPassword(req.Password, user.Passwd.PwHash) { - obj.AuthenticationResult = p.NSLCD_PAM_SUCCESS - obj.AuthorizationResult = obj.AuthenticationResult - obj.UserName = user.Passwd.Name - } - ret <- obj - }() - return ret -} - -func (o *Hackers) PAM_Authorization(cred s.Ucred, req p.Request_PAM_Authorization) <-chan p.PAM_Authorization { - o.lock.RLock() - ret := make(chan p.PAM_Authorization) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - uid := o.name2uid(req.UserName) - if uid < 0 { - return - } - ret <- p.PAM_Authorization{ - Result: p.NSLCD_PAM_SUCCESS, - Error: "", - } - }() - return ret -} - -const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - -func (o *Hackers) PAM_SessionOpen(cred s.Ucred, req p.Request_PAM_SessionOpen) <-chan p.PAM_SessionOpen { - ret := make(chan p.PAM_SessionOpen) - go func() { - defer close(ret) - - sessionid, err := parabola_hackers.RandomString(alphabet, 24) - if err != nil { - return - } - ret <- p.PAM_SessionOpen{SessionID: sessionid} - }() - return ret -} - -func (o *Hackers) PAM_SessionClose(cred s.Ucred, req p.Request_PAM_SessionClose) <-chan p.PAM_SessionClose { - ret := make(chan p.PAM_SessionClose) - go close(ret) - return ret -} - -func (o *Hackers) PAM_PwMod(cred s.Ucred, req p.Request_PAM_PwMod) <-chan p.PAM_PwMod { - ret := make(chan p.PAM_PwMod) - o.lock.Lock() - go func() { - defer close(ret) - defer o.lock.Unlock() - - uid := o.name2uid(req.UserName) - if uid < 0 { - return - } - user := o.users[uid] - - // Check the OldPassword - if req.AsRoot == 1 { - if !checkPassword(req.OldPassword, user.Passwd.PwHash) { - ret <- p.PAM_PwMod{ - Result: p.NSLCD_PAM_PERM_DENIED, - Error: fmt.Sprintf("password change failed: %s", "Old password did not match"), - } - return - } - } - - // Update the PwHash in memory - user.Passwd.PwHash = hashPassword(req.NewPassword, user.Passwd.PwHash) - if user.Passwd.PwHash == "" { - logger.Err("Password hashing failed") - return - } - - // Update the PwHash on disk - passwords := make(map[string]string, len(o.users)) - for _, ouser := range o.users { - passwords[ouser.Passwd.Name] = ouser.Passwd.PwHash - } - passwords[user.Passwd.Name] = user.Passwd.PwHash - err := parabola_hackers.SaveAllPasswords(passwords) - if err != nil { - logger.Err("Writing passwords to disk: %v", err) - return - } - - // Ok, we're done, commit the changes - o.users[uid] = user - ret <- p.PAM_PwMod{ - Result: p.NSLCD_PAM_SUCCESS, - Error: "", - } - }() - return ret -} diff --git a/src/parabola_hackers/nslcd_backend/db_passwd.go b/src/parabola_hackers/nslcd_backend/db_passwd.go deleted file mode 100644 index 3f32ddd..0000000 --- a/src/parabola_hackers/nslcd_backend/db_passwd.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -package hackers_nslcd_backend - -import ( - s "syscall" - - p "lukeshu.com/git/go/libnslcd.git/proto" -) - -/* Note that the output password hash value should be one of: - - no password set, allow login without password - ! - used to prevent logins - x - "valid" encrypted password that does not match any valid password - often used to indicate that the password is defined elsewhere - other - encrypted password, in crypt(3) format */ - -func (o *Hackers) Passwd_ByName(cred s.Ucred, req p.Request_Passwd_ByName) <-chan p.Passwd { - o.lock.RLock() - ret := make(chan p.Passwd) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - uid := o.name2uid(req.Name) - if uid < 0 { - return - } - passwd := o.users[uid].Passwd - passwd.PwHash = "x" // only put actual hashes in the Shadow DB - ret <- passwd - }() - return ret -} - -func (o *Hackers) Passwd_ByUID(cred s.Ucred, req p.Request_Passwd_ByUID) <-chan p.Passwd { - o.lock.RLock() - ret := make(chan p.Passwd) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - user, found := o.users[req.UID] - if !found { - return - } - passwd := user.Passwd - passwd.PwHash = "x" // only put actual hashes in the Shadow DB - ret <- passwd - }() - return ret -} - -func (o *Hackers) Passwd_All(cred s.Ucred, req p.Request_Passwd_All) <-chan p.Passwd { - o.lock.RLock() - ret := make(chan p.Passwd) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - for _, user := range o.users { - passwd := user.Passwd - passwd.PwHash = "x" // only put actual hashes in the Shadow DB - ret <- passwd - } - }() - return ret -} diff --git a/src/parabola_hackers/nslcd_backend/db_shadow.go b/src/parabola_hackers/nslcd_backend/db_shadow.go deleted file mode 100644 index abfff28..0000000 --- a/src/parabola_hackers/nslcd_backend/db_shadow.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -package hackers_nslcd_backend - -import ( - s "syscall" - - p "lukeshu.com/git/go/libnslcd.git/proto" -) - -func (o *Hackers) Shadow_ByName(cred s.Ucred, req p.Request_Shadow_ByName) <-chan p.Shadow { - o.lock.RLock() - ret := make(chan p.Shadow) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - if cred.Uid != 0 { - return - } - uid := o.name2uid(req.Name) - user := o.users[uid] - ret <- p.Shadow{ - Name: user.Passwd.Name, - PwHash: user.Passwd.PwHash, - LastChangeDate: -1, - MinDays: -1, - MaxDays: -1, - WarnDays: -1, - InactDays: -1, - ExpireDate: -1, - Flag: -1, - } - }() - return ret -} - -func (o *Hackers) Shadow_All(cred s.Ucred, req p.Request_Shadow_All) <-chan p.Shadow { - o.lock.RLock() - ret := make(chan p.Shadow) - go func() { - defer o.lock.RUnlock() - defer close(ret) - - if cred.Uid != 0 { - return - } - - for _, user := range o.users { - ret <- p.Shadow{ - Name: user.Passwd.Name, - PwHash: user.Passwd.PwHash, - LastChangeDate: -1, - MinDays: -1, - MaxDays: -1, - WarnDays: -1, - InactDays: -1, - ExpireDate: -1, - Flag: -1, - } - } - }() - return ret -} diff --git a/src/parabola_hackers/nslcd_backend/hackers.go b/src/parabola_hackers/nslcd_backend/hackers.go deleted file mode 100644 index bb03862..0000000 --- a/src/parabola_hackers/nslcd_backend/hackers.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -// Package hackers_nslcd_backend is an nslcd_server Backend that -// speaks to hackers.git. -package hackers_nslcd_backend - -import ( - "parabola_hackers" - "sync" - - nslcd_server "lukeshu.com/git/go/libnslcd.git/proto/server" - nslcd_systemd "lukeshu.com/git/go/libnslcd.git/systemd" - "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" -) - -type config struct { - Pam_password_prohibit_message string -} - -type Hackers struct { - nslcd_server.NilBackend - lock sync.RWMutex - - CfgFilename string - - cfg config - users map[int32]parabola_hackers.User - groups map[string]map[string]bool -} - -var _ nslcd_systemd.Backend = &Hackers{} -var _ nslcd_server.Backend = &Hackers{} - -func (o *Hackers) Init() error { - logger.Debug("hackers.git: CfgFilename = %v", o.CfgFilename) - err := o.Reload() - if err != nil { - logger.Err("hackers.git: Could not initialize: %v", err) - return err - } - return nil -} - -func (o *Hackers) Close() { - logger.Info("hackers.git: Closing session") - o.lock.Lock() - defer o.lock.Unlock() - - o.users = make(map[int32]parabola_hackers.User, 0) - o.groups = make(map[string]map[string]bool) -} - -func (o *Hackers) Reload() error { - logger.Info("hackers.git: Loading session") - o.lock.Lock() - defer o.lock.Unlock() - - var err error - o.cfg, err = parse_config(o.CfgFilename) - if err != nil { - return err - } - logger.Info("hackers.git: pam_password_prohibit_message: %#v", o.cfg.Pam_password_prohibit_message) - - logger.Debug("hackers.git: Parsing user data") - o.users, err = parabola_hackers.LoadAllUsers() - if err != nil { - return err - } - - passwords, err := parabola_hackers.LoadAllPasswords() - if err != nil { - return err - } - - o.groups = make(map[string]map[string]bool) - for uid, user := range o.users { - user.Passwd.GID = usersGid - hash, hasHash := passwords[user.Passwd.Name] - if !hasHash { - hash = "!" - } - user.Passwd.PwHash = hash - o.users[uid] = user - for _, groupname := range user.Groups { - o.add_user_to_group(user.Passwd.Name, groupname) - } - } - return nil -} - -func (o *Hackers) name2uid(name string) int32 { - for uid, data := range o.users { - if data.Passwd.Name == name { - return uid - } - } - return -1 -} - -func (o *Hackers) add_user_to_group(username string, groupname string) { - group, found := o.groups[groupname] - if !found { - group = make(map[string]bool) - o.groups[groupname] = group - } - group[username] = true -} diff --git a/src/parabola_hackers/nslcd_backend/util.go b/src/parabola_hackers/nslcd_backend/util.go deleted file mode 100644 index 4fb28f3..0000000 --- a/src/parabola_hackers/nslcd_backend/util.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -package hackers_nslcd_backend - -import ( - "io/ioutil" - "os" - - yaml "gopkg.in/yaml.v2" - "lukeshu.com/git/go/libgnulinux.git/getgr" -) - -func name2gid(name string) int32 { - gr, err := getgr.ByName(name) - if gr == nil || err != nil { - return -1 - } else { - return int32(gr.Gid) - } -} - -func gid2name(gid int32) (string, bool) { - gr, err := getgr.ByGid(gid) - if gr == nil || err != nil { - return "", false - } else { - return gr.Name, true - } -} - -var usersGid = name2gid("users") - -func parse_config(filename string) (cfg config, err error) { - file, err := os.Open(filename) - if err != nil { - return - } - contents, err := ioutil.ReadAll(file) - if err != nil { - return - } - err = yaml.Unmarshal(contents, &cfg) - return -} diff --git a/src/parabola_hackers/passwords.go.in b/src/parabola_hackers/passwords.go.in deleted file mode 100644 index 0d763b9..0000000 --- a/src/parabola_hackers/passwords.go.in +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -package parabola_hackers - -import ( - "fmt" - "io/ioutil" - "os" - "sort" - "strings" - - "lukeshu.com/git/go/libgnulinux.git/crypt" - "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" -) - -/* Note that the password hash value should be one of: - - no password set, allow login without password - ! - used to prevent logins - x - "valid" encrypted password that does not match any valid password - often used to indicate that the password is defined elsewhere - other - encrypted password, in crypt(3) format */ - -const shadow_file = "@shadow_file@" - -func LoadAllPasswords() (map[string]string, error) { - file, err := os.Open(shadow_file) - if err != nil { - return nil, err - } - contents, err := ioutil.ReadAll(file) - if err != nil { - return nil, err - } - lines := strings.Split(string(contents), "\n") - passwords := make(map[string]string, len(lines)) - for i, line := range lines { - cols := strings.SplitN(line, ":", 2) - if len(cols) != 2 { - logger.Err("hackers.git %s:%d: malformed line", shadow_file, i+1) - continue - } - username := cols[0] - hash := cols[1] - if hash != "!" && !crypt.SaltOk(hash) { - hash = "!" - logger.Err("%s:%d: malformed hash for user: %s", shadow_file, i+1, username) - } - passwords[username] = hash - } - return passwords, nil -} - -func SaveAllPasswords(passwords map[string]string) error { - usernames := make([]string, len(passwords)) - i := 0 - for username, _ := range passwords { - usernames[i] = username - i++ - } - sort.Strings(usernames) - - file, err := os.OpenFile(shadow_file+"-", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) - if err != nil { - return err - } - - for _, username := range usernames { - fmt.Fprintf(file, "%s:%s\n", username, passwords[username]) - } - err = file.Sync() - if err != nil { - return err - } - err = file.Close() - if err != nil { - return err - } - - return os.Rename(shadow_file+"-", shadow_file) -} diff --git a/src/parabola_hackers/users.go.in b/src/parabola_hackers/users.go.in deleted file mode 100644 index aeda069..0000000 --- a/src/parabola_hackers/users.go.in +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -package parabola_hackers - -import ( - "fmt" - "os/exec" - - yaml "gopkg.in/yaml.v2" - nslcd_proto "lukeshu.com/git/go/libnslcd.git/proto" - "lukeshu.com/git/go/libsystemd.git/sd_daemon/logger" -) - -/* Note that the password hash value should be one of: - - no password set, allow login without password - ! - used to prevent logins - x - "valid" encrypted password that does not match any valid password - often used to indicate that the password is defined elsewhere - other - encrypted password, in crypt(3) format */ - -type User struct { - Passwd nslcd_proto.Passwd - Groups []string -} - -func LoadAllUsers() (users map[int32]User, err error) { - contents, err := exec.Command("@bindir@/meta-cat").Output() - if err != nil { - return - } - - var _data interface{} - err = yaml.Unmarshal(contents, &_data) - if err != nil { - return - } - - data, isMap := _data.(map[interface{}]interface{}) - errs := []string{} - if !isMap { - errs = append(errs, "root node is not a map") - } else { - users = make(map[int32]User, len(data)) - for _uid, _user := range data { - uid, isInt := _uid.(int) - if !isInt { - errs = append(errs, fmt.Sprintf("UID is not an int: %T ( %#v )", _uid, _uid)) - continue - } - user, _err := parseUser(_user) - if _err != nil { - errs = append(errs, fmt.Sprintf("Could not parse data for UID %d: %v", uid, _err)) - continue - } - user.Passwd.UID = int32(uid) - logger.Debug("hackers.git: -> User %d(%s) parsed", user.Passwd.UID, user.Passwd.Name) - users[user.Passwd.UID] = user - } - } - if len(errs) > 0 { - users = nil - err = &yaml.TypeError{Errors: errs} - } - return -} - -func parseUser(_data interface{}) (ret User, err error) { - data, isMap := _data.(map[interface{}]interface{}) - errs := []string{} - if !isMap { - errs = append(errs, "root node is not a map") - } else { - if iface, isSet := data["username"]; !isSet { - errs = append(errs, "\"username\" is not set") - } else if str, isTyp := iface.(string); !isTyp { - errs = append(errs, "\"username\" is not a string") - } else { - ret.Passwd.Name = str - ret.Passwd.HomeDir = "/home/" + str - } - - if iface, isSet := data["fullname"]; !isSet { - errs = append(errs, "\"fullname\" is not set") - } else if str, isTyp := iface.(string); !isTyp { - errs = append(errs, "\"fullname\" is not a string") - } else { - ret.Passwd.GECOS = str - } - - if iface, isSet := data["shell"]; !isSet { - errs = append(errs, "\"shell\" is not set") - } else if str, isTyp := iface.(string); !isTyp { - errs = append(errs, "\"shell\" is not a string") - } else { - ret.Passwd.Shell = str - } - - if iface, isSet := data["groups"]; !isSet { - ret.Groups = make([]string, 0) - } else if ary, isTyp := iface.([]interface{}); !isTyp { - errs = append(errs, "\"groups\" is not an array") - } else { - groups := make(map[string]bool, len(ary)) - e := false - for _, iface := range ary { - if str, isTyp := iface.(string); !isTyp { - errs = append(errs, "\"group\" item is not an array") - e = true - break - } else { - groups[str] = true - } - } - if !e { - ret.Groups = Set2list(groups) - } - } - } - if len(errs) > 0 { - err = &yaml.TypeError{Errors: errs} - } - - ret.Passwd.PwHash = "x" // look in shadow for the password hash - ret.Passwd.GID = -1 - - return -} diff --git a/src/parabola_hackers/util.go b/src/parabola_hackers/util.go deleted file mode 100644 index 9a241db..0000000 --- a/src/parabola_hackers/util.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2015-2016 Luke Shumaker . -// -// This 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 2 of -// the License, or (at your option) any later version. -// -// This software 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 this manual; if not, see -// . - -package parabola_hackers - -import ( - "crypto/rand" - "math/big" -) - -func RandomString(alphabet string, n uint) (str string, err error) { - var alphabet_len = big.NewInt(int64(len(alphabet))) - var bigint *big.Int - _str := make([]byte, n) - for i := 0; i < len(_str); i++ { - bigint, err = rand.Int(rand.Reader, alphabet_len) - if err != nil { - return - } - _str[i] = alphabet[bigint.Int64()] - } - str = string(_str[:]) - return -} - -func Set2list(set map[string]bool) []string { - list := make([]string, len(set)) - i := uint(0) - for item, _ := range set { - list[i] = item - i++ - } - return list -} diff --git a/test/.gitignore b/test/.gitignore deleted file mode 100644 index 09230a9..0000000 --- a/test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/runner diff --git a/test/runner.c b/test/runner.c deleted file mode 100644 index 110819d..0000000 --- a/test/runner.c +++ /dev/null @@ -1,168 +0,0 @@ -/* Copyright (C) 2015 Luke Shumaker - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This program 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define _(s) s - -const char *xgetenv(const char *name, const char *unset) { - const char *val = getenv(name); - if (!val) - val = unset; - return val; -} - -char *xasprintf(const char *format, ...) { - va_list arg; - int len; - char *str = NULL; - - va_start(arg, format); - len = vasprintf(&str, format, arg); - va_end(arg); - - if (len < 0) - error(EXIT_FAILURE, errno, _("Could not allocate memory in vasprintf")); - - return str; -} - -#define xasprintfa(...) (__extension__ ({ char *heap = xasprintf(__VA_ARGS__); char *stack = strdupa(heap); free(heap); stack; })) - -int pid = -1; -void -sigchld_handler(int sig __attribute__((__unused__))) { - int status; - pid = waitpid(pid, &status, WNOHANG); - int exited = WEXITSTATUS(status); - error(exited, 0, "%ld exited with status %d", (long)pid, exited); - exit(0); -} - -union addr { - struct sockaddr gen; - struct sockaddr_un un; -}; - -int new_unix_sock(const char *filename, int type) { - union addr addr; - addr.un.sun_family = AF_UNIX; - strcpy(addr.un.sun_path, filename); - - int sock = socket(AF_UNIX, type, 0); - if (sock < 0) - error(EXIT_FAILURE, errno, "socket(%d, %d)", AF_UNIX, type); - unlink(filename); - if (bind(sock, &addr.gen, sizeof(addr))) - error(EXIT_FAILURE, errno, "bind(%d, sockaddr(\"%s\"))", sock, filename); - switch (type) { - case SOCK_STREAM: - case SOCK_SEQPACKET: - if (listen(sock, 5)) - error(EXIT_FAILURE, errno, "listen(%d /* \"%s\" */, %d)", sock, filename, 5); - break; - case SOCK_DGRAM: - break; - default: - error(EXIT_FAILURE, errno, "new_unix_sock: Unrecognized type: %d", type); - } - return sock; -} - -char *cmdname = "nshd_runner"; -const char *notify_sockname = "notify.sock"; -const char *nslcd_sockname = "nslcd.sock"; -void cleanup(void) { - if (nslcd_sockname) - unlink(nslcd_sockname); - if (notify_sockname) - unlink(notify_sockname); - fprintf(stderr, "%s: Exiting\n", cmdname); -} - -int main(int argc, char *argv[]) { - cmdname = argv[0]; - if (argc != 2) { - error(2, 0, _("Usage: %s NSHD_PROGRAM"), argv[0]); - } - - atexit(&cleanup); - int nslcd_sock = new_unix_sock(nslcd_sockname , SOCK_STREAM); - int notify_sock = new_unix_sock(notify_sockname, SOCK_DGRAM ); - - struct sigaction act; - sigemptyset(&act.sa_mask); - act.sa_flags = SA_RESTART; - act.sa_handler = sigchld_handler; - if (sigaction(SIGCHLD, &act, 0)) - error(EXIT_FAILURE, errno, "sigaction"); - - - pid = fork(); - switch (pid) { - case -1: - error(EXIT_FAILURE, errno, "fork"); - case 0: - close(notify_sock); - dup2(nslcd_sock, 3); - if (nslcd_sock != 3) - close(nslcd_sock); - pid = getpid(); - setenv("LISTEN_PID", xasprintfa("%ld", (long)pid), 1); - setenv("LISTEN_FDS", "1", 1); - setenv("NOTIFY_SOCKET", notify_sockname, 1); - execl(argv[1], argv[1], NULL); - error(EXIT_FAILURE, errno, "execl"); - } - - while (1) { - union addr client_addr; - socklen_t client_size; - char buf[4097]; - ssize_t bytes_read = recvfrom(notify_sock, buf, sizeof(buf)-1, 0, &client_addr.gen, &client_size); - if (bytes_read < 1) - error(EXIT_FAILURE, errno, "recvfrom"); - if (buf[bytes_read-1] != '\n') { - buf[bytes_read] = '\n'; - bytes_read++; - } - ssize_t bytes_written = 0; - while (bytes_written < bytes_read) { - ssize_t n = write(2, &(buf[bytes_written]), bytes_read-bytes_written); - if (n < 0) { - bytes_written = -1; - break; - } - bytes_written += n; - } - if (bytes_written < 0) - error(EXIT_FAILURE, errno, "write"); - } - error(EXIT_FAILURE, 0, "not reached"); - return EXIT_FAILURE; -} -- cgit v1.2.2