Build fixes
[public/libpam-csc.git] / pam_csc.c
index 5499f3b..3896e2d 100644 (file)
--- a/pam_csc.c
+++ b/pam_csc.c
@@ -7,21 +7,24 @@
 #include <stdlib.h>
 #include <stdbool.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdbool.h>
 #include <string.h>
-#include <security/pam_modules.h>
 #include <security/pam_appl.h>
 #include <security/pam_appl.h>
+#include <security/pam_modules.h>
 #include <ldap.h>
 #include <sasl/sasl.h>
 #include <syslog.h>
 #include <pwd.h>
 
 #include <ldap.h>
 #include <sasl/sasl.h>
 #include <syslog.h>
 #include <pwd.h>
 
-/* TODO
- *
- * We need to create a cronjob (that runs on logon and every hour) to run
- * 'kinit -p TODO@STUDENT.CS.UWATERLOO.CA -k -c /tmp/krb5cc_pam_csc'
- *
- * We also need to add a keytab entry for TODO@STUDENT.CS.UWATERLOO.CA
- *
- */
+#ifndef LDAP_SASL_QUIET
+#  define LDAP_SASL_QUIET 0
+#endif
+
+#ifndef LOG_AUTHPRIV
+#  define LOG_AUTHPRIV LOG_AUTH
+#endif
+
+#ifndef PAM_EXTERN
+#  define PAM_EXTERN extern
+#endif
 
 #define PAM_CSC_CSC_BASE_DN         "ou=People,dc=csclub,dc=uwaterloo,dc=ca"
 #define PAM_CSC_CSCF_URI \
 
 #define PAM_CSC_CSC_BASE_DN         "ou=People,dc=csclub,dc=uwaterloo,dc=ca"
 #define PAM_CSC_CSCF_URI \
@@ -35,7 +38,6 @@
 #define PAM_CSC_CSCF_PASSWORD_FILE  "/etc/security/pam_csc_cscf_password"
 #define PAM_CSC_CSCF_SASL_REALM     "STUDENT.CS.UWATERLOO.CA"
 #define PAM_CSC_LDAP_TIMEOUT        5
 #define PAM_CSC_CSCF_PASSWORD_FILE  "/etc/security/pam_csc_cscf_password"
 #define PAM_CSC_CSCF_SASL_REALM     "STUDENT.CS.UWATERLOO.CA"
 #define PAM_CSC_LDAP_TIMEOUT        5
-#define PAM_CSC_MINIMUM_UID         1000
 #define PAM_CSC_ALLOWED_USERNAMES   {"nobody"}
 #define PAM_CSC_EXPIRED_MSG \
     "*****************************************************************************\n" \
 #define PAM_CSC_ALLOWED_USERNAMES   {"nobody"}
 #define PAM_CSC_EXPIRED_MSG \
     "*****************************************************************************\n" \
@@ -100,20 +102,20 @@ int pam_csc_sasl_interact(LDAP* ld, unsigned flags, void* def, void* inter)
         {
         case SASL_CB_GETREALM:
             interact->result = param->realm;
         {
         case SASL_CB_GETREALM:
             interact->result = param->realm;
-            interact->len = strlen(param->realm);
+            break;
         case SASL_CB_USER:
             interact->result = param->user;
         case SASL_CB_USER:
             interact->result = param->user;
-            interact->len = strlen(param->user);
             break;
         case SASL_CB_PASS:
             interact->result = param->pass;
             break;
         case SASL_CB_PASS:
             interact->result = param->pass;
-            interact->len = strlen(param->pass);
+            break;
         default:
             syslog(LOG_AUTHPRIV | LOG_NOTICE,
                 PAM_CSC_SYSLOG_SASL_UNRECOGNIZED_CALLBACK, interact->id);
             interact->result = "";
         default:
             syslog(LOG_AUTHPRIV | LOG_NOTICE,
                 PAM_CSC_SYSLOG_SASL_UNRECOGNIZED_CALLBACK, interact->id);
             interact->result = "";
-            interact->len = 0;
+            break;
         }
         }
+        interact->len = strlen(interact->result);
     }
 
     return LDAP_SUCCESS;
     }
 
     return LDAP_SUCCESS;
