Initial checkin.

This commit is contained in:
David Bartley 2007-07-06 01:36:29 -04:00
commit 0e727788cb
11 changed files with 411 additions and 0 deletions

15
Makefile Normal file
View File

@ -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/

6
debian/README vendored Normal file
View File

@ -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

6
debian/README.Debian vendored Normal file
View File

@ -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

5
debian/changelog vendored Normal file
View File

@ -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

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
5

12
debian/control vendored Normal file
View File

@ -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.

26
debian/copyright vendored Normal file
View File

@ -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'.

1
debian/dirs vendored Normal file
View File

@ -0,0 +1 @@
lib/security

1
debian/docs vendored Normal file
View File

@ -0,0 +1 @@
TODO

99
debian/rules vendored Executable file
View File

@ -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

239
pam_csc.c Normal file
View File

@ -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;
}