summaryrefslogtreecommitdiff
path: root/modules/pam_access
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_access')
-rw-r--r--modules/pam_access/Makefile.am16
-rw-r--r--modules/pam_access/README147
-rw-r--r--modules/pam_access/README.xml39
-rw-r--r--modules/pam_access/access.conf53
-rw-r--r--modules/pam_access/access.conf.5160
-rw-r--r--modules/pam_access/access.conf.5.xml203
-rw-r--r--modules/pam_access/pam_access.879
-rw-r--r--modules/pam_access/pam_access.8.xml220
-rw-r--r--modules/pam_access/pam_access.c486
9 files changed, 1283 insertions, 120 deletions
diff --git a/modules/pam_access/Makefile.am b/modules/pam_access/Makefile.am
index 96476f41..15b93581 100644
--- a/modules/pam_access/Makefile.am
+++ b/modules/pam_access/Makefile.am
@@ -1,10 +1,13 @@
#
-# Copyright (c) 2005 Thorsten Kukuk <kukuk@suse.de>
+# Copyright (c) 2005, 2006 Thorsten Kukuk <kukuk@thkukuk.de>
#
CLEANFILES = *~
-EXTRA_DIST = README access.conf
+EXTRA_DIST = README README.xml access.conf $(MANS) $(XMLS)
+
+man_MANS = access.conf.5 pam_access.8
+man_XMLS = access.conf.5.xml pam_access.8.xml
securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
@@ -20,3 +23,12 @@ endif
securelib_LTLIBRARIES = pam_access.la
secureconf_DATA = access.conf
+
+if ENABLE_REGENERATE_MAN
+
+noinst_DATA = README
+
+README.html: pam_access.8.xml access.conf.5.xml
+
+-include $(top_srcdir)/Make.xml.rules
+endif
diff --git a/modules/pam_access/README b/modules/pam_access/README
index c3f81d11..c3561da0 100644
--- a/modules/pam_access/README
+++ b/modules/pam_access/README
@@ -1,44 +1,103 @@
-# Description of its configuration file
-#
-# (The default config file is "/etc/security/access.conf". This
-# default can be overridden with a module config argument
-# 'accessfile=<full-path>'):
-#
-# Login access control table.
-#
-# When someone logs in, the table is scanned for the first entry that
-# matches the (user, host) combination, or, in case of non-networked
-# logins, the first entry that matches the (user, tty) combination. The
-# permissions field of that table entry determines whether the login will
-# be accepted or refused.
-#
-# Format of the login access control table is three fields separated by a
-# ":" character:
-#
-# permission : users : origins
-#
-# The first field should be a "+" (access granted) or "-" (access denied)
-# character.
-#
-# The second field should be a list of one or more login names, group
-# names, or ALL (always matches). A pattern of the form user@host is
-# matched when the login name matches the "user" part, and when the
-# "host" part matches the local machine name.
-#
-# The third field should be a list of one or more tty names (for
-# non-networked logins), host names, domain names (begin with "."), host
-# addresses, internet network numbers (end with "."), ALL (always
-# matches), NONE (matches no tty on non-networked logins) or
-# LOCAL (matches any string that does not contain a "." character).
-#
-# If you run NIS you can use @netgroupname in host or user patterns; this
-# even works for @usergroup@@hostgroup patterns. Weird.
-#
-# The EXCEPT operator makes it possible to write very compact rules.
-#
-# The group file is searched only when a name does not match that of the
-# logged-in user. Both the user's primary group is matched, as well as
-# groups in which users are explicitly listed.
-#
-# Alexei Nogin <alexei@nogin.dnttm.ru> 1997/06/15
-############################################################################
+pam_access — PAM module for logdaemon style login access control
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+DESCRIPTION
+
+The pam_access PAM module is mainly for access management. It provides
+logdaemon style login access control based on login names, host or domain
+names, internet addresses or network numbers, or on terminal line names in case
+of non-networked logins.
+
+By default rules for access management are taken from config file /etc/security
+/access.conf if you don't specify another file.
+
+OPTIONS
+
+accessfile=/path/to/access.conf
+
+ Indicate an alternative access.conf style configuration file to override
+ the default. This can be useful when different services need different
+ access lists.
+
+debug
+
+ A lot of debug informations are printed with syslog(3).
+
+fieldsep=separators
+
+ This option modifies the field separator character that pam_access will
+ recognize when parsing the access configuration file. For example: fieldsep
+ =| will cause the default `:' character to be treated as part of a field
+ value and `|' becomes the field separator. Doing this may be useful in
+ conjuction with a system that wants to use pam_access with X based
+ applications, since the PAM_TTY item is likely to be of the form
+ "hostname:0" which includes a `:' character in its value. But you should
+ not need this.
+
+listsep=separators
+
+ This option modifies the list separator character that pam_access will
+ recognize when parsing the access configuration file. For example: listsep
+ =, will cause the default ` ' (space) and `\t' (tab) characters to be
+ treated as part of a list element value and `,' becomes the only list
+ element separator. Doing this may be useful on a system with group
+ information obtained from a Windows domain, where the default built-in
+ groups "Domain Users", "Domain Admins" contain a space.
+
+EXAMPLES
+
+These are some example lines which might be specified in /etc/security/
+access.conf.
+
+User root should be allowed to get access via cron, X11 terminal :0, tty1, ...,
+tty5, tty6.
+
++ : root : crond :0 tty1 tty2 tty3 tty4 tty5 tty6
+
+User root should be allowed to get access from hosts which own the IPv4
+addresses. This does not mean that the connection have to be a IPv4 one, a IPv6
+connection from a host with one of this IPv4 addresses does work, too.
+
++ : root : 192.168.200.1 192.168.200.4 192.168.200.9
+
++ : root : 127.0.0.1
+
+User root should get access from network 192.168.201. where the term will be
+evaluated by string matching. But it might be better to use network/netmask
+instead. The same meaning of 192.168.201. is 192.168.201.0/24 or 192.168.201.0/
+255.255.255.0.
+
++ : root : 192.168.201.
+
+User root should be able to have access from hosts foo1.bar.org and
+foo2.bar.org (uses string matching also).
+
++ : root : foo1.bar.org foo2.bar.org
+
+User root should be able to have access from domain foo.bar.org (uses string
+matching also).
+
++ : root : .foo.bar.org
+
+User root should be denied to get access from all other sources.
+
+- : root : ALL
+
+User foo and members of netgroup admins should be allowed to get access from
+all sources. This will only work if netgroup service is available.
+
++ : @admins foo : ALL
+
+User john and foo should get access from IPv6 host address.
+
++ : john foo : 2001:4ca0:0:101::1
+
+User john should get access from IPv6 net/mask.
+
++ : john : 2001:4ca0:0:101::/64
+
+All other users should be denied to get access from all sources.
+
+- : ALL : ALL
+
diff --git a/modules/pam_access/README.xml b/modules/pam_access/README.xml
new file mode 100644
index 00000000..8c7d078b
--- /dev/null
+++ b/modules/pam_access/README.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.docbook.org/xml/4.3/docbookx.dtd"
+[
+<!--
+<!ENTITY pamaccess SYSTEM "pam_access.8.xml">
+-->
+<!--
+<!ENTITY accessconf SYSTEM "access.conf.5.xml">
+-->
+]>
+
+<article>
+
+ <articleinfo>
+
+ <title>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_access.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_access-name"]/*)'/>
+ </title>
+
+ </articleinfo>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_access.8.xml" xpointer='xpointer(//refsect1[@id = "pam_access-description"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_access.8.xml" xpointer='xpointer(//refsect1[@id = "pam_access-options"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="access.conf.5.xml" xpointer='xpointer(//refsect1[@id = "access.conf-examples"]/*)'/>
+ </section>
+
+</article>
diff --git a/modules/pam_access/access.conf b/modules/pam_access/access.conf
index 98da5faa..b22f1d43 100644
--- a/modules/pam_access/access.conf
+++ b/modules/pam_access/access.conf
@@ -1,5 +1,8 @@
# Login access control table.
#
+# Comment line must start with "#", no space at front.
+# Order of lines is important.
+#
# When someone logs in, the table is scanned for the first entry that
# matches the (user, host) combination, or, in case of non-networked
# logins, the first entry that matches the (user, tty) combination. The
@@ -31,8 +34,8 @@
# matches), NONE (matches no tty on non-networked logins) or
# LOCAL (matches any string that does not contain a "." character).
#
-# If you run NIS you can use @netgroupname in host or user patterns; this
-# even works for @usergroup@@hostgroup patterns. Weird.
+# You can use @netgroupname in host or user patterns; this even works
+# for @usergroup@@hostgroup patterns.
#
# The EXCEPT operator makes it possible to write very compact rules.
#
@@ -63,3 +66,49 @@
#
# All other accounts are allowed to login from anywhere.
#
+##############################################################################
+# All lines from here up to the end are building a more complex example.
+##############################################################################
+#
+# User "root" should be allowed to get access via cron .. tty5 tty6.
+#+ : root : cron crond :0 tty1 tty2 tty3 tty4 tty5 tty6
+#
+# User "root" should be allowed to get access from hosts with ip addresses.
+#+ : root : 192.168.200.1 192.168.200.4 192.168.200.9
+#+ : root : 127.0.0.1
+#
+# User "root" should get access from network 192.168.201.
+# This term will be evaluated by string matching.
+# comment: It might be better to use network/netmask instead.
+# The same is 192.168.201.0/24 or 192.168.201.0/255.255.255.0
+#+ : root : 192.168.201.
+#
+# User "root" should be able to have access from domain.
+# Uses string matching also.
+#+ : root : .foo.bar.org
+#
+# User "root" should be denied to get access from all other sources.
+#- : root : ALL
+#
+# User "foo" and members of netgroup "nis_group" should be
+# allowed to get access from all sources.
+# This will only work if netgroup service is available.
+#+ : @nis_group foo : ALL
+#
+# User "john" should get access from ipv4 net/mask
+#+ : john : 127.0.0.0/24
+#
+# User "john" should get access from ipv4 as ipv6 net/mask
+#+ : john : ::ffff:127.0.0.0/127
+#
+# User "john" should get access from ipv6 host address
+#+ : john : 2001:4ca0:0:101::1
+#
+# User "john" should get access from ipv6 host address (same as above)
+#+ : john : 2001:4ca0:0:101:0:0:0:1
+#
+# User "john" should get access from ipv6 net/mask
+#+ : john : 2001:4ca0:0:101::/64
+#
+# All other users should be denied to get access from all sources.
+#- : ALL : ALL
diff --git a/modules/pam_access/access.conf.5 b/modules/pam_access/access.conf.5
new file mode 100644
index 00000000..c2207a3d
--- /dev/null
+++ b/modules/pam_access/access.conf.5
@@ -0,0 +1,160 @@
+.\" ** You probably do not want to edit this file directly **
+.\" It was generated using the DocBook XSL Stylesheets (version 1.69.1).
+.\" Instead of manually editing it, you probably should edit the DocBook XML
+.\" source for it and then use the DocBook XSL Stylesheets to regenerate it.
+.TH "ACCESS.CONF" "5" "02/07/2006" "Linux\-PAM Manual" "Linux\-PAM Manual"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+access.conf \- The login access control table file
+.SH "DESCRIPTION"
+.PP
+This module provides logdaemon style login access control based on login names and on host (or domain) names, internet addresses (or network numbers), on terminal line names in case of non\-networked logins or on service name if called by a daemon.
+.PP
+The
+\fI/etc/security/access.conf\fR
+file specifies (\fIuser\fR,
+\fIhost\fR), (\fIuser\fR,
+\fInetwork/netmask\fR) or (\fIuser\fR,
+\fItty\fR) combinations for which a login will be either accepted or refused.
+.PP
+When someone logs in, the file
+\fIaccess.conf\fR
+is scanned for the first entry that matches the (\fIuser\fR,
+\fIhost\fR) or (\fIuser\fR,
+\fInetwork/netmask\fR) combination, or, in case of non\-networked logins, the first entry that matches the (\fIuser\fR,
+\fItty\fR) combination. The permissions field of that table entry determines whether the login will be accepted or refused.
+.PP
+Each line of the login access control table has three fields separated by a ":" character (colon):
+.PP
+\fIpermission\fR:\fIusers\fR:\fIorigins\fR
+.PP
+The first field, the
+\fIpermission\fR
+field, can be either a "\fI+\fR" character (plus) for access granted or a "\fI\-\fR" character (minus) for access denied.
+.PP
+The second field, the
+\fIusers\fR
+field, should be a list of one or more login names, group names, or
+\fIALL\fR
+(which always matches).
+.PP
+The third field, the
+\fIorigins\fR
+field, should be a list of one or more tty names (for non\-networked logins), host names, domain names (begin with "."), host addresses, internet network numbers (end with "."), internet network addresses with network mask (where network mask can be a decimal number or an internet address also),
+\fIALL\fR
+(which always matches) or
+\fILOCAL\fR
+(which matches any string that does not contain a "." character). If supported by the system you can use
+\fI@netgroupname\fR
+in host or user patterns.
+.PP
+The
+\fIexcept\fR
+operator makes it possible to write very compact rules.
+.PP
+The group file is searched only when a name does not match that of the logged\-in user. Only groups are matched in which users are explicitly listed. However the PAM module does not look at the primary group id of a user.
+.PP
+The "\fI#\fR" character at start of line (no space at front) can be used to mark this line as a comment line.
+.SH "EXAMPLES"
+.PP
+These are some example lines which might be specified in
+\fI/etc/security/access.conf\fR.
+.PP
+User
+\fIroot\fR
+should be allowed to get access via
+\fIcron\fR, X11 terminal
+\fI:0\fR,
+\fItty1\fR, ...,
+\fItty5\fR,
+\fItty6\fR.
+.PP
++ : root : crond :0 tty1 tty2 tty3 tty4 tty5 tty6
+.PP
+User
+\fIroot\fR
+should be allowed to get access from hosts which own the IPv4 addresses. This does not mean that the connection have to be a IPv4 one, a IPv6 connection from a host with one of this IPv4 addresses does work, too.
+.PP
++ : root : 192.168.200.1 192.168.200.4 192.168.200.9
+.PP
++ : root : 127.0.0.1
+.PP
+User
+\fIroot\fR
+should get access from network
+192.168.201.
+where the term will be evaluated by string matching. But it might be better to use network/netmask instead. The same meaning of
+192.168.201.
+is
+\fI192.168.201.0/24\fR
+or
+\fI192.168.201.0/255.255.255.0\fR.
+.PP
++ : root : 192.168.201.
+.PP
+User
+\fIroot\fR
+should be able to have access from hosts
+\fIfoo1.bar.org\fR
+and
+\fIfoo2.bar.org\fR
+(uses string matching also).
+.PP
++ : root : foo1.bar.org foo2.bar.org
+.PP
+User
+\fIroot\fR
+should be able to have access from domain
+\fIfoo.bar.org\fR
+(uses string matching also).
+.PP
++ : root : .foo.bar.org
+.PP
+User
+\fIroot\fR
+should be denied to get access from all other sources.
+.PP
+\- : root : ALL
+.PP
+User
+\fIfoo\fR
+and members of netgroup
+\fIadmins\fR
+should be allowed to get access from all sources. This will only work if netgroup service is available.
+.PP
++ : @admins foo : ALL
+.PP
+User
+\fIjohn\fR
+and
+\fIfoo\fR
+should get access from IPv6 host address.
+.PP
++ : john foo : 2001:4ca0:0:101::1
+.PP
+User
+\fIjohn\fR
+should get access from IPv6 net/mask.
+.PP
++ : john : 2001:4ca0:0:101::/64
+.PP
+All other users should be denied to get access from all sources.
+.PP
+\- : ALL : ALL
+.SH "SEE ALSO"
+.PP
+\fBpam_access\fR(8),
+\fBpam.d\fR(5),
+\fBpam\fR(8)
+.SH "AUTHORS"
+.PP
+Original
+\fBlogin.access\fR(5)
+manual was provided by Guido van Rooij which was renamed to
+\fBaccess.conf\fR(5)
+to reflect relation to default config file.
+.PP
+Network address / netmask description and example text was introduced by Mike Becher <mike.becher@lrz\-muenchen.de>.
diff --git a/modules/pam_access/access.conf.5.xml b/modules/pam_access/access.conf.5.xml
new file mode 100644
index 00000000..a7b1c62f
--- /dev/null
+++ b/modules/pam_access/access.conf.5.xml
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+
+<refentry id="access.conf">
+
+ <refmeta>
+ <refentrytitle>access.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>access.conf</refname>
+ <refpurpose>The login access control table file</refpurpose>
+ </refnamediv>
+
+
+ <refsect1 id='access.conf-description'>
+ <title>DESCRIPTION</title>
+
+ <para>
+ This module provides logdaemon style login access control based
+ on login names and on host (or domain) names, internet addresses
+ (or network numbers), on terminal line names in case of
+ non-networked logins or on service name if called by a daemon.
+ </para>
+
+ <para>
+ The <filename>/etc/security/access.conf</filename> file specifies
+ (<replaceable>user</replaceable>, <replaceable>host</replaceable>),
+ (<replaceable>user</replaceable>, <replaceable>network/netmask</replaceable>) or
+ (<replaceable>user</replaceable>, <replaceable>tty</replaceable>)
+ combinations for which a login will be either accepted or refused.
+ </para>
+
+ <para>
+ When someone logs in, the file <filename>access.conf</filename> is
+ scanned for the first entry that matches the
+ (<replaceable>user</replaceable>, <replaceable>host</replaceable>) or
+ (<replaceable>user</replaceable>, <replaceable>network/netmask</replaceable>)
+ combination, or, in case of non-networked logins, the first entry
+ that matches the
+ (<replaceable>user</replaceable>, <replaceable>tty</replaceable>)
+ combination. The permissions field of that table entry determines
+ whether the login will be accepted or refused.
+ </para>
+
+ <para>
+ Each line of the login access control table has three fields separated
+ by a ":" character (colon):
+ </para>
+
+ <para>
+ <replaceable>permission</replaceable>:<replaceable>users</replaceable>:<replaceable>origins</replaceable>
+ </para>
+
+
+ <para>
+ The first field, the <replaceable>permission</replaceable> field, can be either a
+ "<emphasis>+</emphasis>" character (plus) for access granted or a
+ "<emphasis>-</emphasis>" character (minus) for access denied.
+ </para>
+
+ <para>
+ The second field, the <replaceable>users</replaceable>
+ field, should be a list of one or more login names, group names, or
+ <emphasis>ALL</emphasis> (which always matches).
+ </para>
+
+ <para>
+ The third field, the <replaceable>origins</replaceable>
+ field, should be a list of one or more tty names (for non-networked
+ logins), host names, domain names (begin with "."), host addresses,
+ internet network numbers (end with "."), internet network addresses
+ with network mask (where network mask can be a decimal number or an
+ internet address also), <emphasis>ALL</emphasis> (which always matches)
+ or <emphasis>LOCAL</emphasis> (which matches any string that does not
+ contain a "." character). If supported by the system you can use
+ <emphasis>@netgroupname</emphasis> in host or user patterns.
+ </para>
+
+ <para>
+ The <replaceable>except</replaceable> operator makes it possible to
+ write very compact rules.
+ </para>
+
+ <para>
+ The group file is searched only when a name does not match that of
+ the logged-in user. Only groups are matched in which users are
+ explicitly listed. However the PAM module does not look at the
+ primary group id of a user.
+ </para>
+
+
+ <para>
+ The "<emphasis>#</emphasis>" character at start of line (no space
+ at front) can be used to mark this line as a comment line.
+ </para>
+
+ </refsect1>
+
+ <refsect1 id="access.conf-examples">
+ <title>EXAMPLES</title>
+ <para>
+ These are some example lines which might be specified in
+ <filename>/etc/security/access.conf</filename>.
+ </para>
+
+ <para>
+ User <emphasis>root</emphasis> should be allowed to get access via
+ <emphasis>cron</emphasis>, X11 terminal <emphasis remap='I'>:0</emphasis>,
+ <emphasis>tty1</emphasis>, ..., <emphasis>tty5</emphasis>,
+ <emphasis>tty6</emphasis>.
+ </para>
+ <para>+ : root : crond :0 tty1 tty2 tty3 tty4 tty5 tty6</para>
+
+ <para>
+ User <emphasis>root</emphasis> should be allowed to get access from
+ hosts which own the IPv4 addresses. This does not mean that the
+ connection have to be a IPv4 one, a IPv6 connection from a host with
+ one of this IPv4 addresses does work, too.
+ </para>
+ <para>+ : root : 192.168.200.1 192.168.200.4 192.168.200.9</para>
+ <para>+ : root : 127.0.0.1</para>
+
+ <para>
+ User <emphasis>root</emphasis> should get access from network
+ <literal>192.168.201.</literal> where the term will be evaluated by
+ string matching. But it might be better to use network/netmask instead.
+ The same meaning of <literal>192.168.201.</literal> is
+ <emphasis>192.168.201.0/24</emphasis> or
+ <emphasis>192.168.201.0/255.255.255.0</emphasis>.
+ </para>
+ <para>+ : root : 192.168.201.</para>
+
+ <para>
+ User <emphasis>root</emphasis> should be able to have access from hosts
+ <emphasis>foo1.bar.org</emphasis> and <emphasis>foo2.bar.org</emphasis>
+ (uses string matching also).
+ </para>
+ <para>+ : root : foo1.bar.org foo2.bar.org</para>
+
+ <para>
+ User <emphasis>root</emphasis> should be able to have access from
+ domain <emphasis>foo.bar.org</emphasis> (uses string matching also).
+ </para>
+ <para>+ : root : .foo.bar.org</para>
+
+ <para>
+ User <emphasis>root</emphasis> should be denied to get access
+ from all other sources.
+ </para>
+ <para>- : root : ALL</para>
+
+ <para>
+ User <emphasis>foo</emphasis> and members of netgroup
+ <emphasis>admins</emphasis> should be allowed to get access
+ from all sources. This will only work if netgroup service is available.
+ </para>
+ <para>+ : @admins foo : ALL</para>
+
+ <para>
+ User <emphasis>john</emphasis> and <emphasis>foo</emphasis>
+ should get access from IPv6 host address.
+ </para>
+ <para>+ : john foo : 2001:4ca0:0:101::1</para>
+
+ <para>
+ User <emphasis>john</emphasis> should get access from IPv6 net/mask.
+ </para>
+ <para>+ : john : 2001:4ca0:0:101::/64</para>
+
+ <para>
+ All other users should be denied to get access from all sources.
+ </para>
+ <para>- : ALL : ALL</para>
+
+ </refsect1>
+
+ <refsect1 id="access.conf-see_also">
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry><refentrytitle>pam_access</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+ <refsect1 id="access.conf-author">
+ <title>AUTHORS</title>
+ <para>
+ Original <citerefentry><refentrytitle>login.access</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ manual was provided by Guido van Rooij which was renamed to
+ <citerefentry><refentrytitle>access.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ to reflect relation to default config file.
+ </para>
+ <para>
+ Network address / netmask description and example text was
+ introduced by Mike Becher &lt;mike.becher@lrz-muenchen.de&gt;.
+ </para>
+ </refsect1>
+</refentry>
diff --git a/modules/pam_access/pam_access.8 b/modules/pam_access/pam_access.8
new file mode 100644
index 00000000..0746fa8b
--- /dev/null
+++ b/modules/pam_access/pam_access.8
@@ -0,0 +1,79 @@
+.\" ** You probably do not want to edit this file directly **
+.\" It was generated using the DocBook XSL Stylesheets (version 1.69.1).
+.\" Instead of manually editing it, you probably should edit the DocBook XML
+.\" source for it and then use the DocBook XSL Stylesheets to regenerate it.
+.TH "PAM_ACCESS" "8" "02/03/2006" "Linux\-PAM Manual" "Linux\-PAM Manual"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+pam_access \- PAM module for logdaemon style login access control
+.SH "SYNOPSIS"
+.HP 14
+\fBpam_access.so\fR [debug] [accessfile=\fIfile\fR] [fieldsep=\fIsep\fR] [listsep=\fIsep\fR]
+.SH "DESCRIPTION"
+.PP
+The pam_access PAM module is mainly for access management. It provides logdaemon style login access control based on login names, host or domain names, internet addresses or network numbers, or on terminal line names in case of non\-networked logins.
+.PP
+By default rules for access management are taken from config file
+\fI/etc/security/access.conf\fR
+if you don't specify another file.
+.SH "OPTIONS"
+.TP
+\fBaccessfile=\fR\fB\fI/path/to/access.conf\fR\fR
+Indicate an alternative
+\fIaccess.conf\fR
+style configuration file to override the default. This can be useful when different services need different access lists.
+.TP
+\fBdebug\fR
+A lot of debug informations are printed with
+\fBsyslog\fR(3).
+.TP
+\fBfieldsep=\fR\fB\fIseparators\fR\fR
+This option modifies the field separator character that pam_access will recognize when parsing the access configuration file. For example:
+\fIfieldsep=|\fR
+will cause the default `:' character to be treated as part of a field value and `|' becomes the field separator. Doing this may be useful in conjuction with a system that wants to use pam_access with X based applications, since the
+\fIPAM_TTY\fR
+item is likely to be of the form "hostname:0" which includes a `:' character in its value. But you should not need this.
+.TP
+\fBlistsep=\fR\fB\fIseparators\fR\fR
+This option modifies the list separator character that pam_access will recognize when parsing the access configuration file. For example:
+\fIlistsep=,\fR
+will cause the default ` ' (space) and `\\t' (tab) characters to be treated as part of a list element value and `,' becomes the only list element separator. Doing this may be useful on a system with group information obtained from a Windows domain, where the default built\-in groups "Domain Users", "Domain Admins" contain a space.
+.SH "MODULE SERVICES PROVIDED"
+.PP
+The
+\fBauth\fR
+and
+\fBaccount\fR
+services are supported.
+.SH "RETURN VALUES"
+.TP
+PAM_SUCCESS
+Access was granted.
+.TP
+PAM_PERM_DENIED
+Access was not granted.
+.TP
+PAM_IGNORE
+\fBpam_setcred\fR
+was called which does nothing.
+.TP
+PAM_ABORT
+Not all relevant data or options could be gotten.
+.TP
+PAM_USER_UNKNOWN
+The user is not known to the system.
+.SH "FILES"
+.TP
+\fI/etc/security/access.conf\fR
+Default configuration file
+.SH "SEE ALSO"
+.PP
+\fBaccess.conf\fR(5),
+\fBpam.d\fR(8),
+\fBpam\fR(8).
+.SH "AUTHORS"
+.PP
+The logdaemon style login access control scheme was designed and implemented by Wietse Venema. The pam_access PAM module was developed by Alexei Nogin <alexei@nogin.dnttm.ru>. The IPv4(/) IPv6 support and the network(address) / netmask feature was developed and provided by Mike Becher <mike.becher@lrz\-muenchen.de>.
diff --git a/modules/pam_access/pam_access.8.xml b/modules/pam_access/pam_access.8.xml
new file mode 100644
index 00000000..d374ac50
--- /dev/null
+++ b/modules/pam_access/pam_access.8.xml
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+
+<refentry id='pam_access'>
+
+ <refmeta>
+ <refentrytitle>pam_access</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class='setdesc'>Linux-PAM Manual</refmiscinfo>
+ </refmeta>
+
+ <refnamediv id='pam_access-name'>
+ <refname>pam_access</refname>
+ <refpurpose>
+ PAM module for logdaemon style login access control
+ </refpurpose>
+ </refnamediv>
+
+<!-- body begins here -->
+
+ <refsynopsisdiv>
+ <cmdsynopsis id="pam_access-cmdsynopsis">
+ <command>pam_access.so</command>
+ <arg choice="opt">
+ debug
+ </arg>
+ <arg choice="opt">
+ accessfile=<replaceable>file</replaceable>
+ </arg>
+ <arg choice="opt">
+ fieldsep=<replaceable>sep</replaceable>
+ </arg>
+ <arg choice="opt">
+ listsep=<replaceable>sep</replaceable>
+ </arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+
+ <refsect1 id="pam_access-description">
+ <title>DESCRIPTION</title>
+ <para>
+ The pam_access PAM module is mainly for access management.
+ It provides logdaemon style login access control based on login
+ names, host or domain names, internet addresses or network numbers,
+ or on terminal line names in case of non-networked logins.
+ </para>
+ <para>
+ By default rules for access management are taken from config file
+ <filename>/etc/security/access.conf</filename> if you don't specify
+ another file.
+ </para>
+ </refsect1>
+
+ <refsect1 id="pam_access-options">
+ <title>OPTIONS</title>
+ <variablelist>
+
+ <varlistentry>
+ <term>
+ <option>accessfile=<replaceable>/path/to/access.conf</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Indicate an alternative <filename>access.conf</filename>
+ style configuration file to override the default. This can
+ be useful when different services need different access lists.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>debug</option>
+ </term>
+ <listitem>
+ <para>
+ A lot of debug informations are printed with
+ <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>fieldsep=<replaceable>separators</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ This option modifies the field separator character that
+ pam_access will recognize when parsing the access
+ configuration file. For example:
+ <emphasis remap='B'>fieldsep=|</emphasis> will cause the
+ default `:' character to be treated as part of a field value
+ and `|' becomes the field separator. Doing this may be
+ useful in conjuction with a system that wants to use
+ pam_access with X based applications, since the
+ <emphasis remap='B'>PAM_TTY</emphasis> item is likely to be
+ of the form "hostname:0" which includes a `:' character in
+ its value. But you should not need this.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>listsep=<replaceable>separators</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ This option modifies the list separator character that
+ pam_access will recognize when parsing the access
+ configuration file. For example:
+ <emphasis remap='B'>listsep=,</emphasis> will cause the
+ default ` ' (space) and `\t' (tab) characters to be treated
+ as part of a list element value and `,' becomes the only
+ list element separator. Doing this may be useful on a system
+ with group information obtained from a Windows domain,
+ where the default built-in groups "Domain Users",
+ "Domain Admins" contain a space.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id="pam_access-services">
+ <title>MODULE SERVICES PROVIDED</title>
+ <para>
+ The <option>auth</option> and <option>account</option> services are supported.
+ </para>
+ </refsect1>
+
+ <refsect1 id="pam_access-return_values">
+ <title>RETURN VALUES</title>
+ <variablelist>
+ <varlistentry>
+ <term>PAM_SUCCESS</term>
+ <listitem>
+ <para>
+ Access was granted.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_PERM_DENIED</term>
+ <listitem>
+ <para>
+ Access was not granted.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_IGNORE</term>
+ <listitem>
+ <para>
+ <function>pam_setcred</function> was called which does nothing.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_ABORT</term>
+ <listitem>
+ <para>
+ Not all relevant data or options could be gotten.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_USER_UNKNOWN</term>
+ <listitem>
+ <para>
+ The user is not known to the system.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id="pam_access-files">
+ <title>FILES</title>
+ <variablelist>
+ <varlistentry>
+ <term><filename>/etc/security/access.conf</filename></term>
+ <listitem>
+ <para>Default configuration file</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id="pam_access-see_also">
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>access.conf</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>pam.d</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1 id="pam_access-authors">
+ <title>AUTHORS</title>
+ <para>
+ The logdaemon style login access control scheme was designed and implemented by
+ Wietse Venema.
+ The pam_access PAM module was developed by
+ Alexei Nogin &lt;alexei@nogin.dnttm.ru&gt;.
+ The IPv4(/) IPv6 support and the network(address) / netmask feature
+ was developed and provided by Mike Becher &lt;mike.becher@lrz-muenchen.de&gt;.
+ </para>
+ </refsect1>
+</refentry>
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
index 2d8c92b4..2218ca9a 100644
--- a/modules/pam_access/pam_access.c
+++ b/modules/pam_access/pam_access.c
@@ -42,11 +42,9 @@
#include <ctype.h>
#include <sys/utsname.h>
#include <rpcsvc/ypclnt.h>
-
-#ifndef BROKEN_NETWORK_MATCH
-# include <netdb.h>
-# include <sys/socket.h>
-#endif
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
/*
* here, we make definitions for the externally accessible functions
@@ -55,6 +53,7 @@
* modules include file to define their prototypes.
*/
+#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#include <security/_pam_macros.h>
@@ -93,12 +92,14 @@ static const char *sep = ", \t"; /* list-element separator */
* functional interfaces as generic as possible.
*/
struct login_info {
- struct passwd *user;
+ const struct passwd *user;
const char *from;
const char *config_file;
};
-/* --- static functions for checking whether the user should be let in --- */
+/* Print debugging messages.
+ Default is NO which means don't print debugging messages. */
+static char pam_access_debug = NO;
/* Parse module config arguments */
@@ -131,6 +132,8 @@ parse_args(pam_handle_t *pamh, struct login_info *loginfo,
return 0;
}
+ } else if (strcmp (argv[i], "debug") == 0) {
+ pam_access_debug = YES;
} else {
pam_syslog(pamh, LOG_ERR, "unrecognized option [%s]", argv[i]);
}
@@ -139,13 +142,169 @@ parse_args(pam_handle_t *pamh, struct login_info *loginfo,
return 1; /* OK */
}
+/* --- static functions for checking whether the user should be let in --- */
+
typedef int match_func (pam_handle_t *, char *, struct login_info *);
static int list_match (pam_handle_t *, char *, struct login_info *,
match_func *);
static int user_match (pam_handle_t *, char *, struct login_info *);
static int from_match (pam_handle_t *, char *, struct login_info *);
-static int string_match (const char *, const char *);
+static int string_match (pam_handle_t *, const char *, const char *);
+static int network_netmask_match (pam_handle_t *, const char *, const char *);
+
+
+/* isipaddr - find out if string provided is an IP address or not */
+
+static int
+isipaddr (const char *string, int *addr_type,
+ struct sockaddr_storage *addr)
+{
+ struct sockaddr_storage local_addr;
+ int is_ip;
+
+ /* We use struct sockaddr_storage addr because
+ * struct in_addr/in6_addr is an integral part
+ * of struct sockaddr and we doesn't want to
+ * use its value.
+ */
+
+ if (addr == NULL)
+ addr = &local_addr;
+
+ memset(addr, 0, sizeof(struct sockaddr_storage));
+
+ /* first ipv4 */
+ if (inet_pton(AF_INET, string, addr) > 0)
+ {
+ if (addr_type != NULL)
+ *addr_type = AF_INET;
+
+ is_ip = YES;
+ }
+ else if (inet_pton(AF_INET6, string, addr) > 0)
+ { /* then ipv6 */
+ if (addr_type != NULL) {
+ *addr_type = AF_INET6;
+ }
+ is_ip = YES;
+ }
+ else
+ is_ip = NO;
+
+ return is_ip;
+}
+
+
+/* are_addresses_equal - translate IP address strings to real IP
+ * addresses and compare them to find out if they are equal.
+ * If netmask was provided it will be used to focus comparation to
+ * relevant bits.
+ */
+static int
+are_addresses_equal (const char *ipaddr0, const char *ipaddr1,
+ const char *netmask)
+{
+ struct sockaddr_storage addr0;
+ struct sockaddr_storage addr1;
+ int addr_type0 = 0;
+ int addr_type1 = 0;
+
+ if (isipaddr (ipaddr0, &addr_type0, &addr0) == NO)
+ return NO;
+
+ if (isipaddr (ipaddr1, &addr_type1, &addr1) == NO)
+ return NO;
+
+ if (addr_type0 != addr_type1)
+ /* different address types */
+ return NO;
+
+ if (netmask != NULL) {
+ /* Got a netmask, so normalize addresses? */
+ struct sockaddr_storage nmask;
+ unsigned char *byte_a, *byte_nm;
+
+ memset(&nmask, 0, sizeof(struct sockaddr_storage));
+ if (inet_pton(addr_type0, netmask, (void *)&nmask) > 0) {
+ unsigned int i;
+ byte_a = (unsigned char *)(&addr0);
+ byte_nm = (unsigned char *)(&nmask);
+ for (i=0; i<sizeof(struct sockaddr_storage); i++) {
+ byte_a[i] = byte_a[i] & byte_nm[i];
+ }
+
+ byte_a = (unsigned char *)(&addr1);
+ byte_nm = (unsigned char *)(&nmask);
+ for (i=0; i<sizeof(struct sockaddr_storage); i++) {
+ byte_a[i] = byte_a[i] & byte_nm[i];
+ }
+ }
+ }
+
+
+ /* Are the two addresses equal? */
+ if (memcmp((void *)&addr0, (void *)&addr1,
+ sizeof(struct sockaddr_storage)) == 0) {
+ return(YES);
+ }
+
+ return(NO);
+}
+
+static char *
+number_to_netmask (long netmask, int addr_type,
+ char *ipaddr_buf, size_t ipaddr_buf_len)
+{
+ /* We use struct sockaddr_storage addr because
+ * struct in_addr/in6_addr is an integral part
+ * of struct sockaddr and we doesn't want to
+ * use its value.
+ */
+ struct sockaddr_storage nmask;
+ unsigned char *byte_nm;
+ const char *ipaddr_dst = NULL;
+ int i, ip_bytes;
+
+ if (netmask == 0) {
+ /* mask 0 is the same like no mask */
+ return(NULL);
+ }
+
+ memset(&nmask, 0, sizeof(struct sockaddr_storage));
+ if (addr_type == AF_INET6) {
+ /* ipv6 address mask */
+ ip_bytes = 16;
+ } else {
+ /* default might be an ipv4 address mask */
+ addr_type = AF_INET;
+ ip_bytes = 4;
+ }
+
+ byte_nm = (unsigned char *)(&nmask);
+ /* translate number to mask */
+ for (i=0; i<ip_bytes; i++) {
+ if (netmask >= 8) {
+ byte_nm[i] = 0xff;
+ netmask -= 8;
+ } else
+ if (netmask > 0) {
+ byte_nm[i] = 0xff << (8 - netmask);
+ break;
+ } else
+ if (netmask <= 0) {
+ break;
+ }
+ }
+
+ /* now generate netmask address string */
+ ipaddr_dst = inet_ntop(addr_type, &nmask, ipaddr_buf, ipaddr_buf_len);
+ if (ipaddr_dst == ipaddr_buf) {
+ return (ipaddr_buf);
+ }
+
+ return (NULL);
+}
/* login_access - match username/group and host/tty with access control file */
@@ -161,6 +320,12 @@ login_access (pam_handle_t *pamh, struct login_info *item)
int end;
int lineno = 0; /* for diagnostics */
+ if (pam_access_debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "login_access: user=%s, from=%s, file=%s",
+ item->user->pw_name,
+ item->from, item->config_file);
+
/*
* Process the table one line at a time and stop at the first match.
* Blank lines and lines that begin with a '#' character are ignored.
@@ -186,10 +351,10 @@ login_access (pam_handle_t *pamh, struct login_info *item)
if (line[0] == 0) /* skip blank lines */
continue;
- /* Allow trailing: in last field fo froms */
+ /* Allow field seperator in last field of froms */
if (!(perm = strtok(line, fs))
|| !(users = strtok((char *) 0, fs))
- || !(froms = strtok((char *) 0, fs))) {
+ || !(froms = strtok((char *) 0, "\n"))) {
pam_syslog(pamh, LOG_ERR, "%s: line %d: bad field count",
item->config_file, lineno);
continue;
@@ -199,17 +364,31 @@ login_access (pam_handle_t *pamh, struct login_info *item)
item->config_file, lineno);
continue;
}
- match = (list_match(pamh, froms, item, from_match)
- && list_match(pamh, users, item, user_match));
+ if (pam_access_debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "line %d: %s : %s : %s", lineno, perm, users, froms);
+ match = list_match(pamh, froms, item, from_match);
+ if (pam_access_debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "from_match=%d, \"%s\"", match, item->from);
+ match = match && list_match (pamh, users, item, user_match);
+ if (pam_access_debug)
+ pam_syslog (pamh, LOG_DEBUG, "user_match=%d, \"%s\"",
+ match, item->user->pw_name);
}
(void) fclose(fp);
- } else if (errno != ENOENT) {
- pam_syslog(pamh, LOG_ERR, "cannot open %s: %m", item->config_file);
+ } else if (errno == ENOENT) {
+ /* This is no error. */
+ pam_syslog(pamh, LOG_WARNING, "warning: cannot open %s: %m",
+ item->config_file);
+ } else {
+ pam_syslog(pamh, LOG_ERR, "cannot open %s: %m", item->config_file);
return NO;
}
- return (match == 0 || (line[0] == '+'));
+ return (match == NO || (line[0] == '+'));
}
+
/* list_match - match an item against a list of tokens with exceptions */
static int list_match(pam_handle_t *pamh,
@@ -257,23 +436,39 @@ static char * myhostname(void)
/* netgroup_match - match group against machine or user */
-static int netgroup_match(const char *group, const char *machine, const char *user)
+static int
+netgroup_match (pam_handle_t *pamh, const char *group,
+ const char *machine, const char *user)
{
- static char *mydomain = NULL;
+ char *mydomain = NULL;
+ int retval;
+
+ yp_get_default_domain(&mydomain);
+
+
+ retval = innetgr (group, machine, user, mydomain);
+ if (pam_access_debug == YES)
+ pam_syslog (pamh, LOG_DEBUG,
+ "netgroup_match: %d (group=%s, machine=%s, user=%s, domain=%s)",
+ retval, group ? group : "NULL", machine ? machine : "NULL",
+ user ? user : "NULL", mydomain ? mydomain : "NULL");
+ return retval;
- if (mydomain == 0)
- yp_get_default_domain(&mydomain);
- return (innetgr(group, machine, user, mydomain));
}
/* user_match - match a username against one token */
-static int user_match(pam_handle_t *pamh, char *tok, struct login_info *item)
+static int
+user_match (pam_handle_t *pamh, char *tok, struct login_info *item)
{
char *string = item->user->pw_name;
struct login_info fake_item;
char *at;
+ if (pam_access_debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "user_match: tok=%s, item=%s", tok, string);
+
/*
* If a token has the magic value "ALL" the match always succeeds.
* Otherwise, return YES if the token fully matches the username, if the
@@ -286,10 +481,11 @@ static int user_match(pam_handle_t *pamh, char *tok, struct login_info *item)
fake_item.from = myhostname();
if (fake_item.from == NULL)
return NO;
- return (user_match (pamh, tok, item) && from_match (pamh, at + 1, &fake_item));
+ return (user_match (pamh, tok, item) &&
+ from_match (pamh, at + 1, &fake_item));
} else if (tok[0] == '@') /* netgroup */
- return (netgroup_match(tok + 1, (char *) 0, string));
- else if (string_match (tok, string)) /* ALL or exact match */
+ return (netgroup_match (pamh, tok + 1, (char *) 0, string));
+ else if (string_match (pamh, tok, string)) /* ALL or exact match */
return YES;
else if (pam_modutil_user_in_group_nam_nam (pamh, item->user->pw_name, tok))
/* try group membership */
@@ -307,6 +503,10 @@ from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item)
int tok_len;
int str_len;
+ if (pam_access_debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "from_match: tok=%s, item=%s", tok, string);
+
/*
* If a token has the magic value "ALL" the match always succeeds. Return
* YES if the token fully matches the string. If the token is a domain
@@ -316,12 +516,13 @@ from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item)
* if it matches the head of the string.
*/
- if (string != NULL && tok[0] == '@') { /* netgroup */
- return (netgroup_match(tok + 1, string, (char *) 0));
- } else if (string_match(tok, string)) { /* ALL or exact match */
+ if (string == NULL) {
+ return NO;
+ } else if (tok[0] == '@') { /* netgroup */
+ return (netgroup_match (pamh, tok + 1, string, (char *) 0));
+ } else if (string_match(pamh, tok, string)) {
+ /* ALL or exact match */
return (YES);
- } else if (string == NULL) {
- return (NO);
} else if (tok[0] == '.') { /* domain: match last fields */
if ((str_len = strlen(string)) > (tok_len = strlen(tok))
&& strcasecmp(tok, string + str_len - tok_len) == 0)
@@ -329,47 +530,95 @@ from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item)
} else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */
if (strchr(string, '.') == 0)
return (YES);
-#ifdef BROKEN_NETWORK_MATCH
- } else if (tok[(tok_len = strlen(tok)) - 1] == '.' /* network */
- && strncmp(tok, string, tok_len) == 0) {
- return (YES);
-#else /* BROKEN_NETWORK_MATCH */
} else if (tok[(tok_len = strlen(tok)) - 1] == '.') {
- /*
- The code below does a more correct check if the address specified
- by "string" starts from "tok".
- 1998/01/27 Andrey V. Savochkin <saw@msu.ru>
- */
-
- struct hostent *h;
- char hn[3+1+3+1+3+1+3+1+1];
- int r;
-
- h = gethostbyname(string);
- if (h == NULL)
- return (NO);
- if (h->h_addrtype != AF_INET)
- return (NO);
- if (h->h_length != 4)
- return (NO); /* only IPv4 addresses (SAW) */
- r = snprintf(hn, sizeof(hn), "%u.%u.%u.%u.",
- (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1],
- (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
- if (r < 0 || r >= (int)sizeof(hn))
- return (NO);
- if (!strncmp(tok, hn, tok_len))
- return (YES);
-#endif /* BROKEN_NETWORK_MATCH */
+ struct addrinfo *res;
+ struct addrinfo hint;
+
+ memset (&hint, '\0', sizeof (hint));
+ hint.ai_flags = AI_ADDRCONFIG | AI_CANONNAME;
+ hint.ai_family = AF_INET;
+
+ if (getaddrinfo (string, NULL, &hint, &res) != 0)
+ return NO;
+ else
+ {
+ struct addrinfo *runp = res;
+
+ while (runp != NULL)
+ {
+ char buf[INET_ADDRSTRLEN+2];
+
+ if (runp->ai_family == AF_INET)
+ {
+ inet_ntop (runp->ai_family,
+ &((struct sockaddr_in *) runp->ai_addr)->sin_addr,
+ buf, sizeof (buf));
+
+ strcat (buf, ".");
+
+ if (strncmp(tok, buf, tok_len) == 0)
+ {
+ freeaddrinfo (res);
+ return YES;
+ }
+ runp = runp->ai_next;
+ }
+ freeaddrinfo (res);
+ }
+ }
+ } else if (isipaddr(string, NULL, NULL) == YES) {
+ /* Assume network/netmask with a IP of a host. */
+ if (network_netmask_match(pamh, tok, string))
+ return YES;
+ } else {
+ /* Assume network/netmask with a name of a host. */
+ struct addrinfo *res;
+ struct addrinfo hint;
+
+ memset (&hint, '\0', sizeof (hint));
+ hint.ai_flags = AI_ADDRCONFIG | AI_CANONNAME;
+ hint.ai_family = AF_UNSPEC;
+
+ if (getaddrinfo (string, NULL, &hint, &res) != 0)
+ return NO;
+ else
+ {
+ struct addrinfo *runp = res;
+
+ while (runp != NULL)
+ {
+ char buf[INET6_ADDRSTRLEN];
+
+ inet_ntop (runp->ai_family,
+ runp->ai_family == AF_INET
+ ? (void *) &((struct sockaddr_in *) runp->ai_addr)->sin_addr
+ : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr,
+ buf, sizeof (buf));
+
+ if (network_netmask_match(pamh, tok, buf))
+ {
+ freeaddrinfo (res);
+ return YES;
+ }
+ runp = runp->ai_next;
+ }
+ freeaddrinfo (res);
+ }
}
- return (NO);
+
+ return NO;
}
/* string_match - match a string against one token */
static int
-string_match (const char *tok, const char *string)
+string_match (pam_handle_t *pamh, const char *tok, const char *string)
{
+ if (pam_access_debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "string_match: tok=%s, item=%s", tok, string);
+
/*
* If the token has the magic value "ALL" the match always succeeds.
* Otherwise, return YES if the token fully matches the string.
@@ -388,11 +637,79 @@ string_match (const char *tok, const char *string)
return (NO);
}
-/* --- public account management functions --- */
+
+/* network_netmask_match - match a string against one token
+ * where string is an ip (v4,v6) address and tok represents
+ * whether a single ip (v4,v6) address or a network/netmask
+ */
+static int
+network_netmask_match (pam_handle_t *pamh,
+ const char *tok, const char *string)
+{
+ if (pam_access_debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "network_netmask_match: tok=%s, item=%s", tok, string);
+
+ if (isipaddr(string, NULL, NULL) == YES)
+ {
+ char *netmask_ptr = NULL;
+ static char netmask_string[MAXHOSTNAMELEN + 1] = "";
+ int addr_type;
+
+ /* OK, check if tok is of type addr/mask */
+ if ((netmask_ptr = strchr(tok, '/')) != NULL)
+ {
+ long netmask = 0;
+
+ /* YES */
+ *netmask_ptr = 0;
+ netmask_ptr++;
+
+ if (isipaddr(tok, &addr_type, NULL) == NO)
+ { /* no netaddr */
+ return(NO);
+ }
+
+ /* check netmask */
+ if (isipaddr(netmask_ptr, NULL, NULL) == NO)
+ { /* netmask as integre value */
+ char *endptr = NULL;
+ netmask = strtol(netmask_ptr, &endptr, 0);
+ if ((endptr == NULL) || (*endptr != '\0'))
+ { /* invalid netmask value */
+ return(NO);
+ }
+ if ((netmask < 0) || (netmask >= 128))
+ { /* netmask value out of range */
+ return(NO);
+ }
+
+ netmask_ptr = number_to_netmask(netmask, addr_type,
+ netmask_string, MAXHOSTNAMELEN);
+ }
+
+ /* Netmask is now an ipv4/ipv6 address.
+ * This works also if netmask_ptr is NULL.
+ */
+ return (are_addresses_equal(string, tok, netmask_ptr));
+ }
+ else
+ /* NO, then check if it is only an addr */
+ if (isipaddr(tok, NULL, NULL) == YES)
+ { /* check if they are the same, no netmask */
+ return(are_addresses_equal(string, tok, NULL));
+ }
+ }
+
+ return (NO);
+}
+
+
+/* --- public PAM management functions --- */
PAM_EXTERN int
-pam_sm_acct_mgmt (pam_handle_t *pamh, int flags UNUSED,
- int argc, const char **argv)
+pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
+ int argc, const char **argv)
{
struct login_info loginfo;
const char *user=NULL;
@@ -426,16 +743,26 @@ pam_sm_acct_mgmt (pam_handle_t *pamh, int flags UNUSED,
D(("PAM_TTY not set, probing stdin"));
from = ttyname(STDIN_FILENO);
if (from != NULL) {
- if (pam_set_item(pamh, PAM_TTY, from) != PAM_SUCCESS) {
- pam_syslog(pamh, LOG_ERR, "couldn't set tty name");
- return PAM_ABORT;
- }
+ if (pam_set_item(pamh, PAM_TTY, from) != PAM_SUCCESS)
+ pam_syslog(pamh, LOG_WARNING, "couldn't set tty name");
+ } else {
+ if (pam_get_item(pamh, PAM_SERVICE, &void_from) != PAM_SUCCESS
+ || void_from == NULL) {
+ pam_syslog (pamh, LOG_ERR,
+ "cannot determine remote host, tty or service name");
+ return PAM_ABORT;
+ }
+ from = void_from;
+ if (pam_access_debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "cannot determine tty or remote hostname, using service %s",
+ from);
}
}
else
from = void_from;
- if (from[0] == '/') { /* full path */
+ if (from[0] == '/') { /* full path, remove device path. */
const char *f;
from++;
if ((f = strchr(from, '/')) != NULL) {
@@ -444,7 +771,8 @@ pam_sm_acct_mgmt (pam_handle_t *pamh, int flags UNUSED,
}
}
- if ((user_pw=pam_modutil_getpwnam(pamh, user))==NULL) return (PAM_USER_UNKNOWN);
+ if ((user_pw=pam_modutil_getpwnam(pamh, user))==NULL)
+ return (PAM_USER_UNKNOWN);
/*
* Bundle up the arguments to avoid unnecessary clumsiness later on.
@@ -469,6 +797,20 @@ pam_sm_acct_mgmt (pam_handle_t *pamh, int flags UNUSED,
}
}
+PAM_EXTERN int
+pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED,
+ int argc UNUSED, const char **argv UNUSED)
+{
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN int
+pam_sm_acct_mgmt (pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ return pam_sm_authenticate (pamh, flags, argc, argv);
+}
+
/* end of module definition */
#ifdef PAM_STATIC
@@ -477,8 +819,8 @@ pam_sm_acct_mgmt (pam_handle_t *pamh, int flags UNUSED,
struct pam_module _pam_access_modstruct = {
"pam_access",
- NULL,
- NULL,
+ pam_sm_authenticate,
+ pam_sm_setcred,
pam_sm_acct_mgmt,
NULL,
NULL,