diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2018-08-12 19:20:04 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2018-08-12 19:20:04 +0300 |
commit | 6bba235d667f25a9ae6bcd1c560b0ae28ea16505 (patch) | |
tree | 0d9e26952d7ae381f7dd78e6954fdda366a9eed7 | |
parent | 23b718d2a385bb07bcbd4584a694e9e75050098b (diff) | |
download | pam-modules-6bba235d667f25a9ae6bcd1c560b0ae28ea16505.tar.gz pam-modules-6bba235d667f25a9ae6bcd1c560b0ae28ea16505.tar.bz2 |
New module pam_innetgr.
* Makefile.am: Add new module.
* NEWS: Upgrade
* configure.ac: Add configuration for pam_innetgr
* pam_innetgr/Makefile.am: New file.
* pam_innetgr/pam_innetgr.c: New file.
* doc/Makefile.am: Add pam_innetgr.8
* doc/pam-modules.texi: Document pam_innetgr.
* doc/pam_innetgr.8: New file.
* examples/ldappubkey: Bugfixes
(publickeyattribute setting): Accept a list of attributes
(publickeyfilter): New setting.
-rw-r--r-- | Makefile.am | 36 | ||||
-rw-r--r-- | NEWS | 22 | ||||
-rw-r--r-- | configure.ac | 20 | ||||
-rw-r--r-- | doc/Makefile.am | 3 | ||||
-rw-r--r-- | doc/pam-modules.texi | 116 | ||||
-rw-r--r-- | doc/pam_innetgr.8 | 173 | ||||
-rwxr-xr-x | examples/ldappubkey | 65 | ||||
-rw-r--r-- | pam_groupmember/pam_groupmember.c | 2 | ||||
-rw-r--r-- | pam_innetgr/Makefile.am | 22 | ||||
-rw-r--r-- | pam_innetgr/pam_innetgr.c | 319 | ||||
-rw-r--r-- | pam_umotd/pam_umotd.c | 2 |
11 files changed, 735 insertions, 45 deletions
diff --git a/Makefile.am b/Makefile.am index 0b5c6d5..f2f416b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,37 +17,35 @@ AUTOMAKE_OPTIONS = gnits 1.8 ACLOCAL_AMFLAGS = -I m4 -I imprimatur +SUBDIRS = \ + imprimatur\ + doc\ + examples\ + lib\ + pam_sql\ + pamck + if PAM_COND_FSHADOW - FSHADOW_DIR=pam_fshadow + SUBDIRS += pam_fshadow endif if PAM_COND_REGEX - REGEX_DIR=pam_regex + SUBDIRS += pam_regex endif if PAM_COND_LOG - LOG_DIR=pam_log + SUBDIRS += pam_log endif if PAM_COND_LDAPHOME - LDAPHOME_DIR=pam_ldaphome + SUBDIRS += pam_ldaphome endif if PAM_COND_UMOTD - UMOTD_DIR=pam_umotd + SUBDIRS += pam_umotd endif if PAM_COND_GROUPMEMBER - GROUPMEMBER_DIR=pam_groupmember + SUBDIRS += pam_groupmember +endif +if PAM_COND_INNETGR + SUBDIRS += pam_innetgr endif -SUBDIRS = \ - imprimatur\ - doc\ - examples\ - lib\ - $(FSHADOW_DIR)\ - $(REGEX_DIR)\ - $(LOG_DIR)\ - pam_sql\ - $(LDAPHOME_DIR)\ - $(UMOTD_DIR)\ - $(GROUPMEMBER_DIR)\ - pamck EXTRA_DIST=ChangeLog.svn @@ -1,9 +1,25 @@ -pam-modules -- history of user-visible changes. 2018-01-02 -Copyright (C) 2001, 2004-2005, 2007-2012, 2015, 2018 Sergey Poznyakoff +pam-modules -- history of user-visible changes. 2018-08-12 See the end of file for copying conditions. Please send pam-modules bug reports to <bug-pam-modules@gnu.org.ua> +Version 2.2.90 (git) + +* New module pam_innetgr + +This module checks if the current hostname and the name of the user +trying to log in are mentioned in a triple of the specified NIS +netgroup. + +* The ldappubkey utility imporoved. + +The PublicKeyAttribute setting accepts a whitespace-separated list of +attribute names. + +The new setting PublicKeyFilter can be used to supply a LDAP filter +expression to use in place of the default. + + Version 2.2, 2018-01-02 * Improve pam_fshadow @@ -223,7 +239,7 @@ Version 0.1 ========================================================================= Copyright information: -Copyright (C) 2001, 2004-2005, 2007-2015 Sergey Poznyakoff +Copyright (C) 2001, 2004-2005, 2007-2012, 2015, 2018 Sergey Poznyakoff Permission is granted to anyone to make or distribute verbatim copies of this document as received, in any medium, provided that the diff --git a/configure.ac b/configure.ac index 0a4eb33..6869248 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,7 @@ AC_PREREQ(2.63) -AC_INIT(pam-modules, 2.2, bug-pam-modules@gnu.org.ua) +AC_INIT(pam-modules, 2.2.90, bug-pam-modules@gnu.org.ua) AC_CONFIG_SRCDIR(pam_fshadow/pam_fshadow.c) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) @@ -171,6 +171,18 @@ yes) esac ]) +# +AC_CHECK_FUNCS([getdomainname]) +PM_ENABLE(innetgr,[ +case $build_innetgr in +probe) + AC_CHECK_FUNC([innetgr],[build_innetgr=yes],[build_innetgr=no]) + ;; +yes) + AC_CHECK_FUNC([innetgr],[],[AC_MSG_ERROR([cannot build the requested module pam_innetgr because function innetgr is not present in libc])]) + ;; +esac]) + ## ***************** ## debugging support ## ***************** @@ -219,7 +231,7 @@ delim="-------------------------------------------------------------------" echo $delim | tr '-' '*' echo "Modules to build:" res= -for module in fshadow regex log pgsql mysql ldaphome umotd groupmember +for module in fshadow regex log pgsql mysql ldaphome umotd groupmember innetgr do modname=pam_$module eval enable=\${build_$module} @@ -242,7 +254,8 @@ build_pgsql=$build_pgsql build_mysql=$build_mysql build_ldaphome=$build_ldaphome build_umotd=$build_umotd -build_groupmember=$build_groupmember]) +build_groupmember=$build_groupmember +build_innetgr=$build_innetgr]) AC_OUTPUT(Makefile doc/Makefile @@ -255,4 +268,5 @@ AC_OUTPUT(Makefile pam_ldaphome/Makefile pam_umotd/Makefile pam_groupmember/Makefile + pam_innetgr/Makefile pamck/Makefile) diff --git a/doc/Makefile.am b/doc/Makefile.am index 3d588e3..368d3b0 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -43,6 +43,9 @@ endif if PAM_COND_GROUPMEMBER dist_man_MANS += pam_groupmember.8 endif +if PAM_COND_INNETGR + dist_man_MANS += pam_innetgr.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 31f69ed..840a4c1 100644 --- a/doc/pam-modules.texi +++ b/doc/pam-modules.texi @@ -31,10 +31,11 @@ session management. * pam_log: (pam-modules)log. Format and log arbitrary messages to syslog. -* pam_ldaphome (pam-modules)ldaphome Maintain home directories and +* 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. +* pam_umotd (pam-modules)umotd. Display a user-specific MOTD. +* pam_groupmember (pam_modules)groupmember. Test group membership. +* pam_innetgr (pam-modules)innetgr. Check NIS netgroup. * pamck: (pam-modules)pamck. Verify PAM Access. * usergitconfig: (pam-modules)usergitconfig. Initialize user @file{.gitconfig} file. * ldappubkey: (pam-modules)ldappubkey. Get user's public ssh keys from the LDAP database. @@ -90,6 +91,7 @@ Individual modules * ldaphome:: Maintain home directories and SSH keys of LDAP users. * umotd:: Display a user-specific MOTD. * groupmember:: Test group membership. +* innetgr:: Check NIS netgroup. * Reporting Bugs:: How to Report a Bug. @@ -1670,11 +1672,33 @@ LDAP record is searched using the following filter: @end example @end deffn -@deffn {ldap.conf} publickeyattribute @var{attr} -Name of the attribute which holds the public key. Default is +@deffn {ldap.conf} publickeyattribute @var{attr} [@var{attr}...] +List of attributes that hold the public keys. Default is @samp{grayPublicKey} (@pxref{ldap-schema}). @end deffn +@deffn {ldap.conf} publickeyfilter @var{filter} +LDAP filter used to retrieve the objects that contain public keys. The +@var{filter} string can contain the following variables: + +@table @asis +@item $uid +The value of the @samp{uid} setting (see above). + +@item $arg +First command line argument. + +@item $hostname +Full hostname of the machine. +@end table + +The default value is: +@example + (&(objectClass=posixAccount)($uid=$arg)) +@end example +@end deffn + + @node usergitconfig @section usergitconfig @cindex usergitconfig @@ -1854,6 +1878,88 @@ sign. Default is @samp{allow}. @end table +@node innetgr +@chapter Check NIS netgroup +@set MODULE pam_innetgr +@prindex pam_innetgr +@cindex netgroups +@cindex NIS + +The @command{pam_innetgr} module checks if the user and current host +match a triple in the NIS netgroup supplied via the @samp{netgroup} +argument. It returns success if so, and @samp{PAM_AUTH_ERR} otherwise. + +Another possible return values are: @samp{PAM_AUTHINFO_UNAVAIL}, if +the input information was not sufficient (e.g. the username was not +supplied, or the module was unable to determine the host or domain +name), and @samp{PAM_SERVICE_ERR}, if a generic error condition (such +as a lack of memory) occurred. + +In order to determine host and domain name parts, the following +approach is used. First, the @samp{gethostname} function is called to +obtain the hostname part. If the @samp{getdomainname} function is +available, it is used to determine the domain part. If the resulting +domain part is @samp{NULL} or the string @samp{(none)}, the +@samp{gethostbyname} function is invoked with the hostname as its +argument. The returned name (technically speaking, the @samp{h_name} +member of the @samp{struct hostent}) is used as the canonical name of +the server. It is split on the first occurrence of the dot character. +The second part is used as the domain name. The options described +below control this process. + +This module can be used in any PAM service stack. + +@menu +* summary of pam_innetgr options:: +@end menu + +@node summary of pam_innetgr options +@section Summary of @command{pam_innetgr} options + +The following table summarizes the options specific for this +module. @xref{common options}, for the list of common options. + +@table @option +@opsummary{netgroup} +@item netgroup=@var{name} +Name of the netgroup to use. This option is mandatory. + +@opsummary{hostname} +@item hostname=@var{string} +Defines the hostname of the current host. By default it is determined +using the @code{gethostname} system call. + +@opsummary{domainname} +@item domainname=@var{string} +Defines the domainname of the current host. + +@opsummary{nogetdomainname} +@item nogetdomainname +Disable the use of @code{getdomainname} libc function. By default it +is used to determine the domain name. If it fails or returns a string +@samp{(none)}, than the module tries to get the fully qualified name +of the server and uses the part after the first dot as the domain +name. Using the @samp{nogetdomainname} option instructs it to always +use the latter method. + +Never use this option together with @samp{noresove}. + +@opsummary{noresolve} +@item noresolve +Don't fallback to obtaining the fully qualified domain name of the +host from DNS in order to obtain the domain part. This means that +if @code{getdomainname} call fails or is not available on your system, +the module will return @code{PAM_SERVICE_ERR}. + +Never use this option together with @samp{nogetdomainname}. + +@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_innetgr.8 b/doc/pam_innetgr.8 new file mode 100644 index 0000000..335409d --- /dev/null +++ b/doc/pam_innetgr.8 @@ -0,0 +1,173 @@ +.\" This file is part of PAM-Modules -*- nroff -*- +.\" Copyright (C) 2018 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_INNETGR 8 "August 12, 2018" "PAM-MODULES" "Pam-Modules User Reference" +.SH NAME +pam_innetgr \- test NIS netgroup match +.SH SYNOPSIS +.nh +.na +\fBpam_innetgr\fR\ + \fBnetgroup=\fINAME\fR\ + [\fBhostname=\fINAME\fR]\ + [\fBdomainname=\fINAME\fR]\ + [\fBnogetdomainname\fR]\ + [\fBnoresolve\fR]\ + [\fBsense=\fISENSE\fR]\ + [\fBdebug\fR[\fB=\fINUMBER\fR]]\ + [\fBwaitdebug\fR]\ + [\fBaudit\fR] +.ad +.hy +.SH DESCRIPTION +The \fBpam_innetgr\fR module checks if the user and current host +match a triple in the NIS netgroup supplied via the \fBnetgroup\fR +argument. It returns success if so, and \fBPAM_AUTH_ERR\fR otherwise. + +Another possible return values are: \fBPAM_AUTHINFO_UNAVAIL\fR, if +the username was not supplied or the module was unable to determine +the host or domain name, and \fBPAM_SERVICE_ERR\fR, if a generic error +condition (such as a lack of memory) occurred. + +In order to determine host and domain name parts, the following +approach is used. First, the +.BR gethostname (2) +function is called to obtain the hostname part. If the +.BR getdomainname (2) +function is available, it is used to determine the +domain part. If the resulting domain part is NULL or the string "(none)", the +.BR gethostbyname (2) +function is invoked with the hostname as its +argument. The returned name (technically speaking, the \fBh_name\fR +member of the \fBstruct hostent\fR) is used as the canonical name of +the server. It is split on the first occurrence of the dot character. +The second part is used as the domain name. The options described below +control this process. +.SH OPTIONS +.TP +\fBnetgroup=\fISTRING\fR +Name of the netgroup to use. This option is mandatory. +.TP +\fBhostname=\fISTRING\fR +Defines the hostname of the current host. By default it is determined +using the +.BR gethostname (2) +system call. +.TP +\fBdomainname=\fISTRING\fR +Defines the domainname of the current host. +.TP +.B nogetdomainname +Disable the use of +.BR getdomainname (2) +system call. +.sp +Never use this option together \fBnoresove\fR. +.TP +.B noresolve +Don't fallback to obtaining the fully qualified domain name of the +host from DNS in order to obtain the domain part. This means that +if \fBgetdomainname\fR call fails or is not available on your system, +the module will return \fBPAM_SERVICE_ERR\fR. +.sp +Never use this option together with \fBnogetdomainname\fR. +.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 full debugging information (equivalent to \fBdebug=100\fR). +.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_SERVICE_ERR +A generic error condition (such as lack of memory) was encountered. +.TP +.B PAM_USER_UNKNOWN +Supplied username not found. +.SH EXAMPLES +auth required pam_innetgr.so netgroup=grnam +.SH NOTE +This manpage is a short description of \fBpam_innetgr\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 2018 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-functions '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/examples/ldappubkey b/examples/ldappubkey index cecfaa6..5bf2828 100755 --- a/examples/ldappubkey +++ b/examples/ldappubkey @@ -17,6 +17,7 @@ use strict; use Net::LDAP; +use Sys::Hostname; =head1 NAME @@ -62,8 +63,8 @@ Specifies the password to use with B<binddn>. =item B<uid> I<ATTR> -Name of the attribute to use instead of B<uid>. The LDAP record is searched -using the filter B<(&(objectClass=posixAccount)(I<ATTR>=I<LOGIN>))>. +Name of the attribute to use instead of B<uid>. LDAP records are searched +using the filter defined with the B<PublicKeyFilter> attribute (see below). =item B<ssl start_tls> @@ -98,9 +99,34 @@ Specifies acceptable cipher suite and preference order. Specifies what checks to perform on server certificates in a TLS session. I<LEVEL> is one of B<never>, B<allow>, B<try>, B<demand> or B<hard>. -=item B<publickeyattribute> I<ATTR> +=item B<PublicKeyAttribute> I<ATTR> [I<ATTR>...] -Name of the attribute which holds the public key. Default is B<grayPublicKey>. +List of attributes that hold the public key. Default is C<grayPublicKey>. + +=item B<PublicKeyFilter> I<FILTER> + +LDAP filter used to retrieve the objects that may contain public keys. The +I<FILTER> string can contain the following variables + +=over 8 + +=item B<$uid> + +The value of B<uid> setting (see above). + +=item B<$arg> + +First command line argument. + +=item B<$hostname> + +Full hostname of the machine. + +=back + +Default value is: + + (&(objectClass=posixAccount)($uid=$arg)) =back @@ -151,7 +177,8 @@ Sergey Poznyakoff <gray@gnu.org> # ################################### my %config = ('uri' => 'ldap://127.0.0.1', 'uid' => 'uid', - 'publickeyattribute' => 'grayPublicKey'); + 'publickeyattribute' => 'grayPublicKey', + 'publickeyfilter' => '(&(objectClass=posixAccount)($uid=$arg))'); sub read_config_file($) { my $config_file = shift; @@ -164,14 +191,23 @@ sub read_config_file($) { chomp; s/^\s+//; s/\s+$//; - s/#.*//; + s/#.*//; next if ($_ eq ""); - my @kwp = split(/\s*\s+\s*/, $_, 2); + my @kwp = split(/\s+/, $_, 2); $config{lc($kwp[0])} = $kwp[1]; } close($file); } +sub get_fqdn_hostname { + my $name = hostname(); + if (my ($fqdn) = gethostbyname($name)) { + return $fqdn; + } else { + return $name; + } +} + sub assert { my $mesg = shift; my $action = shift; @@ -236,17 +272,20 @@ if (defined($config{'binddn'})) { } assert($ldap->bind(@bindargs), "binding to the server"); -my $attr = $config{'publickeyattribute'}; -my $filter = "(&(objectClass=posixAccount)($config{'uid'}=$ARGV[0]))"; +my @attrs = split /\s+/, $config{'publickeyattribute'}; +my $filter = $config{'publickeyfilter'}; +my $uid = "$config{'uid'}"; +my $arg = $ARGV[0]; +my $hostname = get_fqdn_hostname(); +$filter =~ s/(?<!\\)(\$(?:uid|arg|hostname))/$1/eeg; my $res = assert($ldap->search(base => $config{'base'}, filter => $filter, - attr => [ $attr ] ), + attrs => \@attrs ), "searching for $filter in $config{'base'}"); -foreach my $entry ($res->entry(0)) { - my $keyref = $entry->get_value($attr, asref => 1); - for (@{$keyref}) { +foreach my $entry ($res->entries()) { + for (map { my $a = $entry->get_value($_, asref => 1); $a ? @$a : () } @attrs) { print "$_\n"; } } diff --git a/pam_groupmember/pam_groupmember.c b/pam_groupmember/pam_groupmember.c index ec66d50..ba418d0 100644 --- a/pam_groupmember/pam_groupmember.c +++ b/pam_groupmember/pam_groupmember.c @@ -241,7 +241,7 @@ pam_sm_close_session (pam_handle_t *pamh, int flags, int argc, /* static module data */ struct pam_module _pam_log_modstruct = { - "pam_log", + "pam_groupmember", pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, diff --git a/pam_innetgr/Makefile.am b/pam_innetgr/Makefile.am new file mode 100644 index 0000000..1e5c7f8 --- /dev/null +++ b/pam_innetgr/Makefile.am @@ -0,0 +1,22 @@ +# This file is part of pam-modules. +# Copyright (C) 2018 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/>. + +include $(top_srcdir)/Make.rules + +pamdir=@PAMDIR@ +pam_LTLIBRARIES = pam_innetgr.la +pam_innetgr_la_SOURCES = pam_innetgr.c +AM_CPPFLAGS += -DMODULE_NAME=\"pam_innetgr\" diff --git a/pam_innetgr/pam_innetgr.c b/pam_innetgr/pam_innetgr.c new file mode 100644 index 0000000..f1f5e5d --- /dev/null +++ b/pam_innetgr/pam_innetgr.c @@ -0,0 +1,319 @@ +/* This file is part of pam-modules. + Copyright (C) 2018 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 <stdlib.h> +#include <netdb.h> +#include "graypam.h" + +#ifndef LINUX_PAM +#include <security/pam_appl.h> +#endif +#include <security/pam_modules.h> + +static long debug_level; +char const *host_name; +char const *domain_name; +char const *netgroup_name; +int use_getdomainname; +int use_resolve; +enum { + SENSE_ALLOW, + SENSE_DENY +}; +const char *sense_choice[] = { "allow", "deny", NULL }; +static int sense; + +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_const, &debug_level, { 100 } }, + { PAM_OPTSTR(waitdebug), pam_opt_null, NULL, { 0 }, + gray_wait_debug_fun }, + { PAM_OPTSTR(netgroup), pam_opt_string, &netgroup_name }, + { PAM_OPTSTR(hostname), pam_opt_string, &host_name }, + { PAM_OPTSTR(domainname), pam_opt_string, &domain_name }, + { PAM_OPTSTR(getdomainname), pam_opt_bool, &use_getdomainname }, + { PAM_OPTSTR(resolve), pam_opt_bool, &use_resolve }, + { PAM_OPTSTR(sense), pam_opt_enum, &sense, { enumstr: sense_choice } }, + { NULL } +}; + +#ifndef MAXHOSTNAMELEN +# define MAXHOSTNAMELEN 64 +#endif +#ifndef SIZE_T_MAX +# define SIZE_T_MAX ((size_t)-1) +#endif + +int +xgetname (int (*getfn)(char *, size_t), char **storage) +{ + char *buffer = NULL; + size_t size = 0; + char *p; + + while (1) { + if (size == 0) { + size = MAXHOSTNAMELEN; + p = malloc(size); + } else if (SIZE_T_MAX / 3 * 2 <= size) { + p = NULL; + } else { + size += (size + 1) / 2; + p = realloc(buffer, size); + } + if (!p) { + free(buffer); + errno = ENOMEM; + return -1; + } + buffer = p; + buffer[size - 1] = 0; + if (getfn(buffer, size - 1) == 0) { + if (!buffer[size - 1]) + break; + } else if (errno != 0 + && errno != ENAMETOOLONG + && errno != EINVAL + && errno != ENOMEM) { + int rc = errno; + free(buffer); + errno = rc; + return -1; + } + } + *storage = buffer; + + return 0; +} + +int +stripdomain(char *hostname, char const *domainname) +{ + size_t hlen, dlen; + + if (!domainname) + return -1; + hlen = strlen(hostname); + dlen = strlen(domainname); + if (hlen > dlen + 1 + && hostname[hlen - dlen - 1] == '.' + && strcasecmp(hostname + hlen - dlen, domainname) == 0) { + hostname[hlen - dlen - 1] = 0; + return 0; + } + return -1; +} + +int +get_host_domain_names(char **host_name_ptr, char **domain_name_ptr) +{ + char *hostname; + char *domainname = NULL; + + if (xgetname(gethostname, &hostname)) + return -1; +#if HAVE_GETDOMAINNAME + if (use_getdomainname) { + if (xgetname(getdomainname, &domainname)) { + _pam_log(LOG_ERR, "getdomainname: %s", strerror(errno)); + } else if (strcmp (domainname, "(none)") == 0) { + free(domainname); + domainname = NULL; + } + } +#endif + if (domainname) { + stripdomain(hostname, domainname); + } else if (use_resolve) { + char *p = strchr(hostname, '.'); + if (!p) { + struct hostent *hp = gethostbyname(hostname); + if (hp) { + size_t len = strlen(hp->h_name); + p = realloc(hostname, len + 1); + if (!p) { + free(hostname); + errno = ENOMEM; + return -1; + } + hostname = p; + strcpy(hostname, hp->h_name); + p = strchr(hostname, '.'); + } + } + if (p) { + *p++ = 0; + domainname = strdup(p); + if (!domainname) { + int rc = errno; + _pam_log(LOG_ERR, "getdomainname: %s", + strerror(errno)); + free(hostname); + errno = rc; + return -1; + } + } + } + + *host_name_ptr = hostname; + *domain_name_ptr = domainname; + + return 0; +} + +static int +check_netgroup0(pam_handle_t *pamh, int argc, const char **argv, + const char *func) +{ + int rc; + char *host_name_buf = NULL; + char *domain_name_buf = NULL; + char const *user_name; + + debug_level = 0; + host_name = NULL; + domain_name = NULL; + netgroup_name = NULL; + use_getdomainname = 1; + use_resolve = 1; + sense = SENSE_ALLOW; + + gray_pam_init(PAM_AUTHINFO_UNAVAIL); + gray_log_init(0, MODULE_NAME, LOG_AUTHPRIV); + gray_parseopt(pam_opt, argc, argv); + if (!netgroup_name) { + _pam_log(LOG_ERR, "no netgroup name given"); + return PAM_AUTHINFO_UNAVAIL; + } + + /* + * get username + */ + rc = pam_get_user(pamh, &user_name, "login: "); + if (rc == PAM_SUCCESS) { + DEBUG(10, ("username [%s] obtained", user_name)); + } else { + _pam_log(LOG_NOTICE, "can't get username"); + return PAM_AUTHINFO_UNAVAIL; + } + + if (!host_name || !domain_name) { + if (get_host_domain_names (&host_name_buf, &domain_name_buf)) { + _pam_log(LOG_ERR, "%s", strerror(errno)); + return PAM_SERVICE_ERR; + } + if (!host_name) { + if (!host_name_buf) { + _pam_log(LOG_NOTICE, "can't get hostname"); + return PAM_AUTHINFO_UNAVAIL; + } + host_name = host_name_buf; + } + if (!domain_name) { + if (!domain_name_buf) { + _pam_log(LOG_NOTICE, "can't get domainname"); + return PAM_AUTHINFO_UNAVAIL; + } + domain_name = domain_name_buf; + } + } + + DEBUG(1,("checking (%s, %s, %s)", + host_name, user_name, domain_name)); + + rc = innetgr(netgroup_name, host_name, user_name, domain_name); + + DEBUG(1,("netgroup %s, triple (%s, %s, %s): %d", netgroup_name, + host_name, user_name, domain_name, rc)); + + free(host_name_buf); + free(domain_name_buf); + + if (sense == SENSE_DENY) + rc = !rc; + return rc ? PAM_SUCCESS : PAM_AUTH_ERR; +} + +static int +check_netgroup(pam_handle_t *pamh, int argc, const char **argv, + const char *func) +{ + int rc; + + DEBUG(90,("enter %s", func)); + rc = check_netgroup0(pamh, argc, argv, __FUNCTION__); + 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_netgroup(pamh, argc, argv, __FUNCTION__); +} + +PAM_EXTERN int +pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc,const char **argv) +{ + return check_netgroup(pamh, argc, argv, __FUNCTION__); +} + +PAM_EXTERN int +pam_sm_acct_mgmt (pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return check_netgroup(pamh, argc, argv, __FUNCTION__); +} + +PAM_EXTERN int +pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return check_netgroup(pamh, argc, argv, __FUNCTION__); +} + +PAM_EXTERN int +pam_sm_close_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return check_netgroup(pamh, argc, argv, __FUNCTION__); +} + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_log_modstruct = { + MODULE_NAME, + 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 */ + + diff --git a/pam_umotd/pam_umotd.c b/pam_umotd/pam_umotd.c index a489e24..b14ffab 100644 --- a/pam_umotd/pam_umotd.c +++ b/pam_umotd/pam_umotd.c @@ -385,7 +385,7 @@ pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) /* static module data */ struct pam_module _pam_motd_modstruct = { - "pam_motd", + "pam_umotd", NULL, NULL, NULL, |