240 lines
7.0 KiB
C
240 lines
7.0 KiB
C
#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;
|
|
}
|