#include #include #include #include #include #include #include #include #include #include #include #define LINOTPD_OK ":-)" #define LINOTPD_REJECT ":-(" #define LINOTPD_FAIL ":-/" /*****************************************************************************/ /* We used this one: http://www.freebsd.org/doc/en/articles/pam/pam-sample-module.html */ #ifndef _OPENPAM static char password_prompt[] = "Your OTP:"; #endif #ifndef PAM_EXTERN #define PAM_EXTERN #endif /* config options to be set in the pam configuration: url=http://localhost:5001/validate/simplecheck nosslhostnameverify nosslcertverify */ /************** syslog stuff **********************/ static void log_error (char * format, ...) { va_list args; va_start (args, format); openlog("pam_linotp", LOG_PID , LOG_AUTHPRIV ); vsyslog(LOG_ERR, format,args); closelog(); va_end (args); } static void log_debug (char * format, ...) { va_list args; va_start (args, format); openlog("pam_linotp", LOG_PID , LOG_AUTHPRIV ); vsyslog(LOG_DEBUG, format,args); closelog(); va_end (args); } static void log_warning (char * format, ...) { va_list args; va_start (args, format); openlog("pam_linotp", LOG_PID , LOG_AUTHPRIV ); vsyslog(LOG_WARNING, format,args); closelog(); va_end (args); } static void log_info(char * format, ...) { va_list args; va_start (args, format); openlog("pam_linotp", LOG_PID , LOG_AUTHPRIV ); vsyslog(LOG_INFO, format,args); closelog(); va_end (args); } /*********************************************** Curl stuff ***********************************************/ struct MemoryStruct { char *memory; size_t size; }; static void *myrealloc(void *ptr, size_t size) { void * ret = NULL; if (size > 1024 * 1024) { ret = NULL; } /* There might be a realloc() out there that doesn't like reallocing NULL pointers, so we take care of it here */ if(ptr) ret = realloc(ptr, size); else ret = malloc(size); /* if realloc fails, ptr is not freed */ if (!ret) free(ptr); return ret; } static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)data; /* failsafe */ if (realsize > 1024*1024) { log_error("ERROR: The linotpd responded to our authentication request with more than 1MB of data! Something is really wrong here!"); return mem->size; } /*Check for Max_size*/ mem->memory = myrealloc(mem->memory, mem->size + realsize + 1); if (mem->memory) { memcpy(&(mem->memory[mem->size]), ptr, realsize); mem->size += realsize; mem->memory[mem->size] = 0; } return realsize; } char * createUrl(CURL *curl_handle, const char * validateurl, const char * realm, const char * resConf, const char * user, const char * password) { char * url = NULL; int size = 300; int nchars = 0; // escape user and password char *escPassword; char *escUser; escPassword = curl_easy_escape(curl_handle, password, 0); escUser = curl_easy_escape(curl_handle, user, 0); char *escRealm = curl_easy_escape(curl_handle, realm, 0); char *escResConf = curl_easy_escape(curl_handle, resConf, 0); if (escPassword == NULL || escUser == NULL) { log_error("ERROR: faild to escape user or password"); goto cleanup; } url = (char*) malloc (size); if (url == NULL) { log_error("ERROR: could allocate size for url"); goto cleanup; } // allocate the memory for url string memset(url,'\0',size); nchars = snprintf( url, size-1, "%s?user=%s&pass=%s&realm=%s&resConf=%s", validateurl, escUser, escPassword, escRealm, escResConf ); if (nchars >= size-1) { // reallocate size = nchars +1; url = (char*) myrealloc (url, size); if (url == NULL) { log_error("ERROR: faild to alloc space for url + user and password"); goto cleanup; } memset(url,'\0',size); snprintf(url, size-1, "%s?user=%s&pass=%s&realm=%s&resConf=%s", validateurl, escUser, escPassword, escRealm, escResConf ); } cleanup: return url; } int sendRequest(CURL *curl_handle, char * url, struct MemoryStruct * chunk, int nosslhostnameverify, int nosslcertverify) { int all_status = 0; int status = 0; all_status = 0; status = curl_easy_setopt(curl_handle, CURLOPT_URL, url); all_status += status; status = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); all_status += status; status = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, chunk); all_status += status; status = curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); all_status += status; if ( nosslhostnameverify ) status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); else status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L); all_status += status; if ( nosslcertverify ) status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); else status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L); all_status += status; status = curl_easy_perform(curl_handle); all_status += status; curl_easy_cleanup(curl_handle); return all_status; } /********** LinOTP stuff ***************************/ int linotp_auth(const char *user, const char *password, const char *validateurl, const char *realm, const char *resConf, int nosslhostnameverify, int nosslcertverify, int debug) { CURL *curl_handle = NULL; int returnValue = PAM_AUTH_ERR; char *url = NULL; CURLcode all_status = 0; char errorBuffer[CURL_ERROR_SIZE]; struct MemoryStruct chunk; chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ chunk.size = 0; /* no data at this point */ curl_global_init(CURL_GLOBAL_ALL); curl_handle = curl_easy_init(); if (curl_handle == NULL) { log_error("ERROR: could not get curl_handle!"); returnValue = PAM_AUTH_ERR; goto cleanup; } curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer); url = createUrl(curl_handle, validateurl, realm, resConf, user, password); if (url == NULL) { log_error("ERROR: could allocate size for url"); goto cleanup; } if (debug) { log_debug("DEBUG: connecting to url:%s",url); } all_status = sendRequest(curl_handle, url, (void *)&chunk, nosslhostnameverify, nosslcertverify); /* set default return*/ returnValue = PAM_AUTH_ERR; if (all_status != 0) { log_error("ERROR: Error talking to linotpd server at %s: %s", url, errorBuffer); returnValue = PAM_AUTH_ERR; goto cleanup; } if(chunk.memory == NULL) { log_error("ERROR: No response returned for %s: %s", url, errorBuffer); returnValue = PAM_AUTH_ERR; goto cleanup; } if (strcmp(chunk.memory, LINOTPD_REJECT) == 0) { log_warning("WARNING: user '%s' rejected", user); returnValue = PAM_AUTH_ERR; goto cleanup; } if (strcmp(chunk.memory, LINOTPD_FAIL ) == 0) { log_warning("WARNING: authentication for '%s' failed", user); returnValue = PAM_AUTH_ERR; goto cleanup; } if (strcmp( chunk.memory, LINOTPD_OK ) == 0) { log_info("INFO: user '%s' authenticated successfully\n", user); returnValue = PAM_SUCCESS; goto cleanup; } // default { log_error("ERROR: An error occured for '%s' on '%s'\n", user, validateurl); returnValue = PAM_AUTH_ERR; goto cleanup; } cleanup: if (chunk.memory!= NULL) { free(chunk.memory); } if (url != NULL) { free(url); } /* we're done with libcurl, so clean it up cleanup also takes care of escPassword and escUser */ curl_global_cleanup(); if (debug) { log_debug("linotp_auth exited normally."); } return (returnValue); } /************************************************** PAM stuff */ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { #ifndef _OPENPAM struct pam_conv *conv; struct pam_message msg; const struct pam_message *msgp; struct pam_response *resp; #endif struct passwd *pwd = NULL; const char *user = NULL; char *password = NULL; /* last resort */ int pam_err = PAM_AUTH_ERR; int retry = 0; /* get config options */ unsigned int urlmaxlen = 1000; unsigned int realmmaxlen = 100; unsigned int resmaxlen = 100; char *validateurl = ""; char *realm = ""; char *resConf = ""; int nosslhostnameverify = 0; int nosslcertverify = 0; int debug = 0; /*??? Not testet yet: int use_first_pass = 0; */ /* now check the config options: config options to be set in the pam configuration: url=http://localhost:5001/validate/simplecheck nosslhostnameverify nosslcertverify realm= resConf= */ int i=0; for (i=0; iconv)(1, &msgp, &resp, conv->appdata_ptr); if (resp != NULL) { if (pam_err == PAM_SUCCESS) { password = resp->resp; } else { free(resp->resp); } free(resp); } #endif if (pam_err == PAM_SUCCESS) { break; } } /* end of for */ if (pam_err == PAM_CONV_ERR) { log_error( "Failed to get authtoken. PAM_CONV_ERR occurred." ); goto cleanup; } if (pam_err != PAM_SUCCESS) { log_error( "pam_err: %d", pam_err ); pam_err = PAM_AUTH_ERR; goto cleanup; } } // End of !use_first_pass if (debug) { log_debug("OK, we got some password. Hm, should I tell you which? Ok, you are debugging: %s", password); } /* check passwords */ if ( user != NULL && password != NULL && validateurl != NULL) { log_debug("All other data, user, password, validateurl are fine"); log_debug("user: %s", user); log_debug("url : %s", validateurl); pam_err = linotp_auth(user, password, validateurl, realm, resConf, nosslhostnameverify, nosslcertverify, debug); } else { log_error("The username or the validateurl were NULL!"); } cleanup: log_info( "doing cleanup" ); #ifndef _OPENPAM if (password != NULL) free(password); #endif return (pam_err); } PAM_EXTERN int pam_sm_setcred (pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval; int auth_retval; log_info("Please note: pam_linotp does not support pam_sm_setcred at the moment"); retval = pam_get_data (pamh, "oath_setcred_return", (void*) (intptr_t) &auth_retval); log_debug("retval: %d", auth_retval); if (retval != PAM_SUCCESS) retval = PAM_CRED_UNAVAIL; else switch (auth_retval) { case PAM_SUCCESS: retval = PAM_SUCCESS; break; case PAM_USER_UNKNOWN: retval = PAM_USER_UNKNOWN; break; case PAM_AUTH_ERR: default: retval = PAM_CRED_ERR; break; } log_info("pam_sm_setcred: [%s]", pam_strerror (pamh, retval)); return retval; } PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { log_error("ERROR: pam_linotp does not support pam_sm_acct_mgmt"); return (PAM_SERVICE_ERR); } PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { log_error("ERROR: pam_linotp does not support pam_sm_open_session"); return (PAM_SERVICE_ERR); } PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { log_error("ERROR: pam_linotp does not support pam_sm_close_session"); return (PAM_SERVICE_ERR); } PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { log_error("ERROR: pam_linotp does not support pam_sm_chauthtok"); return (PAM_SERVICE_ERR); } #ifdef PAM_MODULE_ENTRY PAM_MODULE_ENTRY("pam_linotp"); #endif