@@ -146,13 +148,15 @@ char* pam_csc_escape_ldap_string(const char* src)
 int pam_csc_print_message(pam_handle_t* pamh, char* msg, int style)
 {
     int retval = PAM_SUCCESS;
 int pam_csc_print_message(pam_handle_t* pamh, char* msg, int style)
 {
     int retval = PAM_SUCCESS;
-    struct pam_conv* conv;
+    const struct pam_conv* conv;
     struct pam_message message;
     struct pam_message* messages[1];
     struct pam_response* response;
 
     /* output message */
     struct pam_message message;
     struct pam_message* messages[1];
     struct pam_response* response;
 
     /* output message */
-    WARN_PAM( pam_get_item(pamh, PAM_CONV, (void**)&conv) )
+    WARN_PAM( pam_get_item(pamh, PAM_CONV, (const void**)&conv) )
+    if(!conv || !conv->conv)
+        goto cleanup;
     messages[0] = &message;
     message.msg_style = style;
     message.msg = msg;
     messages[0] = &message;
     message.msg_style = style;
     message.msg = msg;
@@ -173,7 +177,7 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
     int i;
     time_t cur_time;
     struct tm* local_time;
     int i;
     time_t cur_time;
     struct tm* local_time;
-    int long_term;
+    int long_term, term_month;
     static const char term_chars[] = {'w', 's', 'f'};
     char cur_term[6], prev_term[6];
     LDAP *ld_csc = NULL, *ld_cscf = NULL;
     static const char term_chars[] = {'w', 's', 'f'};
     char cur_term[6], prev_term[6];
     LDAP *ld_csc = NULL, *ld_cscf = NULL;
@@ -181,7 +185,7 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
     FILE* pass_file = NULL;
     char* username_escaped = NULL;
     char *filter_csc = NULL, *filter_cscf = NULL;
     FILE* pass_file = NULL;
     char* username_escaped = NULL;
     char *filter_csc = NULL, *filter_cscf = NULL;
-    char *attrs_csc[] = {"objectClass", "term", NULL}, 
+    char *attrs_csc[] = {"objectClass", "term", "nonMemberTerm", NULL},
         *attrs_cscf[] = {"objectClass", NULL};
     bool expired;
     const char* pam_rhost;
         *attrs_cscf[] = {"objectClass", NULL};
     bool expired;
     const char* pam_rhost;
@@ -189,7 +193,7 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
     LDAPMessage *res_csc = NULL, *res_cscf = NULL;
     struct timeval timeout = {PAM_CSC_LDAP_TIMEOUT, 0};
     LDAPMessage* entry = NULL;
     LDAPMessage *res_csc = NULL, *res_cscf = NULL;
     struct timeval timeout = {PAM_CSC_LDAP_TIMEOUT, 0};
     LDAPMessage* entry = NULL;
-    char **values = NULL, **values_iter = NULL;
+    char **values = NULL, **nmvalues = NULL, **values_iter = NULL;
 
     /* determine username */
     if((pam_get_user(pamh, &username, NULL) != PAM_SUCCESS) || !username)
 
     /* determine username */
     if((pam_get_user(pamh, &username, NULL) != PAM_SUCCESS) || !username)
@@ -197,11 +201,15 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
         return PAM_USER_UNKNOWN;
     }
 
         return PAM_USER_UNKNOWN;
     }
 
-    /* check uid */
+    /* check uid range */
     pwd = getpwnam(username);
     pwd = getpwnam(username);
-    if(pwd && pwd->pw_uid < PAM_CSC_MINIMUM_UID)
+    if(pwd)
     {
     {
-        return PAM_SUCCESS;
+        /* these ranges are taken from puppet/documents/id-range */
+        if(pwd->pw_uid < 500 || (pwd->pw_uid >= 1000 && pwd->pw_uid < 10000))
+        {
+            return PAM_SUCCESS;
+        }
     }
 
     /* check username */
     }
 
     /* check username */
@@ -223,6 +231,7 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
     sprintf(cur_term, "%c%d", term_chars[long_term % 3], long_term / 3);
     long_term--;
     sprintf(prev_term, "%c%d", term_chars[long_term % 3], long_term / 3);
     sprintf(cur_term, "%c%d", term_chars[long_term % 3], long_term / 3);
     long_term--;
     sprintf(prev_term, "%c%d", term_chars[long_term % 3], long_term / 3);
+    term_month = local_time->tm_mon % 4;
 
     /* connect to CSC */
     WARN_LDAP( ldap_create(&ld_csc) )
 
     /* connect to CSC */
     WARN_LDAP( ldap_create(&ld_csc) )
@@ -230,12 +239,9 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
 
     /* check if we are logging in from a CSCF teaching thin client */
     cscf = false;
 
     /* check if we are logging in from a CSCF teaching thin client */
     cscf = false;
-    if(pam_get_item(pamh, PAM_RHOST, (void**)&pam_rhost) && pam_rhost)
+    if(pam_get_item(pamh, PAM_RHOST, (const void**)&pam_rhost) && pam_rhost)
     {
     {
-        /* TODO: check pam_rhost
-         * It appears that the thin clients all have hostnames of the form
-         * tc[0-9]+\.cs
-         */
+        /* TODO: check if pam_rhost is tcNNN.student.cs */
     }
 
     if(cscf)
     }
 
     if(cscf)
