[D-community-commits] r312 - in trunk: . userdir-ldap userdir-ldap/debian userdir-ldap/doc userdir-ldap/doc/samples userdir-ldap/templates
nd-guest at alioth.debian.org
nd-guest at alioth.debian.org
Thu Jul 24 11:10:48 UTC 2008
Author: nd-guest
Date: 2008-07-24 11:10:47 +0000 (Thu, 24 Jul 2008)
New Revision: 312
Added:
trunk/userdir-ldap/
trunk/userdir-ldap/debian/
trunk/userdir-ldap/debian/changelog
trunk/userdir-ldap/debian/compat
trunk/userdir-ldap/debian/conffiles
trunk/userdir-ldap/debian/control
trunk/userdir-ldap/debian/copyright
trunk/userdir-ldap/debian/postinst
trunk/userdir-ldap/debian/rules
trunk/userdir-ldap/debian/ud-replicate.cron.d
trunk/userdir-ldap/debian/userdir-ldap.postinst.debhelper
trunk/userdir-ldap/debian/userdir-ldap.prerm.debhelper
trunk/userdir-ldap/debian/userdir-ldap.substvars
trunk/userdir-ldap/doc/
trunk/userdir-ldap/doc/makefile
trunk/userdir-ldap/doc/samples/
trunk/userdir-ldap/doc/samples/ud-generate
trunk/userdir-ldap/doc/samples/ud-replicate
trunk/userdir-ldap/doc/samples/ud-zoneupdate
trunk/userdir-ldap/doc/slapd-config.txt
trunk/userdir-ldap/doc/ud-generate.8.yo
trunk/userdir-ldap/doc/ud-gpgimport.8.yo
trunk/userdir-ldap/doc/ud-info.1.yo
trunk/userdir-ldap/doc/ud-mailgate.8.yo
trunk/userdir-ldap/doc/ud-useradd.8.yo
trunk/userdir-ldap/doc/ud-userimport.8.yo
trunk/userdir-ldap/doc/ud-xearth.1.yo
trunk/userdir-ldap/gpgwrapper
trunk/userdir-ldap/sigcheck
trunk/userdir-ldap/templates/
trunk/userdir-ldap/templates/change-reply
trunk/userdir-ldap/templates/error-reply
trunk/userdir-ldap/templates/list-subscribe
trunk/userdir-ldap/templates/passwd-changed
trunk/userdir-ldap/templates/ping-reply
trunk/userdir-ldap/templates/welcome-message
trunk/userdir-ldap/templates/welcome-message-100
trunk/userdir-ldap/templates/welcome-message-60000
trunk/userdir-ldap/templates/welcome-message-800
trunk/userdir-ldap/ud-arbimport
trunk/userdir-ldap/ud-echelon
trunk/userdir-ldap/ud-emailmatcher
trunk/userdir-ldap/ud-fingerserv
trunk/userdir-ldap/ud-fingerserv2.c
trunk/userdir-ldap/ud-forwardlist
trunk/userdir-ldap/ud-generate
trunk/userdir-ldap/ud-gpgimport
trunk/userdir-ldap/ud-gpgsigfetch
trunk/userdir-ldap/ud-groupadd
trunk/userdir-ldap/ud-homecheck
trunk/userdir-ldap/ud-host
trunk/userdir-ldap/ud-info
trunk/userdir-ldap/ud-ldapshow
trunk/userdir-ldap/ud-mailgate
trunk/userdir-ldap/ud-passchk
trunk/userdir-ldap/ud-replicate
trunk/userdir-ldap/ud-roleadd
trunk/userdir-ldap/ud-sshlist
trunk/userdir-ldap/ud-useradd
trunk/userdir-ldap/ud-userimport
trunk/userdir-ldap/ud-xearth
trunk/userdir-ldap/ud-zoneupdate
trunk/userdir-ldap/userdir-ldap.conf
trunk/userdir-ldap/userdir_gpg.py
trunk/userdir-ldap/userdir_ldap.py
Log:
[svn-inject] Installing original source of userdir-ldap
Added: trunk/userdir-ldap/debian/changelog
===================================================================
--- trunk/userdir-ldap/debian/changelog (rev 0)
+++ trunk/userdir-ldap/debian/changelog 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,211 @@
+userdir-ldap (0.3.15~debiancommunity1) etch; urgency=low
+
+ * All I did was s/debconf/debian-community/ and adapted some mail addresses.
+
+ -- Holger Levsen <holger at debian.org> Sun, 16 Mar 2008 20:30:29 +0100
+
+userdir-ldap (0.3.15~debconf1) etch; urgency=low
+
+ * Build for DebConf, so s/debian/debconf/ in many places
+ * Add welcome-message-100 in templates/
+
+ -- Joerg Jaspert <joerg at debian.org> Thu, 27 Sep 2007 20:15:11 +0200
+
+userdir-ldap (0.3.15) unstable; urgency=low
+
+ * userdir_gpg.py: fix RT #70 (sub-key support for mail gateway)
+ * change *PK* mechanism to Linux standard ! mechanism, and check for
+ it in the mail gateway for changes, too.
+ * change packaging to make use of python-support, simplyfing scripts
+ greatly.
+ * Remove obsolete ud-killcrypt that wouldn't work with modern ldap anyways.
+
+ -- Ryan Murray <rmurray at debian.org> Sun, 12 Aug 2007 13:38:11 -0600
+
+userdir-ldap (0.3.14) unstable; urgency=low
+
+ * ud-generate:
+ . Establish *PK* as mechanism for locked accounts with mail forwarding intact.
+ No subscription to debian-private though, and no way to log in
+ . Support [NOPASSWD] for generating configs that only don't
+ contain the password for untrusted hosts that should receive
+ the other regular export (patch by Andreas Barth <aba at not.so.argh.org>)
+ * ud-host:
+ . Import the base DN from the configuration file (patch by Andreas
+ Barth <aba at not.so.argh.org>)
+ * ud-roleadd:
+ . Improved admin output
+ * Removed unused and obsolete whrandom module (ud-host, ud-info, ud.py)
+ * Query the LDAP server if no locally defined group with that name was
+ found (userdir_ldap.py, ud-useradd, ud-roleadd)
+ * Preparations for being useful with the version of Python in etch
+
+ -- Joey Schulze <joey at infodrom.org> Sat, 11 Aug 2007 22:23:25 +0200
+
+userdir-ldap (0.3.13) unstable; urgency=low
+
+ * ud-generate: only look for *LK*, not the fingerprint when auto-disabling
+ mail
+
+ -- Ryan Murray <rmurray at debian.org> Mon, 15 Jan 2007 16:14:29 -0700
+
+userdir-ldap (0.3.12) unstable; urgency=low
+
+ * Add dns-sshfp file with DNS SSHFP records for each host.
+ * Add mail-disable file from LDAP mailDisableMessage
+ * Add mail-greylist and mail-callout files from LDAP mailGreylisting and
+ mailCallout
+ * Add mail-rbl, mail-rhsbl, and mail-whitelist files from LDAP, and add
+ support to set them via mailgate
+ * Add support for additional fields to mailgate arbitrary change and
+ delete functions.
+
+ -- Ryan Murray <rmurray at debian.org> Thu, 28 Dec 2006 05:14:45 -0700
+
+userdir-ldap (0.3.11) unstable; urgency=low
+
+ * Add debianhosts file with ip addresses of all hosts.
+ * Don't output bsmtp or DNS entries for accounts without fingerprints.
+ * ud-replicate:
+ - Adjusted symlink path for ssh_known_hosts inside chroot
+ - Use "db" alias for where to rsync from, rather than a hostname (rmurray)
+ - bsmtp handling updated for exim4 (rmurray)
+ * ud-host:
+ - Initialise Host so that -f will print a full list
+ - Display error message and exit properly for wrong arguments
+ - Take better care of unset attributes
+ * userdir_gpg.py:
+ - Removed reference to FCNTL
+ * ud-info
+ - Display error message and exit properly for wrong arguments
+
+ -- Ryan Murray <rmurray at debian.org> Sun, 13 Nov 2005 16:42:02 -0700
+
+userdir-ldap (0.3.10) unstable; urgency=low
+
+ * See cvs log for detailed changes
+ * Fixes to run properly on sarge, and other misc changes.
+
+ -- Ryan Murray <rmurray at debian.org> Fri, 14 Oct 2005 21:49:28 -0600
+
+userdir-ldap (0.3.9) unstable; urgency=low
+
+ * Added an explicit PATH statement to ud-replicate
+
+ -- Martin Schulze <joey at infodrom.org> Tue, 25 Jan 2005 10:50:54 +0100
+
+userdir-ldap (0.3.8) stable; urgency=low
+
+ * ud-replicate:
+ - Update the ssh shadow files in /etc if they don't exist
+ - Corrected the verbosity detection
+ - Only fiddle inside the chroot if the makedb program exist
+ - Fixed path bug
+ - Added support for hosts without imported shadow file
+ * userdir_ldap.py:
+ - Support pressing C-d and C-c
+ - Support for alphanumeric group names
+ - Added passwdAccessLDAP() for wider use by programs
+ * ud-useradd:
+ - Support for mistyped passwords
+ - Support for alphanumerical group ids
+ - Support for no debian-private subscription
+ * ud-host:
+ - Support for -l to list all hosts
+ - Always perform the list output anonymously
+ - Support for -f to list all fingerprints (-h host optional)
+ * ud-info:
+ - Corrected spelling for labeledURI
+ * ud-mailgate:
+ - Corrected spelling for labeledURI
+ - Added support for deleting the d.net entry
+ - Notify users about unsupported SSH1 keys
+ * Added a proper copyright file
+ * Updated welcome-message-800 from newsamosa
+ * userdir_gpg.py:
+ - Preparations for sarge, added --secret-keyring /dev/null
+ * ud-generate:
+ - Generic support for haydn and alioth as [UNTRUSTED]
+ * userdir_gpg.py:
+ - Create ~/.gnupg automagically
+ * ud-roleadd:
+ - New program
+ - Support for creating role accounts (non-DD accounts)
+
+ -- Martin Schulze <joey at infodrom.org> Mon, 24 Jan 2005 09:04:00 +0100
+
+userdir-ldap (0.3.7) stable; urgency=low
+
+ * ud-fingerserv: Corrected the key/fingerprint feature
+ * ud-fingerserv: Added the ICQ UIN
+ * Case sensive LDAP field names for nearly all programs
+ * ud-generate:
+ . Honour locked accounts when writing the shadow file
+ . Hardcode exception for haydn and costa as alioth hosts
+ * ud-mailgate: No access for locked accounts
+ * sigcheck: imported changes from murphy
+ * Added sigcheck to the programs to be installed
+ * Moved the web pages into webwml/db.debian.org
+ * Moved the CGI programs into its own package
+ * ud-replicate:
+ . Added support for user chroot environments
+ . Remove shadow files if they exist
+ . Remove ud-replicate in /usr/local/bin
+
+ -- Martin Schulze <joey at infodrom.org> Thu, 18 Nov 2004 19:07:01 +0100
+
+userdir-ldap (0.3.6) unstable; urgency=low
+
+ * Specify full path to postmap
+
+ -- Ryan Murray <rmurray at debian.org> Fri, 26 Sep 2003 11:48:25 -0600
+
+userdir-ldap (0.3.5) unstable; urgency=low
+
+ * Add depends on rsync
+ * Generate db of debian.org on postfix systems
+
+ -- Ryan Murray <rmurray at debian.org> Sat, 30 Aug 2003 18:41:29 -0600
+
+userdir-ldap (0.3.4) unstable; urgency=low
+
+ * Use the right python version in the maintainer scripts
+
+ -- Ryan Murray <rmurray at debian.org> Tue, 18 Mar 2003 19:26:31 -0700
+
+userdir-ldap (0.3.3) unstable; urgency=low
+
+ * Rebuild for python2.1 and woody
+
+ -- Ryan Murray <rmurray at debian.org> Wed, 12 Mar 2003 21:30:12 -0700
+
+userdir-ldap (0.3.2) unstable; urgency=low
+
+ * Seperation of bsmtp and zoneupdate
+
+ -- Ryan Murray <rmurray at debian.org> Thu, 8 Aug 2002 12:07:00 -0700
+
+userdir-ldap (0.3.1) unstable; urgency=low
+
+ * Add ud-zoneupdate from klecker's /usr/local/bin
+
+ -- Ryan Murray <rmurray at debian.org> Tue, 6 Aug 2002 22:42:05 -0700
+
+userdir-ldap (0.3) unstable; urgency=low
+
+ * Only use sshrsa{host,}key variable, and store all three types of keys
+ in that attribute.
+
+ -- Jason Gunthrope <jgg at debian.org> Sun, 2 Dec 2001 20:21:26 -0800
+
+userdir-ldap (0.2) unstable; urgency=low
+
+ * What the hey, a new version number.
+
+ -- Jason Gunthrope <jgg at debian.org> Sun, 11 Feb 2001 18:37:27 -0800
+
+userdir-ldap (0.1) unstable; urgency=low
+
+ * Initial Packaging
+
+ -- Jason Gunthrope <jgg at debian.org> Fri, 30 Apr 1999 00:39:31 -0600
Added: trunk/userdir-ldap/debian/compat
===================================================================
--- trunk/userdir-ldap/debian/compat (rev 0)
+++ trunk/userdir-ldap/debian/compat 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1 @@
+5
Added: trunk/userdir-ldap/debian/conffiles
===================================================================
--- trunk/userdir-ldap/debian/conffiles (rev 0)
+++ trunk/userdir-ldap/debian/conffiles 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,8 @@
+/etc/cron.d/ud-replicate
+/etc/userdir-ldap/generate.conf
+/etc/userdir-ldap/userdir-ldap.conf
+/etc/userdir-ldap/templates/list-subscribe
+/etc/userdir-ldap/templates/passwd-changed
+/etc/userdir-ldap/templates/ping-reply
+/etc/userdir-ldap/templates/welcome-message-800
+/etc/userdir-ldap/templates/welcome-message-60000
Added: trunk/userdir-ldap/debian/control
===================================================================
--- trunk/userdir-ldap/debian/control (rev 0)
+++ trunk/userdir-ldap/debian/control 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,20 @@
+Source: userdir-ldap
+Section: admin
+Priority: optional
+Maintainer: Debian Administration team <debian-admin at lists.debian.org>
+Build-Depends-Indep: debhelper, python-support
+Standards-Version: 3.5.8.0
+Uploaders: Ryan Murray <rmurray at debian.org>
+
+Package: userdir-ldap
+Architecture: all
+Depends: ${python:Depends}, python-ldap, perl5, procmail, rsync, libnss-db
+Suggests: libnet-ldap-perl, libcrypt-blowfish-perl, gnupg (>= 1.0.3), python-net, python-gdbm, libdate-manip-perl, liburi-perl, userdir-ldap-cgi
+Description: Login User Directory in LDAP support scripts
+ These scripts simplifiy the creation and management of a LDAP based user
+ directory. Included are scripts to import existing passwd, group and shadow
+ files as well as a 'chfn' like script to allow users and admins to edit
+ their entries.
+ .
+ Finally a script to associate GPG/PGP key finger prints with each user is
+ provided, assuming a secure trusted keyring for the domain also exists.
Added: trunk/userdir-ldap/debian/copyright
===================================================================
--- trunk/userdir-ldap/debian/copyright (rev 0)
+++ trunk/userdir-ldap/debian/copyright 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,23 @@
+userdir-ldap has been released under the GNU General Public License.
+
+ Copyright (c) 1999-2005 Debian Admin Team Members and Developers
+
+ This program 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 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+On Debian GNU/Linux systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
+
+The source of the Debian package is managed through CVS. It is publically
+available at <http://cvs.debian.org/userdir-ldap/?cvsroot=debian-admin>.
Added: trunk/userdir-ldap/debian/postinst
===================================================================
--- trunk/userdir-ldap/debian/postinst (rev 0)
+++ trunk/userdir-ldap/debian/postinst 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,8 @@
+#! /bin/bash -e
+#
+#DEBHELPER#
+if [ "$1" = "configure" ]
+then
+ test ! -f /usr/local/bin/ud-replicate || rm -f /usr/local/bin/ud-replicate
+fi
+exit 0
Added: trunk/userdir-ldap/debian/rules
===================================================================
--- trunk/userdir-ldap/debian/rules (rev 0)
+++ trunk/userdir-ldap/debian/rules 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,72 @@
+#! /usr/bin/make -f
+# -*- make -*-
+# Made with the aid of debmake, by Christoph Lameter,
+# based on the sample debian/rules file for GNU hello by Ian Jackson.
+
+package:=userdir-ldap
+i:=./debian/$(package)
+pysite:=usr/share/python-support
+
+build:
+ dh_testdir
+ touch build
+
+clean:
+ dh_testdir
+ -rm -f build
+ -find . -name '*.py[co]' | xargs rm -f
+ -rm -rf $(i) debian/files* core debian/substvars
+
+instdirs = \
+ usr/bin \
+ $(pysite)/userdir_ldap \
+ usr/share/doc/$(package)/samples \
+ etc/userdir-ldap/templates \
+ etc/cron.d
+
+binary-indep: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs $(instdirs)
+
+ echo "userdir_ldap" > $(i)/$(pysite)/userdir_ldap.pth
+ echo "userdir_gpg" >> $(i)/$(pysite)/userdir_ldap.pth
+ install -m 644 userdir_ldap.py userdir_gpg.py \
+ $(i)/$(pysite)/userdir_ldap/
+ install -m 755 {ud-forwardlist,ud-gpgimport,ud-info,ud-ldapshow,ud-userimport,ud-mailgate,ud-generate,ud-passchk,ud-useradd,ud-replicate,ud-xearth,ud-fingerserv,ud-echelon,ud-groupadd,ud-host,ud-zoneupdate,ud-roleadd} $(i)/usr/bin/
+ install -m 755 sigcheck $(i)/usr/bin/
+ install -m 644 debian/ud-replicate.cron.d $(i)/etc/cron.d/ud-replicate
+
+ install -m 644 doc/slapd-config.txt $(i)/usr/share/doc/$(package)
+ install -m 644 doc/samples/ud-* $(i)/usr/share/doc/$(package)/samples
+ gzip -9 $(i)/usr/share/doc/$(package)/*.txt
+ install -m 644 userdir-ldap.conf $(i)/etc/userdir-ldap/
+ echo "# See /usr/share/doc/userdir-ldap" > $(i)/etc/userdir-ldap/generate.conf
+ chmod 644 $(i)/etc/userdir-ldap/generate.conf
+ install -m 644 templates/*-* $(i)/etc/userdir-ldap/templates/
+
+ dh_installdocs
+ dh_installchangelogs
+ dh_installman
+ dh_fixperms
+ dh_compress
+ dh_pysupport
+ dh_installdeb
+ dh_gencontrol
+# dh_makeshlibs
+ dh_md5sums
+ dh_builddeb
+
+binary-arch: build
+ dh_testdir
+# There are no architecture-dependent files to be uploaded
+# generated by this package. If there were any they would be
+# made here.
+
+
+# Below here is fairly generic really
+
+binary: binary-indep binary-arch
+
+.PHONY: binary binary-arch binary-indep clean
Property changes on: trunk/userdir-ldap/debian/rules
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/debian/ud-replicate.cron.d
===================================================================
--- trunk/userdir-ldap/debian/ud-replicate.cron.d (rev 0)
+++ trunk/userdir-ldap/debian/ud-replicate.cron.d 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,2 @@
+# crontab for the replicate operation, 15 min cycle.
+10,25,40,55 * * * * root if [ -x /usr/bin/ud-replicate ]; then /usr/bin/ud-replicate; fi
Added: trunk/userdir-ldap/debian/userdir-ldap.postinst.debhelper
===================================================================
--- trunk/userdir-ldap/debian/userdir-ldap.postinst.debhelper (rev 0)
+++ trunk/userdir-ldap/debian/userdir-ldap.postinst.debhelper 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,5 @@
+# Automatically added by dh_pysupport
+if which update-python-modules >/dev/null 2>&1; then
+ update-python-modules userdir_ldap
+fi
+# End automatically added section
Added: trunk/userdir-ldap/debian/userdir-ldap.prerm.debhelper
===================================================================
--- trunk/userdir-ldap/debian/userdir-ldap.prerm.debhelper (rev 0)
+++ trunk/userdir-ldap/debian/userdir-ldap.prerm.debhelper 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,5 @@
+# Automatically added by dh_pysupport
+if which update-python-modules >/dev/null 2>&1; then
+ update-python-modules -c userdir_ldap
+fi
+# End automatically added section
Added: trunk/userdir-ldap/debian/userdir-ldap.substvars
===================================================================
--- trunk/userdir-ldap/debian/userdir-ldap.substvars (rev 0)
+++ trunk/userdir-ldap/debian/userdir-ldap.substvars 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,2 @@
+python:Versions=2.3, 2.4, 2.5
+python:Depends=python, python-support (>= 0.7.1)
Added: trunk/userdir-ldap/doc/makefile
===================================================================
--- trunk/userdir-ldap/doc/makefile (rev 0)
+++ trunk/userdir-ldap/doc/makefile 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,11 @@
+.SILENT:
+
+MANPAGES = ud-generate.8 ud-gpgimport.8 ud-info.1 ud-xearth.1 ud-useradd.8 \
+ ud-userimport.8 ud-mailgate.8
+
+all: $(MANPAGES)
+
+$(MANPAGES) :: % : %.yo
+ echo Creating man page $@
+ yodl2man -o $@ $<
+
Added: trunk/userdir-ldap/doc/samples/ud-generate
===================================================================
--- trunk/userdir-ldap/doc/samples/ud-generate (rev 0)
+++ trunk/userdir-ldap/doc/samples/ud-generate 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,2 @@
+# Cron tab for the generate operation, 15 min cycle. Put it in /etc/cron.d/
+08,23,38,53 * * * * sshdist if [ -x /usr/bin/ud-generate ]; then /usr/bin/ud-generate; fi
Added: trunk/userdir-ldap/doc/samples/ud-replicate
===================================================================
--- trunk/userdir-ldap/doc/samples/ud-replicate (rev 0)
+++ trunk/userdir-ldap/doc/samples/ud-replicate 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,3 @@
+# Cron tab for the replicate operation, 15 min cycle, offset from generate.
+# Put it in /etc/cron.d/
+10,25,40,55 * * * * root if [ -x /usr/bin/ud-replicate ]; then /usr/bin/ud-replicate; fi
Added: trunk/userdir-ldap/doc/samples/ud-zoneupdate
===================================================================
--- trunk/userdir-ldap/doc/samples/ud-zoneupdate (rev 0)
+++ trunk/userdir-ldap/doc/samples/ud-zoneupdate 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -e
+
+sed -e "s/[1-9].*; Serial.*$/`date +%Y%m%d%H` ; Serial/" < $1 > $1.new
+mv -f $1.new $1
+/usr/sbin/ndc reload > /dev/null 2>&1
+
Added: trunk/userdir-ldap/doc/slapd-config.txt
===================================================================
--- trunk/userdir-ldap/doc/slapd-config.txt (rev 0)
+++ trunk/userdir-ldap/doc/slapd-config.txt 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,73 @@
+Most of the configuration of the ldap server has to do with getting correct
+access controls to keep the data safe. Here is a sample:
+
+# Turn on automatic last modification time
+lastmod on
+
+# Index some things
+index uid eq
+index keyfingerprint eq
+index cn,sn approx,sub,eq
+
+# Administrate
+#rootdn "uid=admin,ou=users,dc=debian,dc=org"
+#rootpw
+
+# Restrict reading/modification of the password to administration and self
+access to attrs=userpassword,sshrsaauthkey
+ by self write
+ by dn="uid=admin,ou=users,dc=debian,dc=org" write
+ by group="uid=admin,ou=users,dc=debian,dc=org" write
+ by * compare
+
+access to attrs=emailforward
+ by dn="uid=admin,ou=users,dc=debian,dc=org" write
+ by group="uid=admin,ou=users,dc=debian,dc=org" write
+ by self write
+ by addr=127.0.0.1 read
+ by domain=.*\.debian\.org read
+ by * none
+access to attrs=c,l,loginShell,ircNick,labeledURL
+ by dn="uid=admin,ou=users,dc=debian,dc=org" write
+ by group="uid=admin,ou=users,dc=debian,dc=org" write
+ by self write
+access to attrs=facsimileTelephoneNumber,telephoneNumber,postalAddress,postalC
+ode,loginShell,onvacation,privateSub,latitude,longitude
+ by dn="uid=admin,ou=users,dc=debian,dc=org" write
+ by group="uid=admin,ou=users,dc=debian,dc=org" write
+ by self write
+ by dn="uid=.*,ou=users,dc=debian,dc=org" read
+ by * none
+access to *
+ by dn="uid=admin,ou=users,dc=debian,dc=org" write
+ by group="uid=admin,ou=users,dc=debian,dc=org" write
+
+# End----------
+
+Here is the initial seed file to import and setup the proper entries:
+
+dn: dc=org
+dc: net
+objectClass: top
+objectClass: domain
+
+dn: dc=debian,dc=org
+dc: visi
+objectClass: top
+objectClass: domain
+
+dn: ou=users,dc=debian,dc=org
+ou: users
+objectClass: top
+objectClass: organizationalUnit
+
+dn: uid=admin,ou=users,dc=debian,dc=org
+uid: admin
+cn: LDAP administrator
+objectClass: top
+objectClass: groupOfNames
+userPassword: {crypt}?????
+member: uid=jgg,ou=users,dc=debian,dc=org
+member: uid=joey,ou=users,dc=debian,dc=org
+member: uid=troup,ou=users,dc=debian,dc=org
+mail: debian-admin at debian.org
Added: trunk/userdir-ldap/doc/ud-generate.8.yo
===================================================================
--- trunk/userdir-ldap/doc/ud-generate.8.yo (rev 0)
+++ trunk/userdir-ldap/doc/ud-generate.8.yo 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,54 @@
+mailto(admin at db.debian.org)
+manpage(ud-generate)(1)(17 Sep 1999)(userdir-ldap)()
+manpagename(ud-generate)(Produce machine specific formatted version of the
+directory)
+
+manpagesynopsis()
+ ud-generate
+
+manpagedescription()
+
+ud-generate prouces machine specific versions of the directory in the
+following formats:
+
+itemize(
+ it() passwd file [in normal and DB form]
+ it() shadow file [in normal and DB form]
+ it() group file [in normal and DB form]
+ it() Exim forwarding file [cdb]
+ it() XEarth makers file
+ it() SSH authorized key file [cdb and flat]
+ it() debian.net DNS zone
+)
+
+Generation of the files is controlled by the configuration file
+bf(/etc/userdir-ldap/ud-generate.conf). The output is placed in
+bf(/var/cache/userdir-ldap/hosts/<hostname>/). Each host listed in the
+configuration file has its own home dir path and its own list of groups that
+are allowed to login to the machine.
+
+The format of the configuration file is a one line per host with these fields:
+verb(host homedirpath group1 group2 ...)
+Only users who are a member of the named groups or has a specific host acl
+are emitted to the output files.
+
+The special groups bf([DNS]) and bf([PRIVATE]) control replication of the
+debian.net zone and the debian-private subscription list.
+
+Authorization to read protected entries from the directory is achieved by
+reading a username and password from the pass- file in the userdir-ldap
+directory.
+
+manpagefiles()
+itemize(
+ it() /etc/userdir-ldap/userdir-ldap.conf
+ Configuration variables to select what server and what base DN to use.
+ it() /etc/userdir-ldap/ud-generate.conf
+ Configuration variables to determine how hosts are generated.
+ it() /etc/userdir-ldap/pass-<uid>
+ Directory authentication credentials
+)
+
+manpageauthor()
+userdir-ldap was written by Jason Gunthorpe <jgg at debian.org>.
+
Added: trunk/userdir-ldap/doc/ud-gpgimport.8.yo
===================================================================
--- trunk/userdir-ldap/doc/ud-gpgimport.8.yo (rev 0)
+++ trunk/userdir-ldap/doc/ud-gpgimport.8.yo 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,67 @@
+mailto(admin at db.debian.org)
+manpage(ud-gpgimport)(8)(17 Sep 1999)(userdir-ldap)()
+manpagename(ud-gpgimport)(Key Ring Syncronization utility)
+
+manpagesynopsis()
+ ud-gpgimport [options] [keyrings]
+
+manpagedescription()
+ud-gpgimport maintains the key fingerprint to user ID mapping in the
+directory. It takes as input a set of keyrings that represent all keys
+belonging to all users in the directory. It then reads each key and attempts
+to match it up to a user already in the directory. This matching process has
+several steps:
+
+1) If the key fingerprint already exists in the directory then the key is
+assumed to be already assigned so it is ignored
+
+2) If the key email address is in the override table then the key is
+assigned to the user in the override table
+
+3) An exact match of first name + last name from the key's primary UID is
+performed against the directory. If a single hit is found then the key is
+assigned to that user
+
+4) If the email address in the key is within the debian.org domain then the
+key is assigned to the to the mentioned user if the last name from the
+directory appears some place in the key UID. This is called an bf(EmailAppend)
+hit.
+
+5) Nothing is done, but a soundex matcher is invoked to give some suggestions
+on who the key may belong to.
+
+An override table is used to deal with keys that do not exactly match any
+user in the directory. The override table takes the email address that
+appears on a key and maps it to a uid in the directory.
+
+By default the matcher only generates a report on what it would do but makes
+no changes. The -a option must be given and an password entered to allow
+modification.
+
+GnuPG must be properly installed in the system to extract the key
+information from the key rings.
+
+manpageoptions()
+startdit()
+dit(bf(-a))
+Enable modification of the directory.
+
+dit(bf(-u))
+Set the authentication user. This is the user who's authority is used when
+accessing the LDAP directory. The default is to use the current system user
+name.
+
+dit(bf(-m))
+Set the override file to use. The format of the override file is a map of key
+email address to uid, eg verb(foo at bar.com: baz)
+enddit()
+
+manpagefiles()
+itemize(
+ it() /etc/userdir-ldap/userdir-ldap.conf
+ Configuration variables to select what server and what base DN to use.
+)
+
+manpageauthor()
+userdir-ldap was written by Jason Gunthorpe <jgg at debian.org>.
+
Added: trunk/userdir-ldap/doc/ud-info.1.yo
===================================================================
--- trunk/userdir-ldap/doc/ud-info.1.yo (rev 0)
+++ trunk/userdir-ldap/doc/ud-info.1.yo 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,181 @@
+mailto(admin at db.debian.org)
+manpage(ud-info)(1)(17 Sep 1999)(userdir-ldap)()
+manpagename(ud-info)(Command line LDAP user record manipulator)
+
+manpagesynopsis()
+ ud-info [options]
+
+manpagedescription()
+
+ud-info is the command-line tool for end users to manipulate their own
+database information and to view other users information. It also provides
+root functions which when combined with sufficient LDAP privilages allow
+an administrator to completely manipulate a users record.
+
+The defined fields are:
+itemize(
+ it() cn - Common (first) name. [root]
+ it() mn - Middle name or initial. [root]
+ it() sn - Surname (last name). [root]
+ it() cn - ISO 3166 country code, see file(/usr/share/zoneinfo/iso3166.tab)
+ Should be upper case.
+ it() ircnick - IRC nickname.
+ it() l - City name, state/province. The part of a mailing address that is
+ not the street address. e.g.: Dallas, Texas
+ it() postalcode - Postal Code or ZIP Code
+ it() postaladdress - Complete mailing address including postal codes and
+ country designations. Newlines are seperated by a $ character. The
+ address should be formed exactly as it would appear on a parcel.
+ it() latitude/longitude - The physical latitude and longitude. This
+ information is typically used to generate an xearth marker file.
+ See the discussion below on position formats.
+ it() facsimiletelephonenumber - FAX phone number, do not forget to specify a
+ country code [North Armerica is +1].
+ it() telephonenumber - Voice phone number.
+ it() loginshell - Full path to the prefered Unix login shell. e.g. file(/bin/bash)
+ it() emailforward - Destination email address.
+ it() userpassword - Encrypted version of the password. [root]
+ it() sshrsaauthkey - SSH RSA public authentication key.
+ it() supplementarygid - A list of group names that the user belongs.
+ This field emulates the functionality of the traditional Unix group
+ file. [root]
+ it() dnszoneentry - A list of zone file fragments that are placed in
+ the zone file for debian.net. [root]
+ it() allowedhosts - Permits access to hosts outside of the group list. [root]
+ it() onvacation - A message indicating that the user is on vacation. The
+ time of departure and expected return date should be included as
+ well as any special instructions.
+ it() comment - Administrative comment about the account. [root]
+ it() labeledurl - User's web site.
+ it() privatesub - Debian-Private subscription
+ it() icquin - ICQ User Number
+)
+
+When prompted for a password it is possible to enter a blank password and
+access the database anonymously. This is useful to check PGP key
+fingerprints, for instance.
+
+manpagesection(SECURITY AND PRIVACY)
+Three levels of information security are provided by the database. The first
+is completely public information that anyone can see either by issuing an
+LDAP query or by visiting the web site. The next level is "maintainer-only"
+information that requires authentication to the directory before it can be
+accessed. The final level is admin-only or user-only information; this
+information can only be viewed by the user or an administrator.
+
+Maintainer-only information includes precise location information
+[postalcode, postal address, lat/long] telephone numbers, and the vacation
+message.
+
+Admin-only/user-only information includes email forwarding, ssh keys and
+the encrypted password. Note that email forwarding is necessarily publicly
+viewable from accounts on the actual machines.
+
+manpagesection(LAT/LONG POSITION)
+There are three possible formats for giving position information and several
+online sites that can give an accurate position fix based on mailing address.
+
+startdit()
+dit(Decimal Degrees)
+The format is +-DDD.DDDDDDDDDDDDDDD. This is the format programs like
+bf(xearth)
+use and the format that many positioning web sites use. However typically
+the precision is limited to 4 or 5 decimals.
+
+dit(Degrees Minutes (DGM))
+The format is +-DDDMM.MMMMMMMMMMMMM. It is not an arithmetic type, but a
+packed representation of two seperate units, degrees and minutes. This
+output is common from some types of hand held GPS units and from NMEA format
+GPS messages.
+
+dit(Degrees Minutes Seconds (DGMS))
+The format is +-DDDMMSS.SSSSSSSSSSS. Like DGM, it is not an arithmetic type but
+a packed representation of three seperate units, degrees minutes and
+seconds. This output is typically derived from web sites that give 3 values
+for each position. For instance 34:50:12.24523 North might be the position
+given, in DGMS it would be +0345012.24523.
+enddit()
+
+For Latitude + is North, for Longitude + is East. It is important to specify
+enough leading zeros to dis-ambiguate the format that is being used if your
+position is less than 2 degrees from a zero point.
+
+So locations to find positioning information are:
+
+itemize(
+ it() Good starting point - http://www.ckdhr.com/dns-loc/finding.html
+ it() AirNav - GPS locations for airports around the world http://www.airnav.com/
+ it() GeoCode - US index by ZIP Code http://www.geocode.com/eagle.html-ssi
+ it() Map Blast! Canadian, US and some European maps - http://www.mapblast.com/
+ it() Australian Database http://www.environment.gov.au/database/MAN200R.html
+ it() Canadian Database http://GeoNames.NRCan.gc.ca/
+ it() Atlas of the World, indexed by city http://www.astro.com/atlas/
+ it() Xerox PARC Map Viewer http://mapweb.parc.xerox.com/map
+ it() GNU Timezone database, organized partially by country /usr/share/zoneinfo/zone.tab
+)
+
+Remember that we are after reasonable coordinates for drawing an xearth
+graph and looking for people to sign keys, not for coordinates accurate
+enough to land an ICBM on your doorstop!
+
+manpagesection(EDITING SUPPLEMENTAL GIDS)
+When the root function is activated then the supplemental GIDs can be
+manipulated as a list of items. It is possible to add and remove items from
+the list by name. Proper prompts are given. A similar editing function is
+made available for the host acl list.
+
+manpagesection(ENCRYPTION PUBLIC KEYS)
+The directory associates two types of public encryption keys with the user,
+a PGP key fingerprint and a SSH RSA authentication key. It is not possible for
+a user to change their associated key fingerprint, that can only be done by
+the keyring maintainers after performing reasonable verification of the new
+key. Who ever controls the PGP key can make any modification to the LDAP
+account by using the PGP mail gateways.
+
+SSH RSA authentication keys are used by the SSH protocol to authenticate a
+user based on a cryptographic challenge. These keys pairs are created by the
+ssh-keygen program. The public version that is stored in the directory is
+generally placed in a file called identity.pub. SSH RSA authentication keys
+are password equivelents, whoever has the private half of the key can use it
+to login to any machine, but not affect changes to the LDAP entry. SSH
+authentication keys are kept private.
+
+manpagesection(NOTES)
+To lock out an account take the password and prepend *LK* before the hash
+and after the {crypt} this is understood by ssh, shadow and the mailgateway to
+indicate a disabled account. No manipulations what so ever will be permitted.
+
+manpageoptions()
+startdit()
+dit(bf(-a))
+Set the authentication user. This is the user whose authority is used when
+accessing the LDAP directory. The default is to use the current system user
+name.
+
+dit(bf(-u))
+Select the user whose fields will be displayed/edited. The default is to use
+the current system user name.
+
+dit(bf(-c))
+Set both the authentication user and the target user. This option is useful
+if the login name does not match the user who is operating the program.
+
+dit(bf(-r))
+Enable root functions. This enables more options to allow changing
+any entry in the directory. This function only has meaning if the
+authentication user has the necessary permissions at the LDAP server.
+
+dit(bf(-n))
+No actions. Anonymously bind and show the information for the user and then
+exit.
+enddit()
+
+manpagefiles()
+itemize(
+ it() /etc/userdir-ldap/userdir-ldap.conf
+ Configuration variables to select what server and what base DN to use.
+)
+
+manpageauthor()
+userdir-ldap was written by Jason Gunthorpe <jgg at debian.org>.
+
Added: trunk/userdir-ldap/doc/ud-mailgate.8.yo
===================================================================
--- trunk/userdir-ldap/doc/ud-mailgate.8.yo (rev 0)
+++ trunk/userdir-ldap/doc/ud-mailgate.8.yo 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,141 @@
+mailto(admin at db.debian.org)
+manpage(ud-mailgate)(1)(28 Sep 1999)(userdir-ldap)()
+manpagename(ud-mailgate)(PGP mail gateway to the LDAP directory)
+
+manpagesynopsis()
+ ud-mailgate function
+
+manpagedescription()
+ud-mailgate implements a PGP secured mail gateway to an LDAP directory that
+allows users to safely and conviently effect changes to their entries. It
+makes use of PGP signed input messages to positivly identify the user and
+to confirm the validity of the request. Furthermore it implements a replay
+cache that prevents the gateway from accepting the same message more than
+once.
+
+There are three functions logically split into 3 sperate email addresses
+that are implemented by the gateway: bf(ping), bf(new password) and
+bf(changes). The function to act on is the first argument to the program.
+
+ud-mailgate was designed to take its message on stdin from a mailsystem like
+Exim, with full message headers intact. It transparently decodes PGP/MIME
+and PGP clearsigned messages and passes them through GnuPG for verification.
+Support for PGP2.x users is maintained by passing options to GunPG that
+generate encrypted messages they are able to decode, however this option
+is only enabled for PGP2.x keys, OpenPGP keys use the new packet formats.
+
+Error handling is currently done by generating a bounce message and passing
+descriptive error text to the mailer. For mailers like Exim this generates a
+very hard to read message, but it does have the relevent information
+embedded in it.
+
+manpagesection(PING)
+The ping command simply returns the users public record. It is usefull for
+testing the gateway and for the requester to get a basic dump of their
+record. In future this address might 'freshen' the record to indicate the
+user is alive. Any PGP signed message will produce a reply.
+
+manpagesection(NEW PASSWORD)
+If a user looses their password they can request that a new one be generated
+for them. This is done by sending the phrase "Please change my Debian
+password" to chpasswd at db.debian.org. The phrase is required to prevent the
+daemon from triggering on arbitary signed email. The best way to invoke this
+feature is with verb(echo "Please change my Debian password" | gpg
+--clearsign | mail chpasswd at db.debian.org)
+After validating the request the daemon will generate a new random password,
+set it in the directory and respond with an ecrpyted message containing the
+new password. The password can be changed using one of the other interface
+methods.
+
+manpagesection(CHANGES)
+An address is provided for making almost arbitary changes to the contents of
+the record. The daemon parse its input line by line and acts on each line in
+a command oriented manner. Anything, except for passwords, can be changed
+using this mechanism. Note however that because this is a mail gateway it
+does stringent checking on its input. The other tools allow fields to be set
+to virtually anything, the gateway requires specific field formats to be met.
+
+startdit()
+dit(Arbitary Change)
+A line of the form bf('field: value') will change the contents of the field
+to value. Some simple checks are performed on value to make sure that it is
+not sent to nonsense. The values that can be changed are: c, l,
+facsimiletelephonenumber, telephonenumber, postaladdress, postalcode,
+loginshell, emailforward, ircnick, onvacation, and labledurl. See
+ud-info(1) for information on the meanings of each field type.
+
+dit(Latitude/Longitude Change)
+The daemon has a special parser to help changing latitude and longitude
+values. It accepts several common formats for position information and
+converts them to one of the standard forms. The permitted types are
+verb(D = Degrees, M = Minutes, S = Seconds, x = n,s,e,w
++-DDD.DDDDD, +- DDDMM.MMMM, +-DDDMMSS.SSSS [standard forms]
+DDxMM.MMMM, DD:MM.MMMM x, DD:MM:SS.SSS X)
+and the request format is bf('Lat: xxx Long: xxx') where xxx is one of the
+permitted types. The resulting response will include how the input was
+parsed and the value in decimal degrees.
+
+dit(SSH RSA Authentication key load)
+Part of the replicated dataset is a virtual .ssh/authorized_keys file for
+each user. The change address is the simplest way to set the RSA key(s) you
+intend to use. Simply place a key on a line by itself, the full SSH key
+format specification is supported, see sshd(8). Probably the most common way
+to use this function will be verb(cat .ssh/identity.pub | gpg --clearsign |
+mail change at db.debian.org) which will set the authentication key to the
+identity you are using.
+
+Multiple keys per user are supported, but they must all be sent at once.
+
+dit(DNS Zone Entry)
+The only way to get a debian.net address is to use this mail gateway. It
+will verify the request and prevent name collisions automatically. Requests
+can take two forms: bf('foo in a 1.2.3.4') or bf('foo in cname foo.bar.')
+The precise form is critical and must not be deviated from.
+
+Like the SSH function above, multiple hosts are supported, but they must all
+be sent at once. The debian.net zone is only reloaded once per day at
+midnight -0700.
+
+dit(Show Function)
+If the single word bf('show') appears on a line then a PGP encrypted version
+of the entire record will be attached to the result email.
+
+dit(Erasing an entry)
+The command bf('del foo') can be used to erase any of the entries settable by
+the user. The erasable attributes are: c, l, facsimiletelephonenumber,
+telephonenumber, postaladdress, postalcode, emailforward, ircnick,
+onvacation, labeledurl, latitude, longitude, and sshrsaauthkey.
+
+enddit()
+
+After processing the requests the daemon will generate a report which contains
+each input command and the action taken. If there are any parsing errors
+processing stops immediately, but valid changes up to that point are
+processed.
+
+manpagesection(NOTES)
+In this document PGP refers to any message or key that GnuPG is
+able to generate or parse, specificaly it includes both PGP2.x and OpenPGP
+(aka GnuPG) keys.
+
+Due to the replay cache the clock on the computer that generates the
+signatures has to be accurate to at least one day. If it is off by several
+months or more then the deamon will outright reject all messages.
+
+Examples are given using GnuPG, but PGP 2.x can also be used. The correct
+options to generate a clear signed ascii armored message in 'filter' mode
+are bf(pgp -fast) which does the same as bf(gpg --clearsign)
+
+Debian.org machines rely on secured replication to transfer login data out
+of the database. Replication is performed at 15 min intervals so it can take
+a short while before any changes made take effect.
+
+manpagefiles()
+itemize(
+ it() /etc/userdir-ldap/userdir-ldap.conf
+ Configuration variables to select what server and what base DN to use.
+)
+
+manpageauthor()
+userdir-ldap was written by Jason Gunthorpe <jgg at debian.org>.
+
Added: trunk/userdir-ldap/doc/ud-useradd.8.yo
===================================================================
--- trunk/userdir-ldap/doc/ud-useradd.8.yo (rev 0)
+++ trunk/userdir-ldap/doc/ud-useradd.8.yo 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,113 @@
+mailto(admin at db.debian.org)
+manpage(ud-useradd)(8)(17 Sep 1999)(userdir-ldap)()
+manpagename(ud-useradd)(Interactive user addition program)
+
+manpagesynopsis()
+ ud-useradd [options]
+
+manpagedescription()
+ud-uaseradd is an interactive program for adding new users to the directory.
+It takes care of all steps of user addition including generating a random
+new password and sending a greeting form letter.
+
+The operator is taken through a set of prompts to determine the data to be
+loaded into the directory:
+
+startdit()
+dit(PGP Key Fingerprint)
+The first prompt is to determine the user's PGP key. For this to be
+successfull the key must have already been loaded into a keyring referenced
+by the GPG configuration file. The search specification is passed directly
+to GPG and then the results are presented, when a single match is found then
+it is taken as the correct key.
+
+dit(Account Name)
+This is the UID of the user, their login name and email local part. If the
+name already exists then it is possible to update the account directly. This
+feature should probably be used very infrequently as ud-info can adjust
+all of the values.
+
+dit(First, Last and Middle Name)
+The proper name of the user, split into three components. The name
+name attached to the PGP key is provided as a default. In most cases this
+should be adaquate and correct.
+
+dit(Email Forwarding Address)
+The address that all general email should be forwarded to. This is analogous
+to a .forward file in the users home directory except that it applies
+globally to all machines. The email address attached to the PGP key is
+provided as a default.
+
+dit(Debian-Private Subscription)
+The address the user should be subscribed to debian-private with. Currently
+this sets the field in the DB and emails a subscription form to the
+list server.
+
+dit(Group ID Number)
+Main group the user will be part of. The group the user is assigned to
+determines which welcome form they are sent. The default is taken from
+the global configuration file
+
+dit(UID)
+The uid is selected automatically based on the first found free UID.
+
+dit(Password)
+The password can be specified if the user is not legaly able to use
+encryption (they live in France for instance) otherwise pressing enter at
+this prompt will generate a random new password. The password to be entered
+is the plain text version, the script will crypt it automatically.
+enddit()
+
+After the information has been collected a summary is displayed and
+confirmation is required to proceed. Once confirmed the script will create a
+new entry and fill it with the given values. Then it will open the greeting
+form bf(/etc/userdir-ldap/templates/welcome-message-<GID>) and perform a
+variable substitution before sending it. Then the debian-private subscription
+form is sent.
+
+It is expected that the PGP key of the user has already been inserted into a
+local keyring known to GPG.
+
+manpagesection(Substitution Variables)
+A number of values are provided as substitution variables for the greeting
+and subscription message, they are:
+
+itemize(
+ it() __REALNAME__ The combined First/Middle/Last name
+ it() __WHOAMI__ The invoking user ID [unix ID]
+ it() __DATE__ The current date in RFC 822 form
+ it() __LOGIN__ The new users login ID
+ it() __PRIVATE__ The address to subscribe to debian-private
+ it() __EMAIL__ The normal email address of the user
+ it() __PASSWORD__ An ascii armored PGP packet containing the users
+ password.
+ it() __LISTPASS__ The contents of the file ~/.debian-lists_passwd
+)
+
+manpageoptions()
+startdit()
+dit(bf(-u))
+Set the authentication user. This is the user who's authority is used when
+accessing the LDAP directory. The default is to use the current system user
+name.
+
+dit(bf(-m))
+Force resending of the greeting emails.
+
+dit(bf(-a))
+Use all available key rings.
+enddit()
+
+manpagefiles()
+itemize(
+ it() /etc/userdir-ldap/userdir-ldap.conf
+ Configuration variables to select what server and what base DN to use.
+ it() /etc/userdir-ldap/templates/welcome-message-<GID>
+ The welcoming message to send to the user. Each primary group has its
+ own message
+ it() ~/.debian-lists_passwd
+ Authentication password for the list server
+)
+
+manpageauthor()
+userdir-ldap was written by Jason Gunthorpe <jgg at debian.org>.
Added: trunk/userdir-ldap/doc/ud-userimport.8.yo
===================================================================
--- trunk/userdir-ldap/doc/ud-userimport.8.yo (rev 0)
+++ trunk/userdir-ldap/doc/ud-userimport.8.yo 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,76 @@
+mailto(admin at db.debian.org)
+manpage(ud-userimport)(1)(17 Sep 1999)(userdir-ldap)()
+manpagename(ud-userimport)(Perform initial import of date)
+
+manpagesynopsis()
+ ud-userimport [options]
+
+manpagedescription()
+
+ud-userimport is the utility that is used to initially load data into the
+directory. It takes as input a set of normal unix password, group and shadow
+files and loads their contents. Also it provide enough functionality to
+allow simple additions at a later date.
+
+Before attempting to import the data the passwd file should be sanitized
+of any system entries and the GECOs fields should be cleaned of any
+strangeness users may have inserted.
+
+Next the passwd file alone should be added using the command
+verb(ud-userimport -a -p passwd)
+The passwd file will be loaded into the
+empty directory and new entries created for all the users.
+
+The shadow file does not have to be santized, importing it without the -a
+option will automatically skip any records that are not needed.
+The command to use is verb(ud-userimport -s shadow)
+
+Like the passwd file the group file needs to be cleaned of system groups and
+groups that are no longer needed. It is not necessary to remove non-existant
+users from the group lists, they will be automatically ignored. Like for
+the shadow file the command is verb(ud-userimport -a -g group)
+
+After the initial import is completed the ud-info tool can be used to
+manipulate the user records, however new groups can most easially be created
+by giving a file containing only a single group (and its initial membership)
+to ud-userimport.
+
+The importer is optimized to get good speed on updates through the use
+of the async ldap mechanism. If errors are found in the import of the
+passwd file or shadow file it is possible to re-run the import command
+(without the -a option) to freshen the data set.
+
+Aside from the evident transformations, the splitter also processes the
+unix gecos field into split first/last/middle names and it also sanitizes
+the gecos field to follow normal Debian convetions.
+
+manpageoptions()
+startdit()
+dit(bf(-u))
+Set the authentication user. This is the user who's authority is used when
+accessing the LDAP directory. The default is to use the current system user
+name.
+
+dit(bf(-x))
+Do not write new passwords into the directory. This is usefull if other
+information is being freshened but users have changed their passwords.
+
+dit(bf(-p))
+Specify the passwd file to import.
+
+dit(bf(-g))
+Specify the group file to import.
+
+dit(bf(-s))
+Specify the shadow file to import.
+enddit()
+
+manpagefiles()
+itemize(
+ it() /etc/userdir-ldap/userdir-ldap.conf
+ Configuration variables to select what server and what base DN to use.
+)
+
+manpageauthor()
+userdir-ldap was written by Jason Gunthorpe <jgg at debian.org>.
+
Added: trunk/userdir-ldap/doc/ud-xearth.1.yo
===================================================================
--- trunk/userdir-ldap/doc/ud-xearth.1.yo (rev 0)
+++ trunk/userdir-ldap/doc/ud-xearth.1.yo 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,44 @@
+mailto(admin at db.debian.org)
+manpage(ud-xearth)(1)(17 Sep 1999)(userdir-ldap)()
+manpagename(ud-xearth)(Extracts the XEarth marker database)
+
+manpagesynopsis()
+ ud-xearth [options]
+
+manpagedescription()
+ud-xearth simply extracts the lat/long information from the directory and
+formats it in a form suitable for use by XEarth or XPlanet. The program
+takes the lat/long coords stored in the directory and converts them to a
+decimal degrees format and then outputs a file containing the UID of the
+user and their coordinates as well as their full email address in a comment.
+The output is place in a file called ./markers.dat
+
+Since lat/long information is restricted to developers only a valid login is
+required to extract the information.
+
+A good way to make use of the coordinates is the following command:
+verb(xplanet --shade 100 --marker_ developers.coords --color white \
+--output developers.map.jpeg --geometry 750x450)
+
+manpageoptions()
+startdit()
+dit(bf(-u))
+Set the authentication user. This is the user who's authority is used when
+accessing the LDAP directory. The default is to use the current system user
+name.
+
+dit(bf(-a))
+Anonomize the data. Coordinates are truncated and no names are printed. For
+best results the output should be sorted using 'sort -n'. Otherwise the output
+is sorted by name..
+
+enddit()
+
+manpagefiles()
+itemize(
+ it() /etc/userdir-ldap/userdir-ldap.conf
+ Configuration variables to select what server and what base DN to use.
+)
+
+manpageauthor()
+userdir-ldap was written by Jason Gunthorpe <jgg at debian.org>.
Added: trunk/userdir-ldap/gpgwrapper
===================================================================
--- trunk/userdir-ldap/gpgwrapper (rev 0)
+++ trunk/userdir-ldap/gpgwrapper 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+#
+# Check and decode PGP signed emails.
+# This script implements a wrapper around another program. It takes a mail
+# on stdin and processes off a PGP signature, verifying it and seperating
+# out the checked plaintext. It then invokes a sub process and feeds it
+# the verified plain text and sets environment vairables indicating the
+# result of the PGP check. If PGP checking fails then the subprocess is
+# is never run and a bounce message is generated. The wrapper can understand
+# PGP-MIME and all signatures supported by GPG. It completely decodes
+# PGP-MIME before running the subprocess. It also can do optional
+# anti-replay checking on the signatures.
+#
+# If enabled it can also do LDAP checking to determine the uniq UID owner
+# of the key.
+#
+# Options:
+# -r Replay cache file, if unset replay checking is disabled
+# -e Bounce error message template file, if unset very ugly bounces are
+# made
+# -k Colon seperated list of keyrings to use
+# -a Reply to address (mail daemon administrator)
+# -d LDAP search base DN
+# -l LDAP server
+# -m Email address to use when prettying up LDAP_EMAIL
+#
+# It exports the following environment variables:
+# LDAP_EMAIL="Adam Di Carlo <aph at debian.org>"
+# LDAP_UID="aph"
+# PGP_FINGERPRINT="E21E5D13FAD42A54F1AA5A00D801CE55"
+# PGP_KEYID="8FFC405EFD5A67CD"
+# PGP_KEYNAME="Adam Di Carlo <aph at debian.org> "
+# SENDER (from mailer - envelope sender for bounces)
+# REPLYTO (generated from message headers)
+#
+# Typical Debian invokation may look like:
+# ./gpgwrapper -k /usr/share/keyrings/debian-keyring.gpg:/usr/share/keyrings/debian-keyring.pgp \
+# -d ou=users,dc=debian,dc=org -l db.debian.org \
+# -m debian.org -a admin at db.debian.org \
+# -e /etc/userdir-ldap/templtes/error-reply -- test.sh
+
+import sys, traceback, time, os;
+import string, pwd, getopt;
+from userdir_gpg import *;
+
+EX_TEMPFAIL = 75;
+EX_PERMFAIL = 65; # EX_DATAERR
+Error = 'Message Error';
+ReplyTo = "admin at db";
+
+# Configuration
+ReplayCacheFile = None;
+ErrorTemplate = None;
+LDAPDn = None;
+LDAPServer = None;
+EmailAppend = "";
+
+# Safely get an attribute from a tuple representing a dn and an attribute
+# list. It returns the first attribute if there are multi.
+def GetAttr(DnRecord,Attribute,Default = ""):
+ try:
+ return DnRecord[1][Attribute][0];
+ except IndexError:
+ return Default;
+ except KeyError:
+ return Default;
+ return Default;
+
+# Return a printable email address from the attributes.
+def EmailAddress(DnRecord):
+ cn = GetAttr(DnRecord,"cn");
+ sn = GetAttr(DnRecord,"sn");
+ uid = GetAttr(DnRecord,"uid");
+ if cn == "" and sn == "":
+ return "<" + uid + "@" + EmailAppend + ">";
+ return cn + " " + sn + " <" + uid + "@" + EmailAppend + ">"
+
+# Match the key fingerprint against an LDAP directory
+def CheckLDAP(FingerPrint):
+ import ldap;
+
+ # Connect to the ldap server
+ global ErrTyp, ErrMsg;
+ ErrType = EX_TEMPFAIL;
+ ErrMsg = "An error occured while performing the LDAP lookup";
+ global l;
+ l = ldap.open(LDAPServer);
+ l.simple_bind_s("","");
+
+ # Search for the matching key fingerprint
+ Attrs = l.search_s(LDAPDn,ldap.SCOPE_ONELEVEL,"keyfingerprint=" + FingerPrint);
+ if len(Attrs) == 0:
+ raise Error, "Key not found"
+ if len(Attrs) != 1:
+ raise Error, "Oddly your key fingerprint is assigned to more than one account.."
+
+ os.environ["LDAP_UID"] = GetAttr(Attrs[0],"uid");
+ os.environ["LDAP_EMAIL"] = EmailAddress(Attrs[0]);
+
+# Start of main program
+# Process options
+(options, arguments) = getopt.getopt(sys.argv[1:], "r:e:k:a:d:l:m:");
+for (switch, val) in options:
+ if (switch == '-r'):
+ ReplayCacheFile = val;
+ elif (switch == '-e'):
+ ErrorTemplate = val;
+ elif (switch == '-k'):
+ SetKeyrings(string.split(val,":"));
+ elif (switch == '-a'):
+ ReplyTo = val;
+ elif (switch == '-d'):
+ LDAPDn = val;
+ elif (switch == '-l'):
+ LDAPServer = val;
+ elif (switch == '-m'):
+ EmailAppend = val;
+
+# Drop messages from a mailer daemon. (empty sender)
+if os.environ.has_key('SENDER') == 0 or len(os.environ['SENDER']) == 0:
+ sys.exit(0);
+
+ErrMsg = "Indeterminate Error";
+ErrType = EX_TEMPFAIL;
+try:
+ # Startup the replay cache
+ ErrType = EX_TEMPFAIL;
+ if ReplayCacheFile != None:
+ ErrMsg = "Failed to initialize the replay cache:";
+ RC = ReplayCache(ReplayCacheFile);
+ RC.Clean();
+
+ # Get the email
+ ErrType = EX_PERMFAIL;
+ ErrMsg = "Failed to understand the email or find a signature:";
+ Email = mimetools.Message(sys.stdin,0);
+ Msg = GetClearSig(Email);
+
+ ErrMsg = "Message is not PGP signed:"
+ if string.find(Msg[0],"-----BEGIN PGP SIGNED MESSAGE-----") == -1:
+ raise Error, "No PGP signature";
+
+ # Check the signature
+ ErrMsg = "Unable to check the signature or the signature was invalid:";
+ Res = GPGCheckSig(Msg[0]);
+
+ if Res[0] != None:
+ raise Error, Res[0];
+
+ if Res[3] == None:
+ raise Error, "Null signature text";
+
+ # Extract the plain message text in the event of mime encoding
+ global PlainText;
+ ErrMsg = "Problem stripping MIME headers from the decoded message"
+ if Msg[1] == 1:
+ try:
+ Index = string.index(Res[3],"\n\n") + 2;
+ except ValueError:
+ Index = string.index(Res[3],"\n\r\n") + 3;
+ PlainText = Res[3][Index:];
+ else:
+ PlainText = Res[3];
+
+ # Check the signature against the replay cache
+ if ReplayCacheFile != None:
+ ErrMsg = "The replay cache rejected your message. Check your clock!";
+ Rply = RC.Check(Res[1]);
+ if Rply != None:
+ raise Error, Rply;
+ RC.Add(Res[1]);
+
+ # Do LDAP stuff
+ if LDAPDn != None:
+ CheckLDAP(Res[2][1]);
+
+ # Determine the sender address
+ ErrType = EX_PERMFAIL;
+ ErrMsg = "A problem occured while trying to formulate the reply";
+ Sender = Email.getheader("Reply-To");
+ if Sender == None:
+ Sender = Email.getheader("From");
+ if Sender == None:
+ raise Error, "Unable to determine the sender's address";
+
+ # Setup the environment
+ ErrType = EX_TEMPFAIL;
+ ErrMsg = "Problem calling the child process"
+ os.environ["PGP_KEYID"] = Res[2][0];
+ os.environ["PGP_FINGERPRINT"] = Res[2][1];
+ os.environ["PGP_KEYNAME"] = Res[2][2];
+ os.environ["REPLYTO"] = Sender;
+
+ # Invoke the child
+ Child = os.popen(string.join(arguments," "),"w");
+ Child.write(PlainText);
+ if Child.close() != None:
+ raise Error, "Child gave a non-zero return code";
+
+except:
+ # Error Reply Header
+ Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
+ ErrReplyHead = "To: %s\nReply-To: %s\nDate: %s\n" % (os.environ['SENDER'],ReplyTo,Date);
+
+ # Error Body
+ Subst = {};
+ Subst["__ERROR__"] = ErrMsg;
+ Subst["__ADMIN__"] = ReplyTo;
+
+ Trace = "==> %s: %s\n" %(sys.exc_type,sys.exc_value);
+ List = traceback.extract_tb(sys.exc_traceback);
+ if len(List) >= 1:
+ Trace = Trace + "Python Stack Trace:\n";
+ for x in List:
+ Trace = Trace + " %s %s:%u: %s\n" %(x[2],x[0],x[1],x[3]);
+
+ Subst["__TRACE__"] = Trace;
+
+ # Try to send the bounce
+ try:
+ if ErrorTemplate != None:
+ ErrReply = TemplateSubst(Subst,open(ErrorTemplate,"r").read());
+ else:
+ ErrReply = "\n"+str(Subst)+"\n";
+
+ Child = os.popen("/usr/sbin/sendmail -t","w");
+ Child.write(ErrReplyHead);
+ Child.write(ErrReply);
+ if Child.close() != None:
+ raise Error, "Sendmail gave a non-zero return code";
+ except:
+ sys.exit(EX_TEMPFAIL);
+
+ if ErrType != EX_PERMFAIL:
+ sys.exit(ErrType);
+ sys.exit(0);
+
Property changes on: trunk/userdir-ldap/gpgwrapper
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/sigcheck
===================================================================
--- trunk/userdir-ldap/sigcheck (rev 0)
+++ trunk/userdir-ldap/sigcheck 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,193 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+#
+# Check PGP signed emails
+#
+# This script verifies the signature on incoming mail for a couple of things
+# - That the signature is valid, recent and is not replay
+# - The signer is in the LDAP directory and is in the right group
+# - The message contains no extra text that is not signed.
+#
+# Options:
+# -r Replay cache file, if unset replay checking is disabled
+# -k Colon seperated list of keyrings to use
+# -d LDAP search base DN
+# -l LDAP server
+# -g supplementary group membership
+# -p File of Phrases that must be in the plaintext.
+# -m Disallow PGP/MIME
+# -v Verbose mode
+
+# Typical Debian invokation may look like:
+# sigcheck -k /usr/share/keyrings/debian-keyring.gpg:/usr/share/keyrings/debian-keyring.pgp \
+# -d ou=users,dc=debian,dc=org -l db.debian.org \
+# -m debian.org -a admin at db.debian.org \
+# -e /etc/userdir-ldap/templtes/error-reply -- test.sh
+
+import sys, traceback, time, os;
+import string, pwd, getopt;
+from userdir_gpg import *;
+
+EX_TEMPFAIL = 75;
+EX_PERMFAIL = 65; # EX_DATAERR
+Error = 'Message Error';
+
+# Configuration
+ReplayCacheFile = None;
+LDAPDn = None;
+LDAPServer = None;
+GroupMember = None;
+Phrases = None;
+AllowMIME = 1;
+Verbose = 0;
+
+def verbmsg(msg):
+ if Verbose:
+ sys.stderr.write(msg + "\n")
+
+# Match the key fingerprint against an LDAP directory
+def CheckLDAP(FingerPrint):
+ import ldap;
+
+ # Connect to the ldap server
+ global ErrTyp, ErrMsg;
+ ErrType = EX_TEMPFAIL;
+ ErrMsg = "An error occurred while performing the LDAP lookup:";
+ global l;
+ l = ldap.open(LDAPServer);
+ l.simple_bind_s("","");
+
+ # Search for the matching key fingerprint
+ verbmsg("Processing fingerprint %s" % FingerPrint)
+ Attrs = l.search_s(LDAPDn,ldap.SCOPE_ONELEVEL,"keyfingerprint=" + FingerPrint);
+ if len(Attrs) == 0:
+ raise Error, "Key not found"
+ if len(Attrs) != 1:
+ raise Error, "Oddly your key fingerprint is assigned to more than one account.."
+
+ gidnumber_found = 0;
+ for key in Attrs[0][1].keys():
+ if (key == "gidNumber"):
+ gidnumber_found = 1
+
+ if (gidnumber_found != 1):
+ raise Error, "No gidnumber in attributes for fingerprint %s" % FingerPrint
+
+ # Look for the group with the gid of the user
+ GAttr = l.search_s(LDAPDn,ldap.SCOPE_ONELEVEL,"(&(objectClass=debianGroup)(gidnumber=%s))" % Attrs[0][1]["gidNumber"][0], ["gid"])
+ if len(GAttr) == 0:
+ raise Error, "Database inconsistency found: main group for account not found in database"
+
+ # See if the group membership is OK
+ # Only if a group was given on the commandline
+ if GroupMember != None:
+ Hit = 0;
+ # Check primary group first
+ if GAttr[0][1]["gid"][0] == GroupMember:
+ Hit = 1
+ else:
+ # Check supplementary groups
+ for x in Attrs[0][1].get("supplementaryGid",[]):
+ if x == GroupMember:
+ Hit = 1;
+ if Hit != 1:
+ raise Error, "You don't have %s group permissions."%(GroupMember);
+
+# Start of main program
+# Process options
+(options, arguments) = getopt.getopt(sys.argv[1:], "r:k:d:l:g:mp:v");
+for (switch, val) in options:
+ if (switch == '-r'):
+ ReplayCacheFile = val;
+ elif (switch == '-k'):
+ SetKeyrings(string.split(val,":"));
+ elif (switch == '-d'):
+ LDAPDn = val;
+ elif (switch == '-l'):
+ LDAPServer = val;
+ elif (switch == '-g'):
+ GroupMember = val;
+ elif (switch == '-m'):
+ AllowMIME = 0;
+ elif (switch == '-v'):
+ Verbose = 1;
+ elif (switch == '-p'):
+ Phrases = val;
+
+Now = time.strftime("%a, %d %b %Y %H:%M:%S",time.gmtime(time.time()));
+ErrMsg = "Indeterminate Error";
+ErrType = EX_TEMPFAIL;
+MsgID = None;
+try:
+ # Startup the replay cache
+ ErrType = EX_TEMPFAIL;
+ if ReplayCacheFile != None:
+ ErrMsg = "Failed to initialize the replay cache:";
+ RC = ReplayCache(ReplayCacheFile);
+ RC.Clean();
+
+ # Get the email
+ ErrType = EX_PERMFAIL;
+ ErrMsg = "Failed to understand the email or find a signature:";
+ Email = mimetools.Message(sys.stdin,0);
+ MsgID = Email.getheader("Message-ID");
+ print "Inspecting message %s"%MsgID;
+ verbmsg("Processing message %s" % MsgID)
+ Msg = GetClearSig(Email,1);
+ # print Msg
+ if AllowMIME == 0 and Msg[1] != 0:
+ raise Error, "PGP/MIME disallowed";
+
+ ErrMsg = "Message is not PGP signed:"
+ if string.find(Msg[0],"-----BEGIN PGP SIGNED MESSAGE-----") == -1:
+ raise Error, "No PGP signature";
+
+ # Check the signature
+ ErrMsg = "Unable to check the signature or the signature was invalid:";
+ Res = GPGCheckSig(Msg[0]);
+
+ if Res[0] != None:
+ raise Error, Res[0];
+
+ if Res[3] == None:
+ raise Error, "Null signature text";
+
+ # Check the signature against the replay cache
+ if ReplayCacheFile != None:
+ ErrMsg = "The replay cache rejected your message. Check your clock!";
+ Rply = RC.Check(Res[1]);
+ if Rply != None:
+ raise Error, Rply;
+ RC.Add(Res[1]);
+ RC.close();
+
+ # Do LDAP stuff
+ if LDAPDn != None:
+ CheckLDAP(Res[2][1]);
+
+ ErrMsg = "Verifying message:";
+ if Phrases != None:
+ F = open(Phrases,"r");
+ while 1:
+ Line = F.readline();
+ if Line == "": break;
+ if string.find(Res[3],string.strip(Line)) == -1:
+ raise Error,"Phrase '%s' was not found"%(string.strip(Line));
+
+except:
+ ErrMsg = "[%s] \"%s\" \"%s %s\"\n"%(Now,MsgID,ErrMsg,sys.exc_value);
+ sys.stderr.write(ErrMsg);
+
+ Trace = "==> %s: %s\n" %(sys.exc_type,sys.exc_value);
+ List = traceback.extract_tb(sys.exc_traceback);
+ if len(List) >= 1:
+ Trace = Trace + "Python Stack Trace:\n";
+ for x in List:
+ Trace = Trace + " %s %s:%u: %s\n" %(x[2],x[0],x[1],x[3]);
+ #print Trace;
+
+ sys.exit(EX_PERMFAIL);
+
+# For Main
+print "Message %s passed"%MsgID;
+sys.exit(0);
Property changes on: trunk/userdir-ldap/sigcheck
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/templates/change-reply
===================================================================
--- trunk/userdir-ldap/templates/change-reply (rev 0)
+++ trunk/userdir-ldap/templates/change-reply 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,14 @@
+From: __FROM__
+Subject: DB Change Request
+
+Hello __EMAIL__!
+
+Your request to change your directory information has been processed.
+Note that there is a propagation time for many of the entries so please
+be patient. Here are the results:
+
+__RESULT__
+
+Please email __ADMIN__ if you have any questions.
+
+__ATTR__
Added: trunk/userdir-ldap/templates/error-reply
===================================================================
--- trunk/userdir-ldap/templates/error-reply (rev 0)
+++ trunk/userdir-ldap/templates/error-reply 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,13 @@
+From: __ADMIN__
+Subject: Mail Gateway failed: __ERROR__
+
+Hello!
+
+Your request to the mail gateway is malformed, or an internal processing
+error occured. The information below may help you, or the gateway
+administrator to identify the problem.
+
+Error: __ERROR__
+__TRACE__
+
+Please email __ADMIN__ if you have any questions.
Added: trunk/userdir-ldap/templates/list-subscribe
===================================================================
--- trunk/userdir-ldap/templates/list-subscribe (rev 0)
+++ trunk/userdir-ldap/templates/list-subscribe 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,4 @@
+To: debian-private-REQUEST at lists.debian.org
+X-We-Want-Cabal: listmaster at lists.debian.org __LISTPASS__ subscribe __PRIVATE__
+
+foo
Added: trunk/userdir-ldap/templates/passwd-changed
===================================================================
--- trunk/userdir-ldap/templates/passwd-changed (rev 0)
+++ trunk/userdir-ldap/templates/passwd-changed 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,15 @@
+From: __FROM__
+Subject: Password Changed!
+
+Hello __EMAIL__!
+
+Your password has been updated. Enclosed below is the new password encrypted
+with your key. __CRYPTTYPE__
+
+Currently LDAP information is replicated to each machine every 15 mins,
+you can set a proper password only by using the web interface at
+<URL:https://db.debian.org/>.
+
+Please email __ADMIN__ if you have any questions.
+
+__PASSWORD__
Added: trunk/userdir-ldap/templates/ping-reply
===================================================================
--- trunk/userdir-ldap/templates/ping-reply (rev 0)
+++ trunk/userdir-ldap/templates/ping-reply 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,11 @@
+From: __FROM__
+Subject: Ping Reply
+
+Hello __EMAIL__!
+
+Here is a list of all the public fields associated with your LDAP entry:
+
+__LDAPFIELDS__
+
+Please email __ADMIN__ if you have any questions.
+
Added: trunk/userdir-ldap/templates/welcome-message
===================================================================
--- trunk/userdir-ldap/templates/welcome-message (rev 0)
+++ trunk/userdir-ldap/templates/welcome-message 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,92 @@
+To: "__REALNAME__" <__EMAIL__>
+From: __WHOAMI__
+Subject: New Debian maintainer __REALNAME__
+Cc: new-maintainer at debian.org
+Reply-To: new-maintainer at debian.org
+Date: __DATE__
+User-Agent: nm-create script run by __WHOAMI__
+
+[ This is a long (automatically-generated) mail, but it contains
+ important information, please read it all carefully. ]
+
+Dear __REALNAME__!
+
+An account has been created for you on master.debian.org and
+va.debian.org with username '__LOGIN__'. The password for this
+account can be found encrypted with your PGP key and appended to this
+message.
+
+You have been subscribed to the debian-private mailing list as
+<__PRIVATE__> (you should receive seperate confirmation of this from
+smartlist). Please respect the privacy of that list and don't forward
+mail from it elsewhere. E-mail to <__LOGIN__ at debian.org> will be
+forwarded to <__EMAIL__>. To change this, edit the file '.qmail' in
+your home directory on master to point to the new address (don't
+forget the '&' character at the beginning of the line). Please
+subscribe to debian-devel-announce, if you haven't done so already.
+
+We strongly suggest that you use your __LOGIN__ at debian.org address for
+the maintainer field in your packages, because that one will be valid
+as long as you are a Debian developer, even if you change jobs, leave
+university or change Internet Service providers. If you do so, please
+add that address to your PGP key (using `pgp -ke "YOUR USER ID"'), if
+you have one, and your GnuPG key (using `gpg --edit-key "YOUR USER ID"')
+and send it to <keyring-maint at debian.org>.
+
+You can find more information useful to developers at
+<URL:http://www.debian.org/devel/> (in particular, see the subsection
+titled "Debian Developer's reference").
+
+We suggest that you subscribe to debian-mentors at lists.debian.org.
+This list is for new maintainers who seek help with initial packaging
+and other developer-related issues. Those who prefer one-on-one help
+can also post to the list, and an experienced developer may volunteer
+to help you. You can get online help on IRC, too, if you join the
+channel #debian-devel on irc.debian.org. Take a look at the support
+section on www.debian.org in order to find out more information.
+
+You should have read these documents before working on your packages.
+
+ o The Debian Social Contract
+ <URL:http://www.debian.org/social_contract.html>
+
+ o The Debian Policy Manual
+ <URL:http://www.debian.org/doc/debian-policy/>
+
+ o The Debian Packaging Manual
+ <URL:http://www.debian.org/doc/packaging-manuals/packaging.html>
+
+If you have some spare time and want to contribute it to Debian you
+may wish to take a look at the "Work-Needing and Prospective Packages
+for Debian GNU/Linux" also known as WNPP that can be found at
+<URL:ftp://ftp.debian.org/doc/package-developer/prospective-packages.html>
+
+If you plan to make a Debian package from a not yet packaged piece of
+software you *must* announce your intention on the debian-devel mailing
+list to make sure nobody else is working on them.
+
+The machine master.debian.org is our main archive server. Every
+uploaded package finds it's way there (except for Packages covered by
+US crypto laws which go to non-us.debian.org) eventually. That
+machine is also the home of our web pages and our bug tracking system.
+The second machine va.debian.org is supposed to take over some of the
+work master does.
+
+Both machines were sponsored by companies (Novare Inc. for master and
+VA Linux Systems for va). Please don't over-stress them and use your
+accounts carefully.
+
+You should use ssh to log into the machines instead of regular telnet
+or rlogin. We have installed ~/.ssh directories and authorized_keys
+files with appropriate permissions on both machines. If you want to
+ssh to them without typing the password, run ssh-keygen on your
+machine and add the contents of ~/.ssh/identity.pub into the
+authorized_keys files in ~/.ssh on master and va. But please be aware
+of the security implications of doing this.
+
+Welcome to the project!
+
+--
+The Debian New Maintainer Team
+
+__PASSWORD__
Added: trunk/userdir-ldap/templates/welcome-message-100
===================================================================
--- trunk/userdir-ldap/templates/welcome-message-100 (rev 0)
+++ trunk/userdir-ldap/templates/welcome-message-100 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,41 @@
+To: "__REALNAME__" <__EMAIL__>
+From: __WHOAMI__
+Subject: New account for __REALNAME__
+Cc: debian-community at layer-acht.org
+Reply-To: admin at debian-community.org
+Date: __DATE__
+User-Agent: create script run by __WHOAMI__
+
+[ This is a mail with important information, so please read it all
+ carefully. ]
+
+Dear __REALNAME__!
+
+Your account '__LOGIN__' has just been created in the central ldap
+database of "debian-community.org". Please note that it needs a bit of time
+until this information is synced with all accessible machines,
+you should be able to login after about 30-60 minutes.
+The password for this account can be found appended to this message,
+encrypted with your GPG key.
+Email sent to <__LOGIN__ at debian-community.org> will be forwarded to <__EMAIL__>, please
+use it for debian-community-related mail only.
+
+If you need additional software installed on one of the machines (either
+in the host system or a chroot environment) please contact
+admin at debian-community.org, but keep in mind that not all software is
+available on all architectures.
+
+You need to use ssh to log into the machines, telnet and rlogin are
+disabled for security reasons. The LDAP directory is able to share RSA
+ssh keys among machines, please see <URL:http://db.debian-community.org/doc-mail.html>
+Otherwise when you first login a ~/.ssh directory will be created with
+the appropriate permissions. Please be aware of the security
+implications of using RSA authentication and ssh agents.
+
+Note: Passwords are deactivated on all debian-community.org machines. If you should get access
+ to one of those two you need to submit a ssh key to the ldap database.
+
+--
+Your fBoFH
+
+__PASSWORD__
Added: trunk/userdir-ldap/templates/welcome-message-60000
===================================================================
--- trunk/userdir-ldap/templates/welcome-message-60000 (rev 0)
+++ trunk/userdir-ldap/templates/welcome-message-60000 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,37 @@
+To: "__REALNAME__" <__EMAIL__>
+Subject: Debian Guest Account for __REALNAME__
+Cc: debian-admin at lists.debian.org
+Reply-To: debian-admin at lists.debian.org
+Date: __DATE__
+User-Agent: Script run by __WHOAMI__
+
+Dear __REALNAME__!
+
+An account has been created for you on the Debian machine cluster. You can
+use this account to help make software run properly on the Debian GNU/Linux
+distribution. The username for this account is '__LOGIN__'. The password can
+be found encrypted with your PGP key and appended to this message.
+
+See <URL:http://db.debian.org/machines.cgi> for a list of machines that are
+available. The ones marked 'public' are available to non-developers.
+
+Requests for Debian software to be installed on the machines (either
+in the host system or a chroot environment) should be directed at
+debian-admin at lists.debian.org. Please note that not all software is
+available on all architectures. The chroot environments can be
+entered with the 'dchroot' command.
+
+You should use ssh to log into the machines instead of regular telnet
+or rlogin. Our LDAP directory is able to share ssh RSA keys among machines,
+please see <URL:http://db.debian.org/doc-mail.html> Otherwise when you
+first login a ~/.ssh directory will be created with the appropriate
+permissions. Please be aware of the security implications of using RSA
+authentication and ssh agents.
+
+After a short while of inactivity this account will be expired. This account
+is only to be used to help porting/improving free software.
+
+--
+Debian Administration
+
+__PASSWORD__
Added: trunk/userdir-ldap/templates/welcome-message-800
===================================================================
--- trunk/userdir-ldap/templates/welcome-message-800 (rev 0)
+++ trunk/userdir-ldap/templates/welcome-message-800 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,122 @@
+To: "__REALNAME__" <__EMAIL__>
+From: __WHOAMI__
+Subject: New Debian maintainer __REALNAME__
+Cc: da-manager at debian.org
+Reply-To: da-manager at debian.org
+Date: __DATE__
+User-Agent: nm-create script run by __WHOAMI__
+
+[ This is a long mail with important information, so please read it all
+ carefully. ]
+
+Dear __REALNAME__!
+
+Your account '__LOGIN__' has just been created in the central LDAP
+database of the Debian project. Please note that it needs a bit of time
+until this information is synced with all developer-accessible machines.
+You should be able to login or upload packages after about 30-60 minutes.
+The password for this account can be found appended to this message,
+encrypted with your GPG key.
+Email sent to <__LOGIN__ at debian.org> will be forwarded to <__EMAIL__>,
+to change this visit <URL:http://db.debian.org/forward.html>.
+
+You now have access to various project machines, for a list of them take
+a look at <URL:http://db.debian.org/machines.cgi>.
+Please remember that you accepted the Debian Machine Usage Policy in
+your NM process (available at <URL:http://www.debian.org/devel/dmup>).
+
+If you need additional software installed on one of the machines (either
+in the host system or a chroot environment) please contact
+debian-admin at lists.debian.org, but keep in mind that not all software is
+available on all architectures. The chroot environments can be
+entered with the 'dchroot' command; take a look at the list of machines
+to know which one has chroots available for you.
+
+You need to use ssh to log into the machines; telnet and rlogin are
+disabled for security reasons. The LDAP directory is able to share RSA
+ssh keys among machines, please see <URL:http://db.debian.org/doc-mail.html>
+Otherwise when you first login a ~/.ssh directory will be created with
+the appropriate permissions. Please be aware of the security
+implications of using RSA authentication and ssh agents.
+
+To give you a quick overview here is a list of the most important
+machines from the project you can access. First there is
+ftp-master.debian.org, it is our main archive server and the place
+where you upload your packages. It is restricted, so you can only
+upload with anonymous FTP - a tool like dput or dupload can aid this
+process. The Projects web pages, CVS archive, the main shell server
+and various other stuff is located on gluck.debian.org. This machine
+is also reachable as {cvs,people,www}.debian.org. If you want your
+own Debian related site to appear behind
+<URL:http://people.debian.org/~__LOGIN__> then put it at this machine
+in the directory ~/public_html/.
+
+The machine hosting most of our VCS repositories
+({svn,bzr,git,arch,hg}.debian.org) is alioth.debian.org. It's handled
+by a separate team (admin at alioth.debian.org) as it allows login by
+non-Debian developers. You probably already have a *-guest account
+there. Please refer to http://wiki.debian.org/AliothFAQ to learn
+anything you need to know, including how to activate your account and
+how to request the removal of your old -guest account.
+
+There is one developer-only mailing list, debian-private. You have been
+subscribed to this list as <__PRIVATE__>, please respect the privacy of
+this list and don't forward mail from it elsewhere. This subscription, and
+a lot of other data like your private information, can be changed at the
+web interface available behind <URL:https://db.debian.org/>; just login
+with the password information appended to this email.
+
+The information stored here is used to maintain your accounts on various
+Debian machines, and also to allow other developers and general users to
+find out more about you. Many of the fields are only visible to other
+registered Debian developers. This is also the only way to change your
+password. The passwd program does not yet work.
+
+Before we go on with other important information let's remember the most
+important documents from the NM process. That you now got your account
+doesn't mean to stop reading and checking them whenever you do packaging
+or other Debian related work.
+
+ o The Debian Social Contract
+ <URL:http://www.debian.org/social_contract.html>
+
+ o The Debian Policy Manual
+ <URL:http://www.debian.org/doc/debian-policy/>
+
+ o The Debian Developer's reference
+ <URL:http://www.debian.org/doc/developers-reference/>
+
+You can find much more information useful to developers at
+<URL:http://www.debian.org/devel/>.
+
+Also, please subscribe to the mailing list debian-devel-announce, if you
+haven't done so already. All Debian developers are required to read
+this list, as important announcements are made there.
+
+We strongly suggest that you use your __LOGIN__ at debian.org address for
+the maintainer field in your packages, because that one will be valid
+as long as you are a Debian developer, even if you change jobs, leave
+university or change Internet Service providers. If you do so, please
+add that address to your PGP/GPG key(s) (using `gpg --edit-key "YOUR
+USER ID"') and send it to the keyring server at keyring.debian.org
+with `gpg --keyserver keyring.debian.org --send-keys "YOUR USER ID"'.
+
+We suggest that you subscribe to debian-mentors at lists.debian.org.
+This list is for new maintainers who seek help with initial packaging
+and other developer-related issues. Those who prefer one-on-one help
+can also post to the list, and an experienced developer may volunteer
+to help you. You can get online help on IRC, too, if you join the
+channel #debian-devel or #debian-mentors on irc.debian.org. Take a look
+at the support section on <URL:http://www.debian.org/> in order to find
+out more information.
+
+If you have some spare time and want to contribute it to Debian you
+may wish to take a look at the "Work-Needing and Prospective Packages",
+(WNPP) that can be found at <URL:http://www.debian.org/devel/wnpp/>.
+
+Welcome to the project!
+
+--
+The Debian New Maintainer Team
+
+__PASSWORD__
Added: trunk/userdir-ldap/ud-arbimport
===================================================================
--- trunk/userdir-ldap/ud-arbimport (rev 0)
+++ trunk/userdir-ldap/ud-arbimport 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+
+# Copyright (c) 1999 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2004 Joey Schulze <joey at debian.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This script imports arbitary lists of data. The input is a file with
+# the form of:
+# uid: <data>
+
+import string, re, time, ldap, getopt, sys;
+from userdir_ldap import *;
+
+# Process options
+(options, arguments) = getopt.getopt(sys.argv[1:], "u:m:n")
+for (switch, val) in options:
+ if (switch == '-u'):
+ AdminUser = val
+ elif (switch == '-m'):
+ LoadOverride(val);
+ elif (switch == '-n'):
+ NoAct = 1;
+if len(arguments) == 0:
+ print "Give the key to assignt to then the file to import";
+ os.exit(0);
+
+# Main program starts here
+l = passwdAccessLDAP(LDAPServer, BaseDn, AdminUser)
+
+List = open(arguments[1],"r");
+Set = [];
+User = None;
+while(1):
+ Line = List.readline();
+ if Line != "":
+ # Glob similar lines
+ Split = re.split("[:\n]",Line);
+ if User == None:
+ User = Split[0];
+ if Split[0] == User:
+ Set.append(string.strip(Split[1]));
+ continue;
+ else:
+ if len(Set) == 0:
+ break;
+
+ # Generate the command..
+ Rec = [(ldap.MOD_REPLACE,arguments[0],Set[0])];
+ for x in Set[1:]:
+ Rec.append((ldap.MOD_ADD,arguments[0],x))
+
+ Dn = "uid=" + User + "," + BaseDn;
+ try:
+ print Dn,Rec;
+ l.modify_s(Dn,Rec);
+ except:
+ print "Failed",Dn;
+
+ # Out of data..
+ if Line == "":
+ break;
+ User = Split[0];
+ Set = [string.strip(Split[1])];
Property changes on: trunk/userdir-ldap/ud-arbimport
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-echelon
===================================================================
--- trunk/userdir-ldap/ud-echelon (rev 0)
+++ trunk/userdir-ldap/ud-echelon 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,145 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, os, getopt;
+import string, pwd
+from userdir_gpg import *;
+from userdir_ldap import *;
+
+EX_TEMPFAIL = 75;
+EX_PERMFAIL = 65; # EX_DATAERR
+Debug = None;
+
+# Try to extract a key fingerprint from a PGP siged message
+def TryGPG(Email):
+ # Try to get a pgp text
+ try:
+ Msg = GetClearSig(Email);
+ except:
+ # Log an exception.. but continue. This is to deal with 'sort of'
+ # PGP-MIME things
+ S = "%s: %s -> %s\n" %(Now,MsgID,ErrMsg);
+ S = S + " %s: %s\n" %(sys.exc_type,sys.exc_value);
+ ErrLog.write(S);
+ return None;
+
+ if string.find(Msg[0],"-----BEGIN PGP SIGNED MESSAGE-----") == -1:
+ return None;
+
+ Res = GPGCheckSig(Msg[0]);
+
+ # Failed to find a matching sig
+ if Res[0] != None:
+ S = "%s: %s -> PGP Checking failed '%s': %s %s\n" %(Now,MsgID,Email.getheader("From"),str(Res[0]),str(Res[2]));
+ ErrLog.write(S);
+ return None;
+
+ # Search for the matching key fingerprint
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + Res[2][1]);
+ if len(Attrs) == 0:
+ return None;
+ if len(Attrs) != 1:
+ raise Error, "Oddly your key fingerprint is assigned to more than one account.."
+
+ return (Attrs[0][1]["uid"][0],"PGP",FormatPGPKey(Res[2][1]));
+
+# Try to guess the name from the email address
+def TryMatcher(Email):
+ Sender = Email.getheader("From");
+ if Sender == None:
+ return None;
+
+ # Split up the address and invoke the matcher routine
+ UID = GetUID(l,SplitEmail(Sender));
+
+ if UID[0] == None:
+ if UID[1] == None or len(UID[1]) == 0:
+ return None;
+
+ # Print out an error message
+ S = "%s: %s -> Address matching failed '%s'\n" %(Now,MsgID,Sender);
+ for x in UID[1]:
+ S = S + " " + x + "\n";
+ ErrLog.write(S);
+ return None;
+
+ return (UID[0],"FROM",Sender);
+
+# Process options
+(options, arguments) = getopt.getopt(sys.argv[1:], "dr")
+for (switch, val) in options:
+ if (switch == '-d'):
+ Debug = "";
+
+# Open the log files
+if Debug == None:
+ MainLog = open(Ech_MainLog,"a+",0);
+ ErrLog = open(Ech_ErrorLog,"a+",0);
+else:
+ MainLog = open("/dev/stdout","a+",0);
+ ErrLog = open("/dev/stdout","a+",0);
+
+# Start of main program
+ErrMsg = "Indeterminate Error";
+ErrType = EX_TEMPFAIL;
+Now = time.strftime("%a, %d %b %Y %H:%M:%S",time.gmtime(time.time()));
+MsgID = None;
+try:
+ # Get the email
+ ErrType = EX_PERMFAIL;
+ ErrMsg = "Failed to understand the email or find a signature:";
+ Email = mimetools.Message(sys.stdin,0);
+ MsgID = Email.getheader("Message-ID");
+
+ # Connect to the ldap server
+ ErrType = EX_TEMPFAIL;
+ ErrMsg = "An error occured while performing the LDAP lookup";
+ global l;
+ l = ldap.open(LDAPServer);
+ if Debug == None:
+ F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r");
+ AccessPass = string.split(string.strip(F.readline())," ");
+ l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]);
+ F.close();
+ else:
+ l.simple_bind_s("","");
+
+ # Try to decode
+ ErrType = EX_TEMPFAIL;
+ ErrMsg = "An error occured while trying GPG decoding";
+ User = TryGPG(Email);
+ if User == None:
+ ErrMsg = "An error occured while trying Matcher decoding";
+ User = TryMatcher(Email);
+
+ # Get any mailing list information
+ List = Email.getheader("X-Mailing-List");
+ if List == None:
+ List = "-";
+
+ # Tada, write a log message
+ if User != None:
+ Msg = "[%s] \"%s\" \"%s\" \"%s\""%(Now,User[2],List,MsgID);
+ MainLog.write("%s %s %s\n"%(User[0],User[1],Msg));
+ Dn = "uid=" + User[0] + "," + BaseDn;
+ Rec = [(ldap.MOD_REPLACE,"activity-%s"%(User[1]),Msg)];
+ if Debug == None:
+ l.modify_s(Dn,Rec);
+ else:
+ print Rec;
+ else:
+ User = ("-","UKN",Email.getheader("From"));
+ Msg = "[%s] \"%s\" \"%s\" \"%s\""%(Now,User[2],List,MsgID);
+ MainLog.write("%s %s %s\n"%(User[0],User[1],Msg));
+
+except:
+ # Log an exception..
+ S = "%s: %s -> %s\n" %(Now,MsgID,ErrMsg);
+ S = S + "==> %s: %s\n" %(sys.exc_type,sys.exc_value);
+ List = traceback.extract_tb(sys.exc_traceback);
+ if len(List) > 1:
+ for x in List:
+ S = S + " %s %s:%u: %s\n" %(x[2],x[0],x[1],x[3]);
+ ErrLog.write(S);
+ sys.exit(ErrType);
+
+sys.exit(0);
Property changes on: trunk/userdir-ldap/ud-echelon
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-emailmatcher
===================================================================
--- trunk/userdir-ldap/ud-emailmatcher (rev 0)
+++ trunk/userdir-ldap/ud-emailmatcher 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,172 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+# This script tries to match a list of email addresses to the ldap database
+# uids. It makes use of the PGP key ring to determine matches
+
+import string, re, time, ldap, getopt, sys;
+from userdir_ldap import *;
+from userdir_gpg import *;
+
+AddressSplit = re.compile("(.*).*<([^@]*)@([^>]*)>");
+
+# Import an an forward file
+def ImportForward(File,EmailMap):
+ F = open(File,"r");
+ while(1):
+ Line = string.strip(F.readline());
+ if Line == "":
+ break;
+ Split = string.split(Line,":");
+ if len(Split) != 2:
+ continue;
+
+ Addr = string.strip(Split[1]);
+ if EmailMap.has_key(Addr) and EmailMap[Addr] != Split[0]:
+ print "Dup Over Emap",Line,Split
+ else:
+ EmailMap[Addr] = Split[0];
+ F.close();
+
+# Import an override file
+def ImportOverride(File,OverMap):
+ F = open(File,"r");
+ while(1):
+ Line = F.readline();
+ if Line == "":
+ break;
+ Line = string.strip(Line);
+
+ Split = string.split(Line,":");
+ if len(Split) != 2:
+ continue;
+ OverMap[Split[0]] = string.strip(Split[1]);
+ F.close();
+
+(options, arguments) = getopt.getopt(sys.argv[1:], "o:f:")
+
+# Popen GPG with the correct magic special options
+Args = [GPGPath] + GPGBasicOptions + GPGKeyRings;
+for x in arguments:
+ Args.append("--keyring");
+ Args.append(x);
+Args = Args + GPGSearchOptions + [" 2> /dev/null"]
+Keys = os.popen(string.join(Args," "),"r");
+
+l = ldap.open(LDAPServer);
+l.simple_bind_s("","");
+
+# Fetch the key list and map to email address
+PasswdAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyfingerprint=*",\
+ ["uid","keyfingerprint"]);
+KFMap = {}
+for x in PasswdAttrs:
+ if x[1].has_key("keyfingerprint") == 0 or x[1].has_key("uid") == 0:
+ continue;
+ for I in x[1]["keyfingerprint"]:
+ KFMap[I] = x[1]["uid"][0];
+
+# Loop over the GPG key file mapping addresses to uids
+Outstanding = 0;
+Ignored = 0;
+Emails = [];
+EmailMap = {};
+UIDMap = {};
+UID = None;
+FingerPrint = None;
+print "Reading keyrings",
+sys.stdout.flush();
+while(1):
+ Line = Keys.readline();
+ if Line == "":
+ break;
+
+ Split = string.split(Line,":");
+ if len(Split) >= 8 and Split[0] == "pub":
+ if FingerPrint != None and UID != None:
+ for x in Emails:
+ Match = AddressSplit.match(x);
+ if Match == None:
+ continue;
+ Groups = Match.groups();
+ Email = Groups[1]+'@'+Groups[2];
+ if UIDMap.has_key(Groups[1]):
+ UIDMap[Groups[1]].append(Email);
+ else:
+ UIDMap[Groups[1]] = [Email];
+ if EmailMap.has_key(Email) and EmailMap[Email] != UID:
+ print "Dup Emap",Email
+ else:
+ EmailMap[Email] = UID;
+ Emails = [Split[9]];
+ continue;
+ if len(Split) >= 11 and Split[0] == "fpr":
+ FingerPrint = Split[9];
+ if KFMap.has_key(FingerPrint) == 0:
+ print "Failed",FingerPrint;
+ UID = None;
+ continue;
+ UID = KFMap[FingerPrint];
+ if len(Split) >= 9 and Split[0] == "uid":
+ Emails.append(Split[9]);
+print;
+
+# Process the override files
+for (switch, val) in options:
+ if (switch == '-f'):
+ ImportForward(val,EmailMap);
+ BindUser = val;
+ elif (switch == '-o'):
+ ImportOverride(val,EmailMap);
+
+# Map the input
+FinalMap = {};
+while(1):
+ Line = sys.stdin.readline();
+ if Line == "":
+ break;
+ Line = string.strip(Line);
+
+ Split = string.split(Line,"@");
+ if len(Split) != 2:
+ continue;
+
+ # The address is in our domain, go directly
+ if Split[1] == EmailAppend:
+ if FinalMap.has_key(Line):
+ print "Dup",Line
+ Split2 = string.split(Split[0],"-");
+ FinalMap[Line] = Split2[0];
+ continue;
+
+ # Exists in the email map..
+ if EmailMap.has_key(Line):
+ if FinalMap.has_key(Line):
+ print "Dup",Line
+ FinalMap[Line] = EmailMap[Line];
+ continue;
+
+ # Try again splitting off common address appendage modes
+ Split2 = string.split(Split[0],"-");
+ Addr = Split2[0]+'@'+Split[1];
+ if EmailMap.has_key(Addr):
+ if FinalMap.has_key(Addr):
+ print "Dup",Addr
+ FinalMap[Line] = EmailMap[Addr];
+ continue;
+
+ # Failed
+ if UIDMap.has_key(Split[0]):
+ print Line,UIDMap[Split[0]];
+ print Line;
+print "-----";
+
+# Generate a reverse map and check for duplicates
+Back = {};
+for x in FinalMap.keys():
+ if Back.has_key(FinalMap[x]):
+ print "Dup",x,FinalMap[x],Back[FinalMap[x]];
+ Back[FinalMap[x]] = x;
+
+# Print the forward map
+for x in Back.keys():
+ print "%s: %s" % (x,Back[x]);
Property changes on: trunk/userdir-ldap/ud-emailmatcher
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-fingerserv
===================================================================
--- trunk/userdir-ldap/ud-fingerserv (rev 0)
+++ trunk/userdir-ldap/ud-fingerserv 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,225 @@
+#!/usr/bin/perl
+# $Id: ud-fingerserv,v 1.19 2004/11/18 19:10:57 joey Exp $
+
+# (c) 1999 Randolph Chung. Licensed under the GPL. <tausq at debian.org>
+# (c) 2004 Martin Schulze. Licensed under the GPL. <joey at debian.org>
+
+use lib '/var/www/userdir-ldap/';
+#use lib '/home/randolph/projects/userdir-ldap/web';
+use strict vars;
+use IO::Handle;
+use IO::Socket;
+use POSIX qw(:sys_wait_h);
+use Getopt::Std;
+use Util;
+use Net::LDAP qw(:all);
+
+# Global settings...
+my %config = &Util::ReadConfigFile;
+my %opts;
+getopts("iqhv", \%opts);
+my $use_inetd = $config{use_inetd} || $opts{i};
+$| = 1;
+
+my %attrs = (
+ 'cn' => 'First name',
+ 'mn' => 'Middle name',
+ 'sn' => 'Last name',
+ 'email' => 'Email',
+ 'keyfingerprint' => 'Fingerprint',
+ 'key' => 'Key block',
+ 'ircnick' => 'IRC nickname',
+ 'icquin' => 'ICQ UIN',
+ 'jabberjid' => 'Jabber ID',
+ 'labeleduri' => 'URL'
+);
+
+my @summarykeys = ('cn', 'mn', 'sn', 'email', 'labeleduri', 'ircnick', 'icquin', 'jabberjid', 'keyfingerprint', 'key');
+
+$SIG{__DIE__} = \&DieHandler;
+$SIG{INT} = \&DieHandler;
+$SIG{CHLD} = \&Reaper;
+
+&help if (defined($opts{h}));
+#my $logfh = STDOUT; #TODO
+
+&log("Binding to LDAP server at $config{ldaphost}") if (defined($opts{v}));
+my $ldap = Net::LDAP->new($config{ldaphost}) || die $1;
+$ldap->bind;
+
+if (!$use_inetd) {
+ &log("Binding to port 79") if (defined($opts{v}));
+ my $server = IO::Socket::INET->new(Proto => 'tcp',
+ LocalPort => 'finger(79)',
+ Listen => SOMAXCONN,
+ Reuse => 1);
+
+ die "Cannot listen on finger port" unless $server;
+ &log("[Server listening for connections]");
+
+ my ($pid, $client, $hostinfo);
+
+ while ($client = $server->accept()) {
+ &log("Forking to handle client request") if (defined($opts{v}));
+ next if $pid = fork; # parent
+ die "fork: $!" unless defined $pid;
+
+ # child
+ $client->autoflush(1);
+ my $hostinfo = gethostbyaddr($client->peeraddr, AF_INET);
+ &log(sprintf("[Connect from %s]", $hostinfo || $client->peerhost));
+ my $query = &readdata($client);
+ &ProcessQuery($client, $query) if (defined($query));
+ $client->close;
+ exit;
+ } continue {
+ $client->close;
+ }
+} else { # inetd
+ &log("inetd mode");
+ my $sockaddr = getpeername(STDIN);
+ if ($sockaddr) {
+ my ($port, $addr) = unpack_sockaddr_in(getpeername(STDIN));
+ &log(sprintf("[Connect from %s (%s)]", gethostbyaddr($addr, AF_INET), inet_ntoa($addr)));
+ } else {
+ &log("[Connect via terminal]");
+ }
+ my $query = &readdata(\*STDIN);
+ &ProcessQuery(\*STDOUT, $query) if (defined($query));
+ exit;
+}
+
+$ldap->unbind;
+
+sub DieHandler {
+ $ldap->unbind if (defined($ldap));
+ exit 0;
+}
+
+sub Reaper {
+ 1 until (-1 == waitpid(-1, WNOHANG));
+ $SIG{CHLD} = \&Reaper;
+}
+
+sub ProcessQuery {
+ my $client = shift;
+ my $query = shift;
+
+ my ($uid, $fields, $mesg, $entries, $dn, $key, $pid, $data);
+
+ $query =~ s/[^\/,0-9a-z]//gi; # be paranoid about input
+ my ($uid, $fields) = split(/\//, $query, 2);
+
+ if (($uid eq "") || ($uid =~ /^help$/i)) {
+ &sendhelp($client);
+ return;
+ }
+
+ &log("Looking up $uid at $config{basedn}, uid=$uid");
+
+ $mesg = $ldap->search(base => $config{basedn}, filter => "uid=$uid");
+ $mesg->code && die $mesg->error;
+ $entries = $mesg->as_struct;
+
+ if ($mesg->count == 0) {
+ print $client "$uid not found at db.debian.org\n";
+ exit 0;
+ }
+
+ foreach $dn (sort {$entries->{$a}->{sn}->[0] <=> $entries->{$b}->{sn}->[0]} keys(%$entries)) {
+ $data = $entries->{$dn};
+
+ $data->{email}->[0] = sprintf("%s %s %s <%s>", $data->{cn}->[0],
+ $data->{mn}->[0], $data->{sn}->[0],
+ $data->{uid}->[0]."\@$config{emailappend}");
+
+ $data->{email}->[0] =~ s/\s+/ /g;
+
+ my @keyfingerprint = ();
+ for (my $i=0; $i <= $#{$data->{'keyfingerprint'}}; $i++) {
+ push (@keyfingerprint, $data->{keyfingerprint}->[$i]);
+ $data->{keyfingerprint}->[$i] = &Util::FormatFingerPrint($data->{keyfingerprint}->[$i]);
+ $data->{keyfingerprint}->[$i] =~ s, , ,;
+ }
+ print $client "$dn\n";
+ if (!$fields) {
+ push (@{$data->{key}}, sprintf ("finger %s/key\@db.debian.org", $uid));
+ foreach $key (@summarykeys) {
+ foreach (@{$data->{$key}}) {
+ print $client "$attrs{$key}: ";
+ print $client "$_\n";
+ }
+ }
+ } else {
+ # print "$fields\n";
+ foreach $key (split(/,/, $fields)) {
+ if ($key eq 'key') {
+ foreach (@keyfingerprint) {
+ push (@{$data->{key}}, "\n".&Util::FetchKey($_), 0);
+ }
+ }
+ foreach (@{$data->{$key}}) {
+ print $client "$attrs{$key}: ";
+ print $client "$_\n";
+ }
+ }
+ }
+ }
+}
+
+sub help {
+ print "fingerserv [-i | -q | -v | -h]\n";
+ print "-i = inetd mode; otherwise runs standalone\n";
+ print "-q = quiet mode; no output\n";
+ print "-v = verbose mode\n";
+ print "-h = this help message\n";
+ exit 0;
+}
+
+sub log {
+ my $msg = shift;
+ return if (defined($opts{q}));
+
+ my $time = localtime;
+ print STDERR "$time $msg\n";
+}
+
+sub readdata {
+ my $fh = shift;
+ my $in = undef;
+ my $out = undef;
+ my $bytesread = 0;
+ my $ret;
+
+ my $flags= fcntl($fh, F_GETFL, 0)
+ or die "Can't get flags for socket: $!\n";
+ fcntl($fh, F_SETFL, $flags | O_NONBLOCK)
+ or die "Can't make socket nonblocking: $!\n";
+
+ while (($bytesread < 1024) && ($out !~ /\n/)) {
+ $ret = sysread($fh, $in, 1024);
+ return undef if (!defined($ret) || ($ret == 0));
+ $bytesread += $ret;
+ $out .= $in;
+ }
+
+ $out =~ /(.*?)\n/;
+ return $1;
+}
+
+sub sendhelp {
+ my $client = shift;
+
+ print $client "userdir-ldap finger daemon\n";
+ print $client "--------------------------\n";
+ print $client "finger <uid>[/<attributes>]\@db.debian.org\n";
+ print $client " where uid is the user id of the user\n";
+ print $client " the optional attributes parameter specifies what to return\n";
+ print $client " if nothing is specified, all attributes are returned.\n";
+ print $client " The following attributes are currently supported:\n";
+ foreach (@summarykeys) {
+ print $client " $_ : $attrs{$_}\n";
+ }
+ print $client " Multiple attributes can be separated by commas, like this:\n";
+ print $client " finger tux/email,key\@db.debian.org\n";
+}
Property changes on: trunk/userdir-ldap/ud-fingerserv
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-fingerserv2.c
===================================================================
--- trunk/userdir-ldap/ud-fingerserv2.c (rev 0)
+++ trunk/userdir-ldap/ud-fingerserv2.c 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,133 @@
+/* $Id# */
+/* compile: gcc -Wall -o ud-fingerserv2 ud-fingerserv2.c */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define PROGNAME "ud-fingerserv"
+#define VERSION "0.90"
+#define PROGDATE "1999/12/10"
+
+#define FINGERPORT 79
+#define TIMEOUT 600 /* seconds */
+#define PERROR(ctx) do { perror(ctx); exit(1); } while (0);
+#define DEFAULTLOGFILE "/var/log/ud-fingerserv.log"
+
+#define OPT_INETD 1
+#define OPT_LOGSCR (1 << 1)
+
+static FILE *g_logfs = NULL;
+static char *g_logfn = NULL;
+static int g_options = 0;
+
+int processcxn(int, struct sockaddr_in *);
+void logf(char *fmt, ...);
+void cleanup(void);
+void timeout(void);
+void usage(void);
+void sendhelp(void);
+
+/* ********************************************************************** */
+
+int processcxn(int s, struct sockaddr_in *rmtaddr)
+{
+ printf("connected\n");
+ return 0;
+}
+
+void sendhelp(void)
+{
+}
+
+void logf(char *fmt, ...)
+{
+ va_list ap;
+ time_t t;
+ char logline[1024];
+ char *ts;
+
+ t = time(NULL);
+ ts = ctime(&t);
+ ts[strlen(ts)-1] = 0; /* remove stupid newline */
+
+ if (g_logfs == NULL) {
+ if (g_logfn == NULL) g_logfn = DEFAULTLOGFILE;
+ if ((g_logfs = fopen(g_logfn, "a")) == NULL && !(g_options & OPT_LOGSCR)) PERROR("logf");
+ }
+
+ vsnprintf(logline, sizeof(logline), fmt, ap);
+ if (g_logfs) {
+ fprintf(g_logfs, "[%s] " PROGNAME ": %s\n", ts, logline);
+ fflush(g_logfs);
+ }
+
+ if (g_options & OPT_LOGSCR) printf("[%s] " PROGNAME ": %s\n", ts, logline);
+}
+
+void cleanup(void)
+{
+ if (g_logfs) fclose(g_logfs);
+}
+
+void usage(void)
+{
+ fprintf(stderr, "ud-fingerserv " VERSION " " PROGDATE "\n");
+ fprintf(stderr, "\t(c) 1999 Randolph Chung <tausq at debian.org>. Released under the GPL\n");
+ fprintf(stderr, "\tThe following options are recognized:\n");
+ fprintf(stderr, "\t\t-h : this help text\n");
+ fprintf(stderr, "\t\t-i : run in inetd mode; otherwise runs in standalone mode\n");
+ fprintf(stderr, "\t\t-v : logs messages to stdout in addition to log file\n");
+ fprintf(stderr, "\t\t-l <file> : use <file> as the log file, instead of " DEFAULTLOGFILE "\n");
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ int ls, as;
+ int r;
+ struct sockaddr_in myaddr, rmtaddr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+
+ atexit(cleanup);
+
+ while ((r = getopt(argc, argv, "hivl:")) > 0) {
+ switch (r) {
+ case 'i': g_options |= OPT_INETD; break;
+ case 'v': g_options |= OPT_LOGSCR; break;
+ case 'l': g_logfn = strdup(optarg); break;
+ default: usage();
+ }
+ }
+
+ if (g_options & OPT_INETD) {
+ getsockname(fileno(stdin), &rmtaddr, &addrlen);
+ processcxn(fileno(stdin), &rmtaddr);
+ } else {
+ if ((ls = socket(AF_INET, SOCK_STREAM, 0)) < 0) PERROR("socket");
+ memset(&myaddr, 0, sizeof(myaddr));
+ myaddr.sin_family = AF_INET;
+ myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ myaddr.sin_port = htons(FINGERPORT);
+ if (bind(ls, &myaddr, sizeof(myaddr)) < 0) PERROR("bind");
+ if (listen(ls, SOMAXCONN) < 0) PERROR("listen");
+ logf("Waiting for connection");
+ while ((as = accept(ls, &rmtaddr, &addrlen))) {
+ if ((r = fork()) == 0) {
+ processcxn(as, &rmtaddr);
+ exit(0);
+ } else {
+ if (r < 0) PERROR("fork");
+ }
+ }
+ }
+
+ return 0;
+}
+
Added: trunk/userdir-ldap/ud-forwardlist
===================================================================
--- trunk/userdir-ldap/ud-forwardlist (rev 0)
+++ trunk/userdir-ldap/ud-forwardlist 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+# This script takes a list of .forward files and generates a list of colon
+# delimited fields for import into a ldap directory. The fields represent
+# the user and their email forwarding.
+#
+# A sample invokation..
+# cd /home
+# find -name ".foward" -maxdepth 2 | mkforwardlist | sort | less
+# Then correct any invalid forward files if possible. After that stash the
+# output in a file, remove the invalid lines and import it.
+#
+# It also understand .qmail type files
+
+import string, re, time, getopt, os, sys, pwd, stat;
+
+AddressSplit = re.compile("<(.*)>");
+
+while (1):
+ File = string.strip(sys.stdin.readline());
+ if File == "":
+ break;
+
+ # Attempt to determine the UID
+ try:
+ User = pwd.getpwuid(os.stat(File)[stat.ST_UID])[0];
+ except KeyError:
+ print "Invalid0", File;
+ continue;
+
+ # Read the first two non comment non empty lines
+ Forward = open(File,"r");
+ Line = None;
+ while (1):
+ Line2 = string.strip(Forward.readline());
+ if Line2 == "":
+ break;
+ if Line2[0] == '#' or Line2[0] == '\n':
+ continue;
+ if Line == None:
+ Line = Line2;
+ else:
+ break;
+
+ # If we got more than one line or no lines at all it is invalid
+ if Line == None or Line == "" or Line2 != "":
+ print "Invalid1", File;
+ continue;
+
+ # Abort for funky things like pipes or directions to mailboxes
+ if Line[0] == '/' or Line[0] == '|' or Line[0] == '.' or Line[-1] == '/' or \
+ string.find(Line,'@') == -1:
+ print "Invalid2", File;
+ continue;
+
+ # Split off the address part
+ Address = AddressSplit.match(Line);
+ if Address == None:
+ # Or parse a qmail adddress..
+ Address = Line;
+ if Address[0] == '&':
+ Address = Address[1:];
+
+ if Address == "":
+ print "Invalid3", File;
+ continue;
+
+ print User + ":",Address;
Property changes on: trunk/userdir-ldap/ud-forwardlist
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-generate
===================================================================
--- trunk/userdir-ldap/ud-generate (rev 0)
+++ trunk/userdir-ldap/ud-generate 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,840 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+# Generates passwd, shadow and group files from the ldap directory.
+
+# Copyright (c) 2000-2001 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2003-2004 James Troup <troup at debian.org>
+# Copyright (c) 2004-2005,7 Joey Schulze <joey at infodrom.org>
+# Copyright (c) 2001-2007 Ryan Murray <rmurray at debian.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import string, re, time, ldap, getopt, sys, os, pwd, posix, socket, base64, sha
+from userdir_ldap import *;
+
+global Allowed;
+global CurrentHost;
+
+PasswdAttrs = None;
+GroupIDMap = {};
+Allowed = None;
+CurrentHost = "";
+
+EmailCheck = re.compile("^([^ <>@]+@[^ ,<>@]+)?$");
+BSMTPCheck = re.compile(".*mx 0 (gluck)\.debian\.org\..*",re.DOTALL);
+DNSZone = ".debian.net"
+
+def Sanitize(Str):
+ return string.translate(Str,string.maketrans("\n\r\t","$$$"));
+
+def DoLink(From,To,File):
+ try: posix.remove(To+File);
+ except: pass;
+ posix.link(From+File,To+File);
+
+# See if this user is in the group list
+def IsInGroup(DnRecord):
+ if Allowed == None:
+ return 1;
+
+ # See if the primary group is in the list
+ if Allowed.has_key(GetAttr(DnRecord,"gidNumber")) != 0:
+ return 1;
+
+ # Check the host based ACL
+ if DnRecord[1].has_key("allowedHost") != 0:
+ for I in DnRecord[1]["allowedHost"]:
+ if CurrentHost == I:
+ return 1;
+
+ # See if there are supplementary groups
+ if DnRecord[1].has_key("supplementaryGid") == 0:
+ return 0;
+
+ # Check the supplementary groups
+ for I in DnRecord[1]["supplementaryGid"]:
+ if Allowed.has_key(I):
+ return 1;
+ return 0;
+
+def Die(File,F,Fdb):
+ if F != None:
+ F.close();
+ if Fdb != None:
+ Fdb.close();
+ try: os.remove(File + ".tmp");
+ except: pass;
+ try: os.remove(File + ".tdb.tmp");
+ except: pass;
+
+def Done(File,F,Fdb):
+ if F != None:
+ F.close();
+ os.rename(File + ".tmp",File);
+ if Fdb != None:
+ Fdb.close();
+ os.rename(File + ".tdb.tmp",File+".tdb");
+
+# Generate the password list
+def GenPasswd(l,File,HomePrefix):
+ F = None;
+ try:
+ F = open(File + ".tdb.tmp","w");
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ I = 0;
+ for x in PasswdAttrs:
+ if x[1].has_key("uidNumber") == 0 or IsInGroup(x) == 0:
+ continue;
+
+ # Do not let people try to buffer overflow some busted passwd parser.
+ if len(GetAttr(x,"gecos")) > 100 or len(GetAttr(x,"loginShell")) > 50:
+ continue;
+
+ Line = "%s:x:%s:%s:%s:%s%s:%s" % (GetAttr(x,"uid"),\
+ GetAttr(x,"uidNumber"),GetAttr(x,"gidNumber"),\
+ GetAttr(x,"gecos"),HomePrefix,GetAttr(x,"uid"),\
+ GetAttr(x,"loginShell"));
+
+ Line = Sanitize(Line) + "\n";
+ F.write("0%u %s" % (I,Line));
+ F.write(".%s %s" % (GetAttr(x,"uid"),Line));
+ F.write("=%s %s" % (GetAttr(x,"uidNumber"),Line));
+ I = I + 1;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,None,F);
+ raise;
+ Done(File,None,F);
+
+# Generate the shadow list
+def GenShadow(l,File):
+ F = None;
+ try:
+ OldMask = os.umask(0077);
+ F = open(File + ".tdb.tmp","w",0600);
+ os.umask(OldMask);
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ I = 0;
+ for x in PasswdAttrs:
+ if x[1].has_key("uidNumber") == 0 or IsInGroup(x) == 0:
+ continue;
+
+ Pass = GetAttr(x,"userPassword");
+ if Pass[0:7] != "{crypt}" or len(Pass) > 50:
+ Pass = '*';
+ else:
+ Pass = Pass[7:];
+ Line = "%s:%s:%s:%s:%s:%s:%s:%s:" % (GetAttr(x,"uid"),\
+ Pass,GetAttr(x,"shadowLastChange"),\
+ GetAttr(x,"shadowMin"),GetAttr(x,"shadowMax"),\
+ GetAttr(x,"shadowWarning"),GetAttr(x,"shadowinactive"),\
+ GetAttr(x,"shadowexpire"));
+ Line = Sanitize(Line) + "\n";
+ F.write("0%u %s" % (I,Line));
+ F.write(".%s %s" % (GetAttr(x,"uid"),Line));
+ I = I + 1;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,None,F);
+ raise;
+ Done(File,None,F);
+
+# Generate the shadow list
+def GenSSHShadow(l,File):
+ F = None;
+ try:
+ OldMask = os.umask(0077);
+ F = open(File + ".tmp","w",0600);
+ os.umask(OldMask);
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ for x in PasswdAttrs:
+ # If the account is locked, do not write it.
+ # This is a partial stop-gap. The ssh also needs to change this
+ # to ignore ~/.ssh/authorized* files.
+ if (string.find(GetAttr(x,"userPassword"),"*LK*") != -1) \
+ or GetAttr(x,"userPassword").startswith("!"):
+ continue;
+
+ if x[1].has_key("uidNumber") == 0 or \
+ x[1].has_key("sshRSAAuthKey") == 0:
+ continue;
+ for I in x[1]["sshRSAAuthKey"]:
+ User = GetAttr(x,"uid");
+ Line = "%s: %s" %(User,I);
+ Line = Sanitize(Line) + "\n";
+ F.write(Line);
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Generate the group list
+def GenGroup(l,File):
+ F = None;
+ try:
+ F = open(File + ".tdb.tmp","w");
+
+ # Generate the GroupMap
+ GroupMap = {};
+ for x in GroupIDMap.keys():
+ GroupMap[x] = [];
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ # Sort them into a list of groups having a set of users
+ for x in PasswdAttrs:
+ if x[1].has_key("uidNumber") == 0 or IsInGroup(x) == 0:
+ continue;
+ if x[1].has_key("supplementaryGid") == 0:
+ continue;
+
+ for I in x[1]["supplementaryGid"]:
+ if GroupMap.has_key(I):
+ GroupMap[I].append(GetAttr(x,"uid"));
+ else:
+ print "Group does not exist ",I,"but",GetAttr(x,"uid"),"is in it";
+
+ # Output the group file.
+ J = 0;
+ for x in GroupMap.keys():
+ if GroupIDMap.has_key(x) == 0:
+ continue;
+ Line = "%s:x:%u:" % (x,GroupIDMap[x]);
+ Comma = '';
+ for I in GroupMap[x]:
+ Line = Line + ("%s%s" % (Comma,I));
+ Comma = ',';
+ Line = Sanitize(Line) + "\n";
+ F.write("0%u %s" % (J,Line));
+ F.write(".%s %s" % (x,Line));
+ F.write("=%u %s" % (GroupIDMap[x],Line));
+ J = J + 1;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,None,F);
+ raise;
+ Done(File,None,F);
+
+# Generate the email forwarding list
+def GenForward(l,File):
+ F = None;
+ try:
+ OldMask = os.umask(0022);
+ F = open(File + ".tmp","w",0644);
+ os.umask(OldMask);
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ # Write out the email address for each user
+ for x in PasswdAttrs:
+ if x[1].has_key("emailForward") == 0 or IsInGroup(x) == 0:
+ continue;
+
+ # Do not allow people to try to buffer overflow busted parsers
+ if len(GetAttr(x,"emailForward")) > 200:
+ continue;
+
+ # Check the forwarding address
+ if EmailCheck.match(GetAttr(x,"emailForward")) == None:
+ continue;
+ Line = "%s: %s" % (GetAttr(x,"uid"),GetAttr(x,"emailForward"));
+ Line = Sanitize(Line) + "\n";
+ F.write(Line);
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+def GenAllForward(l,File):
+ Fdb = None;
+ try:
+ OldMask = os.umask(0022);
+ Fdb = os.popen("cdbmake %s %s.tmp"%(File,File),"w");
+ os.umask(OldMask);
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ # Write out the email address for each user
+ for x in PasswdAttrs:
+ if x[1].has_key("emailForward") == 0:
+ continue;
+
+ # Do not allow people to try to buffer overflow busted parsers
+ Forward = GetAttr(x,"emailForward");
+ if len(Forward) > 200:
+ continue;
+
+ # Check the forwarding address
+ if EmailCheck.match(Forward) == None:
+ continue;
+
+ User = GetAttr(x,"uid");
+ Fdb.write("+%d,%d:%s->%s\n"%(len(User),len(Forward),User,Forward));
+ Fdb.write("\n");
+ # Oops, something unspeakable happened.
+ except:
+ Fdb.close();
+ raise;
+ if Fdb.close() != None:
+ raise "cdbmake gave an error";
+
+# Generate the anon XEarth marker file
+def GenMarkers(l,File):
+ F = None;
+ try:
+ F = open(File + ".tmp","w");
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ # Write out the position for each user
+ for x in PasswdAttrs:
+ if x[1].has_key("latitude") == 0 or x[1].has_key("longitude") == 0:
+ continue;
+ try:
+ Line = "%8s %8s \"\""%(DecDegree(GetAttr(x,"latitude"),1),DecDegree(GetAttr(x,"longitude"),1));
+ Line = Sanitize(Line) + "\n";
+ F.write(Line);
+ except:
+ pass;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Generate the debian-private subscription list
+def GenPrivate(l,File):
+ F = None;
+ try:
+ F = open(File + ".tmp","w");
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ # Write out the position for each user
+ for x in PasswdAttrs:
+ if x[1].has_key("privateSub") == 0:
+ continue;
+
+ # If the account is locked, do not write it
+ if (string.find(GetAttr(x,"userPassword"),"*LK*") != -1) \
+ or GetAttr(x,"userPassword").startswith("!"):
+ continue;
+
+ # If the account has no PGP key, do not write it
+ if x[1].has_key("keyFingerPrint") == 0:
+ continue;
+
+ # Must be in the Debian group (yuk, hard coded for now)
+ if GetAttr(x,"gidNumber") != "100":
+ continue;
+
+ try:
+ Line = "%s"%(GetAttr(x,"privateSub"));
+ Line = Sanitize(Line) + "\n";
+ F.write(Line);
+ except:
+ pass;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Generate the list of local addresses that refuse all mail
+def GenMailDisable(l,File):
+ F = None;
+ try:
+ F = open(File + ".tmp","w");
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ for x in PasswdAttrs:
+ Reason = None
+
+ # If the account is locked, disable incoming mail
+ if (string.find(GetAttr(x,"userPassword"),"*LK*") != -1):
+ if GetAttr(x,"uid") == "luther":
+ continue
+ else:
+ Reason = "user account locked"
+ else:
+ if x[1].has_key("mailDisableMessage"):
+ Reason = GetAttr(x,"mailDisableMessage")
+ else:
+ continue
+
+ # Must be in the Debian group (yuk, hard coded for now)
+ if GetAttr(x,"gidNumber") != "100":
+ continue;
+
+ try:
+ Line = "%s: %s"%(GetAttr(x,"uid"),Reason);
+ Line = Sanitize(Line) + "\n";
+ F.write(Line);
+ except:
+ pass;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Generate a list of uids that should have boolean affects applied
+def GenMailBool(l,File,Key):
+ F = None;
+ try:
+ F = open(File + ".tmp","w");
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ for x in PasswdAttrs:
+ Reason = None
+
+ if x[1].has_key(Key) == 0:
+ continue
+
+ # Must be in the Debian group (yuk, hard coded for now)
+ if GetAttr(x,"gidNumber") != "100":
+ continue
+
+ if GetAttr(x,Key) != "TRUE":
+ continue
+
+ try:
+ Line = "%s"%(GetAttr(x,"uid"));
+ Line = Sanitize(Line) + "\n";
+ F.write(Line);
+ except:
+ pass;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Generate a list of hosts for RBL or whitelist purposes.
+def GenMailList(l,File,Key):
+ F = None;
+ try:
+ F = open(File + ".tmp","w");
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ for x in PasswdAttrs:
+ Reason = None
+
+ if x[1].has_key(Key) == 0:
+ continue
+
+ # Must be in the Debian group (yuk, hard coded for now)
+ if GetAttr(x,"gidNumber") != "100":
+ continue
+
+ try:
+ found = 0
+ Line = None
+ for z in x[1][Key]:
+ if Key == "mailWhitelist":
+ if re.match('^[-\w.]+(/[\d]+)?$',z) == None:
+ continue
+ else:
+ if re.match('^[-\w.]+$',z) == None:
+ continue
+ if found == 0:
+ found = 1
+ Line = GetAttr(x,"uid")
+ else:
+ Line += " "
+ Line += ": " + z
+ if Key == "mailRHSBL":
+ Line += "/$sender_address_domain"
+
+ if Line != None:
+ Line = Sanitize(Line) + "\n";
+ F.write(Line);
+ except:
+ pass;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Generate the DNS Zone file
+def GenDNS(l,File,HomePrefix):
+ F = None;
+ try:
+ F = open(File + ".tmp","w");
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ # Write out the zone file entry for each user
+ for x in PasswdAttrs:
+ if x[1].has_key("dnsZoneEntry") == 0:
+ continue;
+
+ # If the account has no PGP key, do not write it
+ if x[1].has_key("keyFingerPrint") == 0:
+ continue;
+ try:
+ F.write("; %s\n"%(EmailAddress(x)));
+ for z in x[1]["dnsZoneEntry"]:
+ Split = string.split(string.lower(z));
+ if string.lower(Split[1]) == 'in':
+ for y in range(0,len(Split)):
+ if Split[y] == "$":
+ Split[y] = "\n\t";
+ Line = string.join(Split," ") + "\n";
+ F.write(Line);
+
+ Host = Split[0] + DNSZone;
+ if BSMTPCheck.match(Line) != None:
+ F.write("; Has BSMTP\n");
+
+ # Write some identification information
+ if string.lower(Split[2]) == "a":
+ Line = "%s IN TXT \"%s\"\n"%(Split[0],EmailAddress(x));
+ for y in x[1]["keyFingerPrint"]:
+ Line = Line + "%s IN TXT \"PGP %s\"\n"%(Split[0],FormatPGPKey(y));
+ F.write(Line);
+ else:
+ Line = "; Err %s"%(str(Split));
+ F.write(Line);
+
+ F.write("\n");
+ except:
+ F.write("; Errors\n");
+ pass;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Generate the DNS SSHFP records
+def GenSSHFP(l,File,HomePrefix):
+ F = None
+ try:
+ F = open(File + ".tmp","w")
+
+ # Fetch all the hosts
+ global HostAttrs
+ if HostAttrs == None:
+ raise "No Hosts"
+
+ for x in HostAttrs:
+ if x[1].has_key("hostname") == 0 or \
+ x[1].has_key("sshRSAHostKey") == 0:
+ continue
+ Host = GetAttr(x,"hostname");
+ Algorithm = None
+ for I in x[1]["sshRSAHostKey"]:
+ Split = string.split(I)
+ if Split[0] == 'ssh-rsa':
+ Algorithm = 1
+ if Split[0] == 'ssh-dss':
+ Algorithm = 2
+ if Algorithm == None:
+ continue
+ Fingerprint = sha.new(base64.decodestring(Split[1])).hexdigest()
+ Line = "%s. IN SSHFP %u 1 %s" % (Host,Algorithm,Fingerprint)
+ Line = Sanitize(Line) + "\n"
+ F.write(Line)
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None)
+ raise;
+ Done(File,F,None)
+
+# Generate the BSMTP file
+def GenBSMTP(l,File,HomePrefix):
+ F = None;
+ try:
+ F = open(File + ".tmp","w");
+
+ # Fetch all the users
+ global PasswdAttrs;
+ if PasswdAttrs == None:
+ raise "No Users";
+
+ # Write out the zone file entry for each user
+ for x in PasswdAttrs:
+ if x[1].has_key("dnsZoneEntry") == 0:
+ continue;
+
+ # If the account has no PGP key, do not write it
+ if x[1].has_key("keyFingerPrint") == 0:
+ continue;
+ try:
+ for z in x[1]["dnsZoneEntry"]:
+ Split = string.split(string.lower(z));
+ if string.lower(Split[1]) == 'in':
+ for y in range(0,len(Split)):
+ if Split[y] == "$":
+ Split[y] = "\n\t";
+ Line = string.join(Split," ") + "\n";
+
+ Host = Split[0] + DNSZone;
+ if BSMTPCheck.match(Line) != None:
+ F.write("%s: user=%s group=Debian file=%s%s/bsmtp/%s\n"%(Host,
+ GetAttr(x,"uid"),HomePrefix,GetAttr(x,"uid"),Host));
+
+ except:
+ F.write("; Errors\n");
+ pass;
+
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Generate the ssh known hosts file
+def GenSSHKnown(l,File):
+ F = None;
+ try:
+ OldMask = os.umask(0022);
+ F = open(File + ".tmp","w",0644);
+ os.umask(OldMask);
+
+ global HostAttrs
+ if HostAttrs == None:
+ raise "No Hosts";
+
+ for x in HostAttrs:
+ if x[1].has_key("hostname") == 0 or \
+ x[1].has_key("sshRSAHostKey") == 0:
+ continue;
+ Host = GetAttr(x,"hostname");
+ SHost = string.find(Host,".");
+ for I in x[1]["sshRSAHostKey"]:
+ if SHost == None:
+ Line = "%s,%s %s" %(Host,socket.gethostbyname(Host),I);
+ else:
+ Line = "%s,%s,%s %s" %(Host,Host[0:SHost],socket.gethostbyname(Host),I);
+ Line = Sanitize(Line) + "\n";
+ F.write(Line);
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Generate the debianhosts file (list of all IP addresses)
+def GenHosts(l,File):
+ F = None;
+ try:
+ OldMask = os.umask(0022);
+ F = open(File + ".tmp","w",0644);
+ os.umask(OldMask);
+
+ # Fetch all the hosts
+ HostNames = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"hostname=*",\
+ ["hostname"]);
+
+ if HostNames == None:
+ raise "No Hosts";
+
+ for x in HostNames:
+ if x[1].has_key("hostname") == 0:
+ continue;
+ Host = GetAttr(x,"hostname");
+ try:
+ Addr = socket.gethostbyname(Host);
+ F.write(Addr + "\n");
+ except:
+ pass
+ # Oops, something unspeakable happened.
+ except:
+ Die(File,F,None);
+ raise;
+ Done(File,F,None);
+
+# Connect to the ldap server
+l = ldap.open(LDAPServer);
+F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r");
+Pass = string.split(string.strip(F.readline())," ");
+F.close();
+l.simple_bind_s("uid="+Pass[0]+","+BaseDn,Pass[1]);
+
+# Fetch all the groups
+GroupIDMap = {};
+Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"gid=*",\
+ ["gid","gidNumber"]);
+
+# Generate the GroupMap and GroupIDMap
+for x in Attrs:
+ if x[1].has_key("gidNumber") == 0:
+ continue;
+ GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidNumber"][0]);
+
+# Fetch all the users
+PasswdAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=*",\
+ ["uid","uidNumber","gidNumber","supplementaryGid",\
+ "gecos","loginShell","userPassword","shadowLastChange",\
+ "shadowMin","shadowMax","shadowWarning","shadowinactive",
+ "shadowexpire","emailForward","latitude","longitude",\
+ "allowedHost","sshRSAAuthKey","dnsZoneEntry","cn","sn",\
+ "keyFingerPrint","privateSub","mailDisableMessage",\
+ "mailGreylisting","mailCallout","mailRBL","mailRHSBL",\
+ "mailWhitelist"]);
+# Fetch all the hosts
+HostAttrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"sshRSAHostKey=*",\
+ ["hostname","sshRSAHostKey"]);
+
+# Open the control file
+if len(sys.argv) == 1:
+ F = open(GenerateConf,"r");
+else:
+ F = open(sys.argv[1],"r")
+
+# Generate global things
+GlobalDir = GenerateDir+"/";
+GenSSHShadow(l,GlobalDir+"ssh-rsa-shadow");
+GenAllForward(l,GlobalDir+"mail-forward.cdb");
+GenMarkers(l,GlobalDir+"markers");
+GenPrivate(l,GlobalDir+"debian-private");
+GenSSHKnown(l,GlobalDir+"ssh_known_hosts");
+GenHosts(l,GlobalDir+"debian-communityhosts");
+GenMailDisable(l,GlobalDir+"mail-disable");
+GenMailBool(l,GlobalDir+"mail-greylist","mailGreylisting");
+GenMailBool(l,GlobalDir+"mail-callout","mailCallout");
+GenMailList(l,GlobalDir+"mail-rbl","mailRBL");
+GenMailList(l,GlobalDir+"mail-rhsbl","mailRHSBL");
+GenMailList(l,GlobalDir+"mail-whitelist","mailWhitelist");
+
+# Compatibility.
+GenForward(l,GlobalDir+"forward-alias");
+
+while(1):
+ Line = F.readline();
+ if Line == "":
+ break;
+ Line = string.strip(Line);
+ if Line == "":
+ continue;
+ if Line[0] == '#':
+ continue;
+
+ Split = string.split(Line," ");
+ OutDir = GenerateDir + '/' + Split[0] + '/';
+ try: os.mkdir(OutDir);
+ except: pass;
+
+ # Get the group list and convert any named groups to numerics
+ GroupList = {};
+ ExtraList = {};
+ for I in Split[2:]:
+ if I[0] == '[':
+ ExtraList[I] = None;
+ continue;
+ GroupList[I] = None;
+ if GroupIDMap.has_key(I):
+ GroupList[str(GroupIDMap[I])] = None;
+
+ Allowed = GroupList;
+ if Allowed == {}:
+ Allowed = None
+ CurrentHost = Split[0];
+
+ DoLink(GlobalDir,OutDir,"ssh-rsa-shadow");
+ DoLink(GlobalDir,OutDir,"debian-communityhosts");
+ DoLink(GlobalDir,OutDir,"ssh_known_hosts");
+
+ sys.stdout.flush();
+ GenPasswd(l,OutDir+"passwd",Split[1]);
+ sys.stdout.flush();
+ GenGroup(l,OutDir+"group");
+ if ExtraList.has_key("[UNTRUSTED]"):
+ continue;
+ if not ExtraList.has_key("[NOPASSWD]"):
+ GenShadow(l,OutDir+"shadow");
+
+ # Link in global things
+ DoLink(GlobalDir,OutDir,"markers");
+ DoLink(GlobalDir,OutDir,"mail-forward.cdb");
+ DoLink(GlobalDir,OutDir,"mail-disable");
+ DoLink(GlobalDir,OutDir,"mail-greylist");
+ DoLink(GlobalDir,OutDir,"mail-callout");
+ DoLink(GlobalDir,OutDir,"mail-rbl");
+ DoLink(GlobalDir,OutDir,"mail-rhsbl");
+ DoLink(GlobalDir,OutDir,"mail-whitelist");
+
+ # Compatibility.
+ DoLink(GlobalDir,OutDir,"forward-alias");
+
+ if ExtraList.has_key("[DNS]"):
+ GenDNS(l,OutDir+"dns-zone",Split[1]);
+ GenSSHFP(l,OutDir+"dns-sshfp",Split[1])
+
+ if ExtraList.has_key("[BSMTP]"):
+ GenBSMTP(l,OutDir+"bsmtp",Split[1])
+
+ if ExtraList.has_key("[PRIVATE]"):
+ DoLink(GlobalDir,OutDir,"debian-private")
Property changes on: trunk/userdir-ldap/ud-generate
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-gpgimport
===================================================================
--- trunk/userdir-ldap/ud-gpgimport (rev 0)
+++ trunk/userdir-ldap/ud-gpgimport 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+
+# Copyright (c) 1999-2000 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2004 Joey Schulze <joey at debian.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This script tries to match key fingerprints from a keyring with user
+# name in a directory. When an unassigned key is found a heuristic match
+# against the keys given cn/sn and the directory is performed to try to get
+# a matching. Generally this works about 90% of the time, matching is fairly
+# strict. In the event a non-match a fuzzy sounds-alike search is performed
+# and the results printed to aide the user.
+#
+# GPG is automatically invoked with the correct magic special options,
+# pass the names of all the valid key rings on the command line.
+#
+# The output report will list what actions were taken. Keys that are present
+# in the directory but not in the key ring will be removed from the
+# directory.
+
+import string, re, time, ldap, getopt, sys, pwd, os;
+from userdir_ldap import *;
+from userdir_gpg import *;
+
+# This map deals with people who put the wrong sort of stuff in their pgp
+# key entries
+UnknownMap = {};
+NoAct = 1;
+
+# Read the override file into the unknown map. The override file is a list
+# of colon delimited entires mapping PGP email addresess to local users
+def LoadOverride(File):
+ List = open(File,"r");
+ while(1):
+ Line = List.readline();
+ if Line == "":
+ break;
+ Split = re.split("[:\n]",Line);
+ UnknownMap[Split[0]] = string.strip(Split[1]);
+
+# Process options
+AdminUser = pwd.getpwuid(os.getuid())[0];
+(options, arguments) = getopt.getopt(sys.argv[1:], "au:m:n")
+for (switch, val) in options:
+ if (switch == '-u'):
+ AdminUser = val
+ elif (switch == '-m'):
+ LoadOverride(val);
+ elif (switch == '-a'):
+ NoAct = 0;
+if len(arguments) == 0:
+ print "Give some keyrings to probe";
+ sys.exit(0);
+
+# Main program starts here
+
+# Connect to the ldap server
+if NoAct == 0:
+ l = passwdAccessLDAP(LDAPServer, BaseDn, AdminUser)
+else:
+ l = ldap.open(LDAPServer);
+ l.simple_bind_s("","");
+
+# Download the existing key list and put it into a map
+print "Fetching key list..",
+sys.stdout.flush();
+Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=*",["keyFingerPrint","uid"]);
+KeyMap = {};
+KeyCount = {};
+for x in Attrs:
+ try:
+ # Sense a bad fingerprint.. Slapd has problems, it will store a null
+ # value that ldapsearch doesn't show up.. detect and remove
+ if len(x[1]["keyFingerPrint"]) == 0 or x[1]["keyFingerPrint"][0] == "":
+ print;
+ print "Fixing bad fingerprint for",x[1]["uid"][0],
+ sys.stdout.flush();
+ if NoAct == 0:
+ l.modify_s("uid="+x[1]["uid"][0]+","+BaseDn,\
+ [(ldap.MOD_DELETE,"keyFingerPrint",None)]);
+ else:
+ for I in x[1]["keyFingerPrint"]:
+ KeyMap[I] = [x[1]["uid"][0],0];
+ if KeyCount.has_key(x[1]["uid"][0]):
+ KeyCount[x[1]["uid"][0]] = KeyCount[x[1]["uid"][0]] + 1;
+ else:
+ KeyCount[x[1]["uid"][0]] = 1;
+ except:
+ continue;
+Attrs = None;
+print;
+
+# Popen GPG with the correct magic special options
+Args = [GPGPath] + GPGBasicOptions;
+for x in arguments:
+ Args.append("--keyring");
+ if string.find(x,"/") == -1:
+ Args.append("./"+x);
+ else:
+ Args.append(x);
+Args = Args + GPGSearchOptions + [" 2> /dev/null"]
+Keys = os.popen(string.join(Args," "),"r");
+
+# Loop over the GPG key file
+Outstanding = 0;
+Ignored = 0;
+SeenKeys = {};
+while(1):
+ Line = Keys.readline();
+ if Line == "":
+ break;
+
+ Split = string.split(Line,":");
+ if len(Split) < 8 or Split[0] != "pub":
+ continue;
+
+ while (1):
+ Line2 = Keys.readline();
+ if Line2 == "":
+ break;
+ Split2 = string.split(Line2,":");
+ if len(Split2) < 11 or Split2[0] != "fpr":
+ continue;
+ break;
+ if Line2 == "":
+ break;
+
+ if SeenKeys.has_key(Split2[9]):
+ print "Dup key 0x",Split2[9],"belonging to",KeyMap[Split2[9]][0];
+ continue;
+ SeenKeys[Split2[9]] = None;
+
+ if KeyMap.has_key(Split2[9]):
+ Ignored = Ignored + 1;
+ # print "Ignoring keyID",Split2[9],"belonging to",KeyMap[Split2[9]][0];
+ KeyMap[Split2[9]][1] = 1;
+ continue;
+
+ UID = GetUID(l,SplitEmail(Split[9]),UnknownMap);
+ if UID[0] == None:
+ print "None for",SplitEmail(Split[9]),"'%s'"%(Split[9]);
+ if UID[1] != None:
+ for x in UID[1]: print x;
+ print "MISSING 0x" + Split2[9];
+ continue;
+
+ UID = UID[0]
+ Rec = [(ldap.MOD_ADD,"keyFingerPrint",Split2[9])];
+ Dn = "uid=" + UID + "," + BaseDn;
+ print "Adding key 0x"+Split2[9],"to",UID;
+ if KeyCount.has_key(UID):
+ KeyCount[UID] = KeyCount[UID] + 1;
+ else:
+ KeyCount[UID] = 1;
+
+ if NoAct == 1:
+ continue;
+
+ # Send the modify request
+ l.modify(Dn,Rec);
+ Outstanding = Outstanding + 1;
+ Outstanding = FlushOutstanding(l,Outstanding,1);
+ sys.stdout.flush();
+
+if NoAct == 0:
+ FlushOutstanding(l,Outstanding);
+
+if Keys.close() != None:
+ raise "Error","GPG failed"
+
+print Ignored,"keys already in the directory (ignored)";
+
+# Look for unmatched keys
+for x in KeyMap.keys():
+ if KeyMap[x][1] == 0:
+ print "key 0x%s belonging to %s removed"%(x,KeyMap[x][0]);
+ if KeyCount.has_key(KeyMap[x][0]) :
+ KeyCount[KeyMap[x][0]] = KeyCount[KeyMap[x][0]] - 1
+ if KeyCount[KeyMap[x][0]] <= 0:
+ print "**",KeyMap[x][0],"no longer has any keys";
+ if NoAct == 0:
+ l.modify_s("uid="+KeyMap[x][0]+","+BaseDn,\
+ [(ldap.MOD_DELETE,"keyFingerPrint",x)]);
+
Property changes on: trunk/userdir-ldap/ud-gpgimport
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-gpgsigfetch
===================================================================
--- trunk/userdir-ldap/ud-gpgsigfetch (rev 0)
+++ trunk/userdir-ldap/ud-gpgsigfetch 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+
+import string, re, time, ldap, getopt, sys, pwd, os;
+from userdir_gpg import *;
+Output = "extrakeys.gpg";
+
+# Process options
+AdminUser = pwd.getpwuid(os.getuid())[0];
+(options, arguments) = getopt.getopt(sys.argv[1:], "o:")
+for (switch, val) in options:
+ if (switch == '-o'):
+ Output = val
+
+if len(arguments) == 0:
+ print "Give some keyrings to probe";
+ os.exit(0);
+
+# Popen GPG with the correct magic special options
+Args = [GPGPath] + GPGBasicOptions;
+for x in arguments:
+ Args.append("--keyring");
+ if string.find(x,"/") == -1:
+ Args.append("./"+x);
+ else:
+ Args.append(x);
+Args.append("--fast-list-mode");
+Args.append("--list-sigs");
+Args = Args + GPGSearchOptions + [" 2> /dev/null"]
+Keys = os.popen(string.join(Args," "),"r");
+
+# Loop over the GPG key file
+HaveKeys = {};
+NeedKeys = {};
+print "Reading keys+sigs from keyring";
+while(1):
+ Line = Keys.readline();
+ if Line == "":
+ break;
+
+ Split = string.split(Line,":");
+ if len(Split) >= 8 and Split[0] == "pub":
+ HaveKeys[Split[4]] = "";
+ continue;
+
+ if len(Split) >= 5 and Split[0] == "sig":
+ NeedKeys[Split[4]] = "";
+ continue;
+Keys.close();
+
+# Popen GPG with the correct magic special options
+Args = [GPGPath] + GPGBasicOptions;
+for x in [Output]:
+ Args.append("--keyring");
+ if string.find(x,"/") == -1:
+ Args.append("./"+x);
+ else:
+ Args.append(x);
+OldArgs = Args;
+Args = Args + GPGSearchOptions + [" 2> /dev/null"]
+Keys = os.popen(string.join(Args," "),"r");
+
+print "Reading keys from output ring";
+while(1):
+ Line = Keys.readline();
+ if Line == "":
+ break;
+
+ Split = string.split(Line,":");
+ if len(Split) >= 8 and Split[0] == "pub":
+ HaveKeys[Split[4]] = "";
+ continue;
+Keys.close();
+
+KeysToFetch = [];
+for x in NeedKeys.keys():
+ if not HaveKeys.has_key(x):
+ KeysToFetch.append("0x"+x);
+
+print "Have %u keys and %u sigs, need %u keys"%(len(HaveKeys),len(NeedKeys),len(KeysToFetch));
+
+Args = OldArgs;
+Args.append("--keyserver 18.43.0.48");
+Args.append("--recv-keys");
+I = len(KeysToFetch);
+while (I > 0):
+ OldI = I;
+ I = I - 20;
+ if I < 0: I = 0;
+ print string.join(Args+KeysToFetch[I:OldI]," ")
+ Fetcher = os.popen(string.join(Args+KeysToFetch[I:OldI]," "),"r");
+ while(1):
+ Line = Fetcher.readline();
+ if Line == "":
+ break;
+ print Line;
+ Fetcher.close();
Property changes on: trunk/userdir-ldap/ud-gpgsigfetch
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-groupadd
===================================================================
--- trunk/userdir-ldap/ud-groupadd (rev 0)
+++ trunk/userdir-ldap/ud-groupadd 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+
+# Copyright (c) 2000 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2001-2003 James Troup <troup at debian.org>
+# Copyright (c) 2004 Joey Schulze <joey at debian.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import string, re, time, ldap, getopt, sys, os, pwd;
+from userdir_ldap import *;
+from userdir_gpg import *;
+
+# This tries to search for a free UID. There are two possible ways to do
+# this, one is to fetch all the entires and pick the highest, the other
+# is to randomly guess uids until one is free. This uses the former.
+# Regrettably ldap doesn't have an integer attribute comparision function
+# so we can only cut the search down slightly
+
+# [JT] This is broken with Woody LDAP and the Schema; for now just
+# search through all GIDs.
+def GetFreeID(l):
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,
+ "gidNumber=*",["gidNumber"]);
+ HighestUID = 0;
+ for I in Attrs:
+ ID = int(GetAttr(I,"gidNumber","0"));
+ if ID > HighestUID and ID < 60000:
+ HighestUID = ID;
+ return HighestUID + 1;
+
+# Main starts here
+AdminUser = pwd.getpwuid(os.getuid())[0];
+
+# Process options
+ForceMail = 0;
+OldGPGKeyRings = GPGKeyRings;
+userdir_gpg.GPGKeyRings = [];
+(options, arguments) = getopt.getopt(sys.argv[1:], "u:")
+for (switch, val) in options:
+ if (switch == '-u'):
+ AdminUser = val;
+
+l = passwdAccessLDAP(LDAPServer, BaseDn, AdminUser)
+
+while 1:
+ Group = raw_input("Group name? ");
+ if Group == "":
+ sys.exit(1);
+
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"gid=" + Group);
+ if len(Attrs) == 0:
+ break;
+ print "Group already exists";
+
+Id = GetFreeID(l);
+print "Create group %s ID = %d"%(Group,Id);
+
+# Submit the add request
+Dn = "gid=" + Group + "," + BaseDn;
+print "Updating LDAP directory..",
+sys.stdout.flush();
+l.add_s(Dn,[("gid",Group),
+ ("gidNumber",str(Id)),
+ ("objectClass",("top", "debianGroup"))]);
Property changes on: trunk/userdir-ldap/ud-groupadd
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-homecheck
===================================================================
--- trunk/userdir-ldap/ud-homecheck (rev 0)
+++ trunk/userdir-ldap/ud-homecheck 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+# Checks a directory against the passwd file assuming it is the home
+# directory directory
+
+import string, ldap, getopt, sys, os, pwd;
+
+for x in os.listdir(sys.argv[1]):
+ try:
+ User = pwd.getpwnam(x);
+ st = os.stat(sys.argv[1]+x);
+ if User[2] != st[4] or User[3] != st[5]:
+ print "Bad ownership",x;
+ except:
+ print "Failed",x,"==> %s: %s" %(sys.exc_type,sys.exc_value);
+
Property changes on: trunk/userdir-ldap/ud-homecheck
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-host
===================================================================
--- trunk/userdir-ldap/ud-host (rev 0)
+++ trunk/userdir-ldap/ud-host 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,384 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+
+# Copyright (c) 2000-2001 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2001 Ryan Murray <rmurray at debian.org>
+# Copyright (c) 2003 James Troup <troup at debian.org>
+# Copyright (c) 2004-2005 Joey Schulze <joey at infodrom.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This script is an interactive way to manipulate fields in the LDAP directory.
+# When run it connects to the directory using the current users ID and fetches
+# all the attributes for the first machine. It then formats them nicely and
+# allows the user to change them.
+#
+# Usage: userinfo -a <user> -u <user> -c <user> -r
+# -a Set the authentication user (the user whose password you are
+# going to enter)
+# -h Set the host to display
+# -l list all hosts and their status
+# -f list all SSH fingerprints
+
+import string, time, os, pwd, sys, getopt, ldap, crypt, readline, copy;
+from tempfile import mktemp
+from os import O_CREAT, O_EXCL, O_WRONLY
+from userdir_ldap import *;
+
+RootMode = 0;
+AttrInfo = {"description": ["Machine Descr.", 1],
+ "hostname": ["Host names", 2],
+ "status": ["Status", 3],
+ "l": ["Location", 4],
+ "sponsor": ["Sponsors", 5],
+ "distribution": ["Distribution", 6],
+ "access": ["Access", 7],
+ "admin": ["Admin", 8],
+ "architecture": ["Architecture", 9],
+ "machine": ["Machine Hardware", 10],
+ "memory": ["Memory", 11],
+ "disk": ["Disk", 12],
+ "sshRSAHostKey": ["SSH Host Keys", 14],
+ "bandwidth": ["Bandwidth", 15]};
+
+AttrPrompt = {"description": ["Purpose of the machine"],
+ "hostname": ["The hostnames for the box (ipv4/ipv6)"],
+ "status": ["Blank if Up, explaination if not"],
+ "l": ["Physical location"],
+ "sponsor": ["Sponsors and their URLs"],
+ "distribution": ["The distribution version"],
+ "access": ["all, developer only, restricted"],
+ "admin": ["Admin email address"],
+ "architecture": ["Debian Architecture string"],
+ "machine": ["Hardware description"],
+ "memory": ["Installed RAM"],
+ "disk": ["Disk Space, RAID levels, etc"],
+ "sshRSAHostKey": ["A copy of /etc/ssh/ssh_*host_key.pub"],
+ "bandwidth": ["Available outbound"]};
+
+# Create a map of IDs to desc,value,attr
+OrderedIndex = {};
+for at in AttrInfo.keys():
+ if (AttrInfo[at][1] != 0):
+ OrderedIndex[AttrInfo[at][1]] = [AttrInfo[at][0], "", at];
+OrigOrderedIndex = copy.deepcopy(OrderedIndex);
+
+# Print out the automatic time stamp information
+def PrintModTime(Attrs):
+ Stamp = GetAttr(Attrs,"modifyTimestamp","");
+ if len(Stamp) >= 13:
+ Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
+ int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
+ print "%-24s:" % ("Record last modified on"), time.strftime("%a %d/%m/%Y %X UTC",Time),
+ print "by",ldap.explode_dn(GetAttr(Attrs,"modifiersName"),1)[0];
+
+ Stamp = GetAttr(Attrs,"createTimestamp","");
+ if len(Stamp) >= 13:
+ Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
+ int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
+ print "%-24s:" % ("Record created on"), time.strftime("%a %d/%m/%Y %X UTC",Time);
+
+# Display all of the attributes in a numbered list
+def ShowAttrs(Attrs):
+ print;
+ PrintModTime(Attrs);
+
+ for at in Attrs[1].keys():
+ if AttrInfo.has_key(at):
+ if AttrInfo[at][1] == 0:
+ print " %-18s:" % (AttrInfo[at][0]),
+ for x in Attrs[1][at]:
+ print "'%s'" % (x),
+ print;
+ else:
+ OrderedIndex[AttrInfo[at][1]][1] = Attrs[1][at];
+
+ Keys = OrderedIndex.keys();
+ Keys.sort();
+ for at in Keys:
+ if at < 100 or RootMode != 0:
+ print " %3u) %-18s: " % (at,OrderedIndex[at][0]),
+ for x in OrderedIndex[at][1]:
+ print "'%s'" % (re.sub('[\n\r]','?',x)),
+ print;
+
+def Overview(Attrs):
+ """Display a one-line overview for a given host"""
+ for i in ['host','architecture','distribution','access','status']:
+ if i not in Attrs[1].keys():
+ Attrs[1][i] = ['']
+ print "%-12s %-10s %-38s %-25s %s" % (\
+ Attrs[1]['host'][0], \
+ Attrs[1]['architecture'][0], \
+ Attrs[1]['distribution'][0], \
+ Attrs[1]['access'][0], \
+ Attrs[1]['status'][0])
+
+# Change a single attribute
+def ChangeAttr(Attrs,Attr):
+ if (Attr == "sponsor" or Attr == "sshRSAHostKey"):
+ return MultiChangeAttr(Attrs,Attr);
+
+ print "Old value: '%s'" % (GetAttr(Attrs,Attr,""));
+ print "Press enter to leave unchanged and a single space to set to empty";
+ NewValue = raw_input("New? ");
+
+ # Empty string
+ if (NewValue == ""):
+ print "Leaving unchanged.";
+ return;
+
+ # Single space designates delete, trap the delete error
+ if (NewValue == " "):
+ print "Deleting.",;
+ try:
+ l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,None)]);
+ except ldap.NO_SUCH_ATTRIBUTE:
+ pass;
+
+ print;
+ Attrs[1][Attr] = [""];
+ return;
+
+ # Set a new value
+ print "Setting.",;
+ l.modify_s(HostDn,[(ldap.MOD_REPLACE,Attr,NewValue)]);
+ Attrs[1][Attr] = [NewValue];
+ print;
+
+def MultiChangeAttr(Attrs,Attr):
+ # Make sure that we have an entry
+ if not Attrs[1].has_key(Attr):
+ Attrs[1][Attr] = [];
+
+ Attrs[1][Attr].sort();
+ print "Old values: ",Attrs[1][Attr];
+
+ Mode = string.upper(raw_input("[D]elete or [A]dd? "));
+ if (Mode != 'D' and Mode != 'A'):
+ return;
+
+ NewValue = raw_input("Value? ");
+ # Empty string
+ if (NewValue == ""):
+ print "Leaving unchanged.";
+ return;
+
+ # Delete
+ if (Mode == "D"):
+ print "Deleting.",;
+ try:
+ l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,NewValue)]);
+ except ldap.NO_SUCH_ATTRIBUTE:
+ print "Failed";
+
+ print;
+ Attrs[1][Attr].remove(NewValue);
+ return;
+
+ # Set a new value
+ print "Setting.",;
+ l.modify_s(HostDn,[(ldap.MOD_ADD,Attr,NewValue)]);
+ Attrs[1][Attr].append(NewValue);
+ print;
+
+def CalcTempFile():
+ unique = 0
+ while unique == 0:
+ name = mktemp()
+ try:
+ fd = os.open(name, O_CREAT | O_EXCL | O_WRONLY, 0600)
+ except OSError:
+ continue
+ os.close(fd)
+ unique = 1
+ return name
+
+
+# Main program starts here
+User = pwd.getpwuid(os.getuid())[0];
+BindUser = User;
+ListMode = 0
+FingerPrints = 0
+Host = None
+# Process options
+try:
+ (options, arguments) = getopt.getopt(sys.argv[1:], "nh:a:rlf")
+except getopt.GetoptError, data:
+ print data
+ sys.exit(1)
+
+for (switch, val) in options:
+ if (switch == '-h'):
+ Host = val;
+ elif (switch == '-a'):
+ BindUser = val;
+ elif (switch == '-r'):
+ RootMode = 1;
+ elif (switch == '-n'):
+ BindUser = "";
+ elif (switch == '-l'):
+ BindUser = "";
+ ListMode = 1
+ elif (switch == '-f'):
+ BindUser = "";
+ FingerPrints = 1
+
+if (BindUser != ""):
+ l = passwdAccessLDAP(LDAPServer, BaseDn, BindUser)
+else:
+ l = ldap.open(LDAPServer);
+ l.simple_bind_s("","")
+
+HBaseDn = HostBaseDn
+
+if ListMode == 1:
+ Attrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=*")
+ hosts = []
+ for hAttrs in Attrs:
+ hosts.append(hAttrs[1]['host'][0])
+ hosts.sort()
+
+ print "%-12s %-10s %-38s %-25s %s" % ("Host name","Arch","Distribution","Access","Status")
+ print "-"*115
+ for host in hosts:
+ for hAttrs in Attrs:
+ if host == hAttrs[1]['host'][0]:
+ Overview(hAttrs)
+ sys.exit(0)
+elif FingerPrints == 1:
+ if Host is not None:
+ Attrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host)
+ else:
+ Attrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=*")
+ hosts = []
+ for hAttrs in Attrs:
+ hosts.append(hAttrs[1]['host'][0])
+ hosts.sort()
+
+ tmpfile = CalcTempFile()
+ for host in hosts:
+ for hAttrs in Attrs:
+ if host == hAttrs[1]['host'][0]:
+ if 'sshRSAHostKey' in hAttrs[1].keys():
+ for key in hAttrs[1]['sshRSAHostKey']:
+ tmp = open(tmpfile, 'w')
+ tmp.write(key + '\n')
+ tmp.close()
+ fp = os.popen('/usr/bin/ssh-keygen -l -f ' + tmpfile, "r")
+ input = fp.readline()
+ fp.close()
+ fingerprint = input.split(' ')
+ print "%s %s root@%s" % (fingerprint[0], fingerprint[1], host)
+ os.unlink(tmpfile)
+ sys.exit(0)
+
+HostDn = "host=" + Host + "," + HBaseDn;
+
+# Query the server for all of the attributes
+Attrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host);
+if len(Attrs) == 0:
+ print "Host",Host,"was not found.";
+ sys.exit(0);
+
+# repeatedly show the account configuration
+while(1):
+ ShowAttrs(Attrs[0]);
+ if (BindUser == ""):
+ sys.exit(0);
+
+ if RootMode == 1:
+ print " a) Arbitary Change";
+ print " n) New Host";
+ print " d) Delete Host";
+ print " u) Switch Hosts";
+ print " x) Exit";
+
+ # Prompt
+ Response = raw_input("Change? ");
+ if (Response == "x" or Response == "X" or Response == "q" or
+ Response == "quit" or Response == "exit"):
+ break;
+
+ # Change who we are looking at
+ if (Response == 'u' or Response == 'U'):
+ NewHost = raw_input("Host? ");
+ if NewHost == "":
+ continue;
+ NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
+ if len(NAttrs) == 0:
+ print "Host",NewHost,"was not found.";
+ continue;
+ Attrs = NAttrs;
+ Host = NewHost;
+ HostDn = "host=" + Host + "," + HBaseDn;
+ OrderedIndex = copy.deepcopy(OrigOrderedIndex);
+ continue;
+
+ # Create a new entry and change to it Change who we are looking at
+ if (Response == 'n' or Response == 'N'):
+ NewHost = raw_input("Host? ");
+ if NewHost == "":
+ continue;
+ NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
+ if len(NAttrs) != 0:
+ print "Host",NewHost,"already exists.";
+ continue;
+ NewHostName = raw_input("Hostname? ");
+ if NewHost == "":
+ continue;
+ Dn = "host=" + NewHost + "," + HBaseDn;
+ l.add_s(Dn,[("host", NewHost),
+ ("hostname", NewHostName),
+ ("objectClass", ("top", "debianServer"))]);
+
+ # Switch
+ NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
+ if len(NAttrs) == 0:
+ print "Host",NewHost,"was not found.";
+ continue;
+ Attrs = NAttrs;
+ Host = NewHost;
+ HostDn = "host=" + Host + "," + HBaseDn;
+ OrderedIndex = copy.deepcopy(OrigOrderedIndex);
+ continue;
+
+ # Handle changing an arbitary value
+ if (Response == "a"):
+ Attr = raw_input("Attr? ");
+ ChangeAttr(Attrs[0],Attr);
+ continue;
+
+ if (Response == 'd'):
+ Really = raw_input("Really (type yes)? ");
+ if Really != 'yes':
+ continue;
+ print "Deleting",HostDn;
+ l.delete_s(HostDn);
+ continue;
+
+ # Convert the integer response
+ try:
+ ID = int(Response);
+ if (not OrderedIndex.has_key(ID) or (ID > 100 and RootMode == 0)):
+ raise ValueError;
+ except ValueError:
+ print "Invalid";
+ continue;
+
+ # Print the what to do prompt
+ print "Changing LDAP entry '%s' (%s)" % (OrderedIndex[ID][0],OrderedIndex[ID][2]);
+ print AttrPrompt[OrderedIndex[ID][2]][0];
+ ChangeAttr(Attrs[0],OrderedIndex[ID][2]);
Property changes on: trunk/userdir-ldap/ud-host
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-info
===================================================================
--- trunk/userdir-ldap/ud-info (rev 0)
+++ trunk/userdir-ldap/ud-info 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,418 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+# This script is an interactive way to manipulate fields in the LDAP directory.
+# When run it connects to the directory using the current users ID and fetches
+# all the attributes for that user. It then formats them nicely and allows
+# the user to change them.
+# It is possible to authenticate as someone differnt than you are viewing/changing
+# this allows administrative functions and also allows users to view
+# restricted information about others, such as phone numbers and addresses.
+#
+# Usage: userinfo -a <user> -u <user> -c <user> -r
+# -a Set the authentication user (the user whose password you are
+# going to enter)
+# -u Set the user to display
+# -c Set both -a and -u, use this if your login uid is not in the
+# database
+# -r Enable 'root' functions, do this if your uid has access to
+# restricted variables.
+
+import string, time, os, pwd, sys, getopt, ldap, crypt, readline, copy;
+from userdir_ldap import *;
+
+RootMode = 0;
+AttrInfo = {"cn": ["First Name", 101],
+ "mn": ["Middle Name", 102],
+ "sn": ["Surname", 103],
+ "c": ["Country Code",1],
+ "l": ["Locality",2],
+ "ou": ["Membership",0],
+ "facsimileTelephoneNumber": ["Fax Phone Number",3],
+ "telephoneNumber": ["Phone Number",4],
+ "postalAddress": ["Mailing Address",5],
+ "postalCode": ["Postal Code",6],
+ "uid": ["Unix User ID",0],
+ "loginShell": ["Unix Shell",7],
+ "supplementaryGid": ["Unix Groups",0],
+ "allowedHost": ["Host ACL",0],
+ "member": ["LDAP Group",0],
+ "emailForward": ["Email Forwarding",8],
+ "ircNick": ["IRC Nickname",9],
+ "onVacation": ["Vacation Message",10],
+ "labeledURI": ["Home Page",11],
+ "latitude": ["Latitude",12],
+ "longitude": ["Longitude",13],
+ "icqUin": ["ICQ UIN",14],
+ "jabberJID": ["Jabber ID",15],
+ "privateSub": ["Debian-Private",16],
+ "gender": ["Gender",17],
+ "birthDate": ["Date of Birth",18],
+ "mailDisableMessage": ["Mail Disabled",19],
+ "mailGreylisting": ["Mail Greylisting",20],
+ "mailCallout": ["Mail Callouts",21],
+ "mailRBL": ["Mail RBLs",22],
+ "mailRHSBL": ["Mail RHSBLs",23],
+ "mailWhitelist": ["Mail Whitelist",24],
+ "comment": ["Comment",116],
+ "userPassword": ["Crypted Password",117],
+ "dnsZoneEntry": ["d.net Entry",118]};
+
+AttrPrompt = {"cn": ["Common name or first name"],
+ "mn": ["Middle name (or initial if it ends in a dot)"],
+ "sn": ["Surname or last name"],
+ "c": ["ISO 2 letter country code, such as US, DE, etc"],
+ "l": ["City name, State/Provice (Locality)\n e.g. Dallas, Texas"],
+ "facsimileTelephoneNumber": ["Fax phone number, with area code and country code"],
+ "telephoneNumber": ["Voice phone number"],
+ "postalAddress": ["Complete mailing address including postal codes and country designations\nSeperate lines using a $ character"],
+ "postalCode": ["Postal Code or Zip Code"],
+ "loginShell": ["Login shell with full path (no check is done for validity)"],
+ "emailForward": ["EMail address to send all mail to or blank to disable"],
+ "ircNick": ["IRC nickname if you use IRC"],
+ "onVacation": ["A message if on vaction, indicating the time of departure and return"],
+ "userPassword": ["The users Crypt'd password"],
+ "comment": ["Admin Comment about the account"],
+ "supplementaryGid": ["Groups the user is in"],
+ "allowedHost": ["Grant access to certain hosts"],
+ "privateSub": ["Debian-Private mailing list subscription"],
+ "gender": ["ISO5218 Gender code (1=male,2=female,9=unspecified)"],
+ "birthDate": ["Date of Birth (YYYYMMDD)"],
+ "mailDisableMessage": ["Error message to return via SMTP"],
+ "mailGreylisting": ["SMTP Greylisting (TRUE/FALSE)"],
+ "mailCallout": ["SMTP Callouts (TRUE/FALSE)"],
+ "mailRBL": ["SMTP time RBL lists"],
+ "mailRHSBL": ["SMTP time RHSBL lists"],
+ "mailWhitelist": ["SMTP time whitelist from other checks"],
+ "member": ["LDAP Group Member for slapd ACLs"],
+ "latitude": ["XEarth latitude in ISO 6709 format - see /usr/share/zoneinfo/zone.tab or etak.com"],
+ "longitude": ["XEarth latitude in ISO 6709 format - see /usr/share/zoneinfo/zone.tab or etak.com"],
+ "dnsZoneEntry": ["DNS Zone fragment associated this this user"],
+ "labeledURI": ["Web home page"],
+ "jabberJID": ["Jabber ID"],
+ "icqUin": ["ICQ UIN Number"]};
+
+# Create a map of IDs to desc,value,attr
+OrderedIndex = {};
+for at in AttrInfo.keys():
+ if (AttrInfo[at][1] != 0):
+ OrderedIndex[AttrInfo[at][1]] = [AttrInfo[at][0], "", at];
+OrigOrderedIndex = copy.deepcopy(OrderedIndex);
+
+# Show shadow information
+def PrintShadow(Attrs):
+ Changed = int(GetAttr(Attrs,"shadowLastChange","0"));
+ MinDays = int(GetAttr(Attrs,"shadowMin","0"));
+ MaxDays = int(GetAttr(Attrs,"shadowMax","0"));
+ WarnDays = int(GetAttr(Attrs,"shadowWarning","0"));
+ InactDays = int(GetAttr(Attrs,"shadowinactive","0"));
+ Expire = int(GetAttr(Attrs,"shadowexpire","0"));
+
+ print "%-24s:" % ("Password last changed"),
+ print time.strftime("%a %d/%m/%Y %Z",time.localtime(Changed*24*60*60));
+ if (Expire > 0):
+ print "%-24s:" % ("Account expires on"),
+ print time.strftime("%a %d/%m/%Y %Z",time.localtime(Expire*24*60*60));
+ if (InactDays >= 0 and MaxDays < 99999):
+ print "Account aging is active, you must change your password every", MaxDays, "days."
+
+# Print out the automatic time stamp information
+def PrintModTime(Attrs):
+ Stamp = GetAttr(Attrs,"modifyTimestamp","");
+ if len(Stamp) >= 13:
+ Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
+ int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
+ print "%-24s:" % ("Record last modified on"), time.strftime("%a %d/%m/%Y %X UTC",Time),
+ print "by",ldap.explode_dn(GetAttr(Attrs,"modifiersName"),1)[0];
+
+ Stamp = GetAttr(Attrs,"createTimestamp","");
+ if len(Stamp) >= 13:
+ Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
+ int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
+ print "%-24s:" % ("Record created on"), time.strftime("%a %d/%m/%Y %X UTC",Time);
+
+# Print the PGP key for a user
+def PrintKeys(Attrs):
+ if Attrs[1].has_key("keyFingerPrint") == 0:
+ return;
+ First = 0;
+ for x in Attrs[1]["keyFingerPrint"]:
+ if First == 0:
+ print "%-24s:" % ("PGP/GPG Key Fingerprints"),
+ First = 1;
+ else:
+ print "%-24s:" % (""),
+ print FormatPGPKey(x);
+
+# Print the SSH RSA Authentication keys for a user
+def PrintSshRSAKeys(Attrs):
+ if Attrs[1].has_key("sshRSAAuthKey") == 0:
+ return;
+ First = 0;
+ for x in Attrs[1]["sshRSAAuthKey"]:
+ if First == 0:
+ print "%-24s:" % ("SSH Auth Keys"),
+ First = 1;
+ else:
+ print "%-24s:" % (""),
+
+ print FormatSSHAuth(x);
+
+# Display all of the attributes in a numbered list
+def ShowAttrs(Attrs):
+ print;
+ print EmailAddress(Attrs);
+ PrintModTime(Attrs);
+ PrintShadow(Attrs);
+ PrintKeys(Attrs);
+ PrintSshRSAKeys(Attrs);
+
+ for at in Attrs[1].keys():
+ if AttrInfo.has_key(at):
+ if AttrInfo[at][1] == 0:
+ print " %-18s:" % (AttrInfo[at][0]),
+ for x in Attrs[1][at]:
+ print "'%s'" % (x),
+ if at == "uid":
+ print "(id=%s, gid=%s)" % (GetAttr(Attrs,"uidNumber","-1"),GetAttr(Attrs,"gidNumber","-1")),
+ print;
+ else:
+ OrderedIndex[AttrInfo[at][1]][1] = Attrs[1][at];
+
+ Keys = OrderedIndex.keys();
+ Keys.sort();
+ for at in Keys:
+ if at < 100 or RootMode != 0:
+ print " %3u) %-18s: " % (at,OrderedIndex[at][0]),
+ for x in OrderedIndex[at][1]:
+ print "'%s'" % (re.sub('[\n\r]','?',x)),
+ print;
+
+# Change a single attribute
+def ChangeAttr(Attrs,Attr):
+ if (Attr == "supplementaryGid" or Attr == "allowedHost" or \
+ Attr == "member" or Attr == "dnsZoneEntry" or Attr == "mailWhitelist" or \
+ Attr == "mailRBL" or Attr == "mailRHSBL"):
+ return MultiChangeAttr(Attrs,Attr);
+
+ print "Old value: '%s'" % (GetAttr(Attrs,Attr,""));
+ print "Press enter to leave unchanged and a single space to set to empty";
+ NewValue = raw_input("New? ");
+
+ # Empty string
+ if (NewValue == ""):
+ print "Leaving unchanged.";
+ return;
+
+ # Single space designates delete, trap the delete error
+ if (NewValue == " "):
+ print "Deleting.",;
+ try:
+ l.modify_s(UserDn,[(ldap.MOD_DELETE,Attr,None)]);
+ except ldap.NO_SUCH_ATTRIBUTE:
+ pass;
+
+ print;
+ Attrs[1][Attr] = [""];
+ return;
+
+ # Set a new value
+ print "Setting.",;
+ l.modify_s(UserDn,[(ldap.MOD_REPLACE,Attr,NewValue)]);
+ Attrs[1][Attr] = [NewValue];
+ print;
+
+def MultiChangeAttr(Attrs,Attr):
+ # Make sure that we have an entry
+ if not Attrs[1].has_key(Attr):
+ Attrs[1][Attr] = [];
+
+ Attrs[1][Attr].sort();
+ print "Old values: ",Attrs[1][Attr];
+
+ Mode = string.upper(raw_input("[D]elete or [A]dd? "));
+ if (Mode != 'D' and Mode != 'A'):
+ return;
+
+ NewValue = raw_input("Value? ");
+ # Empty string
+ if (NewValue == ""):
+ print "Leaving unchanged.";
+ return;
+
+ # Delete
+ if (Mode == "D"):
+ print "Deleting.",;
+ try:
+ l.modify_s(UserDn,[(ldap.MOD_DELETE,Attr,NewValue)]);
+ except ldap.NO_SUCH_ATTRIBUTE:
+ print "Failed";
+
+ print;
+ Attrs[1][Attr].remove(NewValue);
+ return;
+
+ # Set a new value
+ print "Setting.",;
+ l.modify_s(UserDn,[(ldap.MOD_ADD,Attr,NewValue)]);
+ Attrs[1][Attr].append(NewValue);
+ print;
+
+# Main program starts here
+User = pwd.getpwuid(os.getuid())[0];
+BindUser = User;
+# Process options
+try:
+ (options, arguments) = getopt.getopt(sys.argv[1:], "nu:c:a:r")
+except getopt.GetoptError, data:
+ print data
+ sys.exit(1)
+
+for (switch, val) in options:
+ if (switch == '-u'):
+ User = val;
+ elif (switch == '-a'):
+ BindUser = val;
+ elif (switch == '-c'):
+ BindUser = val;
+ User = val;
+ elif (switch == '-r'):
+ RootMode = 1;
+ elif (switch == '-n'):
+ BindUser = "";
+
+if (BindUser != ""):
+ print "Accessing LDAP entry for '" + User + "'",
+if (BindUser != User):
+ if (BindUser != ""):
+ print "as '" + BindUser + "'";
+else:
+ print;
+if (BindUser != ""):
+ Password = getpass(BindUser + "'s password: ");
+
+# Connect to the ldap server
+l = ldap.open(LDAPServer);
+UserDn = "uid=" + BindUser + "," + BaseDn;
+if (BindUser != ""):
+ l.simple_bind_s(UserDn,Password);
+else:
+ l.simple_bind_s("","");
+UserDn = "uid=" + User + "," + BaseDn;
+
+# Enable changing of supplementary gid's
+if (RootMode == 1):
+ # Items that root can edit
+ list = ["supplementaryGid","allowedHost","member"];
+ Count = 0;
+ for x in list:
+ AttrInfo[x][1] = 200 + Count;
+ OrderedIndex[AttrInfo[x][1]] = [AttrInfo[x][0], "",x];
+ OrigOrderedIndex[AttrInfo[x][1]] = [AttrInfo[x][0], "",x];
+ Count = Count + 1;
+
+# Query the server for all of the attributes
+Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=" + User);
+if len(Attrs) == 0:
+ print "User",User,"was not found.";
+ sys.exit(0);
+
+# repeatedly show the account configuration
+while(1):
+ ShowAttrs(Attrs[0]);
+ if (BindUser == ""):
+ sys.exit(0);
+
+ if RootMode == 1:
+ print " a) Arbitary Change";
+ print " R) Randomize Password";
+ print " p) Change Password";
+ print " u) Switch Users";
+ print " x) Exit";
+
+ # Prompt
+ Response = raw_input("Change? ");
+ if (Response == "x" or Response == "X" or Response == "q" or
+ Response == "quit" or Response == "exit"):
+ break;
+
+ # Change who we are looking at
+ if (Response == 'u' or Response == 'U'):
+ NewUser = raw_input("User? ");
+ if NewUser == "":
+ continue;
+ NAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=" + NewUser);
+ if len(NAttrs) == 0:
+ print "User",NewUser,"was not found.";
+ continue;
+ Attrs = NAttrs;
+ User = NewUser;
+ UserDn = "uid=" + User + "," + BaseDn;
+ OrderedIndex = copy.deepcopy(OrigOrderedIndex);
+ continue;
+
+ # Handle changing the password
+ if (Response == "p"):
+ print "Please enter a new password. Your password can be of unlimited length,";
+ print "contain spaces and other special characters. No checking is done on the";
+ print "strength of the passwords so pick good ones please!";
+
+ Pass1 = getpass(User + "'s new password: ");
+ Pass2 = getpass(User + "'s new password again: ");
+ if Pass1 != Pass2:
+ print "Passwords did not match";
+ raw_input("Press a key");
+ continue;
+
+ try:
+ Pass = HashPass(Pass1);
+ except:
+ print "%s: %s\n" %(sys.exc_type,sys.exc_value);
+ raw_input("Press a key");
+ continue;
+
+ print "Setting password..";
+ Pass = "{crypt}" + Pass;
+ l.modify_s(UserDn,[(ldap.MOD_REPLACE,"userPassword",Pass)]);
+ Attrs[0][1]["userPassword"] = [Pass];
+ continue;
+
+ # Randomize password
+ if Response == 'R' and RootMode == 1:
+ Resp = raw_input("Randomize Users Password? [no/yes]");
+ if Resp != "yes":
+ continue;
+
+ # Generate a random password
+ try:
+ Password = GenPass();
+ Pass = HashPass(Password);
+ except:
+ print "%s: %s\n" %(sys.exc_type,sys.exc_value);
+ raw_input("Press a key");
+ continue;
+
+ print "Setting password..";
+ Pass = "{crypt}" + Pass;
+ l.modify_s(UserDn,[(ldap.MOD_REPLACE,"userPassword",Pass)]);
+ Attrs[0][1]["userPassword"] = [Pass];
+ continue;
+
+ # Handle changing an arbitary value
+ if (Response == "a"):
+ Attr = raw_input("Attr? ");
+ ChangeAttr(Attrs[0],Attr);
+ continue;
+
+ # Convert the integer response
+ try:
+ ID = int(Response);
+ if (not OrderedIndex.has_key(ID) or (ID > 100 and RootMode == 0)):
+ raise ValueError;
+ except ValueError:
+ print "Invalid";
+ continue;
+
+ # Print the what to do prompt
+ print "Changing LDAP entry '%s' (%s)" % (OrderedIndex[ID][0],OrderedIndex[ID][2]);
+ print AttrPrompt[OrderedIndex[ID][2]][0];
+ ChangeAttr(Attrs[0],OrderedIndex[ID][2]);
Property changes on: trunk/userdir-ldap/ud-info
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-ldapshow
===================================================================
--- trunk/userdir-ldap/ud-ldapshow (rev 0)
+++ trunk/userdir-ldap/ud-ldapshow 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+# Show some reports from the ldap database
+# Call with nokey to generate a missing key report
+# Call with noforward to generate a missing .forward report
+
+import string, re, time, ldap, getopt, sys;
+from userdir_ldap import *;
+
+def ShowDups(Attrs,Len):
+ for x in Attrs:
+ if x[1].has_key("keyFingerPrint") == 0:
+ continue;
+
+ Count = 0;
+ for I in x[1]["keyFingerPrint"]:
+ if len(I) == Len:
+ Count = Count + 1;
+ if Count > 1:
+ for I in x[1]["keyFingerPrint"]:
+ if len(I) == Len:
+ print "%s: %s" % (EmailAddress(x),I);
+
+# Main program starts here
+# Process options
+(options, arguments) = getopt.getopt(sys.argv[1:], "")
+for (switch, val) in options:
+ if (switch == '-a'):
+ DoAdd = 1;
+
+print "Connecting to LDAP directory";
+
+# Connect to the ldap server
+l = ldap.open(LDAPServer);
+l.simple_bind_s("","");
+
+if arguments[0] == "nokey":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(!(keyFingerPrint=*))",\
+ ["uid","cn","sn","emailForward","comment"]);
+ Attrs.sort();
+ for x in Attrs:
+ print "Key Missing:",EmailAddress(x);
+ if GetAttr(x,"emailForward") != "":
+ print " ->",GetAttr(x,"emailForward");
+ if GetAttr(x,"comment") != "":
+ print " :",GetAttr(x,"comment");
+
+if arguments[0] == "noforward":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(!(emailForward=*))",\
+ ["uid","cn","sn","emailForward","comment"]);
+ Attrs.sort();
+ for x in Attrs:
+ print "No Forward:",EmailAddress(x);
+
+if arguments[0] == "badpriv":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(&(!(keyFingerPrint=*))(privateSub=*))",\
+ ["uid","cn","sn","privateSub"]);
+ Attrs.sort();
+ for x in Attrs:
+ print EmailAddress(x)+": "+GetAttr(x,"privateSub");
+
+if arguments[0] == "nopriv":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(&(keyFingerPrint=*)(!(privateSub=*)))",\
+ ["uid","cn","sn","privateSub"]);
+ Attrs.sort();
+ for x in Attrs:
+ print " ",EmailAddress(x)+": "+GetAttr(x,"privateSub");
+
+if arguments[0] == "keymap":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=*",\
+ ["uid","cn","sn","keyFingerPrint"]);
+ Attrs.sort();
+ for x in Attrs:
+ if x[1].has_key("keyFingerPrint"):
+ for I in x[1]["keyFingerPrint"]:
+ print "%s: %s" % (EmailAddress(x),I);
+
+if arguments[0] == "devcount":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(&(keyFingerPrint=*)(gidNumber=800))",\
+ ["uid"]);
+ Count = 0;
+ for x in Attrs:
+ Count = Count + 1;
+ print "There are",Count,"developers as of",time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
+
+if arguments[0] == "echelon":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,\
+ "(&(|(activity-pgp=*)(activity-from=*))(&(keyFingerPrint=*)(gidNumber=800)))",\
+ ["activity-pgp","activity-from"]);
+ Count = 0;
+ PGPCount = 0;
+ for x in Attrs:
+ Count = Count + 1;
+ if x[1].has_key("activity-pgp"):
+ PGPCount = PGPCount + 1;
+ print "Echelon has seen",Count,"developers, with",PGPCount,"PGP confirms as of",time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
+
+if arguments[0] == "missing":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,\
+ "(&(!(|(activity-pgp=*)(activity-from=*)))(&(keyFingerPrint=*)(gidNumber=800)))",\
+ ["uid","cn","sn","mn"]);
+ Attrs.sort();
+ for x in Attrs:
+ print EmailAddress(x);
+
+if arguments[0] == "keystat":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=*",\
+ ["keyFingerPrint"]);
+ KeyCount = 0;
+ GPGCount = 0;
+ for x in Attrs:
+ if x[1].has_key("keyFingerPrint"):
+ KeyCount = KeyCount + 1;
+ for I in x[1]["keyFingerPrint"]:
+ if len(I) == 40:
+ GPGCount = GPGCount + 1;
+ break;
+ print "There are",KeyCount,"accounts with PGP2/5 keys and",GPGCount,"of them have PGP5 keys";
+
+if arguments[0] == "multikeys":
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=*",\
+ ["uid","cn","sn","keyFingerPrint"]);
+ Attrs.sort();
+
+
+ print "--- PGP Keys ---"
+ ShowDups(Attrs,32);
+ print "--- GPG Keys ---"
+ ShowDups(Attrs,40);
+
Property changes on: trunk/userdir-ldap/ud-ldapshow
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-mailgate
===================================================================
--- trunk/userdir-ldap/ud-mailgate (rev 0)
+++ trunk/userdir-ldap/ud-mailgate 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,568 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, os;
+import string, pwd
+from userdir_gpg import *;
+from userdir_ldap import *;
+
+# Error codes from /usr/include/sysexits.h
+ReplyTo = ConfModule.replyto;
+PingFrom = ConfModule.pingfrom;
+ChPassFrom = ConfModule.chpassfrom;
+ChangeFrom = ConfModule.changefrom;
+ReplayCacheFile = ConfModule.replaycachefile;
+
+EX_TEMPFAIL = 75;
+EX_PERMFAIL = 65; # EX_DATAERR
+Error = 'Message Error';
+SeenKey = 0;
+SeenDNS = 0;
+mailRBL = {}
+mailRHSBL = {}
+mailWhitelist = {}
+SeenList = {}
+DNS = {}
+
+ArbChanges = {"c": "..",
+ "l": ".*",
+ "facsimileTelephoneNumber": ".*",
+ "telephoneNumber": ".*",
+ "postalAddress": ".*",
+ "postalCode": ".*",
+ "loginShell": ".*",
+ "emailForward": "^([^<>@]+ at .+)?$",
+ "jabberJID": "^([^<>@]+ at .+)?$",
+ "ircNick": ".*",
+ "icqUin": "^[0-9]*$",
+ "onVacation": ".*",
+ "labeledURI": ".*",
+ "birthDate": "^([0-9]{4})([01][0-9])([0-3][0-9])$",
+ "mailDisableMessage": ".*",
+ "mailGreylisting": "^(TRUE|FALSE)$",
+ "mailCallout": "^(TRUE|FALSE)$",
+};
+
+DelItems = {"c": None,
+ "l": None,
+ "facsimileTelephoneNumber": None,
+ "telephoneNumber": None,
+ "postalAddress": None,
+ "postalCode": None,
+ "emailForward": None,
+ "ircNick": None,
+ "onVacation": None,
+ "labeledURI": None,
+ "latitude": None,
+ "longitude": None,
+ "icqUin": None,
+ "jabberJID": None,
+ "jpegPhoto": None,
+ "dnsZoneEntry": None,
+ "sshRSAAuthKey": None,
+ "sshDSAAuthKey": None,
+ "birthDate" : None,
+ "mailGreylisting": None,
+ "mailCallout": None,
+ "mailRBL": None,
+ "mailRHSBL": None,
+ "mailWhitelist": None,
+ "mailDisableMessage": None,
+ };
+
+# Decode a GPS location from some common forms
+def LocDecode(Str,Dir):
+ # Check for Decimal degrees, DGM, or DGMS
+ if re.match("^[+-]?[\d.]+$",Str) != None:
+ return Str;
+
+ Deg = '0'; Min = None; Sec = None; Dr = Dir[0];
+
+ # Check for DDDxMM.MMMM where x = [nsew]
+ Match = re.match("^(\d+)(["+Dir+"])([\d.]+)$",Str);
+ if Match != None:
+ G = Match.groups();
+ Deg = G[0]; Min = G[2]; Dr = G[1];
+
+ # Check for DD.DD x
+ Match = re.match("^([\d.]+) ?(["+Dir+"])$",Str);
+ if Match != None:
+ G = Match.groups();
+ Deg = G[0]; Dr = G[1];
+
+ # Check for DD:MM.MM x
+ Match = re.match("^(\d+):([\d.]+) ?(["+Dir+"])$",Str);
+ if Match != None:
+ G = Match.groups();
+ Deg = G[0]; Min = G[1]; Dr = G[2];
+
+ # Check for DD:MM:SS.SS x
+ Match = re.match("^(\d+):(\d+):([\d.]+) ?(["+Dir+"])$",Str);
+ if Match != None:
+ G = Match.groups();
+ Deg = G[0]; Min = G[1]; Sec = G[2]; Dr = G[3];
+
+ # Some simple checks
+ if float(Deg) > 180:
+ raise "Failed","Bad degrees";
+ if Min != None and float(Min) > 60:
+ raise "Failed","Bad minutes";
+ if Sec != None and float(Sec) > 60:
+ raise "Failed","Bad seconds";
+
+ # Pad on an extra leading 0 to disambiguate small numbers
+ if len(Deg) <= 1 or Deg[1] == '.':
+ Deg = '0' + Deg;
+ if Min != None and (len(Min) <= 1 or Min[1] == '.'):
+ Min = '0' + Min;
+ if Sec != None and (len(Sec) <= 1 or Sec[1] == '.'):
+ Sec = '0' + Sec;
+
+ # Construct a DGM/DGMS type value from the components.
+ Res = "+"
+ if Dr == Dir[1]:
+ Res = "-";
+ Res = Res + Deg;
+ if Min != None:
+ Res = Res + Min;
+ if Sec != None:
+ Res = Res + Sec;
+ return Res;
+
+# Handle changing a set of arbitary fields
+# <field>: value
+def DoArbChange(Str,Attrs):
+ Match = re.match("^([^ :]+): (.*)$",Str);
+ if Match == None:
+ return None;
+ G = Match.groups();
+
+ attrName = G[0].lower();
+ for i in ArbChanges.keys():
+ if i.lower() == attrName:
+ attrName = i;
+ break;
+ if ArbChanges.has_key(attrName) == 0:
+ return None;
+
+ if re.match(ArbChanges[attrName],G[1]) == None:
+ raise Error, "Item does not match the required format"+ArbChanges[attrName];
+
+# if attrName == 'birthDate':
+# (re.match("^([0-9]{4})([01][0-9])([0-3][0-9])$",G[1]) {
+# $bd_yr = $1; $bd_mo = $2; $bd_day = $3;
+# if ($bd_mo > 0 and $bd_mo <= 12 and $bd_day > 0) {
+# if ($bd_mo == 2) {
+# if ($bd_day == 29 and ($bd_yr == 0 or ($bd_yr % 4 == 0 && ($bd_yr % 100 != 0 || $bd_yr % 400 == 0)))) {
+# $bd_ok = 1;
+# } elsif ($bd_day <= 28) {
+# $bd_ok = 1;
+# }
+# } elsif ($bd_mo == 4 or $bd_mo == 6 or $bd_mo == 9 or $bd_mo == 11) {
+# if ($bd_day <= 30) {
+# $bd_ok = 1;
+# }
+# } else {
+# if ($bd_day <= 31) {
+# $bd_ok = 1;
+# }
+# }
+# }
+# } elsif (not defined($query->param('birthdate')) or $query->param('birthdate') =~ /^\s*$/) {
+# $bd_ok = 1;
+# }
+ Attrs.append((ldap.MOD_REPLACE,attrName,G[1]));
+ return "Changed entry %s to %s"%(attrName,G[1]);
+
+# Handle changing a set of arbitary fields
+# <field>: value
+def DoDel(Str,Attrs):
+ Match = re.match("^del (.*)$",Str);
+ if Match == None:
+ return None;
+ G = Match.groups();
+
+ attrName = G[0].lower();
+ for i in DelItems.keys():
+ if i.lower() == attrName:
+ attrName = i;
+ break;
+ if DelItems.has_key(attrName) == 0:
+ return "Cannot erase entry %s"%(attrName);
+
+ Attrs.append((ldap.MOD_DELETE,attrName,None));
+ return "Removed entry %s"%(attrName);
+
+# Handle a position change message, the line format is:
+# Lat: -12412.23 Long: +12341.2342
+def DoPosition(Str,Attrs):
+ Match = re.match("^lat: ([+\-]?[\d:.ns]+(?: ?[ns])?) long: ([+\-]?[\d:.ew]+(?: ?[ew])?)$",string.lower(Str));
+ if Match == None:
+ return None;
+
+ G = Match.groups();
+ try:
+ sLat = LocDecode(G[0],"ns");
+ sLong = LocDecode(G[1],"ew");
+ Lat = DecDegree(sLat,1);
+ Long = DecDegree(sLong,1);
+ except:
+ raise Error, "Positions were found, but they are not correctly formed";
+
+ Attrs.append((ldap.MOD_REPLACE,"latitude",sLat));
+ Attrs.append((ldap.MOD_REPLACE,"longitude",sLong));
+ return "Position set to %s/%s (%s/%s decimal degrees)"%(sLat,sLong,Lat,Long);
+
+# Handle an SSH authentication key, the line format is:
+# [options] 1024 35 13188913666680[..] [comment]
+def DoSSH(Str,Attrs):
+ Match = SSH2AuthSplit.match(Str);
+ if Match == None:
+ Match = re.compile('^1024 (\d+) ').match(Str)
+ if Match is not None:
+ return "SSH1 keys not supported anymore"
+ return None;
+
+ global SeenKey;
+ if SeenKey:
+ Attrs.append((ldap.MOD_ADD,"sshRSAAuthKey",Str));
+ return "SSH Key added "+FormatSSHAuth(Str);
+
+ Attrs.append((ldap.MOD_REPLACE,"sshRSAAuthKey",Str));
+ SeenKey = 1;
+ return "SSH Keys replaced with "+FormatSSHAuth(Str);
+
+# Handle changing a dns entry
+# host in a 12.12.12.12
+# host in cname foo.bar. <- Trailing dot is required
+def DoDNS(Str,Attrs,DnRecord):
+ cname = re.match("^[-\w]+\s+in\s+cname\s+[-\w.]+\.$",Str,re.IGNORECASE);
+ if re.match('^[-\w]+\s+in\s+a\s+\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$',\
+ Str,re.IGNORECASE) == None and cname == None and \
+ re.match("^[-\w]+\s+in\s+mx\s+\d{1,3}\s+[-\w.]+\.$",Str,re.IGNORECASE) == None:
+ return None;
+
+ # Check if the name is already taken
+ G = re.match('^([-\w+]+)\s',Str).groups();
+
+ # Check for collisions
+ global l;
+ # [JT 20070409 - search for both tab and space suffixed hostnames
+ # since we accept either. It'd probably be better to parse the
+ # incoming string in order to construct what we feed LDAP rather
+ # than just passing it through as is.]
+ filter = "(|(dnsZoneEntry=%s *)(dnsZoneEntry=%s *))" % (G[0], G[0])
+ Rec = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,filter,["uid"]);
+ for x in Rec:
+ if GetAttr(x,"uid") != GetAttr(DnRecord,"uid"):
+ return "DNS entry is already owned by " + GetAttr(x,"uid")
+
+ global SeenDNS;
+ global DNS;
+
+ if cname:
+ if DNS.has_key(G[0]):
+ return "CNAME and other RR types not allowed: "+Str
+ else:
+ DNS[G[0]] = 2
+ else:
+ if DNS.has_key(G[0]) and DNS[G[0]] == 2:
+ return "CNAME and other RR types not allowed: "+Str
+ else:
+ DNS[G[0]] = 1
+
+ if SeenDNS:
+ Attrs.append((ldap.MOD_ADD,"dnsZoneEntry",Str));
+ return "DNS Entry added "+Str;
+
+ Attrs.append((ldap.MOD_REPLACE,"dnsZoneEntry",Str));
+ SeenDNS = 1;
+ return "DNS Entry replaced with "+Str;
+
+# Handle an RBL list (mailRBL, mailRHSBL, mailWhitelist)
+def DoRBL(Str,Attrs):
+ Match = re.compile('^mail(rbl|rhsbl|whitelist) ([-a-z0-9.]+)$').match(string.lower(Str))
+ if Match == None:
+ return None
+
+ if Match.group(1) == "rbl":
+ Key = "mailRBL"
+ if Match.group(1) == "rhsbl":
+ Key = "mailRHSBL"
+ if Match.group(1) == "whitelist":
+ Key = "mailWhitelist"
+ Host = Match.group(2)
+
+ global SeenList
+ if SeenList.has_key(Key):
+ Attrs.append((ldap.MOD_ADD,Key,Host))
+ return "%s added %s" % (Key,Host)
+
+ Attrs.append((ldap.MOD_REPLACE,Key,Host))
+ SeenList[Key] = 1;
+ return "%s replaced with %s" % (Key,Host)
+
+# Handle an [almost] arbitary change
+def HandleChange(Reply,DnRecord,Key):
+ global PlainText;
+ Lines = re.split("\n *\r?",PlainText);
+
+ Result = "";
+ Attrs = [];
+ Show = 0;
+ for Line in Lines:
+ Line = string.strip(Line);
+ if Line == "":
+ continue;
+
+ # Try to process a command line
+ Result = Result + "> "+Line+"\n";
+ try:
+ if Line == "show":
+ Show = 1;
+ Res = "OK";
+ else:
+ Res = DoPosition(Line,Attrs) or DoDNS(Line,Attrs,DnRecord) or \
+ DoArbChange(Line,Attrs) or DoSSH(Line,Attrs) or \
+ DoDel(Line,Attrs) or DoRBL(Line,Attrs);
+ except:
+ Res = None;
+ Result = Result + "==> %s: %s\n" %(sys.exc_type,sys.exc_value);
+
+ # Fail, if someone tries to send someone elses signed email to the
+ # daemon then we want to abort ASAP.
+ if Res == None:
+ Result = Result + "Command is not understood. Halted\n";
+ break;
+ Result = Result + Res + "\n";
+
+ # Connect to the ldap server
+ l = ldap.open(LDAPServer);
+ F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r");
+ AccessPass = string.split(string.strip(F.readline())," ");
+ F.close();
+
+ # Modify the record
+ l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]);
+ oldAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+GetAttr(DnRecord,"uid"));
+ if (string.find(GetAttr(oldAttrs[0],"userPassword"),"*LK*") != -1) \
+ or GetAttr(x,"userPassword").startswith("!"):
+ raise Error, "This account is locked";
+ Dn = "uid=" + GetAttr(DnRecord,"uid") + "," + BaseDn;
+ l.modify_s(Dn,Attrs);
+
+ Attribs = "";
+ if Show == 1:
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+GetAttr(DnRecord,"uid"));
+ if len(Attrs) == 0:
+ raise Error, "User not found"
+ Attribs = GPGEncrypt(PrettyShow(Attrs[0])+"\n","0x"+Key[1],Key[4]);
+
+ Subst = {};
+ Subst["__FROM__"] = ChangeFrom;
+ Subst["__EMAIL__"] = EmailAddress(DnRecord);
+ Subst["__ADMIN__"] = ReplyTo;
+ Subst["__RESULT__"] = Result;
+ Subst["__ATTR__"] = Attribs;
+
+ return Reply + TemplateSubst(Subst,open(TemplatesDir+"change-reply","r").read());
+
+# Handle ping handles an email sent to the 'ping' address (ie this program
+# called with a ping argument) It replies with a dump of the public records.
+def HandlePing(Reply,DnRecord,Key):
+ Subst = {};
+ Subst["__FROM__"] = PingFrom;
+ Subst["__EMAIL__"] = EmailAddress(DnRecord);
+ Subst["__LDAPFIELDS__"] = PrettyShow(DnRecord);
+ Subst["__ADMIN__"] = ReplyTo;
+
+ return Reply + TemplateSubst(Subst,open(TemplatesDir+"ping-reply","r").read());
+
+# Handle a change password email sent to the change password address
+# (this program called with the chpass argument)
+def HandleChPass(Reply,DnRecord,Key):
+ # Generate a random password
+ Password = GenPass();
+ Pass = HashPass(Password);
+
+ # Use GPG to encrypt it
+ Message = GPGEncrypt("Your new password is '" + Password + "'\n",\
+ "0x"+Key[1],Key[4]);
+ Password = None;
+
+ if Message == None:
+ raise Error, "Unable to generate the encrypted reply, gpg failed.";
+
+ if (Key[4] == 1):
+ Type = "Your message was encrypted using PGP 2.x\ncompatibility mode.";
+ else:
+ Type = "Your message was encrypted using GPG (OpenPGP)\ncompatibility "\
+ "mode, without IDEA. This message cannot be decoded using PGP 2.x";
+
+ Subst = {};
+ Subst["__FROM__"] = ChPassFrom;
+ Subst["__EMAIL__"] = EmailAddress(DnRecord);
+ Subst["__CRYPTTYPE__"] = Type;
+ Subst["__PASSWORD__"] = Message;
+ Subst["__ADMIN__"] = ReplyTo;
+ Reply = Reply + TemplateSubst(Subst,open(TemplatesDir+"passwd-changed","r").read());
+
+ # Connect to the ldap server
+ l = ldap.open(LDAPServer);
+ F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r");
+ AccessPass = string.split(string.strip(F.readline())," ");
+ F.close();
+ l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]);
+
+ # Check for a locked account
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+GetAttr(DnRecord,"uid"));
+ if (string.find(GetAttr(Attrs[0],"userPassword"),"*LK*") != -1) \
+ or GetAttr(x,"userPassword").startswith("!"):
+ raise Error, "This account is locked";
+
+ # Modify the password
+ Rec = [(ldap.MOD_REPLACE,"userPassword","{crypt}"+Pass)];
+ Dn = "uid=" + GetAttr(DnRecord,"uid") + "," + BaseDn;
+ l.modify_s(Dn,Rec);
+
+ return Reply;
+
+# Start of main program
+
+# Drop messages from a mailer daemon.
+if os.environ.has_key('SENDER') == 0 or len(os.environ['SENDER']) == 0:
+ sys.exit(0);
+
+ErrMsg = "Indeterminate Error";
+ErrType = EX_TEMPFAIL;
+try:
+ # Startup the replay cache
+ ErrType = EX_TEMPFAIL;
+ ErrMsg = "Failed to initialize the replay cache:";
+ RC = ReplayCache(ReplayCacheFile);
+ RC.Clean();
+
+ # Get the email
+ ErrType = EX_PERMFAIL;
+ ErrMsg = "Failed to understand the email or find a signature:";
+ Email = mimetools.Message(sys.stdin,0);
+ Msg = GetClearSig(Email);
+
+ ErrMsg = "Message is not PGP signed:"
+ if string.find(Msg[0],"-----BEGIN PGP SIGNED MESSAGE-----") == -1 and \
+ string.find(Msg[0],"-----BEGIN PGP MESSAGE-----") == -1:
+ raise Error, "No PGP signature";
+
+ # Check the signature
+ ErrMsg = "Unable to check the signature or the signature was invalid:";
+ Res = GPGCheckSig(Msg[0]);
+
+ if Res[0] != None:
+ raise Error, Res[0];
+
+ if Res[3] == None:
+ raise Error, "Null signature text";
+
+ # Extract the plain message text in the event of mime encoding
+ global PlainText;
+ ErrMsg = "Problem stripping MIME headers from the decoded message"
+ if Msg[1] == 1:
+ try:
+ Index = string.index(Res[3],"\n\n") + 2;
+ except ValueError:
+ Index = string.index(Res[3],"\n\r\n") + 3;
+ PlainText = Res[3][Index:];
+ else:
+ PlainText = Res[3];
+
+ # Check the signature against the replay cache
+ ErrMsg = "The replay cache rejected your message. Check your clock!";
+ Rply = RC.Check(Res[1]);
+ if Rply != None:
+ raise Error, Rply;
+
+ # Connect to the ldap server
+ ErrType = EX_TEMPFAIL;
+ ErrMsg = "An error occured while performing the LDAP lookup";
+ global l;
+ l = ldap.open(LDAPServer);
+ l.simple_bind_s("","");
+
+ # Search for the matching key fingerprint
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + Res[2][1]);
+
+ ErrType = EX_PERMFAIL;
+ if len(Attrs) == 0:
+ raise Error, "Key not found"
+ if len(Attrs) != 1:
+ raise Error, "Oddly your key fingerprint is assigned to more than one account.."
+
+ RC.Add(Res[1]);
+
+ # Determine the sender address
+ ErrMsg = "A problem occured while trying to formulate the reply";
+ Sender = Email.getheader("Reply-To");
+ if Sender == None:
+ Sender = Email.getheader("From");
+ if Sender == None:
+ raise Error, "Unable to determine the sender's address";
+
+ # Formulate a reply
+ Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
+ Reply = "To: %s\nReply-To: %s\nDate: %s\n" % (Sender,ReplyTo,Date);
+
+ # Dispatch
+ if sys.argv[1] == "ping":
+ Reply = HandlePing(Reply,Attrs[0],Res[2]);
+ elif sys.argv[1] == "chpass":
+ if string.find(string.strip(PlainText),"Please change my Debian password") != 0:
+ raise Error,"Please send a signed message where the first line of text is the string 'Please change my Debian password'";
+ Reply = HandleChPass(Reply,Attrs[0],Res[2]);
+ elif sys.argv[1] == "change":
+ Reply = HandleChange(Reply,Attrs[0],Res[2]);
+ else:
+ print sys.argv;
+ raise Error, "Incorrect Invokation";
+
+ # Send the message through sendmail
+ ErrMsg = "A problem occured while trying to send the reply";
+ Child = os.popen("/usr/sbin/sendmail -t","w");
+# Child = os.popen("cat","w");
+ Child.write(Reply);
+ if Child.close() != None:
+ raise Error, "Sendmail gave a non-zero return code";
+
+except:
+ # Error Reply Header
+ Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
+ ErrReplyHead = "To: %s\nReply-To: %s\nDate: %s\n" % (os.environ['SENDER'],ReplyTo,Date);
+
+ # Error Body
+ Subst = {};
+ Subst["__ERROR__"] = ErrMsg;
+ Subst["__ADMIN__"] = ReplyTo;
+
+ Trace = "==> %s: %s\n" %(sys.exc_type,sys.exc_value);
+ List = traceback.extract_tb(sys.exc_traceback);
+ if len(List) > 1:
+ Trace = Trace + "Python Stack Trace:\n";
+ for x in List:
+ Trace = Trace + " %s %s:%u: %s\n" %(x[2],x[0],x[1],x[3]);
+
+ Subst["__TRACE__"] = Trace;
+
+ # Try to send the bounce
+ try:
+ ErrReply = TemplateSubst(Subst,open(TemplatesDir+"error-reply","r").read());
+
+ Child = os.popen("/usr/sbin/sendmail -t","w");
+ Child.write(ErrReplyHead);
+ Child.write(ErrReply);
+ if Child.close() != None:
+ raise Error, "Sendmail gave a non-zero return code";
+ except:
+ sys.exit(EX_TEMPFAIL);
+
+ if ErrType != EX_PERMFAIL:
+ sys.exit(ErrType);
+ sys.exit(0);
+
Property changes on: trunk/userdir-ldap/ud-mailgate
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-passchk
===================================================================
--- trunk/userdir-ldap/ud-passchk (rev 0)
+++ trunk/userdir-ldap/ud-passchk 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+# Checks the passwd file to make sure all entries are in the directory
+
+import string, ldap, getopt, sys, os;
+from userdir_ldap import *;
+
+def PassCheck(l,File,HomePrefix):
+ F = open(File,"r");
+
+ # Fetch all the users and generate a map out of them
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=*",\
+ ["uid","uidNumber","gidNumber","loginShell"]);
+ UIDMap = {};
+ for x in Attrs:
+ if x[1].has_key("uid") == 0:
+ continue;
+ UIDMap[x[1]["uid"][0]] = x[1];
+
+ # Iterate over every user in the passwd file
+ while(1):
+ Line = F.readline();
+ if Line == "":
+ break;
+
+ Split = string.split(Line,":");
+ if UIDMap.has_key(Split[0]) == 0:
+ print Line,
+ continue;
+
+ Ats = UIDMap[Split[0]];
+ Miss = [];
+ if Ats.has_key("uidNumber") and Ats["uidNumber"][0] != Split[2]:
+ Miss.append("UID");
+ if Ats.has_key("uidNumber") and Ats["gidNumber"][0] != Split[3]:
+ Miss.append("GID");
+ if Ats.has_key("homeDirectory") and \
+ split[5] != HomePrefix + Split[0]:
+ Miss.append("Home");
+ if len(Miss) != 0:
+ print "mismatch",Split[0],Miss;
+
+# Connect to the ldap server
+l = ldap.open(LDAPServer);
+l.simple_bind_s("","");
+
+PassCheck(l,sys.argv[1],sys.argv[2]);
Property changes on: trunk/userdir-ldap/ud-passchk
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-replicate
===================================================================
--- trunk/userdir-ldap/ud-replicate (rev 0)
+++ trunk/userdir-ldap/ud-replicate 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,98 @@
+#! /bin/sh
+
+# Copyright (c) 1999-2001 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2002-2003,2006 Ryan Murray <rmurray at debian.org>
+# Copyright (c) 2004-2005 Joey Schulze <joey at infodrom.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+set -e
+
+# Without effect on the commandline
+if [ -z "$TERM" -o "$TERM" = "dumb" ]
+then
+ exec > /dev/null 2>&1
+else
+ verbose=-v
+fi
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+export PATH
+HOST=`hostname -f`
+cd /tmp/
+cd /var/lib/misc || cd /var/state/glibc/ || cd /var/db/
+lockfile -r 1 -l 3600 lock
+trap "rm -f lock" exit
+
+case $HOST in
+*cmburns*)
+ udhost=
+ ;;
+*)
+ udhost="sshdist at db:"
+ ;;
+esac
+
+rsync ${verbose} -e ssh -rp "${udhost}/var/cache/userdir-ldap/hosts/$HOST" .
+
+makedb "$HOST/passwd.tdb" -o passwd.db.t
+if [ -s "$HOST/shadow.tdb" ]
+then
+ (umask 027 && makedb "$HOST/shadow.tdb" -o shadow.db.t)
+ chown root.shadow shadow.db.t
+ chmod 0640 shadow.db.t
+ mv -f shadow.db.t shadow.db
+fi
+makedb "$HOST/group.tdb" -o group.db.t
+mv -f passwd.db.t passwd.db
+mv -f group.db.t group.db
+for a in $HOST/ssh-rsa-shadow $HOST/ssh_known_hosts; do
+ ln -sf $a .
+done
+ln -sf `pwd -P`/ssh-rsa-shadow /etc/ssh
+ln -sf `pwd -P`/ssh_known_hosts /etc/ssh
+
+if [ -x /usr/bin/dchroot ]; then
+ CHROOTS=`dchroot --listpaths`
+ for c in $CHROOTS; do
+ if [ -x "$c/usr/bin/makedb" ]
+ then
+
+ test ! -d "$c/var/lib/misc/$HOST" || mkdir -p "$c/var/lib/misc/$HOST"
+
+ rsync -a ${verbose} $HOST/group.tdb $HOST/passwd.tdb $HOST/ssh* "$c/var/lib/misc/$HOST"
+
+ test ! -f "$c/var/lib/misc/$HOST/shadow.tdb" || rm -f "$c/var/lib/misc/$HOST/shadow.tdb"
+ test ! -f "$c/var/lib/misc/shadow.db" || rm -f "$c/var/lib/misc/shadow.db"
+
+ chroot "$c" makedb "/var/lib/misc/$HOST/passwd.tdb" -o /var/lib/misc/passwd.db.t
+ chroot "$c" makedb "/var/lib/misc/$HOST/group.tdb" -o /var/lib/misc/group.db.t
+ mv -f "$c/var/lib/misc/passwd.db.t" "$c/var/lib/misc/passwd.db"
+ mv -f "$c/var/lib/misc/group.db.t" "$c/var/lib/misc/group.db"
+ ln -sf "$HOST/ssh_known_hosts" "$c/var/lib/misc/"
+ ln -sf ../../var/lib/misc/ssh_known_hosts "$c/etc/ssh"
+ fi
+ done
+fi
+
+if [ -d "/etc/exim4" -a -e "$HOST/bsmtp" ]; then
+ if perl -e 'exit !((stat "/etc/exim4/bsmtp")[9] < time()-3600)'; then
+ cp "$HOST/bsmtp" /etc/exim4/bsmtp
+ fi
+fi
+if [ -d "/etc/postfix" -a -f "$HOST/forward-alias" ]; then
+ sed -e 's/:/@debian-community.org/' $HOST/forward-alias > /etc/postfix/debian
+ /usr/sbin/postmap hash:/etc/postfix/debian < /etc/postfix/debian || true
+fi
Property changes on: trunk/userdir-ldap/ud-replicate
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-roleadd
===================================================================
--- trunk/userdir-ldap/ud-roleadd (rev 0)
+++ trunk/userdir-ldap/ud-roleadd 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+
+# Copyright (c) 1999-2000 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2001-2003 James Troup <troup at debian.org>
+# Copyright (c) 2004-2005 Joey Schulze <joey at infodrom.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import string, time, ldap, getopt, sys, os, pwd
+from userdir_ldap import *
+
+# This tries to search for a free UID. There are two possible ways to do
+# this, one is to fetch all the entires and pick the highest, the other
+# is to randomly guess uids until one is free. This uses the former.
+# Regrettably ldap doesn't have an integer attribute comparision function
+# so we can only cut the search down slightly
+
+# [JT] This is broken with Woody LDAP and the Schema; for now just
+# search through all UIDs.
+def GetFreeID(l):
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,
+ "uidNumber=*",["uidNumber"])
+ HighestUID = 0
+ for I in Attrs:
+ ID = int(GetAttr(I,"uidNumber","0"))
+ if ID > HighestUID:
+ HighestUID = ID
+ return HighestUID + 1
+
+# Main starts here
+AdminUser = pwd.getpwuid(os.getuid())[0]
+
+# Process options
+(options, arguments) = getopt.getopt(sys.argv[1:], "u:")
+for (switch, val) in options:
+ if (switch == '-u'):
+ AdminUser = val
+
+l = passwdAccessLDAP(LDAPServer, BaseDn, AdminUser)
+
+while 1:
+ account = raw_input("Who are you going to add? ")
+ if account == "":
+ sys.exit(0)
+
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=" + account)
+ if len(Attrs) == 0:
+ break
+
+ print "That account already exists."
+
+Res = raw_input("Name for GECOS field? ")
+if Res != "":
+ cn = Res
+
+# GID
+Res = raw_input("Group ID Number? ")
+if Res != "":
+ gidNumber = Group2GID(l, Res)
+ if gidNumber == -1:
+ print "Can't figure out which gid %s is" % Res
+ sys.exit(1)
+
+# UID
+uidNumber = GetFreeID(l)
+
+# Now we have all the bits of information.
+print "------------"
+print "Final information collected:"
+print " Username %s:" % cn
+print " Assigned UID:",uidNumber," GID:", gidNumber
+print " GECOS Field: \"%s,,,,\"" % cn
+print " Login Shell: /bin/false"
+Res = raw_input("Continue [No/yes]? ")
+if Res != "yes":
+ print "Not adding %s" % cn
+ sys.exit(1)
+
+# Submit the modification request
+Dn = "uid=" + account + "," + BaseDn
+print "Updating LDAP directory..",
+sys.stdout.flush()
+
+Details = [("uid",account),
+ ("objectClass",
+ ("top","inetOrgPerson","debianAccount","shadowAccount","debianRoleAccount")),
+ ("uidNumber",str(uidNumber)),
+ ("gidNumber",str(gidNumber)),
+ ("gecos",cn+",,,,"),
+ ("loginShell","/bin/false"),
+ ("cn",cn),
+ ("shadowLastChange",str(int(time.time()/24/60/60))),
+ ("shadowMin","0"),
+ ("shadowMax","99999"),
+ ("shadowWarning","7"),
+ ("userPassword","{crypt}*")]
+l.add_s(Dn,Details)
+
+print
Property changes on: trunk/userdir-ldap/ud-roleadd
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-sshlist
===================================================================
--- trunk/userdir-ldap/ud-sshlist (rev 0)
+++ trunk/userdir-ldap/ud-sshlist 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+# This script takes a list of .forward files and generates a list of colon
+# delimited fields for import into a ldap directory. The fields represent
+# the user and their email forwarding.
+#
+# A sample invokation..
+# cd /home
+# find -name ".foward" -maxdepth 2 | mkforwardlist | sort | less
+# Then correct any invalid forward files if possible. After that stash the
+# output in a file, remove the invalid lines and import it.
+#
+# It also understand .qmail type files
+
+import string, re, time, getopt, os, sys, pwd, stat;
+
+SSHAuthSplit = re.compile('^(.* )?(\d+) (\d+) (\d+) ?(.+)$');
+
+while (1):
+ File = string.strip(sys.stdin.readline());
+ if File == "":
+ break;
+
+ # Attempt to determine the UID
+ try:
+ User = pwd.getpwuid(os.stat(File)[stat.ST_UID])[0];
+ except KeyError:
+ print "Invalid0", File;
+ continue;
+
+ # Read the first two non comment non empty lines
+ Forward = open(File,"r");
+ Lines = [];
+ while (1):
+ Line = string.strip(Forward.readline());
+ if Line == "":
+ break;
+ if Line[0] == '#' or Line[0] == '\n':
+ continue;
+ if SSHAuthSplit.match(Line) == None:
+ print "Bad line", File;
+ else:
+ Lines.append(Line);
+
+ for x in Lines:
+ print User + ":",x;
Property changes on: trunk/userdir-ldap/ud-sshlist
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-useradd
===================================================================
--- trunk/userdir-ldap/ud-useradd (rev 0)
+++ trunk/userdir-ldap/ud-useradd 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,285 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+
+# Copyright (c) 1999-2000 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2001-2003 James Troup <troup at debian.org>
+# Copyright (c) 2004 Joey Schulze <joey at infodrom.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import string, re, time, ldap, getopt, sys, os, pwd;
+from userdir_ldap import *;
+from userdir_gpg import *;
+
+# This tries to search for a free UID. There are two possible ways to do
+# this, one is to fetch all the entires and pick the highest, the other
+# is to randomly guess uids until one is free. This uses the former.
+# Regrettably ldap doesn't have an integer attribute comparision function
+# so we can only cut the search down slightly
+
+# [JT] This is broken with Woody LDAP and the Schema; for now just
+# search through all UIDs.
+def GetFreeID(l):
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,
+ "uidNumber=*",["uidNumber"]);
+ HighestUID = 0;
+ for I in Attrs:
+ ID = int(GetAttr(I,"uidNumber","0"));
+ if ID > HighestUID:
+ HighestUID = ID;
+ return HighestUID + 1;
+
+# Main starts here
+AdminUser = pwd.getpwuid(os.getuid())[0];
+
+# Process options
+ForceMail = 0;
+OldGPGKeyRings = GPGKeyRings;
+userdir_gpg.GPGKeyRings = [];
+(options, arguments) = getopt.getopt(sys.argv[1:], "u:ma")
+for (switch, val) in options:
+ if (switch == '-u'):
+ AdminUser = val;
+ elif (switch == '-m'):
+ ForceMail = 1;
+ elif (switch == '-a'):
+ userdir_gpg.GPGKeyRings = OldGPGKeyRings;
+
+l = passwdAccessLDAP(LDAPServer, BaseDn, AdminUser)
+
+# Locate the key of the user we are adding
+SetKeyrings(["/etc/userdir-ldap/keyring/keyring.gpg"])
+while (1):
+ Foo = raw_input("Who are you going to add (for a GPG search)? ");
+ if Foo == "":
+ sys.exit(0);
+
+ Keys = GPGKeySearch(Foo);
+
+ if len(Keys) == 0:
+ print "Sorry, that search did not turn up any keys."
+ print "Has it been added to the keyring already?"
+ continue;
+ if len(Keys) > 1:
+ print "Sorry, more than one key was found, please specify the key to use by\nfingerprint:";
+ for i in Keys:
+ GPGPrintKeyInfo(i);
+ continue;
+
+ print
+ print "A matching key was found:"
+ GPGPrintKeyInfo(Keys[0]);
+ break;
+
+# Crack up the email address from the key into a best guess
+# first/middle/last name
+Addr = SplitEmail(Keys[0][2]);
+(cn,mn,sn) = NameSplit(re.sub('["]','',Addr[0]))
+email = Addr[1] + '@' + Addr[2];
+account = Addr[1];
+
+privsub = email;
+gidNumber = str(DefaultGID);
+uidNumber = 0;
+
+# Decide if we should use IDEA encryption
+UsePGP2 = 0;
+while len(Keys[0][1]) < 40:
+ Res = raw_input("Use PGP2.x compatibility [No/yes]? ");
+ if Res == "yes":
+ UsePGP2 = 1;
+ break;
+ if Res == "":
+ break;
+
+Update = 0
+Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + Keys[0][1]);
+if len(Attrs) != 0:
+ print "*** This key already belongs to",GetAttr(Attrs[0],"uid");
+ account = GetAttr(Attrs[0],"uid");
+ Update = 1
+
+# Try to get a uniq account name
+while 1:
+ if Update == 0:
+ Res = raw_input("Login account [" + account + "]? ");
+ if Res != "":
+ account = Res;
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=" + account);
+ if len(Attrs) == 0:
+ privsub = "%s at debian.org"%(account);
+ break;
+ Res = raw_input("That account already exists, update [No/yes]? ");
+ if Res == "yes":
+ # Update mode, fetch the default values from the directory
+ Update = 1;
+ privsub = GetAttr(Attrs[0],"privateSub");
+ gidNumber = GetAttr(Attrs[0],"gidNumber");
+ uidNumber = GetAttr(Attrs[0],"uidNumber");
+ email = GetAttr(Attrs[0],"emailForward");
+ cn = GetAttr(Attrs[0],"cn");
+ sn = GetAttr(Attrs[0],"sn");
+ mn = GetAttr(Attrs[0],"mn");
+ if privsub == None or privsub == "":
+ privsub = " ";
+ break;
+ else:
+ sys.exit(1)
+
+# Prompt for the first/last name and email address
+Res = raw_input("First name [" + cn + "]? ");
+if Res != "":
+ cn = Res;
+Res = raw_input("Middle name [" + mn + "]? ");
+if Res != "":
+ mn = Res;
+Res = raw_input("Last name [" + sn + "]? ");
+if Res != "":
+ sn = Res;
+Res = raw_input("Email forwarding address [" + email + "]? ");
+if Res != "":
+ email = Res;
+
+# Debian-Private subscription
+Res = raw_input("Subscribe to debian-private (space is none) [" + privsub + "]? ");
+if Res != "":
+ privsub = Res;
+
+# GID
+Res = raw_input("Group ID Number [" + gidNumber + "]? ");
+if Res != "":
+ gidNumber = Group2GID(l, Res);
+
+# UID
+if uidNumber == 0:
+ uidNumber = GetFreeID(l);
+
+# Generate a random password
+if Update == 0 or ForceMail == 1:
+ Password = raw_input("User's Password (Enter for random)? ");
+
+ if Password == "":
+ print "Randomizing and encrypting password"
+ Password = GenPass();
+ Pass = HashPass(Password);
+
+ # Use GPG to encrypt it, pass the fingerprint to ID it
+ CryptedPass = GPGEncrypt("Your new password is '" + Password + "'\n",\
+ "0x"+Keys[0][1],UsePGP2);
+ Password = None;
+ if CryptedPass == None:
+ raise "Error","Password Encryption failed"
+ else:
+ Pass = HashPass(Password);
+ CryptedPass = "Your password has been set to the previously agreed value.";
+else:
+ CryptedPass = "";
+ Pass = None;
+
+# Now we have all the bits of information.
+if mn != "":
+ FullName = "%s %s %s" % (cn,mn,sn);
+else:
+ FullName = "%s %s" % (cn,sn);
+print "------------";
+print "Final information collected:"
+print " %s <%s@%s>:" % (FullName,account,EmailAppend);
+print " Assigned UID:",uidNumber," GID:", gidNumber;
+print " Email forwarded to:",email;
+print " Private Subscription:",privsub;
+print " GECOS Field: \"%s,,,,\"" % (FullName);
+print " Login Shell: /bin/bash";
+print " Key Fingerprint:",Keys[0][1];
+Res = raw_input("Continue [No/yes]? ");
+if Res != "yes":
+ sys.exit(1);
+
+# Initialize the substitution Map
+Subst = {}
+Subst["__REALNAME__"] = FullName;
+Subst["__WHOAMI__"] = pwd.getpwuid(os.getuid())[0];
+Subst["__DATE__"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
+Subst["__LOGIN__"] = account;
+Subst["__PRIVATE__"] = privsub;
+Subst["__EMAIL__"] = email;
+Subst["__PASSWORD__"] = CryptedPass;
+
+# Submit the modification request
+Dn = "uid=" + account + "," + BaseDn;
+print "Updating LDAP directory..",
+sys.stdout.flush();
+
+if Update == 0:
+ # New account
+ Details = [("uid",account),
+ ("objectClass",
+ ("top","inetOrgPerson","debianAccount","shadowAccount","debianDeveloper")),
+ ("uidNumber",str(uidNumber)),
+ ("gidNumber",str(gidNumber)),
+ ("gecos",FullName+",,,,"),
+ ("loginShell","/bin/bash"),
+ ("keyFingerPrint",Keys[0][1]),
+ ("cn",cn),
+ ("sn",sn),
+ ("emailForward",email),
+ ("shadowLastChange",str(int(time.time()/24/60/60))),
+ ("shadowMin","0"),
+ ("shadowMax","99999"),
+ ("shadowWarning","7"),
+ ("userPassword","{crypt}"+Pass)];
+ if mn:
+ Details.append(("mn",mn));
+ if privsub != " ":
+ Details.append(("privateSub",privsub))
+ l.add_s(Dn,Details);
+else:
+ # Modification
+ Rec = [(ldap.MOD_REPLACE,"uidNumber",str(uidNumber)),
+ (ldap.MOD_REPLACE,"gidNumber",str(gidNumber)),
+ (ldap.MOD_REPLACE,"gecos",FullName+",,,,"),
+ (ldap.MOD_REPLACE,"loginShell","/bin/bash"),
+ (ldap.MOD_REPLACE,"keyFingerPrint",Keys[0][1]),
+ (ldap.MOD_REPLACE,"cn",cn),
+ (ldap.MOD_REPLACE,"mn",mn),
+ (ldap.MOD_REPLACE,"sn",sn),
+ (ldap.MOD_REPLACE,"emailForward",email),
+ (ldap.MOD_REPLACE,"shadowLastChange",str(int(time.time()/24/60/60))),
+ (ldap.MOD_REPLACE,"shadowMin","0"),
+ (ldap.MOD_REPLACE,"shadowMax","99999"),
+ (ldap.MOD_REPLACE,"shadowWarning","7"),
+ (ldap.MOD_REPLACE,"shadowInactive",""),
+ (ldap.MOD_REPLACE,"shadowExpire","")];
+ if privsub != " ":
+ Rec.append((ldap.MOD_REPLACE,"privateSub",privsub));
+ if Pass != None:
+ Rec.append((ldap.MOD_REPLACE,"userPassword","{crypt}"+Pass));
+ # Do it
+ l.modify_s(Dn,Rec);
+
+print;
+
+# Abort email sends for an update operation
+if Update == 1 and ForceMail == 0:
+ print "Account is not new, Not sending mails"
+ sys.exit(0);
+
+# Send the Welcome message
+print "Sending Welcome Email"
+Reply = TemplateSubst(Subst,open(TemplatesDir+"/welcome-message-"+gidNumber,"r").read());
+Child = os.popen("/usr/sbin/sendmail -t","w");
+#Child = os.popen("cat","w");
+Child.write(Reply);
+if Child.close() != None:
+ raise Error, "Sendmail gave a non-zero return code";
Property changes on: trunk/userdir-ldap/ud-useradd
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-userimport
===================================================================
--- trunk/userdir-ldap/ud-userimport (rev 0)
+++ trunk/userdir-ldap/ud-userimport 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+
+# Copyright (c) 1999 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2003 James Troup <troup at debian.org>
+# Copyright (c) 2004 Joey Schulze <joey at debian.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Imports passwd, shadow and group files into the directory.
+# You should cleanse the files of anything you do not want to add to the
+# directory.
+#
+# The first step is to call this script to import the passwd file and
+# create all the new entries. This should be done on an empty freshly
+# initialized directory with the rootdn/password set in the server.
+# The command to execute is
+# ldapimport -a -p ~/passwd
+# The -a tells the script to add all the entries it finds, it should be
+# used only once.
+#
+# The next step is to import the shadow file and group, no clensing need be
+# done for
+# this as any entries that do not exist will be ignored (silently)
+# ldapimport -s /etc/shadow -g /etc/group
+#
+
+import string, re, time, ldap, getopt, sys;
+from userdir_ldap import *;
+
+DoAdd = 0;
+WritePasses = 1;
+Passwd = "";
+Shadow = "";
+Group = "";
+
+# This parses a gecos field and returns a tuple containing the new normalized
+# field and the first, middle and last name of the user. Gecos is formed
+# in the standard debian manner with 5 feilds seperated by commas
+def ParseGecos(Field):
+ Gecos = re.split("[,:]",Field);
+ cn = "";
+ mn = "";
+ sn = "";
+ if (len(Gecos) >= 1):
+ (cn,mn,sn) = NameSplit(Gecos[0]);
+
+ # Normalize the gecos field
+ if (len(Gecos) > 5):
+ Gecos = Gecos[0:4];
+ else:
+ while (len(Gecos) < 5):
+ Gecos.append("");
+ else:
+ Gecos = ["","","","",""];
+
+ # Reconstruct the gecos after mauling it
+ Field = Gecos[0] + "," + Gecos[1] + "," + Gecos[2] + "," + \
+ Gecos[3] + "," + Gecos[4];
+ return (Field,cn,mn,sn);
+
+# Check if a number string is really a number
+def CheckNumber(Num):
+ for x in Num:
+ string.index(string.digits,x);
+
+# Read the passwd file into the database
+def DoPasswd(l,Passwd):
+ # Read the passwd file and import it
+ Passwd = open(Passwd,"r");
+ Outstanding = 0;
+ while(1):
+ Line = Passwd.readline();
+ if Line == "":
+ break;
+
+ Split = re.split("[:\n]",Line);
+ (Split[4],cn,mn,sn) = ParseGecos(Split[4]);
+ CheckNumber(Split[2]);
+ CheckNumber(Split[3]);
+ Rec = [(ldap.MOD_REPLACE,"uid",Split[0]),
+ (ldap.MOD_REPLACE,"uidNumber",Split[2]),
+ (ldap.MOD_REPLACE,"gidNumber",Split[3]),
+ (ldap.MOD_REPLACE,"gecos",Split[4]),
+ (ldap.MOD_REPLACE,"homeDirectory",Split[5]),
+ (ldap.MOD_REPLACE,"loginShell",Split[6]),
+ (ldap.MOD_REPLACE,"cn",cn),
+ (ldap.MOD_REPLACE,"mn",mn),
+ (ldap.MOD_REPLACE,"sn",sn)];
+
+ Dn = "uid=" + Split[0] + "," + BaseDn;
+ print "Importing",Dn,
+ sys.stdout.flush();
+
+ # Unfortunately add_s does not take the same args as modify :|
+ if (DoAdd == 1):
+ try:
+ l.add_s(Dn,[("uid",Split[0]),
+ ("objectClass","top"),
+ ("objectClass","account"),
+ ("objectClass","posixAccount"),
+ ("objectClass","shadowAccount"),
+ ("objectClass","debiandeveloper")]);
+ except ldap.ALREADY_EXISTS:
+ print "exists",;
+
+ # Send the modify request
+ l.modify(Dn,Rec);
+ Outstanding = Outstanding + 1;
+ Outstanding = FlushOutstanding(l,Outstanding,1);
+ print "done";
+ FlushOutstanding(l,Outstanding);
+
+# Read the shadow file into the database
+def DoShadow(l,Shadow):
+ # Read the passwd file and import it
+ Shadow = open(Shadow,"r");
+ Outstanding = 0;
+ while(1):
+ Line = Shadow.readline();
+ if Line == "":
+ break;
+
+ Split = re.split("[:\n]",Line);
+
+ # Ignore system accounts with no password, they do not belong in the
+ # directory.
+ if (Split[1] == 'x' or Split[1] == '*'):
+ print "Ignoring system account,",Split[0];
+ continue;
+
+ for x in range(2,8):
+ CheckNumber(Split[x]);
+
+ Rec = [(ldap.MOD_REPLACE,"shadowLastChange",Split[2]),
+ (ldap.MOD_REPLACE,"shadowMin",Split[3]),
+ (ldap.MOD_REPLACE,"shadowMax",Split[4]),
+ (ldap.MOD_REPLACE,"shadowWarning",Split[5]),
+ (ldap.MOD_REPLACE,"shadowInactive",Split[6]),
+ (ldap.MOD_REPLACE,"shadowExpire",Split[7])];
+ if (WritePasses == 1):
+ Rec.append((ldap.MOD_REPLACE,"userPassword","{crypt}"+Split[1]));
+
+ Dn = "uid=" + Split[0] + "," + BaseDn;
+ print "Importing",Dn,
+ sys.stdout.flush();
+
+ # Send the modify request
+ l.modify(Dn,Rec);
+ Outstanding = Outstanding + 1;
+ print "done";
+ Outstanding = FlushOutstanding(l,Outstanding,1);
+ FlushOutstanding(l,Outstanding);
+
+# Read the group file into the database
+def DoGroup(l,Group):
+ # Read the passwd file and import it
+ Group = open(Group,"r");
+ Outstanding = 0;
+ while(1):
+ Line = Group.readline();
+ if Line == "":
+ break;
+
+ # Split up the group information
+ Split = re.split("[:\n]",Line);
+ Members = re.split("[, ]*",Split[3]);
+ CheckNumber(Split[2]);
+
+ # Iterate over the membership list and add the membership information
+ # To the directory
+ Rec = [(ldap.MOD_ADD,"supplementaryGid",Split[0])];
+ Counter = 0;
+ for x in Members:
+ if x == "":
+ continue;
+
+ Dn = "uid=" + x + "," + BaseDn;
+ print "Adding",Dn,"to group",Split[0];
+ Counter = Counter+1;
+
+ # Send the modify request
+ l.modify(Dn,Rec);
+ Outstanding = Outstanding + 1;
+ Outstanding = FlushOutstanding(l,Outstanding,1);
+
+ if Counter == 0:
+ continue;
+
+ Rec = [(ldap.MOD_REPLACE,"gid",Split[0]),
+ (ldap.MOD_REPLACE,"gidNumber",Split[2])];
+
+ Dn = "gid=" + Split[0] + "," + BaseDn;
+ print "Importing",Dn,
+ sys.stdout.flush();
+
+ # Unfortunately add_s does not take the same args as modify :|
+ if (DoAdd == 1):
+ try:
+ l.add_s(Dn,[("gid",Split[0]),
+ ("objectClass","top"),
+ ("objectClass","posixGroup")]);
+ except ldap.ALREADY_EXISTS:
+ print "exists",;
+
+ # Send the modify request
+ l.modify(Dn,Rec);
+ Outstanding = Outstanding + 1;
+ print ".";
+
+ FlushOutstanding(l,Outstanding);
+
+# Process options
+(options, arguments) = getopt.getopt(sys.argv[1:], "ap:s:g:xu:")
+for (switch, val) in options:
+ if (switch == '-a'):
+ DoAdd = 1;
+ if (switch == '-x'):
+ WritePasses = 0;
+ elif (switch == '-p'):
+ Passwd = val
+ elif (switch == '-s'):
+ Shadow = val
+ elif (switch == '-g'):
+ Group = val
+ elif (switch == '-u'):
+ AdminUser = val
+
+# Main program starts here
+
+# Connect to the ldap server
+l = passwdAccessLDAP(LDAPServer, BaseDn, AdminUser)
+
+if (Passwd != ""):
+ DoPasswd(l,Passwd);
+
+if (Shadow != ""):
+ DoShadow(l,Shadow);
+
+if (Group != ""):
+ DoGroup(l,Group);
Property changes on: trunk/userdir-ldap/ud-userimport
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-xearth
===================================================================
--- trunk/userdir-ldap/ud-xearth (rev 0)
+++ trunk/userdir-ldap/ud-xearth 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+# -*- mode: python -*-
+
+# Copyright (c) 1999-2000 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2004 Joey Schulze <joey at debian.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Generate an xearth database from the LDAP entries
+# LDAP entires for lat/long can be in one of 3 different formats
+# 1) Decimal Degrees
+# +-DDD.DDDDDDDDDDDDDDD
+# 2) Degrees Minutes (DGM), common output from GPS units
+# +-DDDMM.MMMMMMMMMMMMM
+# 3) Degrees Minutes Seconds (DGMS)
+# +-DDDMMSS.SSSSSSSSSSS
+# Decimal Degrees is the most basic format, but to have good accuracy it
+# needs a large number of decimals. The other formats are all derived from it:
+# DGM -> DD DDD + (MM.MMMMMMMM)/60
+# DGMS -> DD DDD + (MM + (SS.SSSSSS)/60)/60
+# For Latitude + is North, for Longitude + is East
+
+import string, re, time, ldap, getopt, sys, pwd, os, posix;
+from userdir_ldap import *;
+
+Anon = 0;
+
+# Main program starts here
+User = pwd.getpwuid(posix.getuid())[0];
+BindUser = User;
+(options, arguments) = getopt.getopt(sys.argv[1:], "au:")
+for (switch, val) in options:
+ if (switch == '-u'):
+ User = val;
+ if (switch == '-a'):
+ Anon = 1;
+
+# Connect to the ldap server
+l = passwdAccessLDAP(LDAPServer, BaseDn, User)
+
+Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"latitude=*",\
+ ["uid","cn","mn","sn","latitude","longitude"]);
+
+Attrs.sort();
+
+print "Markers file will be written to markers.dat,",
+sys.stdout.flush();
+F = open("markers.dat","w");
+Count = 0;
+Failed = 0;
+for x in Attrs:
+ if x[1].has_key("latitude") == 0 or x[1].has_key("longitude") == 0:
+ continue;
+ Count = Count + 1;
+ try:
+ if Anon != 0:
+ F.write("%8s %8s \"\"\n"%(DecDegree(GetAttr(x,"latitude"),Anon),DecDegree(GetAttr(x,"longitude"),Anon)));
+ else:
+ F.write("%16s %16s \"%s\" \t# %s\n"%(DecDegree(GetAttr(x,"latitude"),Anon),DecDegree(GetAttr(x,"longitude"),Anon),GetAttr(x,"uid"),EmailAddress(x)));
+ except:
+ Failed = Failed + 1;
+ if Anon == 0:
+ F.write("# Failed %s => %s: %s\n" %(x[0],sys.exc_type,sys.exc_value));
+ else:
+ F.write("# Failed => %s: %s\n" %(sys.exc_type,sys.exc_value));
+F.close();
+print Count,"entries,",Failed,"failures.";
Property changes on: trunk/userdir-ldap/ud-xearth
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/ud-zoneupdate
===================================================================
--- trunk/userdir-ldap/ud-zoneupdate (rev 0)
+++ trunk/userdir-ldap/ud-zoneupdate 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,10 @@
+#!/bin/sh
+set -e
+
+sed -e "s/[1-9].*; Serial.*$/`date +%Y%m%d%H` ; Serial/" < $1 > $1.new
+mv -f $1.new $1
+if [ -e /etc/init.d/bind9 ]; then
+ /etc/init.d/bind9 reload > /dev/null 2>&1
+else
+ /etc/init.d/bind reload > /dev/null 2>&1
+fi
Property changes on: trunk/userdir-ldap/ud-zoneupdate
___________________________________________________________________
Name: svn:executable
+
Added: trunk/userdir-ldap/userdir-ldap.conf
===================================================================
--- trunk/userdir-ldap/userdir-ldap.conf (rev 0)
+++ trunk/userdir-ldap/userdir-ldap.conf 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,62 @@
+# Config file for ldap scripts
+
+# Basic LDAP configuration
+ldaphost = "db.debian-community.org";
+basedn = "ou=users,dc=debian-community,dc=org";
+hostbasedn = "ou=hosts,dc=debian-community,dc=org";
+adminuser = "admin";
+
+# Printable email addresses are shown as: 'cn mn sn <uid at emailappend>'
+emailappend = "debian-community.org";
+
+# For the mail interface
+maildomain = "db.debian-community.org";
+replyto = "admin@" + maildomain;
+pingfrom = "ping@" + maildomain;
+chpassfrom = "chpasswd@" + maildomain;
+changefrom = "change@" + maildomain;
+templatesdir = "/etc/userdir-ldap/templates/";
+replaycachefile = "/var/cache/userdir-ldap/mail/replay";
+#replaycachefile = "/tmp/replay";
+
+# Echelon
+ech_errorlog = "/org/db.debian-community.org/mail/Log/ech-errors.log"
+ech_mainlog = "/org/db.debian-community.org/mail/Log/ech.log"
+
+# User properties
+defaultgid = 100;
+
+# For the output generator
+generateconf = "/etc/userdir-ldap/generate.conf"
+generatedir = "/var/cache/userdir-ldap/hosts/";
+passdir = "/etc/userdir-ldap/";
+
+# GPG Things
+gpg = "/usr/bin/gpg";
+keyrings = "/etc/userdir-ldap/keyring/keyring.gpg";
+
+# For the WEB interface
+webloginhtml = "login.html";
+websearchhtml = "searchform.html";
+websearchresulthtml = "searchresults.html";
+webupdatehtml = "update.html";
+hosthtml = "hostinfo.html";
+
+webloginurl = "login.cgi";
+websearchurl = "search.cgi";
+webupdateurl = "update.cgi";
+
+weblogfile = "/var/log/userldap-web.log";
+
+# When should authentication tokens expire?
+authexpires = 600;
+
+# How many bytes to use for the blowfish key (max = 56 (448 bits))
+blowfishkeylen = 10;
+
+# Change this!
+authtokenpath = "/var/cache/userdir-ldap/web-cookies";
+countrylist = "/var/www/userdir-ldap/domains.tab";
+
+# Finger daemon settings
+# use_inetd = 1;
Added: trunk/userdir-ldap/userdir_gpg.py
===================================================================
--- trunk/userdir-ldap/userdir_gpg.py (rev 0)
+++ trunk/userdir-ldap/userdir_gpg.py 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,538 @@
+# Copyright (c) 1999-2001 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2005 Joey Schulze <joey at infodrom.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# GPG issues -
+# - gpgm with a status FD being fed keymaterial and other interesting
+# things does nothing.. If it could ID the keys and stuff over the
+# status-fd I could decide what to do with them. I would also like it
+# to report which key it selected for encryption (also if there
+# were multi-matches..) Being able to detect a key-revoke cert would be
+# good too.
+# - I would like to be able to fetch the comment and version fields from the
+# packets so I can tell if a signature is made by pgp2 to enable the
+# pgp2 encrypting mode.
+
+import string, mimetools, multifile, sys, StringIO, os, tempfile, re;
+import rfc822, time, fcntl, anydbm
+
+# General GPG options
+GPGPath = "gpg"
+# "--load-extension","rsa",
+GPGBasicOptions = [
+ "--no-options",
+ "--batch",
+ "--no-default-keyring",
+ "--secret-keyring", "/dev/null",
+ "--always-trust"];
+GPGKeyRings = [];
+GPGSigOptions = ["--output","-"];
+GPGSearchOptions = ["--dry-run","--with-colons","--fingerprint"];
+GPGEncryptOptions = ["--output","-","--quiet","--always-trust",\
+ "--armor","--encrypt"];
+GPGEncryptPGP2Options = ["--set-filename","","--rfc1991",\
+ "--load-extension","idea",\
+ "--cipher-algo","idea"] + GPGEncryptOptions;
+
+# Replay cutoff times in seconds
+CleanCutOff = 7*24*60*60;
+AgeCutOff = 4*24*60*60;
+FutureCutOff = 3*24*60*60;
+
+# Set the keyrings, the input is a list of keyrings
+def SetKeyrings(Rings):
+ for x in Rings:
+ GPGKeyRings.append("--keyring");
+ GPGKeyRings.append(x);
+
+# GetClearSig takes an un-seekable email message stream (mimetools.Message)
+# and returns a standard PGP '---BEGIN PGP SIGNED MESSAGE---' bounded
+# clear signed text.
+# If this is fed to gpg/pgp it will verify the signature and spit out the
+# signed text component. Email headers and PGP mime (RFC 2015) is understood
+# but no effort is made to cull any information outside the PGP boundaries
+# Please note that in the event of a mime decode the mime headers will be
+# present in the signature text! The return result is a tuple, the first
+# element is the text itself the second is a mime flag indicating if the
+# result should be mime processed after sig checking.
+#
+# Paranoid will check the message text to make sure that all the plaintext is
+# in fact signed (bounded by a PGP packet)
+def GetClearSig(Msg,Paranoid = 0):
+ Error = 'MIME Error';
+ # See if this is a MIME encoded multipart signed message
+ if Msg.gettype() == "multipart/signed":
+ Boundary = Msg.getparam("boundary");
+ if not Boundary:
+ raise Error, "multipart/* without a boundary parameter";
+
+ # Create the multipart handler. Regrettably their implementation
+ # Needs seeking..
+ SkMessage = StringIO.StringIO();
+ SkMessage.write(Msg.fp.read());
+ SkMessage.seek(0);
+ mf = multifile.MultiFile(SkMessage)
+ mf.push(Msg.getparam("boundary"));
+
+ # Check the first bit of the message..
+ if Paranoid != 0:
+ Pos = mf.tell();
+ while 1:
+ x = mf.readline();
+ if not x: break;
+ if len(string.strip(x)) != 0:
+ raise Error,"Unsigned text in message (at start)";
+ mf.seek(Pos);
+
+ # Get the first part of the multipart message
+ if not mf.next():
+ raise Error, "Invalid pgp/mime encoding [no section]";
+
+ # Get the part as a safe seekable stream
+ Signed = StringIO.StringIO();
+ Signed.write(mf.read());
+ InnerMsg = mimetools.Message(Signed);
+
+ # Make sure it is the right type
+ if InnerMsg.gettype() != "text/plain":
+ raise Error, "Invalid pgp/mime encoding [wrong plaintext type]";
+
+ # Get the next part of the multipart message
+ if not mf.next():
+ raise Error, "Invalid pgp/mime encoding [no section]";
+ InnerMsg = mimetools.Message(mf);
+ if InnerMsg.gettype() != "application/pgp-signature":
+ raise Error, "Invalid pgp/mime encoding [wrong signature type]";
+ Signature = string.joinfields(mf.readlines(),'');
+
+ # Check the last bit of the message..
+ if Paranoid != 0:
+ mf.pop();
+ Pos = mf.tell();
+ while 1:
+ x = mf.readline();
+ if not x: break;
+ if len(string.strip(x)) != 0:
+ raise Error,"Unsigned text in message (at end)";
+ mf.seek(Pos);
+
+ # Append the PGP boundary header and the signature text to re-form the
+ # original signed block [needs to convert to \r\n]
+ Output = "-----BEGIN PGP SIGNED MESSAGE-----\r\n";
+ # Semi-evil hack to get the proper hash type inserted in the message
+ if Msg.getparam('micalg') != None:
+ Output = Output + "Hash: MD5,SHA1,%s\r\n"%(string.upper(Msg.getparam('micalg')[4:]));
+ Output = Output + "\r\n";
+ Output = Output + string.replace(Signed.getvalue(),"\n-","\n- -") + Signature;
+ return (Output,1);
+ else:
+ if Paranoid == 0:
+ # Just return the message body
+ return (string.joinfields(Msg.fp.readlines(),''),0);
+
+ Body = "";
+ State = 1;
+ for x in Msg.fp.readlines():
+ Body = Body + x;
+ Tmp = string.strip(x);
+ if len(Tmp) == 0:
+ continue;
+
+ # Leading up to the signature
+ if State == 1:
+ if Tmp == "-----BEGIN PGP SIGNED MESSAGE-----":
+ State = 2;
+ else:
+ raise Error,"Unsigned text in message (at start)";
+ continue;
+
+ # In the signature plain text
+ if State == 2:
+ if Tmp == "-----BEGIN PGP SIGNATURE-----":
+ State = 3;
+ continue;
+
+ # In the signature
+ if State == 3:
+ if Tmp == "-----END PGP SIGNATURE-----":
+ State = 4;
+ continue;
+
+ # Past the end
+ if State == 4:
+ raise Error,"Unsigned text in message (at end)";
+ return (Body,0);
+
+# This opens GPG in 'write filter' mode. It takes Message and sends it
+# to GPGs standard input, pipes the standard output to a temp file along
+# with the status FD. The two tempfiles are passed to GPG by fd and are
+# accessible from the filesystem for only a short period. Message may be
+# None in which case GPGs stdin is closed directly after forking. This
+# is best used for sig checking and encryption.
+# The return result is a tuple (Exit,StatusFD,OutputFD), both fds are
+# fully rewound and readable.
+def GPGWriteFilter(Program,Options,Message):
+ # Make sure the tmp files we open are unreadable, there is a short race
+ # between when the temp file is opened and unlinked that some one else
+ # could open it or hard link it. This is not important however as no
+ # Secure data is fed through the temp files.
+ OldMask = os.umask(0777);
+ try:
+ Output = tempfile.TemporaryFile("w+b");
+ GPGText = tempfile.TemporaryFile("w+b");
+ InPipe = os.pipe();
+ InPipe = [InPipe[0],InPipe[1]];
+ finally:
+ os.umask(OldMask);
+
+ try:
+ # Fork off GPG in a horrible way, we redirect most of its FDs
+ # Input comes from a pipe and its two outputs are spooled to unlinked
+ # temp files (ie private)
+ Child = os.fork();
+ if Child == 0:
+ try:
+ os.dup2(InPipe[0],0);
+ os.close(InPipe[1]);
+ os.dup2(Output.fileno(),1);
+ os.dup2(os.open("/dev/null",os.O_WRONLY),2);
+ os.dup2(GPGText.fileno(),3);
+
+ Args = [Program,"--status-fd","3"] + GPGBasicOptions + GPGKeyRings + Options
+ os.execvp(Program,Args);
+ finally:
+ os._exit(100);
+
+ # Get rid of the other end of the pipe
+ os.close(InPipe[0])
+ InPipe[0] = -1;
+
+ # Send the message
+ if Message != None:
+ try:
+ os.write(InPipe[1],Message);
+ except:
+ pass;
+ os.close(InPipe[1]);
+ InPipe[1] = -1;
+
+ # Wait for GPG to finish
+ Exit = os.waitpid(Child,0);
+
+ # Create the result including the new readable file descriptors
+ Result = (Exit,os.fdopen(os.dup(GPGText.fileno()),"r"), \
+ os.fdopen(os.dup(Output.fileno()),"r"));
+ Result[1].seek(0);
+ Result[2].seek(0);
+
+ Output.close();
+ GPGText.close();
+ return Result;
+ finally:
+ if InPipe[0] != -1:
+ os.close(InPipe[0]);
+ if InPipe[1] != -1:
+ os.close(InPipe[1]);
+ Output.close();
+ GPGText.close();
+
+# This takes a text passage, a destination and a flag indicating the
+# compatibility to use and returns an encrypted message to the recipient.
+# It is best if the recipient is specified using the hex key fingerprint
+# of the target, ie 0x64BE1319CCF6D393BF87FF9358A6D4EE
+def GPGEncrypt(Message,To,PGP2):
+ # Encrypt using the PGP5 block encoding and with the PGP5 option set.
+ # This will handle either RSA or DSA/DH asymetric keys.
+ # In PGP2 compatible mode IDEA and rfc1991 encoding are used so that
+ # PGP2 can read the result. RSA keys do not need PGP2 to be set, as GPG
+ # can read a message encrypted with blowfish and RSA.
+ if PGP2 == 0:
+ try:
+ Res = None;
+ Res = GPGWriteFilter(GPGPath,["-r",To]+GPGEncryptOptions,Message);
+ if Res[0][1] != 0:
+ return None;
+ Text = Res[2].read();
+ return Text;
+ finally:
+ if Res != None:
+ Res[1].close();
+ Res[2].close();
+ else:
+ # We have to call gpg with a filename or it will create a packet that
+ # PGP2 cannot understand.
+ TmpName = tempfile.mktemp();
+ try:
+ Res = None;
+ MsgFile = open(TmpName,"wc");
+ MsgFile.write(Message);
+ MsgFile.close();
+ Res = GPGWriteFilter(GPGPath,["-r",To]+GPGEncryptPGP2Options+[TmpName],None);
+ if Res[0][1] != 0:
+ return None;
+ Text = Res[2].read();
+ return Text;
+ finally:
+ try:
+ os.unlink(TmpName);
+ except:
+ pass;
+ if Res != None:
+ Res[1].close();
+ Res[2].close();
+
+# Checks the signature of a standard PGP message, like that returned by
+# GetClearSig. It returns a large tuple of the form:
+# (Why,(SigId,Date,KeyFinger),(KeyID,KeyFinger,Owner,Length,PGP2),Text);
+# Where,
+# Why = None if checking was OK otherwise an error string.
+# SigID+Date represent something suitable for use in a replay cache. The
+# date is returned as the number of seconds since the UTC epoch.
+# The keyID is also in this tuple for easy use of the replay
+# cache
+# KeyID, KeyFinger and Owner represent the Key used to sign this message
+# PGP2 indicates if the message was created using PGP 2.x
+# Text is the full byte-for-byte signed text in a string
+def GPGCheckSig(Message):
+ Res = None;
+ try:
+ Res = GPGWriteFilter(GPGPath,GPGSigOptions,Message);
+ Exit = Res[0];
+
+ # Parse the GPG answer
+ Strm = Res[1];
+ GoodSig = 0;
+ SigId = None;
+ KeyFinger = None;
+ KeyID = None;
+ Owner = None;
+ Date = None;
+ Why = None;
+ TagMap = {};
+ while(1):
+ # Grab and split up line
+ Line = Strm.readline();
+ if Line == "":
+ break;
+ Split = re.split("[ \n]",Line);
+ if Split[0] != "[GNUPG:]":
+ continue;
+
+ # We only process the first occurance of any tag.
+ if TagMap.has_key(Split[1]):
+ continue;
+ TagMap[Split[1]] = None;
+
+ # Good signature response
+ if Split[1] == "GOODSIG":
+ # Just in case GPG returned a bad signal before this (bug?)
+ if Why == None:
+ GoodSig = 1;
+ KeyID = Split[2];
+ Owner = string.join(Split[3:],' ');
+
+ # Bad signature response
+ if Split[1] == "BADSIG":
+ GoodSig = 0;
+ KeyID = Split[2];
+ Why = "Verification of signature failed";
+
+ # Bad signature response
+ if Split[1] == "ERRSIG":
+ GoodSig = 0;
+ KeyID = Split[2];
+ if len(Split) <= 7:
+ Why = "GPG error, ERRSIG status tag is invalid";
+ elif Split[7] == '9':
+ Why = "Unable to verify signature, signing key missing.";
+ elif Split[7] == '4':
+ Why = "Unable to verify signature, unknown packet format/key type";
+ else:
+ Why = "Unable to verify signature, unknown reason";
+
+ if Split[1] == "NO_PUBKEY":
+ GoodSig = 0;
+ Why = "Unable to verify signature, signing key missing.";
+
+ # Expired signature
+ if Split[1] == "SIGEXPIRED" or Split[1] == "EXPSIG":
+ GoodSig = 0;
+ Why = "Signature has expired";
+
+ # Revoked key
+ if Split[1] == "KEYREVOKED" or Split[1] == "REVKEYSIG":
+ GoodSig = 0;
+ Why = "Signing key has been revoked";
+
+ # Corrupted packet
+ if Split[1] == "NODATA" or Split[1] == "BADARMOR":
+ GoodSig = 0;
+ Why = "The packet was corrupted or contained no data";
+
+ # Signature ID
+ if Split[1] == "SIG_ID":
+ SigId = Split[2];
+ Date = long(Split[4]);
+
+ # ValidSig has the key finger print
+ if Split[1] == "VALIDSIG":
+ # Use the fingerprint of the primary key when available
+ if len(Split) >= 12:
+ KeyFinger = Split[11];
+ else:
+ KeyFinger = Split[2];
+
+ # Reopen the stream as a readable stream
+ Text = Res[2].read();
+
+ # A gpg failure is an automatic bad signature
+ if Exit[1] != 0 and Why == None:
+ GoodSig = 0;
+ Why = "GPG execution failed " + str(Exit[0]);
+
+ if GoodSig == 0 and (Why == None or len(Why) == 0):
+ Why = "Checking Failed";
+
+ # Try to decide if this message was sent using PGP2
+ PGP2Message = 0;
+ if (re.search("-----[\n\r][\n\r]?Version: 2\\.",Message) != None):
+ PGP2Message = 1;
+
+ return (Why,(SigId,Date,KeyFinger),(KeyID,KeyFinger,Owner,0,PGP2Message),Text);
+ finally:
+ if Res != None:
+ Res[1].close();
+ Res[2].close();
+
+# Search for keys given a search pattern. The pattern is passed directly
+# to GPG for processing. The result is a list of tuples of the form:
+# (KeyID,KeyFinger,Owner,Length)
+# Which is similar to the key identification tuple output by GPGChecksig
+def GPGKeySearch(SearchCriteria):
+ Args = [GPGPath] + GPGBasicOptions + GPGKeyRings + GPGSearchOptions + \
+ [SearchCriteria," 2> /dev/null"]
+ Strm = None;
+ Result = [];
+ Owner = "";
+ KeyID = "";
+ Hits = {};
+
+ dir = os.path.expanduser("~/.gnupg")
+ if not os.path.isdir(dir):
+ os.mkdir(dir, 0700)
+
+ try:
+ Strm = os.popen(string.join(Args," "),"r");
+
+ while(1):
+ # Grab and split up line
+ Line = Strm.readline();
+ if Line == "":
+ break;
+ Split = string.split(Line,":");
+
+ # Store some of the key fields
+ if Split[0] == 'pub':
+ KeyID = Split[4];
+ Owner = Split[9];
+ Length = int(Split[2]);
+
+ # Output the key
+ if Split[0] == 'fpr':
+ if Hits.has_key(Split[9]):
+ continue;
+ Hits[Split[9]] = None;
+ Result.append( (KeyID,Split[9],Owner,Length) );
+ finally:
+ if Strm != None:
+ Strm.close();
+ return Result;
+
+# Print the available key information in a format similar to GPG's output
+# We do not know the values of all the feilds so they are just replaced
+# with ?'s
+def GPGPrintKeyInfo(Ident):
+ print "pub %u?/%s ??-??-?? %s" % (Ident[3],Ident[0][-8:],Ident[2]);
+ print " key fingerprint = 0x%s" % (Ident[1]);
+
+# Perform a substition of template
+def TemplateSubst(Map,Template):
+ for x in Map.keys():
+ Template = string.replace(Template,x,Map[x]);
+ return Template;
+
+# The replay class uses a python DB (BSD db if avail) to implement
+# protection against replay. Replay is an attacker capturing the
+# plain text signed message and sending it back to the victim at some
+# later date. Each signature has a unique signature ID (and signing
+# Key Fingerprint) as well as a timestamp. The first stage of replay
+# protection is to ensure that the timestamp is reasonable, in particular
+# not to far ahead or too far behind the current system time. The next
+# step is to look up the signature + key fingerprint in the replay database
+# and determine if it has been recived. The database is cleaned out
+# periodically and old signatures are discarded. By using a timestamp the
+# database size is bounded to being within the range of the allowed times
+# plus a little fuzz. The cache is serialized with a flocked lock file
+class ReplayCache:
+ def __init__(self,Database):
+ self.Lock = open(Database + ".lock","w",0600);
+ fcntl.flock(self.Lock.fileno(),fcntl.LOCK_EX);
+ self.DB = anydbm.open(Database,"c",0600);
+ self.CleanCutOff = CleanCutOff;
+ self.AgeCutOff = AgeCutOff;
+ self.FutureCutOff = FutureCutOff;
+
+ # Close the cache and lock
+ def __del__(self):
+ self.close();
+ def close(self):
+ self.DB.close();
+ self.Lock.close();
+
+ # Clean out any old signatures
+ def Clean(self):
+ CutOff = time.time() - self.CleanCutOff;
+ for x in self.DB.keys():
+ if int(self.DB[x]) <= CutOff:
+ del self.DB[x];
+
+ # Check a signature. 'sig' is a 3 tuple that has the sigId, date and
+ # key ID
+ def Check(self,Sig):
+ if Sig[0] == None or Sig[1] == None or Sig[2] == None:
+ return "Invalid signature";
+ if int(Sig[1]) > time.time() + self.FutureCutOff:
+ return "Signature has a time too far in the future";
+ if self.DB.has_key(Sig[0] + '-' + Sig[2]):
+ return "Signature has already been received";
+ if int(Sig[1]) < time.time() - self.AgeCutOff:
+ return "Signature has passed the age cut off ";
+ # + str(int(Sig[1])) + ',' + str(time.time()) + "," + str(Sig);
+ return None;
+
+ # Add a signature, the sig is the same as is given to Check
+ def Add(self,Sig):
+ if Sig[0] == None or Sig[1] == None:
+ raise RuntimeError,"Invalid signature";
+ if Sig[1] < time.time() - self.CleanCutOff:
+ return;
+ Key = Sig[0] + '-' + Sig[2]
+ if self.DB.has_key(Key):
+ if int(self.DB[Key]) < Sig[1]:
+ self.DB[Key] = str(int(Sig[1]));
+ else:
+ self.DB[Key] = str(int(Sig[1]));
+
Added: trunk/userdir-ldap/userdir_ldap.py
===================================================================
--- trunk/userdir-ldap/userdir_ldap.py (rev 0)
+++ trunk/userdir-ldap/userdir_ldap.py 2008-07-24 11:10:47 UTC (rev 312)
@@ -0,0 +1,429 @@
+# Copyright (c) 1999-2000 Jason Gunthorpe <jgg at debian.org>
+# Copyright (c) 2001-2003 Ryan Murray <rmurray at debian.org>
+# Copyright (c) 2004-2005 Joey Schulze <joey at infodrom.org>
+#
+# This program 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 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Some routines and configuration that are used by the ldap progams
+import termios, re, string, imp, ldap, sys, crypt, rfc822;
+import userdir_gpg
+
+try:
+ File = open("/etc/userdir-ldap/userdir-ldap.conf");
+except:
+ File = open("userdir-ldap.conf");
+ConfModule = imp.load_source("userdir_config","/etc/userdir-ldap.conf",File);
+File.close();
+
+# Cheap hack
+BaseDn = ConfModule.basedn;
+HostBaseDn = ConfModule.hostbasedn;
+LDAPServer = ConfModule.ldaphost;
+EmailAppend = ConfModule.emailappend;
+AdminUser = ConfModule.adminuser;
+GenerateDir = ConfModule.generatedir;
+GenerateConf = ConfModule.generateconf;
+DefaultGID = ConfModule.defaultgid;
+TemplatesDir = ConfModule.templatesdir;
+PassDir = ConfModule.passdir;
+Ech_ErrorLog = ConfModule.ech_errorlog;
+Ech_MainLog = ConfModule.ech_mainlog;
+
+# Break up the keyring list
+userdir_gpg.SetKeyrings(string.split(ConfModule.keyrings,":"));
+
+# This is a list of common last-name prefixes
+LastNamesPre = {"van": None, "von": None, "le": None, "de": None, "di": None};
+
+# This is a list of common groups on Debian hosts
+DebianGroups = {
+ "users": 100,
+ "Debian": 800,
+ "guest": 60000,
+ "nogroup": 65534
+ }
+
+# SSH Key splitting. The result is:
+# (options,size,modulous,exponent,comment)
+SSHAuthSplit = re.compile('^(.* )?(\d+) (\d+) (\d+) ?(.+)$');
+SSH2AuthSplit = re.compile('^(.* )?ssh-(dss|rsa) ([a-zA-Z0-9=/+]+) ?(.+)$');
+#'^([^\d](?:[^ "]+(?:".*")?)*)? ?(\d+) (\d+) (\d+) (.+)$');
+
+AddressSplit = re.compile("(.*).*<([^@]*)@([^>]*)>");
+
+# Safely get an attribute from a tuple representing a dn and an attribute
+# list. It returns the first attribute if there are multi.
+def GetAttr(DnRecord,Attribute,Default = ""):
+ try:
+ return DnRecord[1][Attribute][0];
+ except IndexError:
+ return Default;
+ except KeyError:
+ return Default;
+ return Default;
+
+# Return a printable email address from the attributes.
+def EmailAddress(DnRecord):
+ cn = GetAttr(DnRecord,"cn");
+ sn = GetAttr(DnRecord,"sn");
+ uid = GetAttr(DnRecord,"uid");
+ if cn == "" and sn == "":
+ return "<" + uid + "@" + EmailAppend + ">";
+ return cn + " " + sn + " <" + uid + "@" + EmailAppend + ">"
+
+# Show a dump like ldapsearch
+def PrettyShow(DnRecord):
+ Result = "";
+ List = DnRecord[1].keys();
+ List.sort();
+ for x in List:
+ Rec = DnRecord[1][x];
+ for i in Rec:
+ Result = Result + "%s: %s\n" % (x,i);
+ return Result[:-1];
+
+# Function to prompt for a password
+def getpass(prompt = "Password: "):
+ import termios, sys;
+ fd = sys.stdin.fileno();
+ old = termios.tcgetattr(fd);
+ new = termios.tcgetattr(fd);
+ new[3] = new[3] & ~termios.ECHO; # lflags
+ try:
+ termios.tcsetattr(fd, termios.TCSADRAIN, new);
+ try:
+ passwd = raw_input(prompt);
+ except KeyboardInterrupt:
+ termios.tcsetattr(fd, termios.TCSADRAIN, old);
+ print
+ sys.exit(0)
+ except EOFError:
+ passwd = ""
+ finally:
+ termios.tcsetattr(fd, termios.TCSADRAIN, old);
+ print;
+ return passwd;
+
+def passwdAccessLDAP(LDAPServer, BaseDn, AdminUser):
+ """
+ Ask for the AdminUser's password and connect to the LDAP server.
+ Returns the connection handle.
+ """
+ print "Accessing LDAP directory as '" + AdminUser + "'";
+ while (1):
+ Password = getpass(AdminUser + "'s password: ");
+
+ if len(Password) == 0:
+ sys.exit(0)
+
+ l = ldap.open(LDAPServer);
+ UserDn = "uid=" + AdminUser + "," + BaseDn;
+
+ # Connect to the ldap server
+ try:
+ l.simple_bind_s(UserDn,Password);
+ except ldap.INVALID_CREDENTIALS:
+ continue
+ break
+ return l
+
+# Split up a name into multiple components. This tries to best guess how
+# to split up a name
+def NameSplit(Name):
+ Words = re.split(" ",string.strip(Name));
+
+ # Insert an empty middle name
+ if (len(Words) == 2):
+ Words.insert(1,"");
+ if (len(Words) < 2):
+ Words.append("");
+
+ # Put a dot after any 1 letter words, must be an initial
+ for x in range(0,len(Words)):
+ if len(Words[x]) == 1:
+ Words[x] = Words[x] + '.';
+
+ # If a word starts with a -, ( or [ we assume it marks the start of some
+ # Non-name information and remove the remainder of the string
+ for x in range(0,len(Words)):
+ if len(Words[x]) != 0 and (Words[x][0] == '-' or \
+ Words[x][0] == '(' or Words[x][0] == '['):
+ Words = Words[0:x];
+ break;
+
+ # Merge any of the middle initials
+ while len(Words) > 2 and len(Words[2]) == 2 and Words[2][1] == '.':
+ Words[1] = Words[1] + Words[2];
+ del Words[2];
+
+ while len(Words) < 2:
+ Words.append('');
+
+ # Merge any of the last name prefixes into one big last name
+ while LastNamesPre.has_key(string.lower(Words[-2])):
+ Words[-1] = Words[-2] + " " + Words[-1];
+ del Words[-2];
+
+ # Fix up a missing middle name after lastname globbing
+ if (len(Words) == 2):
+ Words.insert(1,"");
+
+ # If the name is multi-word then we glob them all into the last name and
+ # do not worry about a middle name
+ if (len(Words) > 3):
+ Words[2] = string.join(Words[1:]);
+ Words[1] = "";
+
+ return (string.strip(Words[0]),string.strip(Words[1]),string.strip(Words[2]));
+
+# Compute a random password using /dev/urandom
+def GenPass():
+ # Generate a 10 character random string
+ SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/.";
+ Rand = open("/dev/urandom");
+ Password = "";
+ for i in range(0,15):
+ Password = Password + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)];
+ return Password;
+
+# Compute the MD5 crypted version of the given password
+def HashPass(Password):
+ # Hash it telling glibc to use the MD5 algorithm - if you dont have
+ # glibc then just change Salt = "$1$" to Salt = "";
+ SaltVals = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/.";
+ Salt = "$1$";
+ Rand = open("/dev/urandom");
+ for x in range(0,10):
+ Salt = Salt + SaltVals[ord(Rand.read(1)[0]) % len(SaltVals)];
+ Pass = crypt.crypt(Password,Salt);
+ if len(Pass) < 14:
+ raise "Password Error", "MD5 password hashing failed, not changing the password!";
+ return Pass;
+
+# Sync with the server, we count the number of async requests that are pending
+# and make sure result has been called that number of times
+def FlushOutstanding(l,Outstanding,Fast=0):
+ # Sync with the remote end
+ if Fast == 0:
+ print "Waiting for",Outstanding,"requests:",
+ while (Outstanding > 0):
+ try:
+ if Fast == 0 or Outstanding > 50:
+ sys.stdout.write(".",);
+ sys.stdout.flush();
+ if (l.result(ldap.RES_ANY,1) != (None,None)):
+ Outstanding = Outstanding - 1;
+ else:
+ if (l.result(ldap.RES_ANY,1,0) != (None,None)):
+ Outstanding = Outstanding - 1;
+ else:
+ break;
+ except ldap.TYPE_OR_VALUE_EXISTS:
+ Outstanding = Outstanding - 1;
+ except ldap.NO_SUCH_ATTRIBUTE:
+ Outstanding = Outstanding - 1;
+ except ldap.NO_SUCH_OBJECT:
+ Outstanding = Outstanding - 1;
+ if Fast == 0:
+ print;
+ return Outstanding;
+
+# Convert a lat/long attribute into Decimal degrees
+def DecDegree(Posn,Anon=0):
+ Parts = re.match('[-+]?(\d*)\\.?(\d*)',Posn).groups();
+ Val = string.atof(Posn);
+
+ if (abs(Val) >= 1806060.0):
+ raise ValueError,"Too Big";
+
+ # Val is in DGMS
+ if abs(Val) >= 18060.0 or len(Parts[0]) > 5:
+ Val = Val/100.0;
+ Secs = Val - long(Val);
+ Val = long(Val)/100.0;
+ Min = Val - long(Val);
+ Val = long(Val) + (Min*100.0 + Secs*100.0/60.0)/60.0;
+
+ # Val is in DGM
+ elif abs(Val) >= 180 or len(Parts[0]) > 3:
+ Val = Val/100.0;
+ Min = Val - long(Val);
+ Val = long(Val) + Min*100.0/60.0;
+
+ if Anon != 0:
+ Str = "%3.2f"%(Val);
+ else:
+ Str = str(Val);
+ if Val >= 0:
+ return "+" + Str;
+ return Str;
+
+def FormatSSH2Auth(Str):
+ Match = SSH2AuthSplit.match(Str);
+ if Match == None:
+ return "<unknown format>";
+ G = Match.groups();
+
+ if G[0] == None:
+ return "ssh-%s %s..%s %s"%(G[1],G[2][:8],G[2][-8:],G[3]);
+ return "%s ssh-%s %s..%s %s"%(G[0],G[1],G[2][:8],G[2][-8:],G[3]);
+
+def FormatSSHAuth(Str):
+ Match = SSHAuthSplit.match(Str);
+ if Match == None:
+ return FormatSSH2Auth(Str);
+ G = Match.groups();
+
+ # No options
+ if G[0] == None:
+ return "%s %s %s..%s %s"%(G[1],G[2],G[3][:8],G[3][-8:],G[4]);
+ return "%s %s %s %s..%s %s"%(G[0],G[1],G[2],G[3][:8],G[3][-8:],G[4]);
+
+def FormatPGPKey(Str):
+ Res = "";
+
+ # PGP 2.x Print
+ if (len(Str) == 32):
+ I = 0;
+ while (I < len(Str)):
+ if I+2 == 32/2:
+ Res = "%s %s%s "%(Res,Str[I],Str[I+1]);
+ else:
+ Res = "%s%s%s "%(Res,Str[I],Str[I+1]);
+ I = I + 2;
+ elif (len(Str) == 40):
+ # OpenPGP Print
+ I = 0;
+ while (I < len(Str)):
+ if I+4 == 40/2:
+ Res = "%s %s%s%s%s "%(Res,Str[I],Str[I+1],Str[I+2],Str[I+3]);
+ else:
+ Res = "%s%s%s%s%s "%(Res,Str[I],Str[I+1],Str[I+2],Str[I+3]);
+ I = I + 4;
+ else:
+ Res = Str;
+ return string.strip(Res);
+
+# Take an email address and split it into 3 parts, (Name,UID,Domain)
+def SplitEmail(Addr):
+ # Is not an email address at all
+ if string.find(Addr,'@') == -1:
+ return (Addr,"","");
+
+ Res1 = rfc822.AddrlistClass(Addr).getaddress();
+ if len(Res1) != 1:
+ return ("","",Addr);
+ Res1 = Res1[0];
+ if Res1[1] == None:
+ return (Res1[0],"","");
+
+ # If there is no @ then the address was not parsed well. Try the alternate
+ # Parsing scheme. This is particularly important when scanning PGP keys.
+ Res2 = string.split(Res1[1],"@");
+ if len(Res2) != 2:
+ Match = AddressSplit.match(Addr);
+ if Match == None:
+ return ("","",Addr);
+ return Match.groups();
+
+ return (Res1[0],Res2[0],Res2[1]);
+
+# Convert the PGP name string to a uid value. The return is a tuple of
+# (uid,[message strings]). UnknownMpa is a hash from email to uid that
+# overrides normal searching.
+def GetUID(l,Name,UnknownMap = {}):
+ # Crack up the email address into a best guess first/middle/last name
+ (cn,mn,sn) = NameSplit(re.sub('["]','',Name[0]))
+
+ # Brackets anger the ldap searcher
+ cn = re.sub('[(")]','?',cn);
+ sn = re.sub('[(")]','?',sn);
+
+ # First check the unknown map for the email address
+ if UnknownMap.has_key(Name[1] + '@' + Name[2]):
+ Stat = "unknown map hit for "+str(Name);
+ return (UnknownMap[Name[1] + '@' + Name[2]],[Stat]);
+
+ # Then the cruft component (ie there was no email address to match)
+ if UnknownMap.has_key(Name[2]):
+ Stat = "unknown map hit for"+str(Name);
+ return (UnknownMap[Name[2]],[Stat]);
+
+ # Then the name component (another ie there was no email address to match)
+ if UnknownMap.has_key(Name[0]):
+ Stat = "unknown map hit for"+str(Name);
+ return (UnknownMap[Name[0]],[Stat]);
+
+ # Search for a possible first/last name hit
+ try:
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(&(cn=%s)(sn=%s))"%(cn,sn),["uid"]);
+ except ldap.FILTER_ERROR:
+ Stat = "Filter failure: (&(cn=%s)(sn=%s))"%(cn,sn);
+ return (None,[Stat]);
+
+ # Try matching on the email address
+ if (len(Attrs) != 1):
+ try:
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"emailforward=%s"%(Name[2]),["uid"]);
+ except ldap.FILTER_ERROR:
+ pass;
+
+ # Hmm, more than one/no return
+ if (len(Attrs) != 1):
+ # Key claims a local address
+ if Name[2] == EmailAppend:
+
+ # Pull out the record for the claimed user
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(uid=%s)"%(Name[1]),["uid","sn","cn"]);
+
+ # We require the UID surname to be someplace in the key name, this
+ # deals with special purpose keys like 'James Troup (Alternate Debian key)'
+ # Some people put their names backwards on their key too.. check that as well
+ if len(Attrs) == 1 and \
+ (string.find(string.lower(sn),string.lower(Attrs[0][1]["sn"][0])) != -1 or \
+ string.find(string.lower(cn),string.lower(Attrs[0][1]["sn"][0])) != -1):
+ Stat = EmailAppend+" hit for "+str(Name);
+ return (Name[1],[Stat]);
+
+ # Attempt to give some best guess suggestions for use in editing the
+ # override file.
+ Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(sn~=%s)"%(sn),["uid","sn","cn"]);
+
+ Stat = [];
+ if len(Attrs) != 0:
+ Stat = ["None for %s"%(str(Name))];
+ for x in Attrs:
+ Stat.append("But might be: %s %s <%s at debian.org>"%(x[1]["cn"][0],x[1]["sn"][0],x[1]["uid"][0]));
+ return (None,Stat);
+ else:
+ return (Attrs[0][1]["uid"][0],None);
+
+ return (None,None);
+
+def Group2GID(l, name):
+ """
+ Returns the numerical id of a common group
+ on error returns -1
+ """
+ for g in DebianGroups.keys():
+ if name == g:
+ return DebianGroups[g]
+
+ filter = "(gid=%s)" % name
+ res = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,filter,["gidNumber"]);
+ if res:
+ return int(GetAttr(res[0], "gidNumber"))
+
+ return -1
More information about the D-community-commits
mailing list