diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 43 | ||||
m--------- | grecs | 0 | ||||
-rw-r--r-- | ident/Makefile.am | 35 | ||||
-rw-r--r-- | ident/ident.c | 74 | ||||
-rw-r--r-- | ident/ident.h | 51 | ||||
-rw-r--r-- | ident/identity.h | 39 | ||||
-rw-r--r-- | ident/pam.c | 230 | ||||
-rw-r--r-- | ident/provider.c | 161 | ||||
-rw-r--r-- | ident/system.c | 137 | ||||
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/arraymember.c | 33 | ||||
-rw-r--r-- | lib/libpies.h | 2 | ||||
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/acl.c | 164 | ||||
-rw-r--r-- | src/acl.h | 4 | ||||
-rw-r--r-- | src/ctl.c | 68 | ||||
-rw-r--r-- | src/inetd-bi.c | 2 | ||||
-rw-r--r-- | src/pies.c | 63 | ||||
-rw-r--r-- | src/pies.h | 7 | ||||
-rw-r--r-- | src/progman.c | 10 |
21 files changed, 1053 insertions, 78 deletions
diff --git a/Makefile.am b/Makefile.am index 63fdb48..0358e8c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,7 +16,7 @@ ACLOCAL_AMFLAGS = -I m4 -I am -I grecs/am -I imprimatur -SUBDIRS=gnu grecs lib src imprimatur doc po +SUBDIRS=gnu grecs lib ident src imprimatur doc po dist-hook: @PATCHLEV=`echo "$(PACKAGE_VERSION)" | \ diff --git a/configure.ac b/configure.ac index 92ffbbd..8fb1eac 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ # This file is part of GNU Pies. -*- autoconf -*- -# Copyright (C) 2009-2014 Sergey Poznyakoff +# Copyright (C) 2009-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 @@ -33,9 +33,12 @@ AC_PROG_YACC AC_PROG_LEX # Checks for libraries. +AC_CHECK_LIB(crypt, crypt) # Checks for header files. -AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h syslog.h unistd.h utmp.h utmpx.h]) +AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h\ + string.h sys/socket.h sys/time.h syslog.h unistd.h utmp.h utmpx.h\ + crypt.h shadow.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_UID_T @@ -80,6 +83,41 @@ AM_ICONV AM_GNU_GETTEXT([external], [need-formatstring-macros]) AM_GNU_GETTEXT_VERSION([0.18]) +# PAM +status_pam=maybe +AC_ARG_ENABLE([pam], + AC_HELP_STRING([--enable-pam], + [enable PAM]), + [ +case "${enableval}" in + yes) status_pam=yes ;; + no) status_pam=no ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-pam]) ;; +esac],[status_pam=maybe]) + +AC_SUBST(PAM_LIBS,-lpam) +if test "$status_pam" != "no"; then + pam=$status_pam + AC_CHECK_HEADERS(security/pam_appl.h) + if test "$ac_cv_header_security_pam_appl_h" = "yes"; then + AC_CHECK_LIB(dl, dlopen, [PAM_LIBS="$PAM_LIBS -ldl"]) + AC_CHECK_LIB(pam, pam_start, + [status_pam=yes], + [status_pam=no], $PAM_LIBS) + else + status_pam=no + fi + if test $pam = yes && test $pam != $status_pam; then + AC_MSG_ERROR([required module PAM cannot be built because of missing prerequisites]) + fi +fi + +AM_CONDITIONAL([PAM_COND], [test $status_pam = yes]) +if test $status_pam = yes; then + AC_DEFINE_UNQUOTED(WITH_PAM, 1, [PAM support enabled]) +fi + +# Build inetd AC_ARG_ENABLE([inetd], AC_HELP_STRING([--enable-inetd], [build and install a replacement for SBINDIR/inetd]), @@ -100,6 +138,7 @@ AC_CONFIG_FILES([Makefile gnu/Makefile lib/Makefile src/Makefile + ident/Makefile doc/Makefile po/Makefile.in]) AC_OUTPUT diff --git a/grecs b/grecs -Subproject e71c1a855797de245105494a05623753e32844a +Subproject 9e978b089268e6bfc4b8fcdf9ef721f6fa92c11 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 <http://www.gnu.org/licenses/>. */ + +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 <http://www.gnu.org/licenses/>. */ + +#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 <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#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 <http://www.gnu.org/licenses/>. */ + +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 <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 +}; + + + 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 <http://www.gnu.org/licenses/>. */ + +#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 <http://www.gnu.org/licenses/>. */ + +#include "ident.h" +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#if defined(HAVE_CRYPT_H) +# include <crypt.h> +#endif +#if defined(HAVE_SHADOW_H) +# include <shadow.h> +#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 +}; + diff --git a/lib/Makefile.am b/lib/Makefile.am index e389846..89812ca 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -19,6 +19,7 @@ noinst_LIBRARIES=libpies.a noinst_HEADERS = libpies.h libpies_a_SOURCES=\ + arraymember.c\ parsetime.c\ proctitle.c\ strtotok.c diff --git a/lib/arraymember.c b/lib/arraymember.c new file mode 100644 index 0000000..14ace99 --- /dev/null +++ b/lib/arraymember.c @@ -0,0 +1,33 @@ +/* This file is part of GNU Pies. + Copyright (C) 2007, 2008, 2009, 2010, 2013 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/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <string.h> +#include "libpies.h" + +int +is_array_member (char * const * ar, char const *str) +{ + for (; *ar; ++ar) + { + if (strcmp (*ar, str) == 0) + return 1; + } + return 0; +} + diff --git a/lib/libpies.h b/lib/libpies.h index 97188b6..b9c8af7 100644 --- a/lib/libpies.h +++ b/lib/libpies.h @@ -56,4 +56,6 @@ int strtotok_len_ci (struct tokendef *tab, const char *str, size_t len, int strtotok (struct tokendef *tab, const char *str, int *pres); int strtotok_ci (struct tokendef *tab, const char *str, int *pres); int toktostr (struct tokendef *tab, int tok, const char **pres); + +int is_array_member (char * const * ar, char const *str); diff --git a/src/Makefile.am b/src/Makefile.am index 04634c7..ab4546f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -60,15 +60,18 @@ SUFFIXES=.opt .c .h cmdline.h: cmdline.opt LDADD = \ + ../ident/libident.a\ ../lib/libpies.a\ @GRECS_LDADD@\ ../gnu/libgnu.a\ - $(MF_PROCTITLE_LIBS) + $(MF_PROCTITLE_LIBS)\ + @PAM_LIBS@ pkgstatedir=$(localstatedir)/pies AM_CPPFLAGS=\ -I$(top_srcdir)/lib\ + -I$(top_srcdir)/ident\ -I$(top_srcdir)/gnu\ -I$(top_builddir)/gnu\ @GRECS_INCLUDES@\ @@ -33,13 +33,21 @@ struct pies_sockaddr struct sockaddr sa; }; +enum name_match + { + match_none, + match_user_name, + match_group_name + }; + struct acl_entry { grecs_locus_t locus; int allow; int authenticated; pies_acl_t acl; - struct grecs_list *groups; + enum name_match name_match; + char **names; struct grecs_list *sockaddrs; }; @@ -53,14 +61,61 @@ struct pies_acl /* ACL creation */ +void +grecs_locus_point_copy (struct grecs_locus_point *dst, + struct grecs_locus_point *src) +{ + dst->file = grecs_strdup (src->file); + dst->line = src->line; + dst->col = src->col; +} + +void +grecs_locus_copy (struct grecs_locus *dst, struct grecs_locus *src) +{ + grecs_locus_point_copy (&dst->beg, &src->beg); + grecs_locus_point_copy (&dst->end, &src->end); +} + +void +grecs_locus_point_free (struct grecs_locus_point *p) +{ + grecs_free (p->file); +} + +void +grecs_locus_free (struct grecs_locus *loc) +{ + grecs_locus_point_free (&loc->beg); + grecs_locus_point_free (&loc->end); +} + +static void +acl_free_entry (void *p) +{ + struct acl_entry *ent = p; + pies_acl_free (ent->acl); + grecs_locus_free (&ent->locus); + grecs_list_free (ent->sockaddrs); + if (ent->names) + { + size_t i; + + for (i = 0; ent->names[i]; i++) + free (ent->names[i]); + free (ent->names); + } + free (ent); +} pies_acl_t pies_acl_create (const char *name, grecs_locus_t *locus) { pies_acl_t acl = xmalloc (sizeof (acl[0])); acl->name = name ? xstrdup (name) : NULL; - acl->locus = *locus; + grecs_locus_copy (&acl->locus, locus); acl->list = grecs_list_create (); + acl->list->free_entry = acl_free_entry; return acl; } @@ -68,6 +123,7 @@ void pies_acl_free (pies_acl_t acl) { free (acl->name); + grecs_locus_free (&acl->locus); grecs_list_free (acl->list); free (acl); } @@ -196,6 +252,12 @@ _parse_sockaddr (struct acl_entry *entry, const grecs_value_t *value) return 0; } +static void +sockaddr_free (void *p) +{ + free (p); +} + static int _parse_from (struct acl_entry *entry, size_t argc, grecs_value_t **argv) { @@ -223,6 +285,7 @@ _parse_from (struct acl_entry *entry, size_t argc, grecs_value_t **argv) } entry->sockaddrs = grecs_list_create (); + entry->sockaddrs->free_entry = sockaddr_free; if (argv[0]->type == GRECS_TYPE_STRING) { if (_parse_sockaddr (entry, argv[0])) @@ -288,22 +351,38 @@ static int _parse_group (struct acl_entry *entry, size_t argc, grecs_value_t **argv) { if (strcmp (argv[0]->v.string, "group") == 0) + entry->name_match = match_group_name; + else if (strcmp (argv[0]->v.string, "user") == 0) + entry->name_match = match_user_name; + else + entry->name_match = match_none; + + if (entry->name_match != match_none) { argc--; argv++; if (argc == 0) { grecs_error (&entry->locus, 0, - _("expected group list, but found end of statement")); + _("expected identity list, but found end of statement")); return 1; } if (argv[0]->type == GRECS_TYPE_STRING) { - entry->groups = grecs_list_create (); - grecs_list_append (entry->groups, xstrdup (argv[0]->v.string)); + entry->names = xcalloc (2, sizeof (entry->names[0])); + entry->names[0] = xstrdup (argv[0]->v.string); + entry->names[1] = NULL; } else - entry->groups = argv[0]->v.list; + { + size_t i; + struct grecs_list_entry *ep; + entry->names = xcalloc (argv[0]->v.list->count + 1, + sizeof (entry->names[0])); + for (i = 0, ep = argv[0]->v.list->head; ep; ep = ep->next, ++i) + entry->names[i] = xstrdup (ep->data); + entry->names[i] = NULL; + } argc--; argv++; } @@ -327,29 +406,30 @@ parse_acl_line (grecs_locus_t *locus, int allow, pies_acl_t acl, { struct acl_entry *entry = xzalloc (sizeof (*entry)); - entry->locus = *locus; + grecs_locus_copy (&entry->locus, locus); entry->allow = allow; - switch (value->type) - { - case GRECS_TYPE_STRING: - if (_parse_token (entry, value)) - { - grecs_error (&entry->locus, 0, _("unknown word `%s'"), - value->v.string); + if (value) + switch (value->type) + { + case GRECS_TYPE_STRING: + if (_parse_token (entry, value)) + { + grecs_error (&entry->locus, 0, _("unknown word `%s'"), + value->v.string); + return 1; + } + break; + + case GRECS_TYPE_ARRAY: + if (_parse_acl (entry, value->v.arg.c, value->v.arg.v)) return 1; - } - break; - - case GRECS_TYPE_ARRAY: - if (_parse_acl (entry, value->v.arg.c, value->v.arg.v)) + break; + + case GRECS_TYPE_LIST: + grecs_error (locus, 0, _("unexpected list")); return 1; - break; - - case GRECS_TYPE_LIST: - grecs_error (locus, 0, _("unexpected list")); - return 1; - } + } grecs_list_append (acl->list, entry); return 0; } @@ -480,12 +560,12 @@ deny_cb (enum grecs_callback_command cmd, struct grecs_keyword acl_keywords[] = { /* TRANSLATORS: only words within angle brackets are translatable */ - { "allow", N_("[all|authenticated|group <grp: list>] [from <addr: list>]"), + { "allow", N_("[all|authenticated|user <usr: list>|group <grp: list>] [from <addr: list>]"), N_("Allow access"), grecs_type_string, GRECS_MULT, NULL, 0, allow_cb }, /* TRANSLATORS: only words within angle brackets are translatable */ - { "deny", N_("[all|authenticated|group <grp: list>] [from <addr: list>]"), + { "deny", N_("[all|authenticated|user <usr: list>|group <grp: list>] [from <addr: list>]"), N_("Deny access"), grecs_type_string, GRECS_MULT, NULL, 0, deny_cb }, @@ -534,32 +614,30 @@ _check_sockaddr (struct pies_sockaddr *sptr, struct acl_input *input) } static int -match_group (const char **groups, const char *arg) -{ - for (; *groups; groups++) - if (strcmp (*groups, arg) == 0) - return 1; - return 0; -} - -static int _acl_check (struct acl_entry *ent, struct acl_input *input) { int result = 1; if (ent->authenticated) { - result = input->user != NULL; + result = input->identity != NULL; if (!result) return result; } - if (ent->groups) + switch (ent->name_match) { - struct grecs_list_entry *ep; + case match_none: + break; - for (ep = ent->groups->head; result && ep; ep = ep->next) - result = match_group (input->groups, ep->data); + case match_user_name: + result = pies_identity_is_user (input->identity, ent->names); + if (!result) + return result; + break; + + case match_group_name: + result = pies_identity_is_group_member (input->identity, ent->names); if (!result) return result; } @@ -646,7 +724,7 @@ acl_copy (void *a, void *b) } static void -acl_free_entry (void *p) +acl_free (void *p) { pies_acl_free (p); } @@ -664,7 +742,7 @@ pies_acl_install (pies_acl_t acl) acl_compare, acl_copy, NULL, - acl_free_entry); + acl_free); if (!acl_table) xalloc_die (); } @@ -21,11 +21,11 @@ struct acl_input { struct sockaddr *addr; socklen_t addrlen; - const char *user; - const char **groups; + pies_identity_t identity; }; pies_acl_t pies_acl_create (const char *name, grecs_locus_t *locus); +void pies_acl_free (pies_acl_t acl); int pies_acl_check (pies_acl_t acl, struct acl_input *input, int result); int parse_acl_line (grecs_locus_t *locus, int allow, pies_acl_t acl, grecs_value_t *value); @@ -17,11 +17,14 @@ #include "pies.h" #include "prog.h" #include "xvasprintf.h" +#include "identity.h" #define DEFAULT_CONTROL_URL "unix:///tmp/%s.ctl" struct control control; +pies_identity_t identity; + struct ctlbuf { @@ -107,6 +110,7 @@ ctlbuf_chomp (struct ctlbuf *buf) struct ctlio; +static void cmd_auth (struct ctlio *, size_t, char **); static void cmd_quit (struct ctlio *, size_t, char **); static void cmd_noop (struct ctlio *, size_t, char **); static void cmd_help (struct ctlio *, size_t, char **); @@ -129,6 +133,8 @@ struct ctlio_command }; static struct ctlio_command cmdtab[] = { + { "auth", "authenticate", + CTL_INITIAL_STATE, 3, 3, cmd_auth }, { "noop", "no operation", CTL_ALL_STATES, 1, 1, cmd_noop }, { "id", "identify the instance", @@ -167,6 +173,8 @@ ctlio_command_find (char const *verb) struct ctlio { + union pies_sockaddr_storage addr; + socklen_t addrlen; int state; int action; struct ctlbuf ibuf; @@ -181,7 +189,8 @@ ctlio_create (void) struct ctlio *io; io = xmalloc (sizeof (*io)); - io->state = CTL_AUTHENTICATED_STATE; //FIXME CTL_INITIAL_STATE; + io->state = identity_provider_list + ? CTL_INITIAL_STATE : CTL_AUTHENTICATED_STATE; io->action = ACTION_CONT; ctlbuf_init (&io->ibuf); ctlbuf_init (&io->obuf); @@ -308,12 +317,56 @@ static void ctlio_initial_reply (struct ctlio *io) { ctlio_printf (io, "220 %s", instance); - ctlio_printf (io, " <%s>", "foobarbaz"); //FIXME: auth mechanisms ctlio_eol (io); } static void +cmd_auth (struct ctlio *io, size_t argc, char **argv) +{ + struct grecs_list_entry *ep; + pies_identity_t id = pies_identity_create (argv[1]); + int auth = 0; + + for (ep = identity_provider_list->head; ep; ep = ep->next) + { + pies_identity_provider_t provider = ep->data; + char const *pname = pies_identity_provider_name (provider); + + debug(1, ("trying %s...", pname)); + if (pies_authenticate (provider, id, argv[2]) == 0) + { + if (check_acl (control.id_acl, + (struct sockaddr *)&io->addr, io->addrlen, id)) + { + logmsg (LOG_AUTH, "%s authenticated via %s, but failed ACL check", + argv[1], pname); + auth = 0; + } + else + { + logmsg (LOG_AUTH, "%s authenticated via %s", + argv[1], pname); + auth = 1; + } + break; + } + } + + if (auth) + { + ctlio_reply (io, "230", "authentication successful"); + identity = id; + io->state = CTL_AUTHENTICATED_STATE; + } + else + { + pies_identity_destroy (id); + ctlio_reply (io, "531", "access denied"); + } +} + +static void cmd_noop (struct ctlio *io, size_t argc, char **argv) { ctlio_reply (io, "220", "%s attending", instance); @@ -347,8 +400,11 @@ cmd_help (struct ctlio *io, size_t argc, char **argv) ctlio_reply (io, "113", "help text follows"); for (cp = cmdtab; cp->verb; cp++) { - ctlio_printf (io, "%-9s%s", cp->verb, cp->descr); - ctlio_eol (io); + if (cp->states & io->state) + { + ctlio_printf (io, "%-9s%s", cp->verb, cp->descr); + ctlio_eol (io); + } } ctlio_eot (io); } @@ -1000,7 +1056,7 @@ ctl_accept (int socket, void *data) free (s); } - if (check_acl (control.acl, (struct sockaddr *)&addr, addrlen)) + if (check_acl (control.conn_acl, (struct sockaddr *)&addr, addrlen, NULL)) { close (fd); return 1; @@ -1008,6 +1064,8 @@ ctl_accept (int socket, void *data) /* FIXME: Check number of connections? */ io = ctlio_create (); + io->addr = addr; + io->addrlen = addrlen; ctlio_initial_reply (io); register_socket (fd, NULL, ctlwr, NULL, io); diff --git a/src/inetd-bi.c b/src/inetd-bi.c index e472390..de9cfa0 100644 --- a/src/inetd-bi.c +++ b/src/inetd-bi.c @@ -357,7 +357,7 @@ tcpmux (int fd, struct component const *comp) return; } - if (check_acl (comp->acl, (struct sockaddr *) &sa, salen)) + if (check_acl (comp->acl, (struct sockaddr *) &sa, salen, NULL)) { fd_report (fd, "-Service not available\r\n"); return; @@ -18,6 +18,7 @@ #include <locale.h> #include <configmake.h> #include "meta1lex.h" +#include "identity.h" int preprocess_only; /* Preprocess config, do nothing more */ int lint_mode; /* Test configuration syntax and exit */ @@ -1541,9 +1542,15 @@ struct grecs_keyword control_keywords[] = { &control.url, 0, _cb_url}, {"acl", N_("name: string"), - N_("Set ACL."), + N_("Set connection ACL."), + grecs_type_section, GRECS_DFLT, + &control.conn_acl, 0, + acl_section_parser, NULL, acl_keywords}, + {"identity-acl", + N_("name: string"), + N_("Set identity ACL."), grecs_type_section, GRECS_DFLT, - &control.acl, 0, + &control.id_acl, 0, acl_section_parser, NULL, acl_keywords}, {"idle-timeout", "n", @@ -1760,6 +1767,8 @@ struct grecs_keyword pies_keywords[] = { &mailer_command_line, 0, NULL }, + { "identity-provider", "name: string", "Configure identity provider", + grecs_type_section, GRECS_INAC | GRECS_HIDDEN }, {NULL} }; @@ -1775,6 +1784,39 @@ config_init () obstack_grow (&pp_stk, DEFAULT_PREPROCESSOR, sizeof (DEFAULT_PREPROCESSOR) - 1); } + pies_identity_mechanism_register (&system_identity_mechanism); +#ifdef WITH_PAM + pies_identity_mechanism_register (&pam_identity_mechanism); +#endif +} + +static void +config_error () +{ + if (!init_process) + exit (EX_CONFIG); +} + +void +config_parse (char const *name) +{ + struct grecs_node *node; + struct grecs_node *tree = grecs_parse (name); + if (!tree) + config_error (); + + for (node = tree; node; node = node->next) + { + node = grecs_find_node (node, "identity-provider"); + if (!node) + break; + pies_config_provider (node); + } + + if (grecs_tree_process (tree, pies_keywords)) + config_error (); + + grecs_tree_free (tree); } void @@ -1786,6 +1828,7 @@ config_help () "For more information, use `info pies configuration'."); grecs_print_docstring (docstring, 0, stdout); grecs_print_statement_array (pies_keywords, 1, 0, stdout); + pies_config_identity_mechanisms_help (); } static enum config_syntax current_syntax = CONF_PIES; @@ -2241,13 +2284,6 @@ set_state_file_names (const char *base) qotdfile = mkfilename (statedir, base, ".qotd"); } -static void -config_error () -{ - if (!init_process) - exit (EX_CONFIG); -} - int main (int argc, char **argv) { @@ -2367,13 +2403,8 @@ main (int argc, char **argv) switch (file->syntax) { case CONF_PIES: - { - struct grecs_node *tree = grecs_parse (file->name); - if (!tree || grecs_tree_process (tree, pies_keywords)) - config_error (); - grecs_tree_free (tree); - break; - } + config_parse (file->name); + break; case CONF_INETD: if (inetd_parse_conf (file->name)) @@ -58,6 +58,7 @@ #include "quotearg.h" #include "fprintftime.h" +#include "identity.h" #include "acl.h" #include "libpies.h" @@ -338,7 +339,8 @@ void progman_iterate_comp (int (*fun) (struct component *, void *), void fd_report (int fd, const char *msg); -int check_acl (pies_acl_t acl, struct sockaddr *s, socklen_t salen); +int check_acl (pies_acl_t acl, struct sockaddr *s, socklen_t salen, + pies_identity_t identity); void log_setup (int want_stderr); void signal_setup (RETSIGTYPE (*sf)(int)); @@ -561,7 +563,8 @@ void sysvinit_acct (int what, const char *user, const char *id, pid_t pid, struct control { struct pies_url *url; - pies_acl_t acl; + pies_acl_t conn_acl; + pies_acl_t id_acl; unsigned int idle_timeout; }; diff --git a/src/progman.c b/src/progman.c index 12969ab..1ec5a8d 100644 --- a/src/progman.c +++ b/src/progman.c @@ -1301,7 +1301,8 @@ prog_start (struct prog *prog) } int -check_acl (pies_acl_t acl, struct sockaddr *s, socklen_t salen) +check_acl (pies_acl_t acl, struct sockaddr *s, socklen_t salen, + pies_identity_t identity) { struct acl_input input; int rc; @@ -1311,8 +1312,7 @@ check_acl (pies_acl_t acl, struct sockaddr *s, socklen_t salen) input.addr = s; input.addrlen = salen; - input.user = NULL; - input.groups = NULL; + input.identity = identity; rc = pies_acl_check (acl, &input, 1); if (rc == 0) @@ -1374,8 +1374,8 @@ _prog_accept (struct prog *p) free (s); } - if (check_acl (p->v.p.comp->acl, (struct sockaddr *)&addr, addrlen) - || check_acl (pies_acl, (struct sockaddr *)&addr, addrlen)) + if (check_acl (p->v.p.comp->acl, (struct sockaddr *)&addr, addrlen, NULL) + || check_acl (pies_acl, (struct sockaddr *)&addr, addrlen, NULL)) { fd_report (fd, p->v.p.comp->access_denied_message); close (fd); |