From 6bb908898b833ec69c66e918de732af5bad68934 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Wed, 16 Dec 2015 14:58:07 +0200 Subject: Implement authentication on control socket. * Makefile.am (SUBDIRS): Add src. * configure.ac: Check for crypt.h and PAM Build ident/Makefile * grecs: Update. * ident/Makefile.am: New file. * ident/ident.c: New file. * ident/ident.h: New file. * ident/identity.h: New file. * ident/pam.c: New file. * ident/provider.c: New file. * ident/system.c: New file. * lib/Makefile.am: Add arraymember.c * lib/arraymember.c: New file. * lib/libpies.h (is_array_member): New proto. * src/Makefile.am (LDADD): Add libident.a and @PAM_LIBS@ * src/acl.c (acl_entry): Remove groups. Add new members: names and name_match. (pies_acl_create): Deep copy the locus. Set free_entry function for the list. (pies_acl_free): Free locus. (_parse_from): Set free_entry function for the list. (_parse_group): Parse the "user" construct. (parse_acl_line): Deep copy the locus. Allow for null value. (acl_keywords): Update docstrings. (_acl_check): Rewrite identity checks. * src/acl.h (acl_input): Remove. : New member. (pies_acl_free): New proto. * src/ctl.c (identity): New global. (cmdtab): New command: auth (ctlio) : New members. (ctlio_create): Start from authenticated state only if no identity_providers are configured. (cmd_auth): New function. (cmd_help): Print only commands that are available in the current state. (ctl_accept): Initialize io->addr and io->addrlen. * src/inetd-bi.c: Change call to check_acl * src/pies.c: Include identity.h (control_keywords): New statement "identity-acl" (pies_keywords): New statement "identity-provider" (config_init): Register identity mechanisms. (config_parse): New function. (config_help): Print help on identity-provider statements. (main): Use config_parse to parse grecs-style configurations. * src/pies.h: Include identity.h (check_acl): Change argument list. All callers changed. (control): Remove acl. Add conn_acl and id_acl instead. * src/progman.c (check_acl): Change argument list. Take identity as the 3rd argument. --- ident/Makefile.am | 35 +++++++++ ident/ident.c | 74 ++++++++++++++++++ ident/ident.h | 51 ++++++++++++ ident/identity.h | 39 +++++++++ ident/pam.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ident/provider.c | 161 ++++++++++++++++++++++++++++++++++++++ ident/system.c | 137 ++++++++++++++++++++++++++++++++ 7 files changed, 727 insertions(+) create mode 100644 ident/Makefile.am create mode 100644 ident/ident.c create mode 100644 ident/ident.h create mode 100644 ident/identity.h create mode 100644 ident/pam.c create mode 100644 ident/provider.c create mode 100644 ident/system.c (limited to 'ident') diff --git a/ident/Makefile.am b/ident/Makefile.am new file mode 100644 index 0000000..65ac145 --- /dev/null +++ b/ident/Makefile.am @@ -0,0 +1,35 @@ +# 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 . */ + +noinst_LIBRARIES = libident.a +noinst_HEADERS = ident.h identity.h + +libident_a_SOURCES = \ + provider.c\ + ident.c\ + system.c + +if PAM_COND + libident_a_SOURCES += pam.c +endif + +AM_CPPFLAGS=\ + -I$(top_srcdir)/lib\ + -I.\ + -I$(top_srcdir)/gnu\ + -I$(top_builddir)/gnu\ + @GRECS_INCLUDES@ + diff --git a/ident/ident.c b/ident/ident.c new file mode 100644 index 0000000..38ae1a8 --- /dev/null +++ b/ident/ident.c @@ -0,0 +1,74 @@ +/* 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 . */ + +#include "ident.h" + +pies_identity_t +pies_identity_create (char const *user) +{ + pies_identity_t id = xmalloc (sizeof (*id)); + id->provider = NULL; + id->username = xstrdup (user); + id->data = NULL; + return id; +} + +int +pies_authenticate (pies_identity_provider_t pr, pies_identity_t id, + char const *passwd) +{ + if (!pr || !id) + return -1; + + if (pr->mech->authenticate (pr, id, passwd) == 0) + { + id->provider = pr; + return 0; + } + return 1; +} + +int +pies_identity_is_user (pies_identity_t id, char * const * users) +{ + if (!id) + return 0; + return is_array_member (users, id->username); +} + +int +pies_identity_is_group_member (pies_identity_t id, char * const * groups) +{ + pies_identity_provider_t provider; + if (!id) + return 0; + provider = id->provider; + if (!provider) + return 0; + return provider->mech->is_group_member (provider, id, groups); +} + +void +pies_identity_destroy (pies_identity_t id) +{ + pies_identity_provider_t provider = id->provider; + if (provider && provider->mech->destroy_identity) + provider->mech->destroy_identity (provider, id); + free (id); +} + + + diff --git a/ident/ident.h b/ident/ident.h new file mode 100644 index 0000000..313926c --- /dev/null +++ b/ident/ident.h @@ -0,0 +1,51 @@ +/* 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 . */ + +#include +#include "xalloc.h" +#include "libpies.h" +#include "grecs.h" +#include "identity.h" + +struct pies_identity +{ + pies_identity_provider_t provider; + char *username; + void *data; +}; + +struct pies_identity_mechanism +{ + char const *name; + int (*authenticate) (pies_identity_provider_t p, + pies_identity_t id, char const *passwd); + int (*is_group_member) (pies_identity_provider_t p, + pies_identity_t id, char * const * groups); + void (*destroy_identity) (pies_identity_provider_t p, + pies_identity_t id); + int (*configure)(struct grecs_node *, pies_identity_provider_t); + void (*confhelp) (void); +}; + +struct pies_identity_provider +{ + char *name; + pies_identity_mechanism_t mech; + struct grecs_locus locus; + void *data; +}; + + diff --git a/ident/identity.h b/ident/identity.h new file mode 100644 index 0000000..0ee129d --- /dev/null +++ b/ident/identity.h @@ -0,0 +1,39 @@ +/* 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 . */ + +typedef struct pies_identity *pies_identity_t; +typedef struct pies_identity_provider *pies_identity_provider_t; +typedef struct pies_identity_mechanism *pies_identity_mechanism_t; + +pies_identity_t pies_identity_create (char const *user); +void pies_identity_destroy (pies_identity_t id); + +int pies_authenticate (pies_identity_provider_t pr, pies_identity_t id, + char const *passwd); +int pies_identity_is_user (pies_identity_t id, char * const * users); +int pies_identity_is_group_member (pies_identity_t id, char * const * groups); + +char const *pies_identity_provider_name (pies_identity_provider_t p); + +int pies_identity_mechanism_register (pies_identity_mechanism_t mech); +void pies_config_identity_mechanisms_help (void); +int pies_config_provider (struct grecs_node *node); + +extern struct pies_identity_mechanism system_identity_mechanism; +#ifdef WITH_PAM +extern struct pies_identity_mechanism pam_identity_mechanism; +#endif +extern struct grecs_list *identity_provider_list; 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 . */ + +#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; \ + } 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 +}; + + + diff --git a/ident/provider.c b/ident/provider.c new file mode 100644 index 0000000..dd7fc3d --- /dev/null +++ b/ident/provider.c @@ -0,0 +1,161 @@ +/* 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 . */ + +#include "ident.h" + +struct grecs_list *identity_provider_list; + +static struct grecs_symtab *idmech_symtab; + +static int +idmech_copy (void *a, void *b) +{ + pies_identity_mechanism_t ma = a; + pies_identity_mechanism_t mb = b; + + *ma = *mb; + ma->name = xstrdup (mb->name); + return 0; +} + +int +pies_identity_mechanism_register (pies_identity_mechanism_t mech) +{ + int install; + + if (!idmech_symtab) + { + idmech_symtab = grecs_symtab_create (sizeof(*mech), + NULL, + NULL, + idmech_copy, + NULL, + NULL); + if (!idmech_symtab) + grecs_alloc_die(); + } + + install = 1; + if (!grecs_symtab_lookup_or_install (idmech_symtab, mech, &install)) + abort (); + return !install; +} + +pies_identity_mechanism_t +pies_identity_mechanism_lookup (char const *name) +{ + struct pies_identity_mechanism key; + if (!idmech_symtab) + return NULL; + key.name = name; + return grecs_symtab_lookup_or_install (idmech_symtab, &key, NULL); +} + +static int +idmech_help (void *data, void *unused) +{ + pies_identity_mechanism_t p = data; + + if (p->confhelp) + p->confhelp (); + return 0; +} + +void +pies_config_identity_mechanisms_help () +{ + grecs_symtab_enumerate (idmech_symtab, idmech_help, NULL); +} + +static int +get_string_node (struct grecs_node *node, const char *name, + struct grecs_node **pret) +{ + struct grecs_node *p = grecs_find_node (node->down, name); + + if (!p) + { + grecs_error (&node->locus, 0, + "no \"%s\" statement found", name); + return 1; + } + + if (p->type != grecs_node_stmt) + { + grecs_error (&p->locus, 0, "must be simple statement"); + return 1; + } + + if (p->v.value->type != GRECS_TYPE_STRING) + { + grecs_error (&p->locus, 0, "must be scalar"); + return 1; + } + + *pret = p; + return 0; +} + +int +pies_config_provider (struct grecs_node *node) +{ + char const *name; + pies_identity_mechanism_t mp; + struct pies_identity_provider *prov; + struct grecs_node *p; + + if (node->v.value->type != GRECS_TYPE_STRING) + { + grecs_error (&node->locus, 0, "value must be scalar"); + return 1; + } + + name = node->v.value->v.string; + + if (get_string_node (node, "type", &p)) + return 1; + + mp = pies_identity_mechanism_lookup (p->v.value->v.string); + if (!mp) + { + grecs_error (&p->locus, 0, "no such mechanism"); + return 1; + } + + prov = xcalloc (1, sizeof (*prov)); + prov->name = xstrdup (name); + prov->mech = mp; + prov->locus = node->locus; + + if (mp->configure && mp->configure (node, prov)) + { + grecs_error (&node->locus, 0, "provider configration failed"); + //FIXME: memory leak + return 1; + } + + if (!identity_provider_list) + identity_provider_list = grecs_list_create (); + grecs_list_append (identity_provider_list, prov); + + return 0; +} + +char const * +pies_identity_provider_name (pies_identity_provider_t p) +{ + return p->name; +} diff --git a/ident/system.c b/ident/system.c new file mode 100644 index 0000000..bde667c --- /dev/null +++ b/ident/system.c @@ -0,0 +1,137 @@ +/* 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 . */ + +#include "ident.h" +#include +#include +#include +#include +#include +#include +#if defined(HAVE_CRYPT_H) +# include +#endif +#if defined(HAVE_SHADOW_H) +# include +#endif + +struct system_identity_data +{ + gid_t gid; +}; + +static int +system_authenticate (pies_identity_provider_t pr, pies_identity_t id, + char const *passwd) +{ + struct passwd *pwd; + char *encrypted_pass = NULL; + + pwd = getpwnam (id->username); + if (!pwd) + return -1; + + encrypted_pass = pwd->pw_passwd; + +#if defined(HAVE_SHADOW_H) + { + struct spwd *sp; + if ((sp = getspnam (id->username)) != NULL) + { + if (sp->sp_expire > 0 && time (NULL) > sp->sp_expire * 86400) + return -1; + encrypted_pass = sp->sp_pwdp; + } + } +#endif + + if (strcmp (crypt (passwd, encrypted_pass), encrypted_pass) == 0) + { + struct system_identity_data *data = xmalloc (sizeof (*data)); + data->gid = pwd->pw_gid; + id->data = data; + return 0; + } + return -1; +} + +static int +system_is_group_member (pies_identity_provider_t provider, + pies_identity_t id, char * const * groups) +{ + struct system_identity_data *data = id->data; + struct group *gr; + int result = 0; + + setgrent (); + while ((gr = getgrent ())) + { + char **p; + + if (!is_array_member (groups, gr->gr_name)) + continue; + + if (gr->gr_gid == data->gid) + { + result = 1; + break; + } + + for (p = gr->gr_mem; *p; p++) + if (strcmp (*p, id->username) == 0) + { + result = 1; + break; + } + } + endgrent (); + return result; +} + +static void +system_destroy_identity (pies_identity_provider_t p, pies_identity_t id) +{ + if (id->data) + free (id->data); +} + +static void +confhelp (void) +{ + static struct grecs_keyword kw[] = { + { "type", "'system", "Set mechanism type", grecs_type_null }, + { NULL } + }; + + static struct grecs_keyword top[] = { + { "identity-provider", "name: string", + "Configuration for system identity provider", + grecs_type_section, GRECS_INAC, NULL, 0, NULL, NULL, + kw }, + { NULL } + }; + grecs_print_statement_array (top, 1, 0, stdout); +} + +struct pies_identity_mechanism system_identity_mechanism = { + "system", + system_authenticate, + system_is_group_member, + system_destroy_identity, + NULL, + confhelp +}; + -- cgit v1.2.1