Initial checkin. libpam-csc-1.0
authorDavid Bartley <dtbartle@csclub.uwaterloo.ca>
Fri, 6 Jul 2007 05:36:29 +0000 (01:36 -0400)
committerDavid Bartley <dtbartle@csclub.uwaterloo.ca>
Fri, 6 Jul 2007 05:36:29 +0000 (01:36 -0400)
Makefile [new file with mode: 0644]
debian/README [new file with mode: 0644]
debian/README.Debian [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/dirs [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/rules [new file with mode: 0755]
pam_csc.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..f08c234
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+CC=gcc
+CFLAGS=-O2 -fPIC -Wall
+LDFLAGS=-shared -lpam -lldap
+LD=ld
+
+all: pam_csc.so
+
+pam_csc.so: pam_csc.o
+       $(LD) -o $@ $(LDFLAGS) $<
+
+clean:
+       rm -f pam_csc.so pam_csc.o
+
+install:
+       cp pam_csc.so /lib/security/
diff --git a/debian/README b/debian/README
new file mode 100644 (file)
index 0000000..7bf5923
--- /dev/null
@@ -0,0 +1,6 @@
+The Debian Package libpam-csc
+----------------------------
+
+Comments regarding the Package
+
+ -- David Bartley <dtbartle@csclub.uwaterloo.ca>  Sun, 24 Jun 2007 23:18:22 -0400
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644 (file)
index 0000000..b892526
--- /dev/null
@@ -0,0 +1,6 @@
+libpam-csc for Debian
+---------------------
+
+<possible notes regarding this package - if none, delete this file>
+
+ -- David Bartley <dtbartle@csclub.uwaterloo.ca>  Sun, 24 Jun 2007 23:18:22 -0400
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..b0bbc94
--- /dev/null
@@ -0,0 +1,5 @@
+libpam-csc (1.0) unstable; urgency=low
+
+  * Initial Release.
+
+ -- David Bartley <dtbartle@csclub.uwaterloo.ca>  Sun, 24 Jun 2007 23:18:22 -0400
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..8ea3148
--- /dev/null
@@ -0,0 +1,12 @@
+Source: libpam-csc
+Section: net
+Priority: optional
+Maintainer: David Bartley <dtbartle@csclub.uwaterloo.ca>
+Build-Depends: debhelper (>= 4.0.0), libldap-dev, libpam0g-dev
+Standards-Version: 3.7.2
+
+Package: libpam-csc
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: Custom CSC PAM module to handle account expiration.
+ Custom CSC PAM module to handle account expiration.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..53889ef
--- /dev/null
@@ -0,0 +1,26 @@
+This is libpam-csc, written and maintained by David Bartley <dtbartle@csclub.uwaterloo.ca>
+on Sun, 24 Jun 2007 23:18:22 -0400.
+
+The original source can always be found at:
+       ftp://ftp.debian.org/dists/unstable/main/source/
+
+Copyright Holder:  David Bartley
+
+License:
+
+  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 package; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+On Debian systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
diff --git a/debian/dirs b/debian/dirs
new file mode 100644 (file)
index 0000000..d1f6515
--- /dev/null
@@ -0,0 +1 @@
+lib/security
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..1333ed7
--- /dev/null
@@ -0,0 +1 @@
+TODO
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..0d37306
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+
+
+
+CFLAGS = -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -O0
+else
+       CFLAGS += -O2
+endif
+
+configure: configure-stamp
+configure-stamp:
+       dh_testdir
+       # Add here commands to configure the package.
+
+       touch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: configure-stamp 
+       dh_testdir
+
+       # Add here commands to compile the package.
+       $(MAKE)
+       #docbook-to-man debian/libpam-csc.sgml > libpam-csc.1
+
+       touch $@
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp configure-stamp
+
+       # Add here commands to clean up after the build process.
+       -$(MAKE) clean
+
+       dh_clean 
+
+install: build-stamp
+       dh_testdir
+       dh_testroot
+       dh_clean -k 
+       dh_installdirs
+       install -m 644 pam_csc.so $(CURDIR)/debian/libpam-csc/lib/security
+
+       # Add here commands to install the package into debian/libpam-csc.
+#      $(MAKE) DESTDIR=$(CURDIR)/debian/libpam-csc install
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+       dh_installchangelogs 
+       dh_installdocs
+       dh_installexamples
+#      dh_install
+#      dh_installmenu
+#      dh_installdebconf       
+#      dh_installlogrotate
+#      dh_installemacsen
+#      dh_installpam
+#      dh_installmime
+#      dh_python
+#      dh_installinit
+#      dh_installcron
+#      dh_installinfo
+       dh_installman
+       dh_link
+       dh_strip
+       dh_compress
+       dh_fixperms
+#      dh_perl
+#      dh_makeshlibs
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/pam_csc.c b/pam_csc.c
new file mode 100644 (file)
index 0000000..79dd69d
--- /dev/null
+++ b/pam_csc.c
@@ -0,0 +1,239 @@
+#define PAM_SM_ACCOUNT
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <security/pam_modules.h>
+#include <security/pam_appl.h>
+#include <ldap.h>
+#include <syslog.h>
+#include <pwd.h>
+
+#define PAM_CSC_LDAP_URI \
+    "ldap://caffeine.csclub.uwaterloo.ca ldap://perpugilliam.csclub.uwaterloo.ca"
+#define PAM_CSC_LDAP_USER_BASE_DN       "ou=People,dc=csclub,dc=uwaterloo,dc=ca"
+#define PAM_CSC_LDAP_GROUP_BASE_DN      "ou=Group,dc=csclub,dc=uwaterloo,dc=ca"
+#define PAM_CSC_LDAP_TIMEOUT            5
+#define PAM_CSC_ALLOWED_GROUPS          "cn=staff"
+#define PAM_CSC_MINIMUM_UID             1000
+#define PAM_CSC_EXPIRED_MSG \
+    "*****************************************************************************\n" \
+    "*                                                                           *\n" \
+    "*    Your account has expired - please contact the Computer Science Club    *\n" \
+    "*                                                                           *\n" \
+    "*****************************************************************************\n"
+
+/*
+ * User terms are defined as (3 * year + term) where term is:
+ *   0 = Winter, 1 = Spring, 2 = Fall
+ * Term is a string in the form [f|w|s][year]
+ */
+
+enum check_user_type_t
+{
+    check_user_exists,
+    check_user_cur_term,
+    check_user_prev_term,
+    check_user_groups
+};
+
+#define HANDLE_WARN \
+{ \
+    syslog(LOG_AUTHPRIV | LOG_WARNING, "pam_csc generated a warning on line %d of %s\n", __LINE__, __FILE__); \
+    retval = PAM_SUCCESS; \
+    goto cleanup; \
+}
+
+#define WARN_ZERO(x) \
+    if( (x) == 0 ) HANDLE_WARN
+
+#define WARN_NEG1(x) \
+    if( (x) == -1 ) HANDLE_WARN
+
+#define WARN_PAM(x) \
+    if( (x) != PAM_SUCCESS ) HANDLE_WARN
+
+#define WARN_LDAP(x) \
+    if( (x) != LDAP_SUCCESS ) HANDLE_WARN
+
+char* escape_ldap_string(const char* src)
+{
+    char *dst, *dstPtr;
+    int i;
+
+    if(!(dst = malloc(2 * strlen(src) + 1)))
+        return NULL;
+    dstPtr = dst;
+
+    for(i = 0; i < strlen(src); i++)
+    {
+        if(src[i] == '*' || src[i] == '(' || src[i] == ')' || src[i] == '\\')
+        {
+            dstPtr[0] = '\\';
+            dstPtr++;
+        }
+        dstPtr[0] = src[i];
+        dstPtr++;
+    }
+    dstPtr[0] = '\0';
+
+    return dst;
+}
+
+int check_user(const char* username, enum check_user_type_t checkType)
+{
+    int retval = PAM_SUCCESS;
+    time_t curTime;
+    struct tm* localTime;
+    int longTerm, year, term;
+    LDAP* ld = NULL;
+    static const char termChars[] = {'w', 's', 'f'};
+    char* usernameEscaped = NULL;
+    char* filter = NULL;
+    char* attr[] = {"objectClass", NULL};
+    struct timeval timeout = {PAM_CSC_LDAP_TIMEOUT, 0};
+    LDAPMessage* res = NULL;
+    char* baseDN = NULL;
+
+    /* fail-safe for root */
+    if(strcmp(username, "root") == 0)
+    {
+        return PAM_SUCCESS;
+    }
+
+    /* connect and bind */
+    WARN_LDAP( ldap_initialize(&ld, PAM_CSC_LDAP_URI) )
+    WARN_NEG1( ldap_simple_bind(ld, NULL, NULL) )
+
+    WARN_ZERO( usernameEscaped = escape_ldap_string(username) );
+    switch(checkType)
+    {
+    case check_user_exists:
+
+        /* format filter */
+        WARN_ZERO( filter = malloc(50 + strlen(usernameEscaped)) )
+        sprintf(filter, "(uid=%s)", usernameEscaped);
+        baseDN = PAM_CSC_LDAP_USER_BASE_DN;
+        break;
+
+    case check_user_prev_term:
+    case check_user_cur_term:
+
+        /* get term info and compute current and previous term */
+        WARN_NEG1( curTime = time(NULL) )
+        WARN_ZERO( localTime = localtime(&curTime) )
+        longTerm = 3 * (1900 + localTime->tm_year) + (localTime->tm_mon / 4);
+        if(checkType == check_user_prev_term)
+            longTerm--;
+        term = termChars[longTerm % 3];
+        year = longTerm / 3;
+
+        /* format filter */
+        WARN_ZERO( filter = malloc(100 + strlen(usernameEscaped)) )
+        sprintf(filter, "(&(uid=%s)(|(&(objectClass=member)(term=%c%d))(!(objectClass=member))))", 
+            usernameEscaped, term, year);
+        baseDN = PAM_CSC_LDAP_USER_BASE_DN;
+        break;
+
+    case check_user_groups:
+
+        /* format filter */
+        WARN_ZERO( filter = malloc(50 + strlen(PAM_CSC_ALLOWED_GROUPS) + strlen(usernameEscaped)) )
+        sprintf(filter, "(&(objectClass=posixGroup)(%s)(memberUid=%s))", PAM_CSC_ALLOWED_GROUPS, usernameEscaped);
+        baseDN = PAM_CSC_LDAP_GROUP_BASE_DN;
+        break;
+    }
+
+    /* search */
+    WARN_LDAP( ldap_search_st(ld, baseDN, LDAP_SCOPE_SUBTREE, filter, attr, 1, &timeout, &res) )
+    if((term = ldap_count_entries(ld, res)) == 0)
+        retval = PAM_AUTH_ERR;
+
+cleanup:
+
+    if(usernameEscaped) free(usernameEscaped);
+    if(res) ldap_msgfree(res);
+    if(filter) free(filter);
+    if(ld) ldap_unbind(ld);
+
+    return retval;
+}
+
+int print_pam_message(pam_handle_t* pamh, char* msg, int style)
+{
+    int retval = PAM_SUCCESS;
+    struct pam_conv* pamConv;
+    struct pam_message pamMessage;
+    struct pam_message* pamMessages[1];
+    struct pam_response* pamResponse;
+
+    /* output message */
+    WARN_PAM( pam_get_item(pamh, PAM_CONV, (const void**)&pamConv) )
+    pamMessages[0] = &pamMessage;
+    pamMessage.msg_style = style;
+    pamMessage.msg = msg;
+    WARN_PAM( pamConv->conv(1, (const struct pam_message**)pamMessages, 
+        &pamResponse, pamConv->appdata_ptr) )
+
+cleanup:
+
+    return retval;
+}
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const char* argv[])
+{
+    const char* username;
+    struct passwd* pwd;
+
+    /* determine username */
+    if((pam_get_user(pamh, &username, NULL) != PAM_SUCCESS) || !username)
+    {
+        return PAM_USER_UNKNOWN;
+    }
+
+    /* check uid */
+    pwd = getpwnam(username);
+    if(pwd && pwd->pw_uid < PAM_CSC_MINIMUM_UID)
+    {
+        return PAM_SUCCESS;
+    }
+
+    /* check if user exists in ldap */
+    if(check_user(username, check_user_exists) == PAM_AUTH_ERR)
+    {
+        return PAM_SUCCESS;
+    }
+
+    /* check if user is registered for the current term */
+    if(check_user(username, check_user_cur_term) == PAM_SUCCESS)
+    {
+        return PAM_SUCCESS;
+    }
+
+    /* check if user is registered for the previous term */
+    if(check_user(username, check_user_prev_term) == PAM_SUCCESS)
+    {
+        /* show warning */
+        syslog(LOG_AUTHPRIV | LOG_NOTICE, "(pam_csc): %s was not registered for current term but was registered for previous term - permitting login\n", username);
+        print_pam_message(pamh, PAM_CSC_EXPIRED_MSG, PAM_TEXT_INFO);
+        return PAM_SUCCESS;
+    }
+
+    /* check if user is in allowed groups */
+    if(check_user(username, check_user_groups) == PAM_SUCCESS)
+    {
+        /* show warning */
+        print_pam_message(pamh, PAM_CSC_EXPIRED_MSG, PAM_TEXT_INFO);
+        syslog(LOG_AUTHPRIV | LOG_NOTICE, "(pam_csc): %s was not registered but was in allowed groups - permitting login\n", username);
+        return PAM_SUCCESS;
+    }
+
+    /* account has expired - show prompt */
+    print_pam_message(pamh, PAM_CSC_EXPIRED_MSG, PAM_ERROR_MSG);
+    syslog(LOG_AUTHPRIV | LOG_NOTICE, "(pam_csc): %s was not registered and was not in allowed groups - denying login\n", username);
+
+    return PAM_AUTH_ERR;
+}