aboutsummaryrefslogtreecommitdiff
path: root/ident/pam.c
diff options
context:
space:
mode:
Diffstat (limited to 'ident/pam.c')
-rw-r--r--ident/pam.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/ident/pam.c b/ident/pam.c
new file mode 100644
index 0000000..ef32c4d
--- /dev/null
+++ b/ident/pam.c
@@ -0,0 +1,230 @@
+/* This file is part of GNU Pies.
+ Copyright (C) 2015 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 <http://www.gnu.org/licenses/>. */
+
+#include "ident.h"
+#include <grp.h>
+#include <security/pam_appl.h>
+
+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; \
+ } 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 ((gr = getgrent ()))
+ {
+ char **p;
+
+ if (!is_array_member (groups, gr->gr_name))
+ continue;
+
+ for (p = gr->gr_mem; *p; p++)
+ if (strcmp (*p, id->username) == 0)
+ {
+ result = 1;
+ break;
+ }
+ }
+ 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 = xcalloc (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
+};
+
+
+

Return to:

Send suggestions and report system problems to the System administrator.