commit
0e727788cb
@ -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/
|
@ -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 |
@ -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 |
@ -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 |
@ -0,0 +1 @@ |
||||
5 |
@ -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. |
@ -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'. |
@ -0,0 +1 @@ |
||||
lib/security |
@ -0,0 +1 @@ |
||||
TODO |
@ -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 |
@ -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; |
||||
} |
Loading…
Reference in new issue