@@ -263,8 +269,8 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
     }
 
     /* create CSC request string */
     }
 
     /* create CSC request string */
-    WARN_ZERO( filter_csc = malloc(100 + strlen(username_escaped)) )
-    sprintf(filter_csc, "(&(uid=%s)(|(&(objectClass=member)(|(term=%s)(term=%s)))(!(objectClass=member))))", username_escaped, cur_term, prev_term);
+    WARN_ZERO( filter_csc = malloc(140 + strlen(username_escaped)) )
+    sprintf(filter_csc, "(&(uid=%s)(|(&(objectClass=member)(|(term=%s)(term=%s)(nonMemberTerm=%s)(nonMemberTerm=%s)))(!(objectClass=member))))", username_escaped, cur_term, prev_term, cur_term, prev_term);
 
     /* issue CSC request */
     WARN_NEG1( msg_csc = ldap_search(ld_csc, PAM_CSC_CSC_BASE_DN, 
 
     /* issue CSC request */
     WARN_NEG1( msg_csc = ldap_search(ld_csc, PAM_CSC_CSC_BASE_DN, 
@@ -298,7 +304,9 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
     /* get CSC entry */
     WARN_ZERO( entry = ldap_first_entry(ld_csc, res_csc) )
     values = ldap_get_values(ld_csc, entry, "term");
     /* get CSC entry */
     WARN_ZERO( entry = ldap_first_entry(ld_csc, res_csc) )
     values = ldap_get_values(ld_csc, entry, "term");
-    if(!values)
+    nmvalues = ldap_get_values(ld_csc, entry, "nonMemberTerm");
+
+    if(!values && !nmvalues)
     {
         syslog(LOG_AUTHPRIV | LOG_NOTICE, PAM_CSC_SYSLOG_NOT_A_MEMBER, 
             username);
     {
         syslog(LOG_AUTHPRIV | LOG_NOTICE, PAM_CSC_SYSLOG_NOT_A_MEMBER, 
             username);
@@ -308,25 +316,50 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
 
     /* iterate through term attributes */
     expired = true;
 
     /* iterate through term attributes */
     expired = true;
-    values_iter = values;
-    while(*values_iter)
-    {
-        if(strcmp(*values_iter, cur_term) == 0)
+    if (values) {
+        values_iter = values;
+        while(*values_iter)
         {
         {
-            /* user is registered in current term */
-            expired = false;
-            break;
+            if(strcmp(*values_iter, cur_term) == 0)
+            {
+                /* user is registered in current term */
+                expired = false;
+                break;
+            }
+            values_iter++;
+        }
+    }
+    if (nmvalues) {
+        values_iter = nmvalues;
+        while (*values_iter) {
+            if (strcmp(*values_iter, cur_term) == 0) {
+                expired = false;
+                break;
+            }
+            values_iter++;
         }
         }
-        values_iter++;
     }
 
     /* check if account is expired */
     if(expired)
     {
     }
 
     /* check if account is expired */
     if(expired)
     {
-        /* show notice and continue */
-        pam_csc_print_message(pamh, PAM_CSC_EXPIRED_MSG, PAM_TEXT_INFO);
-        syslog(LOG_AUTHPRIV | LOG_NOTICE, PAM_CSC_SYSLOG_EXPIRED_ERROR, 
-            username);
+        /* we allow once month grace-period */
+        if(term_month == 0)
+        {
+            /* show notice and continue */
+            pam_csc_print_message(pamh, PAM_CSC_EXPIRED_MSG, PAM_TEXT_INFO);
+            syslog(LOG_AUTHPRIV | LOG_NOTICE, PAM_CSC_SYSLOG_EXPIRED_ERROR, 
+                username);
+        }
+        else
+        {
+            /* show notice and disallow login */
+            pam_csc_print_message(pamh, PAM_CSC_EXPIRED_MSG, PAM_ERROR_MSG);
+            syslog(LOG_AUTHPRIV | LOG_NOTICE, PAM_CSC_SYSLOG_EXPIRED_WARNING, 
+                username);
+            retval = PAM_AUTH_ERR;
+            goto cleanup;
+        }
     }
 
     if(cscf)
     }
 
     if(cscf)
@@ -350,6 +383,7 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t* pamh, int flags, int argc, const c
 cleanup:
 
     if(values) ldap_value_free(values);
 cleanup:
 
     if(values) ldap_value_free(values);
+    if(nmvalues) ldap_value_free(nmvalues);
     if(res_csc) ldap_msgfree(res_csc);
     if(res_cscf) ldap_msgfree(res_cscf);
     if(ld_csc) ldap_unbind(ld_csc);
     if(res_csc) ldap_msgfree(res_csc);
     if(res_cscf) ldap_msgfree(res_cscf);
     if(ld_csc) ldap_unbind(ld_csc);