aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--NEWS8
-rw-r--r--configure.ac7
-rw-r--r--doc/Makefile.am3
-rw-r--r--doc/pam-modules.texi46
-rw-r--r--doc/pam_groupmember.8125
-rw-r--r--lib/graypam.h2
-rw-r--r--lib/mem.c17
-rw-r--r--pam_groupmember/Makefile.am22
-rw-r--r--pam_groupmember/pam_groupmember.c261
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
diff --git a/NEWS b/NEWS
index e88a8e2..200204a 100644
--- a/NEWS
+++ b/NEWS
@@ -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);
diff --git a/lib/mem.c b/lib/mem.c
index d87be96..f90195b 100644
--- a/lib/mem.c
+++ b/lib/mem.c
@@ -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 */
+
+
+
+
+

Return to:

Send suggestions and report system problems to the System administrator.