/* This file is part of GNU Pies. Copyright (C) 2015-2017 Sergey Poznyakoff GNU Pies 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 3, or (at your option) any later version. GNU Pies 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 GNU Pies. If not, see . */ #include "ident.h" #include #include struct pam_identity_provider_data { char *service; }; struct pam_cred { const char *user; const char *pass; }; #define COPY_STRING(s) (s) ? strdup(s) : NULL #define overwrite_and_free(ptr) \ do { \ char *s = ptr; \ while (*s) \ *s++ = 0; \ free (ptr); \ } while (0) #ifndef PAM_AUTHTOK_RECOVER_ERR # define PAM_AUTHTOK_RECOVER_ERR PAM_CONV_ERR #endif static int pies_conv (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { int status = PAM_SUCCESS; int i; struct pam_response *reply = NULL; struct pam_cred *cred = appdata_ptr; reply = calloc (num_msg, sizeof (*reply)); if (!reply) return PAM_CONV_ERR; for (i = 0; i < num_msg && status == PAM_SUCCESS; i++) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = COPY_STRING (cred->user); /* PAM frees resp */ break; case PAM_PROMPT_ECHO_OFF: if (cred->pass) { reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = COPY_STRING (cred->pass); /* PAM frees resp */ } else status = PAM_AUTHTOK_RECOVER_ERR; break; case PAM_TEXT_INFO: case PAM_ERROR_MSG: reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = NULL; break; default: status = PAM_CONV_ERR; } } if (status != PAM_SUCCESS) { for (i = 0; i < num_msg; i++) if (reply[i].resp) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: overwrite_and_free (reply[i].resp); break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: free (reply[i].resp); } } free (reply); } else *resp = reply; return status; } static int authenticate (pies_identity_provider_t provider, pies_identity_t id, char const *passwd) { struct pam_identity_provider_data *pamdata = provider->data; pam_handle_t *pamh; int pamerror; struct pam_cred cred = { id->username, passwd }; struct pam_conv pam_conv = { &pies_conv, &cred }; char const *service = pamdata->service ? pamdata->service : "pies"; do { pamerror = pam_start (service, id->username, &pam_conv, &pamh); if (pamerror != PAM_SUCCESS) break; pamerror = pam_authenticate (pamh, 0); if (pamerror != PAM_SUCCESS) break; pamerror = pam_acct_mgmt (pamh, 0); if (pamerror != PAM_SUCCESS) break; pamerror = pam_setcred (pamh, PAM_ESTABLISH_CRED); } while (0); pam_end (pamh, PAM_SUCCESS); switch (pamerror) { case PAM_SUCCESS: return 0; case PAM_AUTH_ERR: return -1; } //FIXME _log(L_ERR, 0, "PAM authentication error"); return -1; } static int is_group_member (pies_identity_provider_t p, pies_identity_t id, char * const * groups) { struct group *gr; int result = 0; setgrent (); while (!result && (gr = getgrent ())) { if (is_array_member (groups, gr->gr_name)) result = is_array_member (gr->gr_mem, id->username); } endgrent (); return result; } static struct grecs_keyword pam_kw[] = { { "type", "'pam", "Set mechanism type", grecs_type_null }, { "service", NULL, "Service name", grecs_type_string, GRECS_DFLT, NULL, offsetof(struct pam_identity_provider_data, service) }, { NULL } }; static int configure (struct grecs_node *node, pies_identity_provider_t provider) { int i; struct pam_identity_provider_data *data = grecs_calloc (1, sizeof (*data)); provider->data = data; for (i = 0; pam_kw[i].ident; i++) pam_kw[i].varptr = data; if (grecs_tree_process (node->down, pam_kw)) { //FIXME: memory leak return -1; } provider->locus = node->locus; return 0; } static void confhelp (void) { static struct grecs_keyword top[] = { { "identity-provider", "name: string", "Configuration for system identity provider", grecs_type_section, GRECS_INAC, NULL, 0, NULL, NULL, pam_kw }, { NULL } }; grecs_print_statement_array (top, 1, 0, stdout); } struct pies_identity_mechanism pam_identity_mechanism = { "pam", authenticate, is_group_member, NULL, configure, confhelp };