diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2014-05-06 17:24:58 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2014-05-06 17:24:58 +0300 |
commit | 0c37dfded3f1208bb1fa4ff06dd30ac5ff522308 (patch) | |
tree | 30095b6536d0f9aa0f28e6a8b3480a2dd0c5343d | |
parent | 43427d51405bbda49f8f86e0a5426940a6fb578a (diff) | |
download | pam-modules-0c37dfded3f1208bb1fa4ff06dd30ac5ff522308.tar.gz pam-modules-0c37dfded3f1208bb1fa4ff06dd30ac5ff522308.tar.bz2 |
New module: pam_groupmember
* Makefile.am: Add pam_groupmember.
* configure.ac: Likewise.
* NEWS: Update.
* pam_groupmember/Makefile.am: New file.
* pam_groupmember/pam_groupmember.c: New file,
* doc/Makefile.am: Add pam_groupmember.8
* doc/pam-modules.texi: Document pam_groupmember
* doc/pam_groupmember.8: New file.
* lib/graypam.h (gray_calloc, gray_strdup): New protos.
* lib/mem.c (gray_calloc, gray_strdup): New functions.
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | configure.ac | 7 | ||||
-rw-r--r-- | doc/Makefile.am | 3 | ||||
-rw-r--r-- | doc/pam-modules.texi | 46 | ||||
-rw-r--r-- | doc/pam_groupmember.8 | 125 | ||||
-rw-r--r-- | lib/graypam.h | 2 | ||||
-rw-r--r-- | lib/mem.c | 17 | ||||
-rw-r--r-- | pam_groupmember/Makefile.am | 22 | ||||
-rw-r--r-- | pam_groupmember/pam_groupmember.c | 261 |
10 files changed, 491 insertions, 4 deletions
diff --git a/Makefile.am b/Makefile.am index 3953956..e72f2c3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,9 @@ endif if PAM_COND_UMOTD UMOTD_DIR=pam_umotd endif +if PAM_COND_GROUPMEMBER + GROUPMEMBER_DIR=pam_groupmember +endif SUBDIRS = \ imprimatur\ doc\ @@ -42,6 +45,7 @@ SUBDIRS = \ pam_sql\ $(LDAPHOME_DIR)\ $(UMOTD_DIR)\ + $(GROUPMEMBER_DIR)\ pamck EXTRA_DIST=ChangeLog.svn @@ -1,4 +1,4 @@ -pam-modules -- history of user-visible changes. 2014-03-28 +pam-modules -- history of user-visible changes. 2014-05-06 Copyright (C) 2001, 2004-2005, 2007-2012, 2014 Sergey Poznyakoff See the end of file for copying conditions. @@ -7,7 +7,11 @@ Please send pam-modules bug reports to <bug-pam-modules@gnu.org.ua> Version 1.8.92, (Git) -Bugfixes +* New module pam_groupmember + +Tests whether the user is a member of one or more groups. + +* Bugfixes Version 1.8, 2013-07-29 diff --git a/configure.ac b/configure.ac index 77b9dcb..fdc272e 100644 --- a/configure.ac +++ b/configure.ac @@ -94,6 +94,7 @@ PM_ENABLE(fshadow,[ PM_ENABLE(log) PM_ENABLE(regex) +PM_ENABLE(groupmember) # Check for SQL support @@ -229,7 +230,7 @@ delim="-------------------------------------------------------------------" echo $delim | tr '-' '*' echo "Modules to build:" res= -for module in fshadow regex log pgsql mysql ldaphome umotd +for module in fshadow regex log pgsql mysql ldaphome umotd groupmember do modname=pam_$module eval enable=\${build_$module} @@ -251,7 +252,8 @@ build_log=$build_log build_pgsql=$build_pgsql build_mysql=$build_mysql build_ldaphome=$build_ldaphome -build_umotd=$build_umotd]) +build_umotd=$build_umotd +build_groupmember=$build_groupmember]) AC_OUTPUT(Makefile doc/Makefile @@ -262,4 +264,5 @@ AC_OUTPUT(Makefile pam_sql/Makefile pam_ldaphome/Makefile pam_umotd/Makefile + pam_groupmember/Makefile pamck/Makefile) diff --git a/doc/Makefile.am b/doc/Makefile.am index d42be90..e792390 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -39,6 +39,9 @@ endif if PAM_COND_PGSQL dist_man_MANS += pam_pgsql.8 endif +if PAM_COND_GROUPMEMBER + dist_man_MANS += pam_groupmember.8 +endif config.so: $(top_srcdir)/configure.ac $(top_srcdir)/doc/Makefile.am $(AM_V_GEN){\ diff --git a/doc/pam-modules.texi b/doc/pam-modules.texi index 28043f4..a0578da 100644 --- a/doc/pam-modules.texi +++ b/doc/pam-modules.texi @@ -34,6 +34,7 @@ * pam_ldaphome (pam-modules)ldaphome Maintain home directories and SSH keys od LDAP users. * pam_umotd (pam-modules)umotd Display a user-specific MOTD. +* pam_groupmember (pam_modules)groupmember Test group membership. * pamck: (pam-modules)pamck. Verify PAM Access. @end direntry @end ifinfo @@ -85,6 +86,7 @@ Individual modules * sql:: Modules for SQL authentication and session management. * ldaphome:: Maintain home directories and SSH keys of LDAP users. * umotd:: Display a user-specific MOTD. +* groupmember:: Test group membership. * Reporting Bugs:: How to Report a Bug. @@ -1453,6 +1455,50 @@ Exit immediately if the 5-minute load average is greater than or equal to @var{d} (a floating-point number). @end table +@node groupmember +@chapter pam_groupmember +@set MODULE pam_umotd +@cindex groupmember +@cindex group membership +@cindex test group membership + +The @command{pam_groupmember} module checks whether the user is member +of one or more groups. Both primary and supplementary groups are +checked. The list of groups to be checked is given with the +@option{groups} option. Its argument is a comma-separated list of +group names of numeric IDs, prefixed with @samp{+} sign. + +The module returns PAM_SUCCESS if the user is member of one of the +supplied groups and PAM_AUTH_ERR on otherwise. The return value can +be inverted using the @option{sense=deny} option. + +Additionally, the module can return PAM_USER_UNKNOWN if the user is +not known and PAM_AUTHINFO_UNAVAIL if unable to retrieve the user +name. + +The @command{pam_groupmember} module can be used in any PAM service stack. + +@menu +* summary of pam_groupmember options:: +@end menu + +@node summary of pam_groupmember options +@section Summary of @command{pam_groupmember} options + +@table @option +@opsummary{groups} +@item groups=@var{group-list} +Defines groups to check against. The argument is a comma-separated +list of group names or IDs. Group IDs must be prefixed with a plus +sign. + +@opsummary{sense} +@item sense=@{allow|deny@} + What to do on success. The value @samp{allow} means to return +@code{PAM_SUCCESS}, @samp{deny} means to return @code{PAM_AUTH_ERR}. +Default is @samp{allow}. +@end table + @node Reporting Bugs @chapter How to Report a Bug diff --git a/doc/pam_groupmember.8 b/doc/pam_groupmember.8 new file mode 100644 index 0000000..243cefb --- /dev/null +++ b/doc/pam_groupmember.8 @@ -0,0 +1,125 @@ +.\" This file is part of PAM-Modules -*- nroff -*- +.\" Copyright (C) 2001-2014 Sergey Poznyakoff +.\" +.\" PAM-Modules 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. +.\" +.\" PAM-Modules 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 PAM-Modules. If not, see <http://www.gnu.org/licenses/>. +.TH PAM_GROUPMEMBER 8 "May 6, 2014" "PAM-MODULES" "Pam-Modules User Reference" +.SH NAME +pam_groupmember \- test group membership +.SH SYNOPSIS +.nh +.na +\fBpam_groupmember\fR\ + \fBgroups=\fIGROUPLIST\fR\ + [\fBsense=\fISENSE\fR]\ + [\fBdebug\fR[\fB=\fINUMBER\fR]]\ + [\fBwaitdebug\fR]\ + [\fBaudit\fR] +.ad +.hy +.SH DESCRIPTION +Tests if the user is member of at least one group from +\fIGROUPLIST\fR. \fIGROUPLIST\fR is a comma-separated list of +group names or IDs (numbers prefixed with a plus sign). +.SH OPTIONS +.TP +\fBgroups=\fIGROUPLIST\fR +Defines a list of groups to check against. \fIGROUPLIST\fR is a +comma-separated list of group names. A group name prefixed with a +plus sign is treated as GID number. +.TP +\fBsense=allow\fR|\fBdeny\fR +What to do if the user name matches the expression given by the +\fBregex\fR option. The value \fBallow\fR (the default) instructs the +module to return \fBPAM_SUCCESS\fR, the \fBdeny\fR instructs it to +return \fBPAM_AUTH_ERR\fR. +.TP +\fBdebug\fR[\fB=\fINUMBER\fR] +Set debugging level (0 <= \fINUMBER\fR <= 100). +.TP +\fBaudit\fR +Log auditing information. +.TP +\fBwaitdebug=\fIN\fR +Wait for \fIN\fR seconds before starting up. This option is intended +to facilitate attaching to the module with +.BR gdb (1). +It is available only if the package was configured with +the \fB\-\-enable\-debug\fR option. +.SH MODULE TYPES PROVIDED +All module types (\fBaccount\fR, \fBauth\fR, \fBpassword\fR and +\fBsession\fR) are provided. +.SH RETURN VALUES +.TP +.B PAM_SUCCESS +Successful return. +.TP +.B PAM_AUTH_ERR +Authentication failed. +.TP +.B PAM_AUTHINFO_UNAVAIL +The input information is not sufficient. +.TP +.B PAM_USER_UNKNOWN +Supplied username not found. +.SH EXAMPLES +auth required pam_groupmember.so groups=users,+80,ftp +.SH NOTE +This manpage is a short description of \fBpam_groupmember\fR. For a detailed +discussion, including examples and usage recommendations, refer to the +\fBPAM-modules Manual\fR available in texinfo format. If the \fBinfo\fR +reader and the tar documentation are properly installed on your +system, the command +.PP +.RS +4 +.B info pam-modules +.RE +.PP +should give you access to the complete manual. +.PP +You can also view the manual using the info mode in +.BR emacs (1), +or find it in various formats online at +.PP +.RS +4 +.B http://www.gnu.org.ua/software/pam-modules/manual +.RE +.PP +If any discrepancies occur between this manpage and the +\fBPAM-modules Manual\fR, the later shall be considered the authoritative +source. +.SH "SEE ALSO" +.BR pam.conf (5), +.BR pam.d (5), +.BR pam (8). +.SH AUTHORS +Sergey Poznyakoff <gray@gnu.org> +.SH "BUG REPORTS" +Report bugs to <bug\-pam\-modules@gnu.org.ua>. +.SH COPYRIGHT +Copyright \(co 2001-2014 Sergey Poznyakoff +.br +.na +License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> +.br +.ad +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. +.\" Local variables: +.\" eval: (add-hook 'write-file-hooks 'time-stamp) +.\" time-stamp-start: ".TH [A-Z_][A-Z0-9_.\\-]* [0-9] \"" +.\" time-stamp-format: "%:B %:d, %:y" +.\" time-stamp-end: "\"" +.\" time-stamp-line-limit: 20 +.\" end: + diff --git a/lib/graypam.h b/lib/graypam.h index 58d0b97..8d0e1c0 100644 --- a/lib/graypam.h +++ b/lib/graypam.h @@ -92,7 +92,9 @@ void gray_raise(const char *fmt, ...); void *gray_malloc(size_t size); void *gray_zalloc(size_t size); +void *gray_calloc(size_t count, size_t size); void *gray_realloc(void *ptr, size_t size); +char *gray_strdup(const char *str); void gray_pam_delete(char *x); void gray_cleanup_string(pam_handle_t *pamh, void *x, int error_status); @@ -48,6 +48,12 @@ gray_zalloc(size_t size) } void * +gray_calloc(size_t count, size_t size) +{ + return gray_zalloc(count * size); +} + +void * gray_realloc(void *ptr, size_t size) { ptr = realloc(ptr, size); @@ -56,6 +62,17 @@ gray_realloc(void *ptr, size_t size) return ptr; } +char * +gray_strdup(const char *str) +{ + char *p; + + if (!str) + return NULL; + p = gray_malloc(strlen(str) + 1); + return strcpy(p, str); +} + void gray_pam_delete(char *x) diff --git a/pam_groupmember/Makefile.am b/pam_groupmember/Makefile.am new file mode 100644 index 0000000..985721e --- /dev/null +++ b/pam_groupmember/Makefile.am @@ -0,0 +1,22 @@ +# This file is part of pam-modules. +# Copyright (C) 2014 Sergey Poznyakoff +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +pamdir=@PAMDIR@ +pam_LTLIBRARIES = pam_groupmember.la +pam_groupmember_la_SOURCES = pam_groupmember.c +AM_CPPFLAGS=-DMODULE_NAME=\"pam_groupmember\" -DSYSCONFDIR=\"${sysconfdir}\" + +include $(top_srcdir)/Make.rules diff --git a/pam_groupmember/pam_groupmember.c b/pam_groupmember/pam_groupmember.c new file mode 100644 index 0000000..f12f365 --- /dev/null +++ b/pam_groupmember/pam_groupmember.c @@ -0,0 +1,261 @@ +/* This file is part of pam-modules. + Copyright (C) 2014 Sergey Poznyakoff + + This program 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 of the License, or (at your + option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE__PAM_ACONF_H +#include <security/_pam_aconf.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <errno.h> +#include <pwd.h> +#include <grp.h> + +#include "graypam.h" + +#ifndef LINUX_PAM +#include <security/pam_appl.h> +#endif /* LINUX_PAM */ +#include <security/pam_modules.h> + +#define SENSE_ALLOW 0 +#define SENSE_DENY 1 +const char *sense_choice[] = { "allow", "deny", NULL }; + +static int sense; +static int cntl_flags; +static long debug_level; +static char *groups; + +struct pam_opt pam_opt[] = { + { PAM_OPTSTR(debug), pam_opt_long, &debug_level }, + { PAM_OPTSTR(debug), pam_opt_const, &debug_level, { 1 } }, + { PAM_OPTSTR(audit), pam_opt_bitmask, &cntl_flags, { CNTL_AUDIT } }, + { PAM_OPTSTR(waitdebug), pam_opt_null, NULL, { 0 }, + gray_wait_debug_fun }, + { PAM_OPTSTR(sense), pam_opt_enum, &sense, { enumstr: sense_choice } }, + { PAM_OPTSTR(groups), pam_opt_string, &groups }, + { NULL } +}; + +static char ** +split(const char *str, int delim) +{ + const char *p; + size_t c = 1, i; + char **v; + + for (p = str; *p; p++) + if (*p == delim) + ++c; + + ++c; + + v = gray_calloc(c, sizeof(*v)); + + for (p = str, i = 0; *p; p++) { + if (*p == delim) { + size_t len = p - str; + char *elt = gray_malloc(len + 1); + memcpy(elt, str, len); + elt[len] = 0; + v[i++] = elt; + str = ++p; + } + } + v[i++] = gray_strdup(str); + v[i] = 0; + return v; +} + +static int +check_groups(char **groupnames, struct passwd *pw) +{ + int i, j; + struct group *gr; + + for (i = 0; groupnames[i]; i++) { + char *gs = groupnames[i]; + + if (gs[0] == '+') { + char *ep; + unsigned long n = strtoul(gs + 1, &ep, 10); + if (*ep) { + _pam_log(LOG_NOTICE, "not a valid number: %s", + gs); + continue; + } + gr = getgrgid(n); + if (gr) + DEBUG(1,("got group %s <- %d", + gr->gr_name, gr->gr_gid)); + + } else { + gr = getgrnam(gs); + if (gr) + DEBUG(1,("got group %s -> %d", + gr->gr_name, gr->gr_gid)); + } + + if (!gr) { + _pam_log(LOG_NOTICE, "no such group: %s", gs); + continue; + } + if (gr->gr_gid == pw->pw_gid) { + DEBUG(1,("primary gid matches %s", gr->gr_name)); + return 0; + } + + for (j = 0; gr->gr_mem[j]; j++) + if (strcmp(gr->gr_mem[j], pw->pw_name) == 0) { + DEBUG(1,("supplementary gid matches %s", + gr->gr_name)); + return 0; + } + } + return 1; +} + +static void +argv_free(char **wv) +{ + int i; + + for (i = 0; wv[i]; i++) + free(wv[i]); + free(wv); +} + +static int +check_membership0(pam_handle_t *pamh, int argc, const char **argv) +{ + char *name; + char **groupnames; + int rc; + struct passwd *pw; + static int retval[] = { PAM_SUCCESS, PAM_AUTH_ERR }; + + gray_pam_init(PAM_AUTHINFO_UNAVAIL); + gray_log_init(0, MODULE_NAME, LOG_AUTHPRIV); + gray_parseopt(pam_opt, argc, argv); + if (!groups) { + _pam_log(LOG_ERR, "no group names given"); + return PAM_SERVICE_ERR; + } + /* + * get username + */ + rc = pam_get_user(pamh, (const char**)&name, "login: "); + if (rc == PAM_SUCCESS) { + DEBUG(10, ("username [%s] obtained", name)); + } else { + _pam_log(LOG_NOTICE, "can't get username"); + return PAM_AUTHINFO_UNAVAIL; + } + + pw = getpwnam(name); + if (!pw) + return PAM_USER_UNKNOWN; + + groupnames = split(groups, ','); + rc = check_groups(groupnames, pw); + argv_free(groupnames); + + if (sense == SENSE_DENY) + rc = !rc; + + return retval[rc]; +} + +static int +check_membership(pam_handle_t *pamh, int argc, const char **argv, + const char *func) +{ + int rc; + + DEBUG(90,("enter %s", func)); + rc = check_membership0(pamh, argc, argv); + DEBUG(90,("leave %s=%d", func, rc)); + return rc; +} + +PAM_EXTERN int +pam_sm_authenticate(pam_handle_t *pamh, + int flags, + int argc, + const char **argv) +{ + return check_membership(pamh, argc, argv, __FUNCTION__); +} + +PAM_EXTERN int +pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return check_membership(pamh, argc, argv, __FUNCTION__); +} + +PAM_EXTERN int +pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc,const char **argv) +{ + return check_membership(pamh, argc, argv, __FUNCTION__); +} + +PAM_EXTERN int +pam_sm_acct_mgmt (pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return check_membership(pamh, argc, argv, __FUNCTION__); +} + +PAM_EXTERN int +pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return check_membership(pamh, argc, argv, __FUNCTION__); +} + +PAM_EXTERN int +pam_sm_close_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return check_membership(pamh, argc, argv, __FUNCTION__); +} + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_log_modstruct = { + "pam_log", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + pam_sm_open_session, + pam_sm_close_session, + pam_sm_chauthtok, +}; + +#endif + +/* end of module definition */ + + + + + |