Initial checkin.
This commit is contained in:
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