#include "config.h" #include <stdio.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif #ifdef HAVE_MEMORY_H #include <memory.h> #endif #include "auth_client.h" /* These #defines must be present according to PAM documentation. */ #define PAM_SM_AUTH #ifdef HAVE_SECURITY_PAM_APPL_H #include <security/pam_appl.h> #endif #ifdef HAVE_SECURITY_PAM_MODULES_H #include <security/pam_modules.h> #endif #ifdef HAVE_PAM_PAM_APPL_H #include <pam/pam_appl.h> #endif #ifdef HAVE_PAM_PAM_MODULES_H #include <pam/pam_modules.h> #endif #ifndef PAM_EXTERN #ifdef PAM_STATIC #define PAM_EXTERN static #else #define PAM_EXTERN extern #endif #endif static const char *kPasswordPrompt = "Password: "; static const char *kOtpPrompt = "OTP Token: "; struct cfg { int debug; int use_first_pass; int try_first_pass; char *auth_server; char *ssl_crt; char *ssl_key; char *ca_file; }; static void parse_cfg(int argc, const char **argv, struct cfg *cfg) { int i; memset(cfg, 0, sizeof(struct cfg)); for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "debug")) { cfg->debug = 1; } else if (!strcmp(argv[i], "try_first_pass")) { cfg->try_first_pass = 1; } else if (!strcmp(argv[i], "use_first_pass")) { cfg->use_first_pass = 1; } else if (!strncmp(argv[i], "auth_server=", 12)) { cfg->auth_server = (char *)(argv[i] + 12); } else if (!strncmp(argv[i], "ssl_crt=", 8)) { cfg->ssl_crt = (char *)(argv[i] + 8); } else if (!strncmp(argv[i], "ssl_key=", 8)) { cfg->ssl_key = (char *)(argv[i] + 8); } else if (!strncmp(argv[i], "ca=", 3)) { cfg->ca_file = (char *)(argv[i] + 3); } } } PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { int i, retval, err; const char *service = NULL; const char *username = NULL; const char *password = NULL; const char *otp_token = NULL; const char *source_ip = NULL; struct pam_conv *conv = NULL; struct cfg cfg; auth_client_t ac; parse_cfg(argc, argv, &cfg); err = pam_get_user(pamh, &username, NULL); if (err != PAM_SUCCESS) { D(("pam_get_user() error: %s", pam_strerror(pamh, err))); return PAM_AUTH_ERR; } err = pam_get_item(pamh, PAM_SERVICE, (PAM_CONST void **)&service); if (err != PAM_SUCCESS) { D(("pam_get_item(service) error: %s", pam_strerror(pamh, err))); return PAM_AUTH_ERR; } pam_get_item(pamh, PAM_RHOST, (PAM_CONST void **)&source_ip); if (cfg.try_first_pass || cfg.use_first_pass) { if (pam_get_item(pamh, PAM_AUTHTOK, (PAM_CONST void **)&password) != PAM_SUCCESS) { return PAM_AUTH_ERR; } } if (cfg.use_first_pass && password == NULL) { return PAM_AUTH_ERR; } if (password == NULL) { // Ask for the password interactively. struct pam_message *pmsg[1], msg[1]; struct pam_response *resp; err = pam_get_item(pamh, PAM_CONV, (PAM_CONST void **)&conv); if (err != PAM_SUCCESS) { D(("pam_get_item(conv) error: %s", pam_strerror(pamh, err))); return PAM_AUTH_ERR; } pmsg[0] = &msg[0]; msg[0].msg = (char *)kPasswordPrompt; msg[0].msg_style = PAM_PROMPT_ECHO_OFF; err = conv->conv(1, (const struct pam_message **)pmsg, &resp, conv->appdata_ptr); if (err != PAM_SUCCESS) { D(("conv() error: %s", pam_strerror(pamh, err))); return PAM_AUTH_ERR; } password = resp->resp; } // Create the auth client request. ac = auth_client_new(service, cfg.auth_server); if (cfg.ssl_crt && cfg.ssl_key && cfg.ca_file) { auth_client_set_certificate(ac, cfg.ca_file, cfg.ssl_crt, cfg.ssl_key); } retval = PAM_AUTH_ERR; // Allow two authentication attempts in case we receive an // OTP_REQUIRED response from the server. for (i = 0; i < 2; i++) { int ac_err = auth_client_authenticate(ac, username, password, otp_token, source_ip); if (ac_err == AC_OK) { retval = PAM_SUCCESS; } else if (ac_err == AC_ERR_OTP_REQUIRED) { struct pam_message *pmsg[1], msg[1]; struct pam_response *resp; // Ask for the OTP token interactively. if (conv == NULL) { err = pam_get_item(pamh, PAM_CONV, (PAM_CONST void **)&conv); if (err != PAM_SUCCESS) { D(("pam_get_item(conv) error: %s", pam_strerror(pamh, err))); break; } } pmsg[0] = &msg[0]; msg[0].msg = (char *)kOtpPrompt; msg[0].msg_style = PAM_PROMPT_ECHO_ON; err = conv->conv(1, (const struct pam_message **)pmsg, &resp, conv->appdata_ptr); if (err != PAM_SUCCESS) { D(("conv() error: %s", pam_strerror(pamh, err))); break; } otp_token = resp->resp; continue; } else { D(("auth_client error: %s", auth_client_strerror(ac_err))); } break; } auth_client_free(ac); return retval; } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pam, int flags, int argc, const char **argv) { return PAM_SUCCESS; } #ifdef PAM_STATIC struct pam_module _pam_authclient_modstruct = { "pam_authclient", pam_sm_authenticate, pam_sm_setcred, NULL, NULL, NULL, NULL }; #endif