summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2000-06-20 22:10:38 +0000
committerAndrew G. Morgan <morgan@kernel.org>2000-06-20 22:10:38 +0000
commitea488580c42e8918445a945484de3c8a5addc761 (patch)
treec992f3ba699caafedfadc16af38e6359c3c24698
Initial revision
-rw-r--r--.cvsignore4
-rw-r--r--CHANGELOG1244
-rw-r--r--Copyright41
-rw-r--r--Makefile283
-rw-r--r--README167
-rw-r--r--bin/.cvsignore3
-rw-r--r--bin/README45
-rw-r--r--conf/.cvsignore2
-rw-r--r--conf/Makefile34
-rwxr-xr-xconf/install178
-rwxr-xr-xconf/install_conf36
-rwxr-xr-xconf/md5itall51
-rwxr-xr-xconf/mkdirp50
-rw-r--r--conf/pam.conf126
-rw-r--r--conf/pam_conv1/.cvsignore3
-rw-r--r--conf/pam_conv1/Makefile41
-rw-r--r--conf/pam_conv1/README10
-rw-r--r--conf/pam_conv1/pam_conv.lex42
-rw-r--r--conf/pam_conv1/pam_conv.y203
-rw-r--r--defs/debian.defs40
-rw-r--r--defs/hpux.defs36
-rw-r--r--defs/linux.defs32
-rw-r--r--defs/morgan.defs36
-rw-r--r--defs/redhat.defs36
-rw-r--r--defs/redhat4.defs35
-rw-r--r--defs/solaris-2.1.5.defs45
-rw-r--r--defs/solaris.defs48
-rw-r--r--defs/sunos.defs37
-rw-r--r--defs/suse.defs36
-rw-r--r--doc/.cvsignore2
-rw-r--r--doc/CREDITS48
-rw-r--r--doc/Makefile101
-rw-r--r--doc/NOTES16
-rw-r--r--doc/figs/pam_orient.txt23
-rw-r--r--doc/html/.cvsignore1
-rw-r--r--doc/html/index.html21
-rw-r--r--doc/man/pam.8279
-rw-r--r--doc/man/pam.conf.81
-rw-r--r--doc/man/pam.d.81
-rw-r--r--doc/man/pam_authenticate.391
-rw-r--r--doc/man/pam_chauthtok.3101
-rw-r--r--doc/man/pam_close_session.31
-rw-r--r--doc/man/pam_end.31
-rw-r--r--doc/man/pam_fail_delay.3130
-rw-r--r--doc/man/pam_open_session.399
-rw-r--r--doc/man/pam_setcred.379
-rw-r--r--doc/man/pam_start.398
-rw-r--r--doc/man/pam_strerror.351
-rw-r--r--doc/man/template-man52
-rw-r--r--doc/modules/README13
-rw-r--r--doc/modules/module.sgml-template170
-rw-r--r--doc/modules/pam_access.sgml93
-rw-r--r--doc/modules/pam_chroot.sgml86
-rw-r--r--doc/modules/pam_cracklib.sgml259
-rw-r--r--doc/modules/pam_deny.sgml179
-rw-r--r--doc/modules/pam_env.sgml141
-rw-r--r--doc/modules/pam_filter.sgml150
-rw-r--r--doc/modules/pam_ftp.sgml93
-rw-r--r--doc/modules/pam_group.sgml108
-rw-r--r--doc/modules/pam_issue.sgml120
-rw-r--r--doc/modules/pam_krb4.sgml126
-rw-r--r--doc/modules/pam_lastlog.sgml119
-rw-r--r--doc/modules/pam_limits.sgml197
-rw-r--r--doc/modules/pam_listfile.sgml138
-rw-r--r--doc/modules/pam_mail.sgml137
-rw-r--r--doc/modules/pam_mkhomedir.sgml83
-rw-r--r--doc/modules/pam_motd.sgml77
-rw-r--r--doc/modules/pam_nologin.sgml75
-rw-r--r--doc/modules/pam_permit.sgml83
-rw-r--r--doc/modules/pam_pwdb.sgml252
-rw-r--r--doc/modules/pam_radius.sgml117
-rw-r--r--doc/modules/pam_rhosts.sgml164
-rw-r--r--doc/modules/pam_rootok.sgml85
-rw-r--r--doc/modules/pam_securetty.sgml72
-rw-r--r--doc/modules/pam_time.sgml166
-rw-r--r--doc/modules/pam_unix.sgml289
-rw-r--r--doc/modules/pam_userdb.sgml112
-rw-r--r--doc/modules/pam_warn.sgml67
-rw-r--r--doc/modules/pam_wheel.sgml125
-rw-r--r--doc/pam_appl.sgml1643
-rw-r--r--doc/pam_modules.sgml1476
-rw-r--r--doc/pam_source.sgml1173
-rw-r--r--doc/ps/.cvsignore1
-rw-r--r--doc/ps/README3
-rw-r--r--doc/ps/missfont.log36
-rw-r--r--doc/specs/.cvsignore1
-rw-r--r--doc/specs/draft-morgan-pam.raw702
-rw-r--r--doc/specs/formatter/.cvsignore3
-rw-r--r--doc/specs/formatter/Makefile16
-rw-r--r--doc/specs/formatter/parse.lex11
-rw-r--r--doc/specs/formatter/parse.y293
-rw-r--r--doc/specs/rfc86.0.txt1851
-rw-r--r--doc/txts/.cvsignore1
-rw-r--r--doc/txts/README3
-rw-r--r--examples/.cvsignore3
-rw-r--r--examples/Makefile42
-rw-r--r--examples/blank.c182
-rw-r--r--examples/check_user.c71
-rw-r--r--examples/test.c105
-rw-r--r--examples/vpass.c47
-rw-r--r--examples/xsh.c148
-rw-r--r--libpam/.cvsignore2
-rw-r--r--libpam/Makefile162
-rw-r--r--libpam/include/security/_pam_compat.h122
-rw-r--r--libpam/include/security/_pam_macros.h166
-rw-r--r--libpam/include/security/_pam_types.h316
-rw-r--r--libpam/include/security/pam_appl.h92
-rw-r--r--libpam/include/security/pam_malloc.h79
-rw-r--r--libpam/include/security/pam_modules.h195
-rw-r--r--libpam/pam_account.c13
-rw-r--r--libpam/pam_auth.c67
-rw-r--r--libpam/pam_data.c105
-rw-r--r--libpam/pam_delay.c168
-rw-r--r--libpam/pam_dispatch.c282
-rw-r--r--libpam/pam_end.c72
-rw-r--r--libpam/pam_env.c389
-rw-r--r--libpam/pam_handlers.c896
-rw-r--r--libpam/pam_item.c304
-rw-r--r--libpam/pam_log.c382
-rw-r--r--libpam/pam_malloc.c404
-rw-r--r--libpam/pam_map.c85
-rw-r--r--libpam/pam_misc.c305
-rw-r--r--libpam/pam_password.c52
-rw-r--r--libpam/pam_private.h312
-rw-r--r--libpam/pam_second.c46
-rw-r--r--libpam/pam_session.c41
-rw-r--r--libpam/pam_start.c112
-rw-r--r--libpam/pam_static.c141
-rw-r--r--libpam/pam_strerror.c112
-rw-r--r--libpam/pam_tokens.h111
-rw-r--r--libpam_misc/.cvsignore7
-rw-r--r--libpam_misc/Makefile96
-rw-r--r--libpam_misc/help_env.c118
-rw-r--r--libpam_misc/misc_conv.c344
-rw-r--r--libpam_misc/pam_misc.h56
-rw-r--r--libpam_misc/xstrdup.c31
-rw-r--r--libpamc/.cvsignore3
-rw-r--r--libpamc/License42
-rw-r--r--libpamc/Makefile110
-rw-r--r--libpamc/include/security/pam_client.h186
-rw-r--r--libpamc/libpamc.h63
-rw-r--r--libpamc/pamc_client.c189
-rw-r--r--libpamc/pamc_converse.c211
-rw-r--r--libpamc/pamc_load.c477
-rwxr-xr-xlibpamc/test/agents/secret@here305
-rw-r--r--libpamc/test/modules/Makefile9
-rw-r--r--libpamc/test/modules/pam_secret.c670
-rw-r--r--libpamc/test/regress/Makefile7
-rwxr-xr-xlibpamc/test/regress/run_test.sh6
-rw-r--r--libpamc/test/regress/test.libpamc.c334
-rwxr-xr-xlibpamc/test/regress/test.secret@here152
-rw-r--r--modules/Makefile70
-rw-r--r--modules/README55
-rw-r--r--modules/dont_makefile19
-rwxr-xr-xmodules/download-all30
-rw-r--r--modules/pam_access/.cvsignore1
-rw-r--r--modules/pam_access/Makefile106
-rw-r--r--modules/pam_access/README40
-rw-r--r--modules/pam_access/access.conf52
-rwxr-xr-xmodules/pam_access/install_conf46
-rw-r--r--modules/pam_access/pam_access.c424
-rw-r--r--modules/pam_cracklib/.cvsignore1
-rw-r--r--modules/pam_cracklib/Makefile118
-rw-r--r--modules/pam_cracklib/README21
-rw-r--r--modules/pam_cracklib/pam_cracklib.c776
-rw-r--r--modules/pam_deny/.cvsignore1
-rw-r--r--modules/pam_deny/Makefile131
-rw-r--r--modules/pam_deny/README4
-rw-r--r--modules/pam_deny/pam_deny.c100
-rw-r--r--modules/pam_env/.cvsignore1
-rw-r--r--modules/pam_env/Makefile111
-rw-r--r--modules/pam_env/README72
-rwxr-xr-xmodules/pam_env/install_conf46
-rw-r--r--modules/pam_env/pam_env.c839
-rw-r--r--modules/pam_env/pam_env.conf-example72
-rw-r--r--modules/pam_filter/.cvsignore2
-rw-r--r--modules/pam_filter/.upperLOWER1
-rw-r--r--modules/pam_filter/Makefile140
-rw-r--r--modules/pam_filter/README94
-rw-r--r--modules/pam_filter/include/pam_filter.h32
-rw-r--r--modules/pam_filter/pam_filter.c738
-rw-r--r--modules/pam_filter/upperLOWER/.cvsignore1
-rw-r--r--modules/pam_filter/upperLOWER/Makefile64
-rw-r--r--modules/pam_filter/upperLOWER/upperLOWER.c174
-rw-r--r--modules/pam_ftp/.cvsignore1
-rw-r--r--modules/pam_ftp/Makefile88
-rw-r--r--modules/pam_ftp/README18
-rw-r--r--modules/pam_ftp/pam_ftp.c299
-rw-r--r--modules/pam_group/.cvsignore1
-rw-r--r--modules/pam_group/Makefile98
-rw-r--r--modules/pam_group/group.conf60
-rwxr-xr-xmodules/pam_group/install_conf46
-rw-r--r--modules/pam_group/pam_group.c843
-rw-r--r--modules/pam_issue/.cvsignore1
-rw-r--r--modules/pam_issue/Makefile86
-rw-r--r--modules/pam_issue/pam_issue.c265
-rw-r--r--modules/pam_lastlog/.cvsignore1
-rw-r--r--modules/pam_lastlog/Makefile112
-rw-r--r--modules/pam_lastlog/pam_lastlog.c463
-rw-r--r--modules/pam_limits/.cvsignore1
-rw-r--r--modules/pam_limits/Makefile98
-rw-r--r--modules/pam_limits/README87
-rwxr-xr-xmodules/pam_limits/install_conf46
-rw-r--r--modules/pam_limits/limits.skel42
-rw-r--r--modules/pam_limits/pam_limits.c635
-rw-r--r--modules/pam_listfile/.cvsignore1
-rw-r--r--modules/pam_listfile/Makefile84
-rw-r--r--modules/pam_listfile/README25
-rw-r--r--modules/pam_listfile/pam_listfile.c437
-rw-r--r--modules/pam_mail/.cvsignore1
-rw-r--r--modules/pam_mail/Makefile113
-rw-r--r--modules/pam_mail/README17
-rw-r--r--modules/pam_mail/pam_mail.c504
-rw-r--r--modules/pam_mkhomedir/.cvsignore1
-rw-r--r--modules/pam_mkhomedir/Makefile90
-rw-r--r--modules/pam_mkhomedir/pam_mkhomedir.c370
-rw-r--r--modules/pam_motd/.cvsignore1
-rw-r--r--modules/pam_motd/Makefile90
-rw-r--r--modules/pam_motd/pam_motd.c119
-rw-r--r--modules/pam_nologin/.cvsignore1
-rw-r--r--modules/pam_nologin/Makefile86
-rw-r--r--modules/pam_nologin/README12
-rw-r--r--modules/pam_nologin/pam_nologin.c130
-rw-r--r--modules/pam_permit/.cvsignore1
-rw-r--r--modules/pam_permit/Makefile132
-rw-r--r--modules/pam_permit/README4
-rw-r--r--modules/pam_permit/pam_permit.c128
-rw-r--r--modules/pam_pwdb/.cvsignore2
-rw-r--r--modules/pam_pwdb/BUGS14
-rw-r--r--modules/pam_pwdb/CHANGELOG10
-rw-r--r--modules/pam_pwdb/Makefile193
-rw-r--r--modules/pam_pwdb/README41
-rw-r--r--modules/pam_pwdb/TODO34
-rw-r--r--modules/pam_pwdb/bigcrypt.-c114
-rw-r--r--modules/pam_pwdb/md5.c270
-rw-r--r--modules/pam_pwdb/md5.h34
-rw-r--r--modules/pam_pwdb/md5_crypt.c153
-rw-r--r--modules/pam_pwdb/pam_pwdb.c256
-rw-r--r--modules/pam_pwdb/pam_unix_acct.-c298
-rw-r--r--modules/pam_pwdb/pam_unix_auth.-c131
-rw-r--r--modules/pam_pwdb/pam_unix_md.-c73
-rw-r--r--modules/pam_pwdb/pam_unix_passwd.-c404
-rw-r--r--modules/pam_pwdb/pam_unix_pwupd.-c260
-rw-r--r--modules/pam_pwdb/pam_unix_sess.-c118
-rw-r--r--modules/pam_pwdb/pwdb_chkpwd.c204
-rw-r--r--modules/pam_pwdb/support.-c938
-rw-r--r--modules/pam_radius/.cvsignore1
-rw-r--r--modules/pam_radius/Makefile99
-rw-r--r--modules/pam_radius/README58
-rw-r--r--modules/pam_radius/pam_radius.c193
-rw-r--r--modules/pam_radius/pam_radius.h38
-rw-r--r--modules/pam_rhosts/.cvsignore1
-rw-r--r--modules/pam_rhosts/Makefile98
-rw-r--r--modules/pam_rhosts/README57
-rw-r--r--modules/pam_rhosts/pam_rhosts_auth.c829
-rw-r--r--modules/pam_rootok/.cvsignore1
-rw-r--r--modules/pam_rootok/Makefile117
-rw-r--r--modules/pam_rootok/README18
-rw-r--r--modules/pam_rootok/pam_rootok.c109
-rw-r--r--modules/pam_securetty/.cvsignore1
-rw-r--r--modules/pam_securetty/Makefile83
-rw-r--r--modules/pam_securetty/README9
-rw-r--r--modules/pam_securetty/pam_securetty.c191
-rw-r--r--modules/pam_shells/.cvsignore1
-rw-r--r--modules/pam_shells/Makefile84
-rw-r--r--modules/pam_shells/README10
-rw-r--r--modules/pam_shells/pam_shells.c133
-rw-r--r--modules/pam_stress/.cvsignore1
-rw-r--r--modules/pam_stress/Makefile115
-rw-r--r--modules/pam_stress/README66
-rw-r--r--modules/pam_stress/pam_stress.c565
-rw-r--r--modules/pam_tally/.cvsignore2
-rw-r--r--modules/pam_tally/Makefile109
-rw-r--r--modules/pam_tally/README95
-rw-r--r--modules/pam_tally/faillog.h55
-rw-r--r--modules/pam_tally/pam_tally.c689
-rw-r--r--modules/pam_tally/pam_tally_app.c7
-rw-r--r--modules/pam_time/.cvsignore1
-rw-r--r--modules/pam_time/Makefile98
-rw-r--r--modules/pam_time/README43
-rwxr-xr-xmodules/pam_time/install_conf46
-rw-r--r--modules/pam_time/pam_time.c612
-rw-r--r--modules/pam_time/time.conf64
-rw-r--r--modules/pam_unix/.cvsignore4
-rw-r--r--modules/pam_unix/CHANGELOG55
-rw-r--r--modules/pam_unix/Makefile155
-rw-r--r--modules/pam_unix/README39
-rw-r--r--modules/pam_unix/bigcrypt.c119
-rw-r--r--modules/pam_unix/lckpwdf.-c117
-rw-r--r--modules/pam_unix/md5.c256
-rw-r--r--modules/pam_unix/md5.h35
-rw-r--r--modules/pam_unix/md5_crypt.c149
-rwxr-xr-xmodules/pam_unix/need_nsl.sh7
-rw-r--r--modules/pam_unix/pam_unix_acct.c210
-rw-r--r--modules/pam_unix/pam_unix_auth.c220
-rw-r--r--modules/pam_unix/pam_unix_passwd.c985
-rw-r--r--modules/pam_unix/pam_unix_sess.c138
-rw-r--r--modules/pam_unix/support.c821
-rw-r--r--modules/pam_unix/support.h139
-rw-r--r--modules/pam_unix/unix_chkpwd.c324
-rw-r--r--modules/pam_unix/yppasswd.h51
-rw-r--r--modules/pam_unix/yppasswd_xdr.c41
-rw-r--r--modules/pam_userdb/.cvsignore1
-rw-r--r--modules/pam_userdb/Makefile81
-rw-r--r--modules/pam_userdb/README30
-rw-r--r--modules/pam_userdb/conv.c125
-rw-r--r--modules/pam_userdb/create.pl23
-rwxr-xr-xmodules/pam_userdb/libdbfound.sh15
-rw-r--r--modules/pam_userdb/pam_userdb.c302
-rw-r--r--modules/pam_userdb/pam_userdb.h61
-rw-r--r--modules/pam_warn/.cvsignore1
-rw-r--r--modules/pam_warn/Makefile102
-rw-r--r--modules/pam_warn/README26
-rw-r--r--modules/pam_warn/pam_warn.c132
-rw-r--r--modules/pam_wheel/.cvsignore1
-rw-r--r--modules/pam_wheel/Makefile86
-rw-r--r--modules/pam_wheel/README33
-rw-r--r--modules/pam_wheel/pam_wheel.c275
-rwxr-xr-xmodules/register_static49
-rw-r--r--pgp.keys.asc103
320 files changed, 48373 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 00000000..31265a3f
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,4 @@
+default.defs
+.freezemake
+.filelist
+include \ No newline at end of file
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 00000000..59da5b64
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,1244 @@
+
+$Id$
+
+-----------------------------
+
+TODO:
+
+ - autoconf the modules?
+ - sanitize use of md5 throughout distribution.. Make a static
+ library for helping to develop modules that contains it and other
+ stuff. Also add sha-1 and ripemd-160 digest algorithms.
+ - once above is done. remove hacks from the secret@here module etc..
+ - remove prototype for gethostname in pam_access.c (Derrick)
+ - document PAM_INCOMPLETE changes
+ - verify that the PAM_INCOMPLETE interface is sensible. Can we
+ catch errors? should we permit item changing etc., between
+ pam_authenticate re-invocations?
+ - verify that the PAM_INCOMPLETE interface works (auth seems ok..)
+ - add PAM_INCOMPLETE support to modules (partially added to pam_pwdb)
+ - work on RFC.
+ - do we still need to remove openlog/closelog from modules..?
+ - auth and acct support in pam_cracklib, "yes, I know the password
+ you just typed was valid, I just don't think it was very strong..."
+
+0.73: please submit patches for this section with actual code/doc
+ patches!
+
+Planning to include:
+
+ - some autoconf support and other outstanding changes from Jan..
+ - some outstanding changes from Ben
+ - add in the pam_cap and pam_netid modules
+
+0.72: Mon Dec 13 22:41:11 PST 1999
+
+* patches from Debian (Ben Collins): pam_ftp supports event driven
+ conversations now; pwdb_chkpwd cleanup; pam_warn static compile fix;
+ user_db compiler warnings removed; debian defs file; pam_mail can
+ now be used as a session module
+* ndbm compilation option for user_db module (fix explained by Richard Khoo)
+* pam_cracklib bug fix
+* packaging fixes & build from scratch stuff (Konst Bulatnikov & Frodo
+ Looijaard)
+* -ldl appended to the libpam.so compilation make rule. (Charles Seeger)
+* Red Hat security patch for pam_pwdb forwarded by Debian! (Ben
+ Collins. Fix provided by Andrey as it caught the problem earlier in the
+ code.)
+* heuristic to prevent leaking filedescriptors to an agent. [This needs
+ to be better supported perhaps by an additional libpamc API function?]
+* pam_userdb segfault fix from (Ben Collins)
+* PAM draft spec extras added at request of 'sen_ml'
+
+0.71: Sun Nov 7 20:21:19 PST 1999
+
+* added -lc to linker pass for pam_nologin module (glibc is weird).
+* various header changes to lower the number of warnings on glibc
+ systems (Dan Yefimov)
+* merged a bunch of Debian fixes/patches/documentation (Ben Collins)
+ things touched: libpam (minor); doc/modules/pam_unix.sgml; pam_env
+ (plus docs); pam_mkhomedir (new module for new home directories on
+ the fly...); pam_motd (new module); pam_limits (adjust to match
+ docs); pam_issue (new module + doc) [Some of these were also
+ submitted by Thorsten Kukuk]
+* small hack to lower the number of warnings that pam_client.h was
+ generating.
+* debian and SuSE apparently can use the pam_ftp module, so
+ removed the obsolete comment about this from the docs. (Thorsten
+ Kukuk)
+
+0.70: Fri Oct 8 22:05:30 PDT 1999
+
+* bug fix for parsing of value=action tokens in libpam/pam_misc.c was
+ segfaulting (Jan Rekorajski and independently Matthew Melvin)
+* numerous fixes from Thorsten Kukuk (icluding much needed fixes for
+ bitrot in modules and some documentation) that got included in SuSE 6.2.
+* reentrancy issues in pam_unix and pam_cracklib resolved (Jan Rekorajski)
+* added hosts_equiv_rootok module option to pam_rhosts module (Tim Berger)
+* added comment about 'expose_account' module argument to admin and
+ module writers' docs (request from Michael K Johnson).
+* myriad of bug fixes for libpamc - library now built by default and
+ works with the biomouse fingerprint scanner agent/module
+ (distributed separately).
+
+0.69: Sun Aug 1 20:25:37 PDT 1999
+
+* c++ header #ifdef'ing for pam_appl.h (Tuomo Pyhala)
+* added pam_userdb module (Cristian Gafton)
+* minor documentation changes
+* added in revised pam_client library (libpamc). Not installed by
+ default yet, since the example agent/module combo is not very secure.
+* glibc fixes (Thorsten Kukuk, Adam J. Richter)
+
+0.68: Sun Jul 4 23:04:13 PDT 1999
+
+* completely new pam_unix module from Jan Rekorajski and Stephen Langasek
+* Jan Rekorajski pam_mail - support for Maildir format mailboxes
+* Jan Rekorajski pam_cracklib - support for old password comparison
+* Jan Rekorajski bug fix for pam_pwdb setcred reusing auth retval
+* Andrey's pam_tally patch (lstat -> fstat)
+* Robert Milkowski's additional pam_tally patches to **change format of
+ /var/log/faillog** to one from shadow-utils, add new option "per_user"
+ for pam_tally module, failure time logging, support for fail_line
+ field, and support for fail_locktime field with new option
+ no_lock_time.
+* pam_tally: clean up the tally application too.
+* Marcin Korzonek added process priority settings to pam_limits (bonus
+ points for adding to documentation!)
+* Andrey's pam_pwdb patch (cleanup + md5 endian fubar fix)
+* more binary prompt preparations (make misc conv more compatible with spec)
+* modified callback hook for fail delay to be more useful with event
+ driven applications (changed function prototype - suspect no one
+ will notice). Documented this in app developer guide.
+* documentation for pam_access from Tim Berger
+* syntax fixes for the documentation - a long time since I've built it :*(
+ added some more names to the CREDITS file.
+
+0.67: Sat Jun 19 14:01:24 PDT 1999
+
+* [dropped libpam_client - libpamc will be in the next release and
+ conforms to the developing spec in doc/specs/draft-morgan-pam.raw.
+ Sorry if you are keeping a PAM tree in CVS. CVS is a pain for
+ directories, but this directory was actually not referenced by
+ anything so the disruption should be light.]
+* updates to pam_tally from Tim
+* multiple updates from Stephen Langasek to pam_unix
+* pam_filter had some trouble compiling (bug report from Sridhar)
+* pam_wheel now attempts to identify the wheel group for the local
+ system instead of blindly assuming it is gid=0. In the case that
+ there is no "wheel" group, we default to assuming gid=0 is what was
+ meant - former behavior. (courtesy of Sridhar)
+* NIS+ changes to pam_unix module from Dmitry O Panov
+* hopefully, a fix for redefinition of LOG_AUTHPRIV (bug report Luke
+ Kenneth Casson Leighton)
+* fix for minor typo in pam_wheel documentation (Jacek Kopecky)
+* slightly more explanation of the [x=y] pam.conf syntax in the sys
+ admin guide.
+
+0.66: Mon Dec 28 20:22:23 PST 1998 <morgan@linux.kernel.org>
+
+* Started using cvs to keep track of changes to Linux-PAM. This will
+ likely break some of the automated building stuff (RPMs etc..).
+* security bug fix to pam_unix and pam_tally from Andrey.
+* modules make file is now more automatic. It should be possible to
+ unpack an external module in the modules directory and have it automatically
+ added to the build process. Also added a modules/download-all script
+ that will make such downloading easier. I'm happy to receive patches to
+ this file, informing the distribution of places from which to enrich itself.
+* removed pam_system_log stuff. Thought about it long and hard: a
+ bad idea. If libc cannot guarantee a thread safe syslog, it needs
+ to be fixed and compatibility with other PAM libraries was
+ unnecessarily strained.
+* SAG documentation changes: Seth Chaiklin
+* rhosts: problems with NIS lookup failures with the root-uid check.
+ As a work-around, I've partially eliminated the need for the lookup
+ by supplying two new arguments: no_uid_check, superuser=<username>.
+ As a general rule this is more pluggable, since this module might be
+ used as an authentication scheme for a network service that does not
+ need root privilege...
+* authenticate retval -> setcred for pam_pwdb (likeauth arg).
+* pam_pwdb event driven support
+* non openlog pam_listfile logging
+* BUGFIX: close filedescriptor in pam_group and pam_time (Emmanuel Galanos)
+* Chris Adams' mailhash change for pam_mail module
+* fixed malloc failure check in pam_handlers.c (follow up to comment
+ by Brad M. Garcia).
+* update to _pam_compat.h (Brad M. Garcia)
+* support static modules in libpam again (Brad M. Garcia)
+* libpam/pam_misc.c for egcs to grok the code (Brad M. Garcia)
+* added a solaris-2.5.1 defs file (revived by Derrick J Brashear)
+* pam_listfile logs failed attempts
+* added a comment (Michael K Johnson pointed it out) about sgml2latex
+ having a new syntax. I'll make it the change real when I upgrade...
+* a little more text to the RFC, spelling fix from William J Buffam.
+* minor changes to pam_securetty to accommodate event driven support.
+
+0.65: Sun Apr 5 22:29:09 PDT 1998 <morgan@linux.kernel.org>
+
+* added event driven programming extensions to libpam
+ - added PAM_INCOMPLETE handling to libpam/pam_dispatch.c
+ - added PAM_CONV_AGAIN which is a new conversation response that
+ should be mapped to PAM_INCOMPLETE by the module.
+ - ensured that the pam_get_user() function can resume
+ - changes to pam_strerror to accommodate above return codes
+ - clean up _pam_former_state at pam_end()
+ - ensured that former state is correctly initialized
+ - added resumption tests to pam_authenticate(), pam_chauthtok()
+ - added PAM_FAIL_DELAY item for pausing on failure
+
+* improved _pam_macros.h so that macros can be used as single commands
+ (Andrey)
+
+* reimplemented logging to avoid bad interactions with libc. Added
+ new functions, pam_[,v]system_log() to libpam's API. A programmer
+ can check for this function's availablility by checking if
+ HAVE_PAM_SYSTEM_LOG is #defined.
+
+* removed the reduce conflict from pam_conv1 creation -- I can sleep
+ again now. :^]
+
+* made building of static and dynamic libpam separate. This is
+ towards making it possible to build both under Solaris (for Derrick)
+
+* made USE_CRACKLIB a condition in unix module (Luke Kenneth Casson Leighton)
+
+* automated (quiet) config installation (Andrey)
+
+0.64: Thu Feb 19 23:30:24 PST 1998 Andrew Morgan <morgan@linux.kernel.org>
+
+* miscellaneous patches for building under Solaris (Derrick J Brashear)
+
+* removed STATIC support from a number of module Makefiles. Notably,
+ these modules are those that use libpwdb and caused difficulties
+ satisfying the build process. (Please submit patches to fix this...;)
+
+* reomved the union for binary packet conversations from
+ (_pam_types.h). This is now completely implemented in libpam_client.
+
+* Andrey's patch for working environment variable handling in
+ sh_secret module.
+
+* made the libpam_misc conversation function a bit more flexible with
+ respect to binary conversations.
+
+* added top level define (DEBUG_REL) for compiling in the form of
+ a debugging release. I use this on a Red Hat 4.2 system with little
+ chance of crashing the system as a whole. (Andrey has another
+ implementation of this -- with a spec file to match..)
+
+0.63: Wed Jan 28 22:55:30 PST 1998 Andrew Morgan <morgan@linux.kernel.org>
+
+* added libpam_client "convention" library. This makes explicit the
+ use of PAM_BINARY_PROMPT. It is a first cut, so don't take it too
+ seriously yet. Comments/suggestions for improvements are very
+ welcome. Note, this library does not compile by default. It will
+ be enabled when it is judged stable. The library comes with two
+ module/agent pairs and can be used with ssh using a patch available
+ from my pre-release directory [where you got this file.]
+
+* backward compatibility patch for libpam/pam_handlers.c (PAM_IGNORE
+ was working with neither "requistie" nor "required") and a DEBUG'ing
+ compile time bug with pam_dispatch.c (Savochkin Andrey Vladimirovich)
+
+* minor Makefile change from (Savochkin Andrey Vladimirovich)
+
+* added pam_afsauth, pam_afspass, pam_restrict, and pam_syslog hooks
+ (Derrick J Brashear)
+
+* pam_access use of uname(2) problematic (security problem
+ highlighted by Olaf Kirch).
+
+* pam_listfile went a bit crazy reading group membersips (problem
+ highlighted by Olaf Kirch and patched independently by Cristian
+ Gafton and Savochkin Andrey Vladimirovich)
+
+* compatibility hooks for solaris and hpux (Derrick J Brashear)
+
+* 64 bit Linux/alpha bug fixed in pam_rhosts (Andrew D. Isaacson)
+
+0.62: Wed Jan 14 14:10:55 PST 1998 Andrew Morgan <morgan@linux.kernel.org>
+
+* Derrick J Brashear's patches: adds the HP stuff missed in the first
+ patch; adds SunOS support; adds support for the Solaris native ld
+ instead of requiring gnu ld.
+
+* last line of .rhosts file need not contain a newline. (Bug reported by
+ Thompson Freeman.)
+
+0.61: Thu Jan 8 22:57:44 PST 1998 Andrew Morgan <morgan@linux.kernel.org>
+
+* complete rewrite of the "control flag" logic. Formerly, we were
+ limited to four flags: requisite, required, sufficient, optional.
+ We can now use these keywords _and_ a great deal more besides.
+ The extra logic was inspired by Vipin Samar, a preliminary patch was
+ written by Andy Berkheimer, but I "had some ideas of my own" and
+ that's what I've actually included. The basic idea is to allow the
+ admin to custom build a control flag with a series of token=value
+ pairs inside square brackets. Eg., '[default=die success=ok]' which
+ is pretty close to a synonym for 'requisite'. I'll try to document it
+ better in the sys-admin guide but I'm pretty sure it is a change for
+ the better.... If what is in the sys-admin guide is not good enough
+ for you, just take a look at the source for libpam ;^)
+
+0.59: Thu Jan 8 22:27:22 PST 1998 Andrew Morgan <morgan@linux.kernel.org>
+
+* better handling of empty lines in .rhosts file. (Formerly, we asked
+ the nameserver about them!) Fix from Hugh Daschbach.
+
+* _broke_some_binary_compatibility_ with previous versions to become
+ compliant with X/Open's XSSO spec. Specifically, this has been
+ by changing the prototype for pam_strerror().
+
+* altered the convention for the conversation mechanism to agree
+ with that of Sun. (number of responses 'now=' number of messages
+ with help from Cristian for finding a bug.. Cristian also found a
+ nasty speradic segfault bug -- Thanks!)
+
+* added NIS+ support to pam_unix_*
+
+* fixed a "regular file checking" problem with the ~/.rhosts sanity
+ check. Added "privategroup" option to permit group write permission
+ on the ~/.rhosts file in the case that the group owner has the same
+ name as the authenticating user. :*) "promiscuous" and "suppress"
+ were not usable!
+
+* added glibc compatibility to pam_rhosts_auth (protected __USE_MISC
+ with #ifndef since my libc already defines it!).
+
+* Security fix from Savochkin Andrey Vladimirovich with suggested
+ modification from Olaf Seibert.
+
+* preC contains mostly code clean-ups and a number of changes to
+ _pam_macros.
+
+0.58: whenever
+
+* pam_getenvlist() has a more robust definition (XSSO) than was previously
+ thought. It would seem that we no longer need pam_misc_copy_env()
+ which was there to provide the robustness that pam_getenvlist()
+ lacked before...
+
+ Accordingly, I have REMOVED the prototype from libpam_misc. (The
+ function, however, will remain in the library as a wrapper for
+ legacy apps, but will likely be removed from libpam_misc-1.0.) PLEASE
+ FIX YOUR APPS *BEFORE* WE GET THERE!
+
+* Alexy Nogin reported garbage output from pam_env in the case of
+ a non-existent environment variable.
+
+* 'fixed' pwdb compilation for pam_wheel. Not very cleanly
+ done.. Mmmm. Should really clean up the entire source tree...
+
+* added prototypes for mapping functions
+
+ <**WARNING**>
+
+ various constants have had there names changed. Numerical values have
+ been retained but be aware some source old modules/applications will
+ need to be fixed before recompilation.
+
+ </**WARNING**>
+
+* appended documentation to README for pam_rhosts module (Nicolai
+ Langfeldt).
+
+* verified X/Open compatibility of header files - note, where we differ
+ it is at the level of compilation warnings and the use of 'const char *'
+ instead of 'char *'. Previously, Sun(X/open) have revised their spec
+ to be more 'const'-ervative in the light of comments from Linux-PAM
+ development.
+
+* Ooops! PAM_AUTHTOKEN_REQD should have been PAM_NEW_AUTHTOK_REQD.
+
+ changed: pam_pwdb(pam_unix_acct) (also bug fix for
+ _shadow_acct_mgmt_exp() return value), pam_stress,
+ libpam/pam_dispatch, blank, xsh.
+
+* New: PAM_AUTHTOK_EXPIRED - password has expired.
+
+* Ooops! PAM_CRED_ESTABLISH (etc.) should have been PAM_ESTABLISH_CRED
+ etc... (changed - this may break some people's modules - PLEASE TAKE
+ NOTE!)
+ changed: pam_group, pam_mail, blank, xsh; module and appl
+ docs, pam_setcred manual page.
+
+* renamed internal _pam_handle structure to be pam_handle as per XSSO.
+
+* added PAM_RADIO_TYPE (for multiple choice input method). Also
+ added PAM_BINARY_{MSG,PROMPT} (for interaction out of sight of user
+ - this could be used for RSA type authentication but is currently
+ just there for experimental purposes). The _BINARY_ types are now
+ usable with hooks in the libpam_misc conversation function. Still
+ have to add PAM_RADIO_TYPE.
+
+* added pam_access module (Alexei Nogin)
+
+* added documentation for pam_lastlog. Also modified the module to
+ not (by default) print "welcome to your new account" when it cannot
+ find a utmp entry for the user (you can turn this on with the
+ "never" argument).
+
+* small correction to the pam_fail_delay manual page. Either the appl or
+ the modules header file will prototype this function.
+
+* added "bigcrypt" (DEC's C2) algorithm(0) to pam_pwdb. (Andy Phillips)
+
+* *BSD tweaking for various #include's etc. (pam_lastlog, pam_rhosts,
+ pam_wheel, libpam/pam_handlers). (Michael Smith)
+
+* added configuration directory $SCONFIGED for module specific
+ configuration files.
+
+* added two new "linked" man pages (pam.conf(8) and pam.d(8))
+
+* included a reasonable default for /etc/pam.conf (which can be
+ translated to /etc/pam.d/* files with the pam_conv1 binary)
+
+* fixed the names of the new configuration files in
+ conf/pam_conv1/pam_conv.y
+
+* fixed make check.
+
+* pam_lastlog fixed to handle UID in virgin part of /var/log/lastlog
+ (bug report from Ronald Wahl).
+
+* grammar fix in pam_cracklib
+
+* segfault avoided in pam_pwdb (getting user). Updating of passwords
+ that are directed to a "new" database are more robust now (bug noted
+ by Michael K. Johnson). Added "unix" module argument for migrating
+ passwords from another database to /etc/passwd. (documentation
+ updated). Removed "bad username []" warning for empty passwords -
+ on again if you supply the 'debug' module argument.
+
+* ctrl-D respected in conversation function (libpam_misc)
+
+* Removed -DPAM_FAIL_DELAY_ON from top-level Makefile. Nothing in
+ the distribution uses it. I guess this change happened a while
+ back, basically I'm trying to make the module parts of the
+ distribution "source compatible" with the RFC definition of PAM.
+ This implementation of PAM is a superset of that definition. I have
+ added the following symbols to the Linux-PAM header files:
+
+ PAM_DATA_SILENT (see _pam_types.h)
+ HAVE_PAM_FAIL_DELAY (see _pam_types.h)
+ PAM_DATA_REPLACE (see _pam_modules.h)
+
+ Any module (or application) that wants to utilize these features,
+ should check (#ifdef) for these tokens before using the associated
+ functionality. (Credit to Michael K. Johnson for pointing out my
+ earlier omission: not documenting this change :*)
+
+* first stab at making modules more independent of full library
+ source. Modules converted:
+ pam_deny
+ pam_permit
+ pam_lastlog
+ pam_pwdb
+
+* pam_env.c: #include <errno.h> added to ease GNU libc use. (Michael
+ K. Johnson)
+
+* pam_unix_passwd fixes to shadow aging code (Eliot Frank)
+
+* added README for pam_tally
+
+0.57: Fri Apr 4 23:00:45 PST 1997 Andrew Morgan <morgan@parc.power.net>
+
+* added "nodelay" argument to pam_pwdb. This can be used to turn off
+ the call to pam_fail_delay that takes effect when the user fails to
+ authenticate themself.
+
+* added "suppress" argument to pam_rhosts_auth module. This will stop
+ printing the "rlogin failure message" when the user does not have a
+ .rhosts file.
+
+* Extra fixes for FAKEROOT in Makefiles (Savochkin Andrey
+ Vladimirovich)
+
+* pam_tally added to tree courtesy of Tim Baverstock
+
+* pam_rhosts_auth was failing to read NFS mounted .rhosts
+ files. (Fixed by Peter Allgeyer). Refixed and further enhanced
+ (netgroups) by Nicolai Langfeldt. [Credit also to G.Wilford for some
+ changes that were not actually included..]
+
+* optional (#ifdef PAM_READ_BOTH_CONFS) support for parsing of pam.d/
+ AND pam.conf files (Elliot Lee).
+
+* Added (and signed) Cristian's PGP key. (I've never met him, but I am
+ convinced the key belongs to the guy that is making the PAM rpms and
+ also producing libpwdb. Please note, I will not be signing anyone
+ else's key without a personal introduction..)
+
+* fixed erroneous syslog warning in pam_listfile (Savochkin Andrey
+ Vladimirovich, whole file reformatted by Cristian)
+
+* modified pam_securetty to return PAM_IGNORE in the case that the user's
+ name is not known to the system (was previously, PAM_USER_UNKNOWN). The
+ Rationale is that pam_securetty's sole purpose is to prevent superuser
+ login anywhere other than at the console. It is not its concern that the
+ user is unknown - only that they are _not_ root. Returning
+ PAM_IGNORE, however, insures that the pam_securetty can never be used to
+ "authenticate" a non-existent user. (Cristian Gafton with bug report from
+ Roger Hu)
+
+* Modified pam_nologin to display the no-login message when the user
+ is not known. The return value in this case is still PAM_USER_UNKNOWN.
+ (Bug report from Cristian Gafton)
+
+* Added NEED_LCKPWD for pam_unix/ This is used to define the locking
+ functions and should only be turned on if you don't have them in
+ your libc.
+
+* tidied up pam_lastlog and pam_pwdb: removed function that was never used.
+
+* Note for package maintainers: I have added $(FAKEROOT) to the list of
+ environment variables. This should help greatly when you build PAM
+ in a subdirectory. I've gone through the tree and tried to make
+ everything compatible with it.
+
+* added pam_env (courtesy of Dave Kinchlea)
+
+* removed pam_passwd+ from the tree. It has not been maintained in a
+ long time and running a shell script was basically insecure. I've
+ indicated where you can pick up the source if you want it.
+
+* #define HAVE_PAM_FAIL_DELAY . Applications can conditionally compile
+ with this if they want to see if the facility is available. It is
+ now always available. (corresponding compilation cleanups..)
+
+* _pam_sanitize() added to pam_misc. It purges the PAM_AUTHTOK and
+ PAM_OLDAUTHTOK items. (calls replaced in pam_auth and pam_password)
+
+* pam_rhosts now knows about the '+' entry. Since I think this is a
+ dangerous thing, I have required that the sysadmin supply the
+ "promiscuous" flag for it in the corresponding configuration file
+ before it will work.
+
+* FULL_LINUX_PAM_SOURCE_TREE exported from the top level make file.
+ If you want to build a module, you can test for this to determine if
+ it should take its directions from above or supply default locations
+ for installation. Etc.
+
+0.56: Sat Feb 15 12:21:01 PST 1997 <morgan@parc.power.net>
+
+* pam_handlers.c can now interpret the pam.d/ service config tree:
+ - if /etc/pam.d/ exists /etc/pam.conf is IGNORED
+ (otherwise /etc/pam.conf is treated as before)
+ - given /etc/pam.d/
+ . config files are named (in lower case) by service-name
+ . config files have same syntax as /etc/pam.conf except
+ that the "service-name" field is not present. (there
+ are thus three manditory fields (and arguments are
+ optional):
+
+ module-type control-flag module-path optional-args...
+
+ )
+
+* included conf/pam_conv1 for converting pam.conf to a pam.d/ version
+ 1.0 directory tree. This program reads a pam.conf file on the
+ standard input stream and creates ./pam.d/ (in the local directory)
+ and fills it with ./pam.d/"service-name" files.
+
+ *> Note: It will fail if ./pam.d/ already exists.
+
+ PLEASE REPORT ANY BUGS WITH THIS CONVERSION PROGRAM... It currently
+ cannot retain comments from the old conf file, so take care to do this
+ by hand. Also, please email me with the fix that makes the
+ shift/reduce conflict go away...
+
+* Added default module path to libpam for modules (see pam_handlers.c)
+ it makes use of Makfile defined symbol: DEFAULT_MODULE_PATH which is
+ inhereted from the defs/* variable $(SECUREDIR). Removed module
+ paths from the sample pam.conf file as they are no longer needed.
+
+* pam_pwdb can now verify read protected passwords when it is not run
+ by root. This is via a helper binary that is setuid root.
+
+* pam_permit now prompts for a username if it is not already determined
+
+* pam_rhosts now honors "debug" and no longer hardwire's "root" as the
+ superuser's name.
+
+* pam_securetty now honors the "debug" flag
+
+* trouble parsing extra spaces fixed in pam_time and pam_group
+
+* added Michael K. Johnson's PGP key to the pgp.keys.asc list
+
+* pam_end->env not being free()'d: fixed
+
+* manuals relocated to section 3
+
+* fixed bug in pam_mail.c, and enhanced to recognize '~' as a prefix
+ to indicate the $HOME of the user (courtesy David
+ Kinchlea). *Changed* from a "session" module to an "auth"
+ module. It cannot be used to authenticate a user, but it can be used
+ in setting credentials.
+
+* fixed a stupid bug in pam_warn.. Only PAM_SERVICE was being read :*(
+
+* pam_radius rewritten to exclusively make use of libpwdb. (minor fix
+ to Makefile for cleaning up - AGM)
+
+* pam_limits extended to limit the total number of logins on a system
+ at any given time.
+
+* libpam and libpam_misc use $(MAJOR_REL) and $(MINOR_REL) to set their
+ version numbers [defined in top level makefile]
+
+* bugfix in sed command in defs/redhat.defs (AGM's fault)
+
+* The following was related to a possibility of buffer overruns in
+ the syslogging code: removed fixed length array from syslogging
+ function in the following modules [capitalized the log identifier
+ so the sysadmin can "know" these are fixed on the local system],
+
+ pam_ftp, pam_stress, pam_rootok, pam_securetty,
+ pam_listfile, pam_shells, pam_warn, pam_lastlog
+ and
+ pam_unix_passwd (where it was definitely _not_ exploitable)
+
+0.55: Sat Jan 4 14:43:02 PST 1997, Andrew Morgan <morgan@parc.power.net>
+
+* added "requisite" control_flag to /etc/pam.conf syntax. [See
+ Sys. Admin. Guide for explanation] changes to pam_handlers.c
+
+* completely new handling of garbled pam.conf lines. The modus
+ operandi now is to assume that any errors in the line are minor.
+ Errors of this sort should *most definitely* lead to the module
+ failing, however, just ignoring the line (as was the case
+ previously) can lead to gaping security holes(! Not foreseen by the
+ RFC). The "motivation" for the RFC's comments about ignoring garbled
+ lines is present in spirit in the new code: basically a garbled line
+ is treated like an instance of the pam_deny.so module.
+ changes to pam_handlers.c and pam_dispatch.c .
+
+* patched libpam, to (a) call _pam_init_handlers from pam_start() and
+ (b) to log a text error if there are no modules defined for a given
+ service when a call to a module is requested. [pam_start() and
+ pam_dispatch() were changed].
+
+* patched pam_securetty to deal with "/dev/" prefix on PAM_TTY item.
+
+* reorganized the modules/Makefile to include *ALL* modules. It is now
+ the responsibility of the modules themselves to test whether they can
+ be compiled locally or not.
+
+* modified pam_group to add to the getgroups() list rather than overwrite
+ it. [In the case of "HAVE_LIBPWDB" we use the pwdb_..() calls to
+ translate the group names.]. Module now pays attention to
+ PAM_CRED_.. flag(!)
+
+* identified and removed bugs in field reading code of pam_time and
+ (thus) pam_group.
+
+* Cristian's patches to pam_listfile module, corresponding change to
+ documentation.
+
+* I've discovered &ero; for sgml!
+ Added pam_time documentation to the admin guide.
+
+* added manual pages: pam.8, pam_start.2(=pam_end.2),
+ pam_authenticate.2, pam_setcred.2, pam_strerror.2,
+ pam_open_session.2(=pam_close_session.2) and pam_chauthtok.2 .
+
+* added new modules:
+
+ - pam_mail (tells the user if they have any new mail
+ and sets their MAIL env variable)
+ - pam_lastlog (reports on the last time this user called
+ this module)
+
+* new module hooks provided.
+
+* added a timeout feature to the conversation function in
+ libpam_misc. Documented it in the application developers' guide.
+
+* fixed bug in pam_misc_paste_env() function..
+
+* slight modifications to wheel and rhosts writeup.
+
+* more security issues added to module and application guides.
+
+--
+Things present but not mentioned in previous release (sorry)
+
+* pam_pwdb module now resets the "last_change" entry before updating a
+ password.
+--
+
+Sat Nov 30 19:30:20 PST 1996, Andrew Morgan <morgan@parc.power.net>
+
+* added environment handling to libpam. involved change to _pam_types.h
+ also added supplementary functions to libpam_misc
+
+* added pam_radius - Cristian
+
+* slight speed up for pam_rhosts
+
+* significantly enhanced sys-admin documentation (8 p -> 41 p in
+ PostScript). Added to other documentation too. Mostly the changes
+ in the other docs concern the new PAM-environment support, there is
+ also some coverage of libpam_misc in the App. Developers' guide.
+
+* Cristian's patches to pam_limits and pam_pwdb. Fixing bugs. (MORE added)
+
+* adopted Cristian's _pam_macros.h file to help with common macros and
+ debugging stuff, gone through tree tidying up debugging lines to use
+ this [not complete].
+
+ - for consistency replaced DROP() with _pam_drop()
+
+* commented memory debugging in top level makefile
+
+* added the following modules
+
+ - pam_warn log information to syslog(3) about service application
+ - pam_ftp if user is 'ftp' then set PAM_RUSER/PAM_RHOST with password
+ (comment about nologin added to last release's notes)
+
+* modified the pam_listfile module. It now declares a meaningful static
+ structure name.
+
+Sun Nov 10 13:26:39 PST 1996, Andrew Morgan <morgan@parc.power.net>
+
+ **PLEASE *RE*AMEND YOUR PERSONAL LINKS**
+
+ -------> http://parc.power.net/morgan/Linux-PAM/index.html <-------
+
+ **PLEASE *RE*AMEND YOUR PERSONAL LINKS**
+
+A brief summary of what has changed:
+
+* many modules have been modified to accomodate fixing the pam_get_user()
+ change. Please take note if you have a module in this distribution.
+
+* pam_unix is now the pam_unix that Red Hat has been using and which
+ should be fairly well debugged.
+
+ - I've added some #ifdef's to make it compile for me, and also
+ updated it with respect to the libpam-0.53, so have a look at the
+ .../modules/pam_unix/Makefile to enable cracklib and shadow features
+
+ ** BECAUSE OF THIS, I cannot guarantee this code works as it **
+ ** did for Red Hat. Please test and report any problems. **
+
+* the pam_unix of .52 (renamed to pam_pwdb) has been enhanced and made
+ more flexible with by implementing it with respect to the new
+ "Password Database Library" see
+
+ http://parc.power.net/morgan/libpwdb/index.html
+
+ modules included in this release that require this library to
+ function are the following:
+
+ - pam_pwdb (ne pam_unix-0.52 + some enhancements)
+ - pam_wheel
+ - pam_limits
+ - pam_nologin
+
+* Added some optional code for memory debugging. In order to support
+ this you have to enable MEMORY_DEBUG in the top level makefile and
+ also #define MEMORY_DEBUG in your applications when they are compiled.
+ The extra code resides in libpam (compiled if MEMORY_DEBUG is defined)
+ and the macros for malloc etc. are to be found at the end of
+ _pam_types.h
+
+* used above code to locate two memory leaks in pam_unix module and two
+ in libpam (pam_handlers.h)
+
+* pam_get_user() now sets the PAM_USER item. After reading the Sun
+ manual page again, it was clear that it should do this. Various
+ modules have been assuming this and now I have modified most of them
+ to account for this change. Additionally, pam_get_user() is now
+ located in the module include file; modules are supposed to be the
+ ones that use it(!) [Note, this is explicitly contrary to the Sun
+ manual page, but in the spirit of the Linux distribution to date.]
+
+* replaced -D"LINUX" with -D"LINUX_PAM" as this is more explicit and less
+ likely to be confused with -D"linux".
+ Also, modified the libpam #include files to behave more like the Sun
+ ones #ifndef LINUX_PAM.
+
+* removed <bf/ .. / from documentation titles. This was not giving
+ politically correct html..
+
+----- My vvvvvvvvvvvvvvvvvvv was a long time ago ;*] -----
+
+Wed Sep 4 23:57:19 PDT 1996 (Andrew Morgan <morgan@physics.ucla.edu>
+
+0. Before I begin, Linux-PAM has a new primary distribution site (kindly
+donated by Power Net Inc., Los Angeles)
+
+ **PLEASE AMMEND YOUR PERSONAL LINKS**
+
+ -------> http://www.power.net/morgan/Linux-PAM <-------
+
+ **PLEASE AMMEND YOUR PERSONAL LINKS**
+
+1. I'm hoping to make the next release a bug-fix release... So please find
+ all the bugs(! ;^)
+
+2. here are the changes for .52:
+
+* minor changes to module documentation [Incidently, it is now
+ available on-line from the WWW page above]. More changes to follow in
+ the next two releases. PLEASE EMAIL me or the list if there is
+ anything that isn't clear!
+
+* completely changed the unix module. Now a single module for all four
+ management groups (this meant that I could define all functions as
+ static that were not part of the pam_sm_... scheme. AGM)
+
+ - Shadow support added
+PASSWD - Elliot's account management included, and enhanced by Cristian Gafton.
+ - MD5 password support added by Cristian Gafton.
+ - maxtries for authentication now enforced.
+ - Password changing function in pam_unix now works!
+ Although obviously, I'm not going to *guarantee* it ;^) .
+ - stole Marek's locking code from the Red Hat unix module.
+ [ If you like you can #ifdef it in or out ... ]
+
+ You can configure the module more from its Makefile in
+ 0.52/modules/pam_unix/
+
+ If you are nervous that it will destroy your /etc/passwd or shadow
+ files then EDIT the 0.52/modules/pam_unix/pam_unix_pass.-c file.
+ Here is the warning comment from this file...
+
+-------------8<-----------------
+/* <WARNING>
+ *
+ * Uncomment the following #define if you are paranoid, and do not
+ * want to risk losing your /etc/passwd or shadow files.
+ * It works for me (AGM) but there are no guarantees.
+ *
+ * </WARNING>
+ */
+/* #define TMP__FILE */
+------------->8-----------------
+
+ *** If anyone has any trouble, please *say*. Your problem will be
+ fixed in the next release. Also please feel free to scour the
+ code for race conditions etc...
+
+[* The above change requires that you purge your /usr/lib/security
+ directory of the old pam_unix_XXX.so modules: they will NOT be deleted
+ with a 'make remove'.]
+
+* the prototype for the cleanup function supplied to pam_set_data used
+ to return "int". According to Sun it should be "void". CHANGED.
+
+* added some definitions for the 'error_status' mask values that are
+ passed to the cleanup function associated with each
+ module-data-item. These numbers were needed to keep up with changing
+ a data item (see for example the code in pam_unix/support.-c that
+ manages the maximum number of retries so far). Will see what Sun says
+ (current indications are positive); this may be undone before 1.0 is
+ released. Here are the definitions (from pam_modules.h).
+
+#define PAM_DATA_SILENT 0x40000000 /* used to suppress messages... */
+#define PAM_DATA_REPLACE 0x20000000 /* used when replacing a data item */
+
+* Changed the .../conf/pam.conf file. It now points to the new
+ pam_unix module for 'su' and 'passwd' [can get these as SimpleApps --
+ I use them for testing. A more extensive selection of applications is
+ available from Red Hat...]
+
+* corrected a bug in pam_dispatch. Basically, the problem was that if
+ all the modules were "sufficient" then the return value for this
+ function was never set. The net effect was that _pam_dispatch_aux
+ returned success when all the sufficient modules failed. :^( I think
+ this is the correct fix to a problem that the Red Hat folks had
+ found...
+
+sopwith* Removed advisory locking from libpam (thanks for the POSIX patch
+ goes to Josh Wilmes's, my apologies for not using it in the
+ end.). Advisory locking did not seem sufficiently secure for libpam.
+ Thanks to Werner Almesberger for identifying the corresponding "denial
+ of service attack". :*(
+
+* related to fix, have introduced a lock file /var/lock/subsys/PAM
+ that can be used to indicate the system should pay attention to
+ advisory locking on /etc/pam.conf file. To implement this you need to
+ define PAM_LOCKING though. (see .52/libpam)
+
+* modified pam_fail_delay() function. Couldn't find the "not working"
+ problem indicated by Michael, but modified it to do pseudo-random
+ delays based on the values indicated by pam_fail_delay() -- the
+ function "that may eventually go away"... Although Sun is warming to
+ the idea.
+
+* new modules include:
+
+ pam_shells - authentication for users with a shell listed in
+ /etc/shells. Erik Troan <ewt@redhat.com>
+
+ pam_listfile - authentication based on the contents of files.
+ Set to be more general than the above in the
+ future. UNTESTED. Elliot Lee <@redhat.com>
+ [Note, this module compiles with a non-trivial
+ warning: AGM]
+
+Thu Aug 8 22:32:15 PDT 1996 (Andrew Morgan <morgan@physics.ucla.edu>)
+
+* modified makefiles to take more of their installation instructions
+ from the top level makefile. Desired for integration into the Debian
+ distribution, and generally a good idea.
+
+* fixed memory arithmetic in pam_handlers
+ -- still need to track down why failure to load modules can lead to
+ authentication succeding..
+
+* added tags for new modules (smartcards from Alex -- just a promise
+ at this stage) and a new module from Elliot Lee; pam_securetty
+
+* I have not had time to smooth out the wrinkles with it, but Alex's
+ pam_unix modifications are provided in pam_unix-alex (in the modules
+ directory) they will not be compiled by 'make all' and I can't even
+ say if they do compile... I will try to look at them for .52 but, in
+ the mean time please feel free to study/fix/discuss what is there.
+
+* pam_rhosts module. Removed code for manually setting the ruser
+ etc. This was not very secure.
+
+* [remade .ps docs to be in letter format -- my printer complains
+ about a4]
+
+Sunday July, 7 12:45:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>)
+
+* No longer accompanying the Linux-PAM release with apps installed.
+ [Will provide what was here in a separate package.. (soon)
+lib Also see http://www.redhat.com/pam for some more (in .rpm form...)]
+
+* renamed libmisc to libpam_misc. It is currently configured to only compile
+ the static library. For some strange reason (perhaps someone can
+ investigate) my Linux 2.0.0 kernel with RedHat 3.0.3 system
+ segfaults when I compile it to be a dynamic library. The segfault
+ seems to be inside the call to the ** dl_XXX ** function...!?
+
+ There is a simple flag in the libpam_misc/Makefile to turn on dynamic
+ compiles.
+
+* Added a little unofficial code for delay support in libpam (will probably
+ disappear later..) There is some documentation for it in the pam_modules
+ doc now. That will obviously go too.
+
+* rewritten pam_time to use *logic* to specify the stringing together of
+ users/times/terminals etc.. (what was there before was superficially
+ logical but basically un-predictable!)
+
+* added pam_group. Its syntax is almost identical to pam_time but it
+ has another field added; a list of groups to make the user a member
+ of if they pass the previous tests. It seems to not co-exist too well
+ with the groups in the /etc/group but I hope to have that fixed by
+ the next release...
+
+* minor re-formatting of pam_modules documentation
+
+* removed ...// since it wasn't being used and didn't look like it
+ would be!
+
+GCCSunday 23 22:35:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>)
+
+* The major change is the addition of a new module: pam_time for
+ restricting access on terminals at given times for indicated users
+ it comes with its own configuration file /etc/security/time.conf
+ and the sample file simply restricts 'you' from satisfying the blank
+ application if they try to use blank from any tty*
+
+* Small changes include
+- altered pam.conf to demonstrate above new module (try typing username: you)
+- very minor changes to the docs (pam_appl and pam_modules)
+
+Saturday June 2 01:40:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>)
+
+*** PLEASE READ THE README, it has changed ***
+
+* NOTE, 'su' exhibits a "system error", when static linking is
+ used. This is because the pam_unix_... module currently only has
+ partial static linking support. This is likely to change on Monday
+ June 3, when Alex makes his latest version availible. I will include
+ the updated module in next release.
+
+changes for .42:
+
+* modified the way in which libpam/pam_modules.h defines prototypes for
+ the pam_sm_ functions. Now the module must declare which functions it
+ is to provide *before* the #include <security/pam_modules.h> line.
+ (for contrasting examples, see the pam_deny and pam_rootok modules)
+ This removed the ugly hack of defining functions that are never called
+ to overcome warnings... This seems much tidier.
+insterted* updated the TODO list. (changed mailing list address)
+* updated README in .../modules to reflect modifications to static
+ compliation protocol
+* modified the pam_modules documentation to describe this.
+* corrected last argument of pam_get_item( ... ) in
+ pam_appl/modules.sgml, to "const void **".
+* altered GNU GPL's in the documentation, and various other parts of
+ the distribution. *Please check* that any code you are responsible for
+ is corrected.
+* Added ./Copyright (please check that it is acceptable)
+* updated ./README to make current and indicate the new mailing list
+ address
+* have completely rewritten pam_filter. It now runs modular filter
+ executables (stored in /usr/sbin/pam_filter/) This should make it
+ trivial for others to write their own filters.. If you want yours
+ included in the distribution please email the list/me.
+* changes to libpam; there was a silly bug with multiple arguments on a
+ pam.conf line that was broken with a '\<LF>'.
+* 'su' rearranged code (to make better use of PAM)
+ *Also* now uses POSIX signals--this should help the Alpha port.
+* 'passwd' now uses getlogin() to determine who's passwords to change.
+
+Sunday May 26 9:00:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>)
+
+* fixed module makefiles to create needed dynamic/static subdirectories
+
+Saturday May 25 20:30:27.8 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>)
+
+* LOTS has changed regarding how the modules/libpam are built.
+* Michael's mostly complete changes for static support--see below
+ (Andrew got a little carried away and automated the static linking
+ of modules---bugs are likely mine ;( )
+* Thanks mostly to Michael, libpam now compiles without a single warning :^]
+* made static modules/library optional.
+CFLAGS* added 'make sterile' to top level makefile. This does extraclean and remove
+* added Michael and Joseph to documentation credits (and a subsection for
+ future documentation of static module support in pam_modules.sgml)
+* libpam; many changes to makefiles and also automated the inclusion of
+ static module objects in pam_static.c
+* modified modules for automated static/dynamic support. Added static &
+ dynamic subdirectories, as instructed by Michael
+* removed an annoying syslog message from pam_filter: "parent exited.."
+* updated todo list (anyone know anything about svgalib/X? we probably should
+ have some support for these...)
+
+Friday May 24 16:30:15 EDT 1996 (Michael K. Johnson <johnsonm@redhat.com>)
+
+* Added first (incomplete) cut at static support.
+ This includes:
+ . changes in libpam, including a new file, pam_static.c
+ . changes to modules including exporting struct of function pointers
+ . static and dynamic linking can be combined
+ . right now, the only working combinations are just dynamic
+ linking and dynamic libpam.so with static modules linked
+ into libpam.so. That's on the list of things to fix...
+ . modules are built differently depending on whether they
+ are static or dynamic. Therefore, there are two directories
+ under each module directory, one for static, and one for
+ dynamic modules.
+* Fixed random brokenness in the Makefiles. [ foo -nt bar ] is
+ rather redundant in a makefile, for instance. Also, passing
+ on the command line is broken because it cannot be
+ overridden in any way (even adding important parts) in lower-level
+ makefiles.
+* Unfortunately, fixing some of the brokenness meant that I used
+ GNU-specific stuff. However, I *think* that there was GNU-specific
+ stuff already. And I think that we should just use the GNU
+ extensions, because any platform that GNU make doesn't port to
+ easily will be hard to port to anyway. It also won't be likely
+passwd to handle autoconf, which was Ted's suggestion for getting
+ around limitations in standard make...
+ For now, I suggest that we just use some simple GNU-specific
+ extensions.
+
+Monday May 20 22:00:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>)
+
+* added some text to pam_modules.sgml
+* corrected Marek's name in all documentation
+* made pam_stress conform to chauthtok conventions -- ie can now request
+ old password before proceeding.
+* included Alex's latest unix module
+* included Al's + password strength checking module
+* included pam_rootok module
+* fixed too many bugs in libpam.. all subtly related to the argument lists
+ or use of syslog. Added more debugging lines here too.
+* fixed the pam.conf file
+* deleted pam_test module. It is pretty old and basically superceeded
+ by pam_stress
+
+Friday May 9 1:00:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>)
+
+* updated documentaion, added Al Longyear to credits and corrected the
+ spelling of Jeff's name(!). Most changes to pam.sgml (even added a figure!)
+* new module pam_rhosts_auth (from Al Longyear)
+* new apps rlogind and ftpd (a patch) from Al.
+* modified 'passwd' to not call pam_authenticate (note, none of the
+ modules respect this convention yet!)
+* fixed bug in libpam that caused trouble if the last line of a
+ pam.conf file ends with a module name and no newline character
+* also made more compatable with documentation, in that bad lines in
+ pam.conf are now ignored rather than causing libpam to return an
+ error to the app.
+* libpam now overwrites the AUTHTOKs when returning from
+ pam_authenticate and pam_chauthtok calls (as per Sun/RFC too)
+* libpam is now installed as libpam.so.XXX in a way that ldconfig can
+ handle!
+
+
+Wednesday May 1 22:00:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>)
+
+* removed .../test directory, use .../examples from now on.
+* added .../apps directory for fully functional applications
+ - the apps directory contains directories that actually contain the apps.
+ the idea is to make application compilation conditional on the presence
+ of the directory. Note, there are entries in the Makefile for
+ 'login' and 'ftpd' that are ready for installation... Email me if
+ you want to reserve a directory name for an application you are
+ working on...
+* similar changes to .../modules makefile [entries for pam_skey and
+ pam_kerberos created---awaiting the directories.] Email me if you
+ want to register another module...
+* minor changes to docs.. Not really worth reprinting them quite yet!
+ [save the trees]
+* added misc_conv to libmisc. it is a generic conversation function
+ for text based applications. [would be nice to see someone create
+ an Xlib and/or svgalib version]
+* fixed ctrl-z/c bug with pam_filter module [try xsh with the default
+ pam.conf file]
+* added 'required' argument to 'pam_stress' module.
+* added a TODO list... other suggestions to the list please.
+
+Saturday April 7 00:00:00 PST 1996 ( Andrew Morgan <morgan@physics.ucla.edu> )
+
+* Alex and Marek please note I have altered _pam_auth_unix a little, to
+ make it get the passwords with the "proper method" (and also fixed it
+ to not have as many compiler warnings)
+* updated the conf/pam.conf file
+* added new example application examples/xsh.c (like blank but invokes
+ /bin/sh)
+* Marc's patches for examples/blank.c (and AGM's too)
+* fixed stacking of modules in libpam/pam_handlers.c
+* fixed RESETing in libpam/pam_item.c
+* added new module modules/pam_filter/ to demonstrate the possibility
+ of inserting an arbitrary filter between the terminal and the
+ application that could do customized logging etc... (see use of
+ bin/xsh as defined in conf/pam.conf)
+
+
+Saturday March 16 19:00:00 PST 1996 ( Andrew Morgan <morgan@physics.ucla.edu> )
+
+These notes are for 0.3 I don't think I've left anything important
+out, but I will use emacs 'C-x v a' next time! (Thanks Jeff)
+
+ * not much has changed with the functionality of the Linux-PAM lib
+ .../libpam
+ - pam_password calls module twice with different arguments
+ - added const to some of the function arguments
+ - added PAM_MAX_MES_ to <security/_pam_types.h>
+ - was a lot over zealous about purging old passwords...
+ I have removed much of this from source to make it
+ more compatible with SUN.
+ - moved some PAM_... tokens to pam_modules.h from _pam_types.h
+ (no-one should notice)
+
+ * added three modules: pam_permit pam_deny pam_stress
+ no prizes for guessing what the first two do. The third is
+ a reasonably complete (functional) module. Is intended for testing
+ applications with.
+
+ * fixed a few pieces of examples/blank.c so that it works (with
+ pam_stress)
+
+ * ammended the documentation. Looking better, but suggestions/comments
+ very welcome!
+
+Sunday March 10 10:50:00 PST 1996 ( Andrew Morgan <morgan@physics.ucla.edu> )
+
+These notes are for Linux-PAM release 0.21. They cover what's changed
+since I relased 0.2.
+
+ * am now using RCS
+ * substantially changed ./README
+ * fixed bug reading \\\n in pam.conf file
+ * small changes to documentation
+ * added `blank' application to ./examples (could be viewed as
+ a `Linux-PAM aware' application template.)
+ * oops. now including pam_passwd.o and pam_session.o in pamlib.so
+ * compute md5 checksums for all the source when making a release
+ - added `make check' and `make RCScheck' to compute md5 checksums
+ * create a second tar file with all the RCS files in.
+ * removed the .html and .txt docs, supplying sgml sources instead.
+ - see README for info on where to get .ps files
+
+Thursday March 6 0:44:?? PST 1996 ( Andrew Morgan <morgan@physics.ucla.edu> )
+
+These notes are for Linux-PAM release 0.2. They cover what's changed
+since Marc Ewing relased 0.1.
+
+**** Please note. All of the directories in this release have been modified
+**** slightly to conform to the new pamlib. A couple of new directories have
+**** been added. As well as some documentation. If some of your code
+**** was in the previous release. Feel free to update it, but please
+**** try to conform to the new headers and Makefiles.
+
+* Andrew Morgan (morgan@physics.ucla.edu) is making this release
+ availible, Marc has been busy...!
+
+* Marc's pam-0.1/lib has been (quietly) enhanced and integrated into
+ Alex Yurie's collected tree of library and module code
+ (linux-pam.prop.1.tar.gz). Most of the changes are to do with error
+ checking. Some more robustness in the reading of the pam.conf file
+ and the addition of the pam_get_user() function.
+
+* The pam_*.h files have been reorganized to logically enforce the
+ separation of modules from applications. [Don't panic! Apart from
+ changing references of the form
+
+ #include "pam_appl.h"
+
+ to
+
+ #include <security/pam_appl.h>
+
+ The reorganization should be backwardly compatable (ie. a module
+ written for SUN will be as compatable as it was before with the
+ previous version ;)~ ]
+
+ (All of the source in this tree now conforms to this scheme...)
+
+ The new reorganization means that modules can be compiled with a
+ single header, <security/pam_modules.h>, and applications with
+ <security/pam_appl.h>.
+
+* I have tried to remove all the compiler warnings from the updated
+ "pamlib/*.c" files. On my system, (with a slightly modified <dlfcn.h>
+ email me if it interests you..) there are only two warnings that
+ remain: they are that ansi does not permit void --> fn ptr
+ assignment. K&Rv2 doesn't mention this....? As a matter of principle,
+ if anyone knows how to get rid of that warning... please
+ tell. Thanks! "-pedantic"
+
+* you can "make all" as a plain user, but
+
+* to "make install" you must be root. The include files are placed in
+ /usr/include/security. The libpam.so library is installed in /usr/lib
+ and the modules in /usr/lib/security. The two test binaries
+ are installed in the Linux-PAM-0.2/bin directory and a chance is given to
+ replace your /etc/pam.conf file with the one in Linux-PAM-0.2/conf.
+
+* I have included some documentation (pretty preliminary at the
+moment) which I have been working on in .../doc .
+
+I have had a little trouble with the modules, but atleast there are no
+segfaults! Please try it out and discuss your results... I actually
+hope it all works for you. But, Email any bugs/suggestions to the
+Linux-PAM list: linux-pam@mit.edu .....
+
+Regards,
+
+Andrew Morgan
+(morgan@physics.ucla.edu)
+
+
+Sat Feb 17 17:30:24 EST 1996 (Alexander O. Yuriev alex@bach.cis.temple.edu)
+
+ * conf directory created with example of pam_conf
+ * stable code from pam_unix is added to modules/pam_unix
+ * test/test.c now requests username and password and attempts
+ to perform authentication
+
diff --git a/Copyright b/Copyright
new file mode 100644
index 00000000..2f27a2ee
--- /dev/null
+++ b/Copyright
@@ -0,0 +1,41 @@
+Unless otherwise *explicitly* stated the following text describes the
+licensed conditions under which the contents of this Linux-PAM release
+may be distributed:
+
+-------------------------------------------------------------------------
+Redistribution and use in source and binary forms of Linux-PAM, with
+or without modification, are permitted provided that the following
+conditions are met:
+
+1. Redistributions of source code must retain any existing copyright
+ notice, and this entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+2. Redistributions in binary form must reproduce all prior and current
+ copyright notices, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+3. The name of any author may not be used to endorse or promote
+ products derived from this software without their specific prior
+ written permission.
+
+ALTERNATIVELY, this product may be distributed under the terms of the
+GNU General Public License, in which case the provisions of the GNU
+GPL are required INSTEAD OF the above restrictions. (This clause is
+necessary due to a potential conflict between the GNU GPL and the
+restrictions contained in a BSD-style copyright.)
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+-------------------------------------------------------------------------
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..7a5923e7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,283 @@
+##
+## $Id$
+##
+##
+
+# major and minor numbers of this release
+MAJOR_REL=0
+MINOR_REL=72
+DEBUG_REL=no
+#DEBUG_REL=yes
+
+# this should be the name of this directory (under CVS)
+DISTNAME = Linux-PAM
+# this should be the name of this directory (when generating the release)
+RELNAME = $(DISTNAME)-$(MAJOR_REL).$(MINOR_REL)
+
+# this is the name of the archive file
+DISTFILE = $(RELNAME).tar.gz
+
+# define this to indicate to subdirectories that they are part of the
+# full source tree.
+FULL_LINUX_PAM_SOURCE_TREE=yes
+export FULL_LINUX_PAM_SOURCE_TREE
+
+DYNLOAD="dl"
+DYNTYPE="so"
+
+# Comment out either line to disable that type of linking for *modules only*
+# Both at once is a legal configuration!
+DYNAMIC=-DPAM_DYNAMIC
+#STATIC=-DPAM_STATIC
+
+# Comment out these lines to disable building dynamic/static libpam.*
+DYNAMIC_LIBPAM=yes
+#STATIC_LIBPAM=yes
+
+# All combinations of the above four variable definitions are legal,
+# however, not defining either dynamic or static modules and yet
+# creating a some flavor of LIBPAM will make an authentication library
+# that always fails!
+
+# Here we indicate which libraries are present on the local system
+# they control the building of some modules in this distribution
+# Note, these definitions are all "export"ed below...
+
+HAVE_PWDBLIB=yes
+HAVE_CRACKLIB=yes
+HAVE_AFSLIBS=no
+HAVE_KRBLIBS=no
+
+# NB. The following is the generic defines for compilation.
+# They can be overridden in the default.defs file below
+#
+WARNINGS = -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align \
+ -Wtraditional -Wstrict-prototypes -Wmissing-prototypes \
+ -Wnested-externs -Winline -Wshadow -pedantic
+PIC=-fPIC
+
+# Mode to install shared libraries with
+SHLIBMODE=755
+
+#
+# Conditional defines..
+#
+
+ifdef DYNAMIC
+# need the dynamic library functions
+LIBDL=-l$(DYNLOAD)
+ifdef STATIC_LIBPAM
+# needed because pam_xxx() fn's are now in statically linked library
+RDYNAMIC = -rdynamic
+endif
+endif
+
+# Here we include the defines for the preferred operating system
+# these include things like CC, CFLAGS and destination directories
+# etc.. By default, this is a symbolic link to one of the .defs files
+# the .../defs/ directory. Please take a moment to check that you are
+# using the correct one.
+
+include default.defs
+
+# to turn on the fprintf(stderr, ..) debugging lines throughout the
+# distribution uncomment this line
+#EXTRAS += -DDEBUG
+
+# For serious memory allocation tracing uncomment the following
+#MEMORY_DEBUG=-DMEMORY_DEBUG
+
+#######################################################################
+# The pam_unix module in this file will not work on NIS based systems.#
+#######################################################################
+
+# ////////////////////////////////////////////////////
+# // You should not modify anything below this line //
+# ////////////////////////////////////////////////////
+
+# the sub-directories to make things in
+
+DIRS = modules libpam conf libpam_misc libpamc examples
+
+#
+# basic defines
+#
+
+INCLUDEDIR=-I$(shell pwd)/include
+PAMLIB=-L$(shell pwd)/libpam
+PAMMISCLIB=-L$(shell pwd)/libpam_misc
+ifeq ($(DEBUG_REL),yes)
+ PAMLIB += -lpamd
+ PAMMISCLIB += -lpamd_misc
+else
+ PAMLIB += -lpam
+ PAMMISCLIB += -lpam_misc
+endif
+
+
+# This is Linux-PAM and not a version from Sun etc..
+# [Note, this does not describe the operating system you are using
+# only that you are compiling the "Linux" (read FREE) implementation
+# of Pluggable Authentication Modules.
+EXTRAS += -DLINUX_PAM
+
+#
+# build composite defines
+#
+
+LOADLIBES = $(PAMLIB) $(RDYNAMIC) $(PAMMISCLIB) $(LIBDL) $(ULIBS)
+
+CFLAGS += $(EXTRAS) $(MEMORY_DEBUG) $(WARNINGS) $(INCLUDEDIR) $(PIC)
+ifneq ($(strip $(OS)),)
+CFLAGS += -D$(OS)
+endif
+ifneq ($(strip $(ARCH)),)
+CFLAGS += -D$(ARCH)
+endif
+
+#
+# export the libraries-available info; the modules should know how
+# to deal with this...
+#
+export HAVE_PWDBLIB
+export HAVE_CRACKLIB
+export HAVE_AFSLIBS
+export HAVE_KRBLIBS
+
+#
+# generic exports
+#
+export MAJOR_REL # the major release of this distribution
+export MINOR_REL # the minor release of this distribution
+export DEBUG_REL # for installing a debugging version of PAM
+export OS # operating system
+export ARCH # architecture
+export CC # the C compiler
+export INSTALL # to do instalations with
+export MKDIR # to ensure directories exist
+export CFLAGS # CC flags used to compile everything
+export LD_D # build a shared object file (module)
+export LD_L # build a shared library (e.g. libpam)
+export USESONAME # does shlib link command require soname option
+export SOSWITCH # shlib lib soname switch name
+export LINKLIBS # libraries to append when making dynamic libs
+export NEEDSONAME # does shared library link need versioned lib
+export LD # build a generic library
+export LDCONFIG # rebuild the shared libraries
+export AR # build a static library
+export RANLIB # reorder a static library
+export LOADLIBES # libraries needed for application linking
+export PAMLIB # where to find the local libpam.xx file
+export DYNTYPE # which suffix is used for libraries
+export SHLIBMODE # file mode for shared objects
+export EXTRALS # libraries that some modules need
+export LIBDL # extra library for libpam.so
+#
+# where to install things
+#
+export FAKEROOT # for package maintainers
+#
+export PREFIX # basic prefix for all other directories
+export SUPLEMENTED # where to store module helper binaries
+export LIBDIR # where libpam and libpam_misc go
+export SECUREDIR # where the modules will be placed
+export INCLUDED # where to store pam---.h files
+export CONFIGED # where pam.conf and pam.d/ go
+export SCONFIGED # where modules' config files go
+
+#
+# Conditional exporting ( ... these go on for a while... )
+#
+ifdef DYNAMIC
+export DYNAMIC
+endif
+ifdef STATIC
+export STATIC
+endif
+ifdef DYNAMIC_LIBPAM
+export DYNAMIC_LIBPAM
+endif
+ifdef STATIC_LIBPAM
+export STATIC_LIBPAM
+endif
+ifdef MEMORY_DEBUG
+export MEMORY_DEBUG
+endif
+
+##
+## the rules
+##
+
+all: .freezemake headers
+
+ @for i in $(DIRS) ; do \
+ $(MAKE) -C $$i all ; \
+ if [ $$? -ne 0 ]; then break ; fi ; \
+ done
+
+.freezemake: Makefile
+ @touch .freezemake
+ @echo "*WARNING*: If you are running a system that is dependent"
+ @echo " on PAM to work. DO NOT make sterile NOR make remove."
+ @echo " These options will delete the PAM files on your system"
+ @echo " and make it unusable!"
+ @echo ""
+ @echo "If you are in any doubt, just do 'make all' (or just"
+ @echo "'make'). It is likely that this is the SAFEST thing to do...."
+ @exit 1
+
+install:
+ @for i in $(DIRS) ; do \
+ $(MAKE) -C $$i install ; \
+ if [ $$? -ne 0 ]; then break ; fi ; \
+ done
+
+sterile: .freezemake
+ @$(MAKE) remove
+ @$(MAKE) extraclean
+
+remove: .freezemake
+ @for i in $(DIRS) ; do \
+ $(MAKE) -C $$i remove ; \
+ done
+
+clean:
+ @rm -f *~ core
+ @for i in $(DIRS) ; do \
+ $(MAKE) -C $$i clean ; \
+ done
+
+headers:
+ @mkdir -p include/security && cd include/security \
+ && ln -fs ../../libpam/include/security/*.h . \
+ && ln -fs ../../libpam_misc/*.h . \
+ && ln -fs ../../libpamc/include/security/*.h .
+
+cl_headers:
+ @cd include/security ; rm -f *.h
+
+extraclean:
+ make cl_headers
+ @for i in $(DIRS) doc ; do \
+ $(MAKE) -C $$i extraclean ; \
+ done
+
+check:
+ @$(MAKE) -C conf check
+
+open:
+ @find . \( -type f -a -perm 644 \) -print
+
+release:
+ @egrep '^DEBUG\_REL\=yes' Makefile|grep -v grep > /dev/null ;\
+ if [ $$? -eq 0 ]; then \
+ echo "You should first set DEBUG_REL to no" ; exit 1 ; fi
+ $(MAKE) extraclean
+ rm -f .freezemake
+ touch .filelist
+ chmod 600 .filelist
+ cd .. ; find $(RELNAME) \! -type d -print | fgrep -v 'conf/.md5sum' > $(RELNAME)/.filelist
+ chmod 400 .filelist
+ $(MAKE) check
+ (cat .filelist ; echo $(RELNAME)/conf/.md5sum) | (cd .. ; tar -cz -f$(DISTFILE) -T-)
diff --git a/README b/README
new file mode 100644
index 00000000..8f988213
--- /dev/null
+++ b/README
@@ -0,0 +1,167 @@
+#
+# $Id$
+#
+
+Hello!
+
+Thanks for downloading Linux-PAM.
+
+--------------------------------------------------------------------
+Before you begin:
+
+ * This distribution requires GNU's Make
+ * It requires GNU's C-compiler: gcc (and 'ld')
+ * it also requires the GNU shell: bash
+ * some of the modules require the presence of libpwdb see redhat
+ * two modules have some need for libcrack too..
+
+--------------------------------------------------------------------
+[
+Zeroth (optional) thing to do: check the detatched "pgp" signature for
+this distribution file, it should be signed by
+
+Type Bits/KeyID Date User ID
+pub 1024/2A398175 1996/11/17 Andrew G. Morgan <morgan@linux.kernel.org>
+]
+
+First thing to do (I assume you have successfully unpacked it!) is to
+run:
+
+ make check [ requires md5sum to be present ]
+
+This will also check that the distribution has arrived intact. [
+Later, If you change some things, running this command from this
+directory will show you what files you have altered. ]
+
+If you choose to get and install the RCS files that accompany this
+release, you may also run
+
+ make RCScheck
+
+from this directory.
+
+Next, you should check the symbolic link
+
+ .../Linux-PAM-X.YY/default.defs
+
+points to the file that best describes your system. The various *.defs
+files that are included in this distribution are to be found in the
+directory:
+
+ .../Linux-PAM-X.YY/defs/
+
+This should configure the distribution to compile on your system. The
+default is the version I use for maintaining the distribution. [If you
+don't find one that suits your needs, please try to create one, email
+it to me and I will include it in a future release.]
+
+If you are running an ELF based Linux system you should be able to
+compile the distribution straight from the box. If you are running an
+a.out based system, then some of the functionality of Linux-PAM will
+be unavailable to you. Instead, you must switch the DYNAMIC variables
+*off* in your "defs" file: comment out the DYNAMIC and DYNAMIC_LIBPAM
+defines and uncomment the STATIC and STATIC_LIBPAM defines. NOTE, for
+ELF based systems, almost any combination of these four definitions is
+legal... If you have ELF, I recommend the default however.
+
+Second, try to compile it. Use the following command in *this*
+directory:
+
+ make
+
+[ or 'make all' if you prefer ]. The first time you type make, it is
+likely to complain. This is to remind you to remove any libraries from
+previous versions of the distribution that are likely to confuse this
+make... Type 'make' again.
+
+Before you do the third thing. You should think about whether you want
+the default configuration scripts to be installed or not. If you have
+a working PAM based system you probably do *not* want this.. Whatever,
+before Linux-PAM installs the default scripts you will be prompted as
+to whether it is a good idea. Be sure to say NO if you are worried!
+** You have been warned. **
+
+Third, to install the stuff you need to be root. Do the following:
+
+ su -c "make install"
+
+If everything has worked as intended there should now be
+
+ some executables in ./bin/
+ some filters for pam_filter in /usr/sbin/pam_filter/
+ some configuration files:
+ /etc/pam.conf
+ /etc/security/*.conf
+ libpam_misc.a (static library) in /usr/lib/
+
+In addition:
+
+ if dynamically linked:
+
+ libpam.so.XXX (shared library) in /usr/lib/
+ libpam_misc.so.XXX (shared library) in /usr/lib/
+ pam_*.so (modules) in /usr/lib/security/
+
+ if statically linked:
+
+ libpam.a (static library) in /usr/lib/
+
+[These are the default directories that I use. Your own system may
+differ as specified in your XXX.defs file.]
+
+NOTES:
+
+* The documentation, what there is of it, is in ./doc. I am only
+including the sgml format source-files. But try to make .ps files
+available from the above http address. To locally use these sgml files
+you should have linuxdoc-sgml installed. Sorry, but I'm conserving net
+bandwidth by only including sources!
+
+* The source for each module is to be found in ./modules/XXX. If you
+want to add a new one, make a directory like XXX for it. Add the name
+(XXX) to MODDIRS in ./modules/Makefile and hopefully it will become
+part of the overall make. Note, the Makefile in ./modules/ is now
+smart enough to check if the directory is there before it changes into
+it; If you want to start working on a module, send me its name and I
+will add it to the "official" Makefile.. This way, you should be able
+to insert your developing module into any new release, and not have to
+worry at first about letting it out to the public. This may also give
+other people some idea about whether a module is currently being
+worked on or not.
+
+* Currently, you have to 'make' binaries from this directory. 'make
+clean', however, works in any directory that has a Makefile.
+
+* Also, you can 'make remove' (as root) from *this* directory and it
+will delete the various installed files dotted around the system. THIS
+IS A VERY BAD IDEA IF YOUR SYSTEM DEPENDS ON PAM TO WORK!!!
+
+* 'make sterile' does 'make remove' and then 'make extraclean', this
+might be required if you are alternating your choice of
+STATIC(_LIBPAM) and DYNAMIC(_LIBPAM) compilation. SEE COMMENT IN
+UPPERCASE IN PARAGRAPH ABOVE!!!!
+
+Best wishes
+
+Andrew Morgan
+
+Email bugs/comments to: the Linux-PAM list <pam-list@redhat.com>
+or me <morgan@linux.kernel.org>
+
+To see about joining the mailing list, send the following email:
+--------------------------------
+To: pam-list-request@redhat.com
+Subject: help
+<empty text>
+--------------------------------
+
+Additionally, some Linux-PAM files have been known to be found at one
+or more of the following places (they are not always the most up to
+date...):
+
+http://www.redhat.com/linux-info/pam/
+
+ftp://bach.cis.temple.edu/pub/People/Alex/private/PAM
+ftp://ftp.redhat.com/pub/misc/
+ftp://linux.nrao.edu/pub/linux/ALPHA/PAM/
+ftp://tsx-11.mit.edu/pub/linux/ALPHA/PAM/
diff --git a/bin/.cvsignore b/bin/.cvsignore
new file mode 100644
index 00000000..2769a41e
--- /dev/null
+++ b/bin/.cvsignore
@@ -0,0 +1,3 @@
+blank
+xsh
+check_user
diff --git a/bin/README b/bin/README
new file mode 100644
index 00000000..6ab61b77
--- /dev/null
+++ b/bin/README
@@ -0,0 +1,45 @@
+##
+# $Id$
+##
+# $Log$
+# Revision 1.1 2000/06/20 22:10:44 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:14 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.6 1997/02/15 19:21:08 morgan
+# fixed email
+#
+# Revision 1.5 1996/08/09 05:29:43 morgan
+# trimmed in line with the removal of applications from the distribution
+#
+#
+##
+
+(now we are getting networked apps, be careful to try and test on a
+securely isolated system!)
+
+N=2 <-- blank xsh
+
+Following a 'make install' (which should be done as root) in the
+parent directory this directory will contain $N binaries. The source
+for these programs is in ../examples. They are various short programs
+to use and otherwise test-drive the Linux-PAM libraries/modules with.
+
+These programs grant no privileges, but they give an idea of how well
+the modules are working.
+
+blank is new as of Linux-PAM-0.21. If you are writing/modifying an
+application it might be a place to start...
+
+xsh is new as of Linux-PAM-0.31, it is identical to blank, but invokes
+/bin/sh if the user is authenticated.
+
+[other apps are to be found in SimplePAMApps and many more on Red
+Hat's server.. http://www.redhat.com/]
+
+Best wishes
+
+Andrew
+(morgan@parc.power.net)
diff --git a/conf/.cvsignore b/conf/.cvsignore
new file mode 100644
index 00000000..93724758
--- /dev/null
+++ b/conf/.cvsignore
@@ -0,0 +1,2 @@
+.ignore_age
+.md5sum
diff --git a/conf/Makefile b/conf/Makefile
new file mode 100644
index 00000000..d829a38c
--- /dev/null
+++ b/conf/Makefile
@@ -0,0 +1,34 @@
+#
+# $Id$
+#
+#
+
+dummy:
+ @echo "*** This is not a top level Makefile!"
+
+##########################################################
+
+all:
+ $(MAKE) -C pam_conv1 all
+
+install: $(FAKEROOT)$(CONFIGED)/pam.conf
+ $(MAKE) -C pam_conv1 install
+
+$(FAKEROOT)$(CONFIGED)/pam.conf: ./pam.conf
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(CONFIGED)/pam.conf
+ $(MAKE) -C pam_conv1 remove
+
+check:
+ bash -f ./md5itall
+
+lclean:
+ rm -f core *~ .ignore_age
+
+clean: lclean
+ $(MAKE) -C pam_conv1 clean
+
+extraclean: lclean
+ $(MAKE) -C pam_conv1 extraclean
diff --git a/conf/install b/conf/install
new file mode 100755
index 00000000..2eae3671
--- /dev/null
+++ b/conf/install
@@ -0,0 +1,178 @@
+#!/bin/sh
+#
+# [This file was lifted from an X distribution. There was no explicit
+# copyright in the file, but the following text was associated with it.
+# should anyone from the X Consortium wish to alter the following
+# text. Please email <morgan@parc.power.net> Thanks. ]
+#
+# --------------------------
+# The X Consortium maintains and distributes the X Window System and
+# related software and documentation in coordinated releases. A release
+# consists of two distinct parts:
+#
+# 1) Specifications and Sample implementations of X Consortium
+# standards, and
+#
+# 2) software and documentation contributed by the general X Consortium
+# community.
+#
+# The timing and contents of a release are determined by the Consortium
+# staff based on the needs and desires of the Members and the advice of
+# the Advisory Board, tempered by the resource constraints of the
+# Consortium.
+#
+# Members have access to all X Consortium produced software and
+# documentation prior to release to the public. Each Member can receive
+# pre-releases and public releases at no charge. In addition, Members
+# have access to software and documentation while it is under
+# development, and can periodically request snapshots of the development
+# system at no charge.
+#
+# The X Consortium also maintains an electronic mail system for
+# reporting problems with X Consortium produced software and
+# documentation. Members have access to all bug reports, as well as all
+# software patches as they are incrementally developed by the Consortium
+# staff between releases.
+#
+# In general, all materials included in X Consortium releases are
+# copyrighted and contain permission notices granting unrestricted use,
+# sales and redistribution rights provided that the copyrights and the
+# permission notices are left intact. All materials are provided "as
+# is," without express or implied warranty.
+# --------------------------
+#
+# This accepts bsd-style install arguments and makes the appropriate calls
+# to the System V install.
+#
+
+flags=""
+dst=""
+src=""
+dostrip=""
+owner=""
+mode=""
+
+while [ x$1 != x ]; do
+ case $1 in
+ -c) shift
+ continue;;
+
+ -m) flags="$flags $1 $2 "
+ mode="$2"
+ shift
+ shift
+ continue;;
+
+ -o) flags="$flags -u $2 "
+ owner="$2"
+ shift
+ shift
+ continue;;
+
+ -g) flags="$flags $1 $2 "
+ shift
+ shift
+ continue;;
+
+ -s) dostrip="strip"
+ shift
+ continue;;
+
+ *) if [ x$src = x ]
+ then
+ src=$1
+ else
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+case "$mode" in
+"")
+ ;;
+*)
+ case "$owner" in
+ "")
+ flags="$flags -u root"
+ ;;
+ esac
+ ;;
+esac
+
+if [ x$src = x ]
+then
+ echo "$0: no input file specified"
+ exit 1
+fi
+
+if [ x$dst = x ]
+then
+ echo "$0: no destination specified"
+ exit 1
+fi
+
+
+# set up some variable to be used later
+
+rmcmd=""
+srcdir="."
+
+# if the destination isn't a directory we'll need to copy it first
+
+if [ ! -d $dst ]
+then
+ dstbase=`basename $dst`
+ cp $src /tmp/$dstbase
+ rmcmd="rm -f /tmp/$dstbase"
+ src=$dstbase
+ srcdir=/tmp
+ dst="`echo $dst | sed 's,^\(.*\)/.*$,\1,'`"
+ if [ x$dst = x ]
+ then
+ dst="."
+ fi
+fi
+
+
+# If the src file has a directory, copy it to /tmp to make install happy
+
+srcbase=`basename $src`
+
+if [ "$src" != "$srcbase" -a "$src" != "./$srcbase" ]
+then
+ cp $src /tmp/$srcbase
+ src=$srcbase
+ srcdir=/tmp
+ rmcmd="rm -f /tmp/$srcbase"
+fi
+
+# do the actual install
+
+if [ -f /usr/sbin/install ]
+then
+ installcmd=/usr/sbin/install
+elif [ -f /etc/install ]
+then
+ installcmd=/etc/install
+else
+ installcmd=install
+fi
+
+# This rm is commented out because some people want to be able to
+# install through symbolic links. Uncomment it if it offends you.
+rm -f $dst/$srcbase
+(cd $srcdir ; $installcmd -f $dst $flags $src)
+
+if [ x$dostrip = xstrip ]
+then
+ strip $dst/$srcbase
+fi
+
+# and clean up
+
+$rmcmd
+
+exit
+
diff --git a/conf/install_conf b/conf/install_conf
new file mode 100755
index 00000000..db650a05
--- /dev/null
+++ b/conf/install_conf
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+CONFILE="$FAKEROOT"$CONFIGED/pam.conf
+IGNORE_AGE=./.ignore_age
+CONF=./pam.conf
+
+echo
+
+if [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "\
+An older Linux-PAM configuration file already exists ($CONFILE)"
+ WRITE=overwrite
+ fi
+ echo -n "\
+Do you wish to copy the $CONF file in this distribution
+to $CONFILE ? (y/n) [n] "
+ read yes
+else
+ yes=n
+fi
+
+if [ "$yes" = "y" ]; then
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ touch "$IGNORE_AGE"
+ echo " Skipping $CONF installation"
+fi
+
+echo
+
+exit 0
diff --git a/conf/md5itall b/conf/md5itall
new file mode 100755
index 00000000..14cd7c01
--- /dev/null
+++ b/conf/md5itall
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# $Id$
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:10:45 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:14 morgan
+# Linux PAM sources pre-0.66
+#
+#
+# Created by Andrew G. Morgan (morgan@parc.power.net)
+#
+
+MD5SUM=md5sum
+CHKFILE1=./.md5sum
+CHKFILE2=./.md5sum-new
+
+which $MD5SUM > /dev/null
+result=$?
+
+if [ -x "$MD5SUM" ] || [ $result -eq 0 ]; then
+ rm -f $CHKFILE2
+ echo -n "computing md5 checksums."
+ for x in `cat ../.filelist` ; do
+ (cd ../.. ; $MD5SUM $x) >> $CHKFILE2
+ echo -n "."
+ done
+ echo
+ if [ -f "$CHKFILE1" ]; then
+ echo "\
+---> Note, since the last \`make check', the following file(s) have changed:
+==========================================================================="
+ diff $CHKFILE1 $CHKFILE2
+ if [ $? -eq 0 ]; then
+ echo "\
+--------------------------- Nothing has changed ---------------------------"
+ fi
+ echo "\
+==========================================================================="
+ fi
+ rm -f "$CHKFILE1"
+ mv "$CHKFILE2" "$CHKFILE1"
+ chmod 400 "$CHKFILE1"
+else
+ echo "\
+Please install \`$MD5SUM'.
+[It is used to check the integrity of this distribution]
+---> no check done."
+fi
diff --git a/conf/mkdirp b/conf/mkdirp
new file mode 100755
index 00000000..b0e04b05
--- /dev/null
+++ b/conf/mkdirp
@@ -0,0 +1,50 @@
+#!/bin/sh
+#
+# this is a wrapper for difficult mkdir programs...
+#
+
+for d in $*
+do
+ if [ ! -d $d ]; then
+ mkdir -p $d
+ if [ $? -ne 0 ]; then exit $? ; fi
+ fi
+done
+
+exit 0
+
+##########################################################################
+# if your mkdir does not support the -p option delete the above lines and
+# use what follows:
+--------------------
+#!/bin/sh
+
+#VERBOSE=yes
+Cwd=`pwd`
+
+for d in $*
+do
+ if [ "`echo $d|cut -c1`" != "/" ]; then
+ x=`pwd`/$d
+ else
+ x=$d
+ fi
+ x="`echo $x|sed -e 'yX/X X'`"
+ cd /
+ for s in $x
+ do
+ if [ -d $s ]; then
+ if [ -n "$VERBOSE" ]; then echo -n "[$s/]"; fi
+ cd $s
+ else
+ mkdir $s
+ if [ $? -ne 0 ]; then exit $? ; fi
+ if [ -n "$VERBOSE" ]; then echo -n "$s/"; fi
+ cd $s
+ fi
+ done
+ if [ -n "$VERBOSE" ]; then echo ; fi
+ cd $Cwd
+done
+
+exit 0
diff --git a/conf/pam.conf b/conf/pam.conf
new file mode 100644
index 00000000..3f10b106
--- /dev/null
+++ b/conf/pam.conf
@@ -0,0 +1,126 @@
+# ---------------------------------------------------------------------------#
+# /etc/pam.conf #
+# #
+# Last modified by Andrew G. Morgan <morgan@parc.power.net> #
+# ---------------------------------------------------------------------------#
+# $Id$
+# ---------------------------------------------------------------------------#
+# serv. module ctrl module [path] ...[args..] #
+# name type flag #
+# ---------------------------------------------------------------------------#
+#
+# The PAM configuration file for the `chfn' service
+#
+chfn auth required pam_pwdb.so
+chfn account required pam_pwdb.so
+chfn password required pam_cracklib.so retry=3
+chfn password required pam_pwdb.so shadow md5 use_authtok
+#
+# The PAM configuration file for the `chsh' service
+#
+chsh auth required pam_pwdb.so
+chsh account required pam_pwdb.so
+chsh password required pam_cracklib.so retry=3
+chsh password required pam_pwdb.so shadow md5 use_authtok
+#
+# The PAM configuration file for the `ftp' service
+#
+ftp auth requisite pam_listfile.so \
+ item=user sense=deny file=/etc/ftpusers onerr=succeed
+ftp auth requisite pam_shells.so
+ftp auth required pam_pwdb.so
+ftp account required pam_pwdb.so
+#
+# The PAM configuration file for the `imap' service
+#
+imap auth required pam_pwdb.so
+imap account required pam_pwdb.so
+#
+# The PAM configuration file for the `login' service
+#
+login auth requisite pam_securetty.so
+login auth required pam_pwdb.so
+login auth optional pam_group.so
+login account requisite pam_time.so
+login account required pam_pwdb.so
+login password required pam_cracklib.so retry=3
+login password required pam_pwdb.so shadow md5 use_authtok
+login session required pam_pwdb.so
+#
+# The PAM configuration file for the `netatalk' service
+#
+netatalk auth required pam_pwdb.so
+netatalk account required pam_pwdb.so
+#
+# The PAM configuration file for the `other' service
+#
+other auth required pam_deny.so
+other auth required pam_warn.so
+other account required pam_deny.so
+other password required pam_deny.so
+other password required pam_warn.so
+other session required pam_deny.so
+#
+# The PAM configuration file for the `passwd' service
+#
+passwd password requisite pam_cracklib.so retry=3
+passwd password required pam_pwdb.so shadow md5 use_authtok
+#
+# The PAM configuration file for the `rexec' service
+#
+rexec auth requisite pam_securetty.so
+rexec auth requisite pam_nologin.so
+rexec auth sufficient pam_rhosts_auth.so
+rexec auth required pam_pwdb.so
+rexec account required pam_pwdb.so
+rexec session required pam_pwdb.so
+rexec session required pam_limits.so
+#
+# The PAM configuration file for the `rlogin' service
+# this application passes control to `login' if it fails
+#
+rlogin auth requisite pam_securetty.so
+rlogin auth requisite pam_nologin.so
+rlogin auth required pam_rhosts_auth.so
+rlogin account required pam_pwdb.so
+rlogin password required pam_cracklib.so retry=3
+rlogin password required pam_pwdb.so shadow md5 use_authtok
+rlogin session required pam_pwdb.so
+rlogin session required pam_limits.so
+#
+# The PAM configuration file for the `rsh' service
+#
+rsh auth requisite pam_securetty.so
+rsh auth requisite pam_nologin.so
+rsh auth sufficient pam_rhosts_auth.so
+rsh auth required pam_pwdb.so
+rsh account required pam_pwdb.so
+rsh session required pam_pwdb.so
+rsh session required pam_limits.so
+#
+# The PAM configuration file for the `samba' service
+#
+samba auth required pam_pwdb.so
+samba account required pam_pwdb.so
+#
+# The PAM configuration file for the `su' service
+#
+su auth required pam_wheel.so
+su auth sufficient pam_rootok.so
+su auth required pam_pwdb.so
+su account required pam_pwdb.so
+su session required pam_pwdb.so
+#
+# The PAM configuration file for the `vlock' service
+#
+vlock auth required pam_pwdb.so
+#
+# The PAM configuration file for the `xdm' service
+#
+xdm auth required pam_pwdb.so
+xdm account required pam_pwdb.so
+#
+# The PAM configuration file for the `xlock' service
+#
+xlock auth required pam_pwdb.so
+
diff --git a/conf/pam_conv1/.cvsignore b/conf/pam_conv1/.cvsignore
new file mode 100644
index 00000000..200a991e
--- /dev/null
+++ b/conf/pam_conv1/.cvsignore
@@ -0,0 +1,3 @@
+lex.yy.c
+pam_conv.tab.c
+pam_conv1
diff --git a/conf/pam_conv1/Makefile b/conf/pam_conv1/Makefile
new file mode 100644
index 00000000..7691dc31
--- /dev/null
+++ b/conf/pam_conv1/Makefile
@@ -0,0 +1,41 @@
+#
+#
+ifeq ($(OS),solaris)
+
+clean:
+ @echo not available in Solaris
+
+all:
+ @echo not available in Solaris
+
+install:
+ @echo not available in Solaris
+
+else
+
+all: pam_conv1
+
+pam_conv1: pam_conv.tab.c lex.yy.c
+ $(CC) -o pam_conv1 pam_conv.tab.c -lfl
+
+pam_conv.tab.c: pam_conv.y lex.yy.c
+ bison pam_conv.y
+
+lex.yy.c: pam_conv.lex
+ flex pam_conv.lex
+
+lclean:
+ rm -f core pam_conv1 lex.yy.c pam_conv.tab.c *.o *~
+ rm -rf ./pam.d pam_conv.output
+
+clean: lclean
+
+install: pam_conv1
+ cp -f ./pam_conv1 ../../bin
+
+endif
+
+remove:
+ rm -f ../../bin/pam_conv1
+
+extraclean: remove clean
diff --git a/conf/pam_conv1/README b/conf/pam_conv1/README
new file mode 100644
index 00000000..8d420ce4
--- /dev/null
+++ b/conf/pam_conv1/README
@@ -0,0 +1,10 @@
+$Id$
+
+This directory contains a untility to convert pam.conf files to a pam.d/
+tree. The conversion program takes pam.conf from the standard input and
+creates the pam.d/ directory in the current directory.
+
+The program will fail if ./pam.d/ already exists.
+
+Andrew Morgan, February 1997
+
diff --git a/conf/pam_conv1/pam_conv.lex b/conf/pam_conv1/pam_conv.lex
new file mode 100644
index 00000000..a7df2b06
--- /dev/null
+++ b/conf/pam_conv1/pam_conv.lex
@@ -0,0 +1,42 @@
+
+%{
+/*
+ * $Id$
+ *
+ * Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net>
+ *
+ * This file is covered by the Linux-PAM License (which should be
+ * distributed with this file.)
+ */
+
+ const static char lexid[]=
+ "$Id$\n"
+ "Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net>\n";
+
+ extern int current_line;
+%}
+
+%%
+
+"#"[^\n]* ; /* skip comments (sorry) */
+
+"\\\n" {
+ ++current_line;
+}
+
+([^\n\t ]|[\\][^\n])+ {
+ return TOK;
+}
+
+[ \t]+ ; /* Ignore */
+
+<<EOF>> {
+ return EOFILE;
+}
+
+[\n] {
+ ++current_line;
+ return NL;
+}
+
+%%
diff --git a/conf/pam_conv1/pam_conv.y b/conf/pam_conv1/pam_conv.y
new file mode 100644
index 00000000..18bedccf
--- /dev/null
+++ b/conf/pam_conv1/pam_conv.y
@@ -0,0 +1,203 @@
+%{
+
+/*
+ * $Id$
+ *
+ * Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net>
+ *
+ * This file is covered by the Linux-PAM License (which should be
+ * distributed with this file.)
+ */
+
+ const static char bisonid[]=
+ "$Id$\n"
+ "Copyright (c) Andrew G. Morgan 1997-8 <morgan@linux.kernel.org>\n";
+
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+ int current_line=1;
+ extern char *yytext;
+
+/* XXX - later we'll change this to be the specific conf file(s) */
+#define newpamf stderr
+
+#define PAM_D "./pam.d"
+#define PAM_D_MODE 0755
+#define PAM_D_MAGIC_HEADER \
+ "#%PAM-1.0\n" \
+ "#[For version 1.0 syntax, the above header is optional]\n"
+
+#define PAM_D_FILE_FMT PAM_D "/%s"
+
+ const char *old_to_new_ctrl_flag(const char *old);
+ void yyerror(const char *format, ...);
+%}
+
+%union {
+ int def;
+ char *string;
+}
+
+%token NL EOFILE TOK
+
+%type <string> tok path tokenls
+
+%start complete
+
+%%
+
+complete
+:
+| complete NL
+| complete line
+| complete EOFILE {
+ return 0;
+}
+;
+
+line
+: tok tok tok path tokenls NL {
+ char *filename;
+ FILE *conf;
+ int i;
+
+ /* make sure we have lower case */
+ for (i=0; $1[i]; ++i) {
+ $1[i] = tolower($1[i]);
+ }
+
+ /* $1 = service-name */
+ yyerror("Appending to " PAM_D "/%s", $1);
+
+ filename = malloc(strlen($1) + sizeof(PAM_D) + 6);
+ sprintf(filename, PAM_D_FILE_FMT, $1);
+ conf = fopen(filename, "r");
+ if (conf == NULL) {
+ /* new file */
+ conf = fopen(filename, "w");
+ if (conf != NULL) {
+ fprintf(conf, PAM_D_MAGIC_HEADER);
+ fprintf(conf,
+ "#\n"
+ "# The PAM configuration file for the `%s' service\n"
+ "#\n", $1);
+ }
+ } else {
+ fclose(conf);
+ conf = fopen(filename, "a");
+ }
+ if (conf == NULL) {
+ yyerror("trouble opening %s - aborting", filename);
+ exit(1);
+ }
+ free(filename);
+
+ /* $2 = module-type */
+ fprintf(conf, "%-10s", $2);
+ free($2);
+
+ /* $3 = required etc. */
+ {
+ const char *trans;
+
+ trans = old_to_new_ctrl_flag($3);
+ free($3);
+ fprintf(conf, " %-10s", trans);
+ }
+
+ /* $4 = module-path */
+ fprintf(conf, " %s", $4);
+ free($4);
+
+ /* $5 = arguments */
+ if ($5 != NULL) {
+ fprintf(conf, " \\\n\t\t%s", $5);
+ free($5);
+ }
+
+ /* end line */
+ fprintf(conf, "\n");
+
+ fclose(conf);
+}
+| error NL {
+ yyerror("malformed line");
+}
+;
+
+tokenls
+: {
+ $$=NULL;
+}
+| tokenls tok {
+ int len;
+
+ if ($1) {
+ len = strlen($1) + strlen($2) + 2;
+ $$ = malloc(len);
+ sprintf($$,"%s %s",$1,$2);
+ free($1);
+ free($2);
+ } else {
+ $$ = $2;
+ }
+}
+;
+
+path
+: TOK {
+ /* XXX - this could be used to check if file present */
+ $$ = strdup(yytext);
+}
+
+tok
+: TOK {
+ $$ = strdup(yytext);
+}
+
+%%
+
+#include "lex.yy.c"
+
+const char *old_to_new_ctrl_flag(const char *old)
+{
+ static const char *clist[] = {
+ "requisite",
+ "required",
+ "sufficient",
+ "optional",
+ NULL,
+ };
+ int i;
+
+ for (i=0; clist[i]; ++i) {
+ if (strcasecmp(clist[i], old) == 0) {
+ break;
+ }
+ }
+
+ return clist[i];
+}
+
+void yyerror(const char *format, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "line %d: ", current_line);
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+
+void main()
+{
+ if (mkdir(PAM_D, PAM_D_MODE) != 0) {
+ yyerror(PAM_D " already exists.. aborting");
+ exit(1);
+ }
+ yyparse();
+}
diff --git a/defs/debian.defs b/defs/debian.defs
new file mode 100644
index 00000000..19ba4663
--- /dev/null
+++ b/defs/debian.defs
@@ -0,0 +1,40 @@
+##
+# defs for Debian
+# Ben Collins <bcollins@debian.org>
+##
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+##
+
+CFLAGS := -O2 -I${shell pwd}/include # -D__NO_STRING_INLINES
+ifneq (,$(findstring $(DEB_BUILD_OPTIONS),debug DEBUG Debug))
+ CFLAGS += -g
+endif
+
+OS := $(shell dpkg-architecture -qDEB_BUILD_GNU_SYSTEM)
+ARCH := $(shell dpkg-architecture -qDEB_BUILD_GNU_CPU)
+CC := gcc
+INSTALL := install
+MKDIR := mkdir -p
+ULIBS :=
+LD := ld
+LD_D := gcc -shared -Xlinker -x
+LD_L := $(LD) -x -shared
+AR := ar -cr
+RANLIB := ranlib
+PREFIX :=
+LIBDIR := $(PREFIX)/lib
+USESONAME := yes
+SOSWITCH := -soname
+LINKLIBS := -lc -L${shell pwd}/libpam -L${shell pwd}/libpam_misc
+NEEDSONAME := no
+LDCONFIG := /sbin/ldconfig
+FAKEROOT :=
+SUPLEMENTED := $(PREFIX)/sbin
+SECUREDIR := $(LIBDIR)/security
+INCLUDED := /usr/include/security
+CONFIGED := /etc
+SCONFIGED := /etc/security
+EXTRALS := -lnsl -lcrypt
+WARNINGS := -Wall
diff --git a/defs/hpux.defs b/defs/hpux.defs
new file mode 100644
index 00000000..d8341983
--- /dev/null
+++ b/defs/hpux.defs
@@ -0,0 +1,36 @@
+##
+# HPUX defs contributed by Derrick J Brashear <shadow@dementia.org>
+##
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+#
+# This file is the default version. Please look in .../defs/ for your
+# preferred OS/vendor.
+
+OS=hpux9
+ARCH=hpux
+CC=gcc
+INSTALL=install
+MKDIR=mkdir -p
+CFLAGS=-g -DPAM_SHL -DHAVE_UTMP_H
+ULIBS=
+LD=ld
+LD_D=$(LD) -b
+LD_L=$(LD) -b
+USESONAME=no
+NEEDSONAME=no
+LDCONFIG=:
+AR=ar -cr
+RANLIB=ranlib
+FAKEROOT=
+PREFIX=/usr
+SUPLEMENTED=$(PREFIX)/sbin
+LIBDIR=$(PREFIX)/lib
+SECUREDIR=$(LIBDIR)/security
+INCLUDED=/usr/include/security
+CONFIGED=/etc
+SCONFIGED=/etc/security
+DYNLOAD="dld"
+DYNTYPE="sl"
+SHLIBMODE=755
diff --git a/defs/linux.defs b/defs/linux.defs
new file mode 100644
index 00000000..0e274320
--- /dev/null
+++ b/defs/linux.defs
@@ -0,0 +1,32 @@
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+#
+# This file is the default version. Please look in .../defs/ for your
+# preferred OS/vendor.
+
+OS=linux
+ARCH=i386 # should be changed for alpha
+CC=gcc
+INSTALL=install
+MKDIR=mkdir -p
+CFLAGS=-O7 -pipe -g
+ULIBS=#-lefence
+LD=ld
+LD_D=gcc -shared -Xlinker -x
+LD_L=$(LD) -x -shared
+USESONAME=yes
+LINKLIBS=-lc
+SOSWITCH=-soname
+NEEDSONAME=no
+LDCONFIG=/sbin/ldconfig
+AR=ar -cr
+RANLIB=ranlib
+FAKEROOT=
+PREFIX=/usr
+SUPLEMENTED=$(PREFIX)/sbin
+LIBDIR=$(PREFIX)/lib
+SECUREDIR=$(LIBDIR)/security
+INCLUDED=/usr/include/security
+CONFIGED=/etc
+SCONFIGED=/etc/security
diff --git a/defs/morgan.defs b/defs/morgan.defs
new file mode 100644
index 00000000..2b0cf289
--- /dev/null
+++ b/defs/morgan.defs
@@ -0,0 +1,36 @@
+##
+# defs for Andrew's debugging version (which is a modified Red Hat
+# box)
+##
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+#
+# This file is the version used for Red Hat Linux.
+
+OS=linux
+ARCH=i386
+CC=gcc
+INSTALL=install
+MKDIR=mkdir -p
+CFLAGS=$(RPM_OPT_FLAGS) -pipe -g
+ULIBS=
+#-lefence
+LD=ld
+LD_D=gcc -shared -Xlinker -x
+LD_L=$(LD) -x -shared
+USESONAME=yes
+SOSWITCH=-soname
+LINKLIBS=-lc
+NEEDSONAME=no
+LDCONFIG=/sbin/ldconfig
+AR=ar -cr
+RANLIB=ranlib
+FAKEROOT=$(RPM_BUILD_ROOT)
+PREFIX=
+SUPLEMENTED=$(PREFIX)/sbin
+LIBDIR=$(PREFIX)/lib
+SECUREDIR=$(LIBDIR)/security.d
+INCLUDED=/usr/include/security
+CONFIGED=/etc
+SCONFIGED=/etc/security
diff --git a/defs/redhat.defs b/defs/redhat.defs
new file mode 100644
index 00000000..a6ed36da
--- /dev/null
+++ b/defs/redhat.defs
@@ -0,0 +1,36 @@
+##
+# defs for Red Hat Linux
+# Michael K. Johnson <johnsonm@redhat.com>
+##
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+#
+# This file is the version used for Red Hat Linux.
+
+OS=linux
+ARCH=$(shell rpm --showrc | grep '^build arch' | sed 's/^.*: //g')
+CC=gcc
+INSTALL=install
+MKDIR=mkdir -p
+CFLAGS=$(RPM_OPT_FLAGS) -pipe -g
+ULIBS=#-lefence
+LD=ld
+LD_D=gcc -shared -Xlinker -x
+LD_L=$(LD) -x -shared
+USESONAME=yes
+SOSWITCH=-soname
+LINKLIBS=-lc
+NEEDSONAME=no
+LDCONFIG=/sbin/ldconfig
+AR=ar -cr
+RANLIB=ranlib
+FAKEROOT=$(RPM_BUILD_ROOT)
+PREFIX=
+SUPLEMENTED=$(PREFIX)/sbin
+LIBDIR=$(PREFIX)/lib
+SECUREDIR=$(LIBDIR)/security
+INCLUDED=/usr/include/security
+CONFIGED=/etc
+SCONFIGED=/etc/security
+EXTRALS=-lcrypt
diff --git a/defs/redhat4.defs b/defs/redhat4.defs
new file mode 100644
index 00000000..219abeb6
--- /dev/null
+++ b/defs/redhat4.defs
@@ -0,0 +1,35 @@
+##
+# defs for Red Hat Linux
+# Michael K. Johnson <johnsonm@redhat.com>
+##
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+#
+# This file is the version used for Red Hat Linux.
+
+OS=linux
+ARCH=$(shell rpm --showrc | grep '^build arch' | sed 's/^.*: //g')
+CC=gcc
+INSTALL=install
+MKDIR=mkdir -p
+CFLAGS=$(RPM_OPT_FLAGS) -pipe -g
+ULIBS=#-lefence
+LD=ld
+LD_D=gcc -shared -Xlinker -x
+LD_L=$(LD) -x -shared
+USESONAME=yes
+SOSWITCH=-soname
+LINKLIBS=-lc
+NEEDSONAME=no
+LDCONFIG=/sbin/ldconfig
+AR=ar -cr
+RANLIB=ranlib
+FAKEROOT=$(RPM_BUILD_ROOT)
+PREFIX=
+SUPLEMENTED=$(PREFIX)/sbin
+LIBDIR=$(PREFIX)/lib
+SECUREDIR=$(LIBDIR)/security
+INCLUDED=/usr/include/security
+CONFIGED=/etc
+SCONFIGED=/etc/security
diff --git a/defs/solaris-2.1.5.defs b/defs/solaris-2.1.5.defs
new file mode 100644
index 00000000..4624b604
--- /dev/null
+++ b/defs/solaris-2.1.5.defs
@@ -0,0 +1,45 @@
+##
+# Solaris defs contributed by Josh Wilmes <josh@makita.jpl.nasa.gov>
+##
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+#
+# This file is the default version. Please look in .../defs/ for your
+# preferred OS/vendor.
+
+# Please note that the linker used must be the GNU ld, not the native Sun
+# linker. It is fairly common for the gnu linker (/usr/ccs/bin/ld) to be
+# configured as the default linker for gcc. To tell gcc to use the
+# gnu linker, you need to set the GCC_EXEC_PREFIX environment variable
+# to point at the directory where the gnu linker is installed. Here's
+# what I do:
+# $ mkdir /tmp/foo
+# $ ln -s /path/to/gnu/ld /tmp/foo/ld
+# $ export GCC_EXEC_PREFIX=/tmp/foo/
+# $ export PATH=/tmp/foo:$PATH
+
+OS=solaris
+ARCH=sun
+CC=gcc
+INSTALL=install
+MKDIR=mkdir -p
+CFLAGS=-O7 -pipe -g -D__EXTENSIONS__ -Dsolaris
+ULIBS=
+LD_D=gcc -shared -Xlinker -x
+LD=ld
+LD_L=$(LD) -G
+USESONAME=yes
+SOSWITCH=-h
+NEEDSONAME=no
+LDCONFIG=/sbin/echo
+AR=ar -cr
+RANLIB=ranlib
+FAKEROOT=
+PREFIX=/usr
+SUPLEMENTED=$(PREFIX)/sbin
+LIBDIR=$(PREFIX)/lib
+SECUREDIR=$(LIBDIR)/security
+INCLUDED=/usr/include/security
+CONFIGED=/etc
+SCONFIGED=/etc/security
diff --git a/defs/solaris.defs b/defs/solaris.defs
new file mode 100644
index 00000000..f9f26529
--- /dev/null
+++ b/defs/solaris.defs
@@ -0,0 +1,48 @@
+##
+# Solaris defs contributed by Josh Wilmes <josh@makita.jpl.nasa.gov>
+##
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+#
+# This file is the default version. Please look in .../defs/ for your
+# preferred OS/vendor.
+
+# Please note that the linker used must be the GNU ld, not the native Sun
+# linker. It is fairly common for the gnu linker (/usr/ccs/bin/ld) to be
+# configured as the default linker for gcc. To tell gcc to use the
+# gnu linker, you need to set the GCC_EXEC_PREFIX environment variable
+# to point at the directory where the gnu linker is installed. Here's
+# what I do:
+# $ mkdir /tmp/foo
+# $ ln -s /path/to/gnu/ld /tmp/foo/ld
+# $ export GCC_EXEC_PREFIX=/tmp/foo/
+# $ export PATH=/tmp/foo:$PATH
+
+OS=solaris
+ARCH=sun
+CC=cc
+INSTALL=install
+MKDIR=mkdir -p
+WARNINGS = -D_POSIX_SOURCE
+PIC=-KPIC
+CFLAGS=-g -D__EXTENSIONS__ -Dsolaris
+ULIBS=
+LD=ld
+LD_L=$(LD) -G
+LD_D=$(LD_L)
+RDYNAMIC=
+USESONAME=yes
+SOSWITCH=-h
+NEEDSONAME=no
+LDCONFIG=echo
+AR=ar -cr
+RANLIB=ranlib
+FAKEROOT=
+PREFIX=/usr
+SUPLEMENTED=$(PREFIX)/sbin
+LIBDIR=$(PREFIX)/lib
+SECUREDIR=$(LIBDIR)/security
+INCLUDED=/usr/include/security
+CONFIGED=/etc
+SCONFIGED=/etc/security
diff --git a/defs/sunos.defs b/defs/sunos.defs
new file mode 100644
index 00000000..158accc5
--- /dev/null
+++ b/defs/sunos.defs
@@ -0,0 +1,37 @@
+##
+# SunOS defs contributed by Derrick J Brashear <shadow@dementia.org>
+##
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+#
+# This file is the SunOS version. Please look in .../defs/ for your
+# preferred OS/vendor.
+
+OS=sunos
+ARCH=sun
+CC=gcc
+INSTALL=install
+MKDIR=mkdir -p
+CFLAGS=-O2 -pipe -g -D__EXTENSIONS__
+ULIBS=
+LD_D=gcc -shared -Xlinker -x
+LD=ld
+LD_L=$(LD)
+USESONAME=no
+NEEDSONAME=yes
+LDCONFIG=/usr/etc/ldconfig
+AR=ar cr
+RANLIB=ranlib
+FAKEROOT=
+PREFIX=/usr
+SUPLEMENTED=$(PREFIX)/sbin
+LIBDIR=$(PREFIX)/lib
+SECUREDIR=$(LIBDIR)/security
+INCLUDED=/usr/include/security
+CONFIGED=/etc
+SCONFIGED=/etc/security
+WARNINGS= -ansi -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align \
+ -Wtraditional -Wstrict-prototypes -Wmissing-prototypes \
+ -Wnested-externs -Winline -Wshadow
diff --git a/defs/suse.defs b/defs/suse.defs
new file mode 100644
index 00000000..1fc6b741
--- /dev/null
+++ b/defs/suse.defs
@@ -0,0 +1,36 @@
+##
+# defs for SuSE Linux
+# Thorsten Kukuk <kukuk@suse.de>
+##
+# this file indicates the compiler and the various hardware/OS dependent
+# flags for installation. It also defines the various destinations of
+# installed files on the system.
+#
+# This file is the version used for SuSE Linux.
+
+OS=linux
+ARCH=$(shell rpm --showrc | grep 'build arch' | grep -v "compatible" | sed 's/^.*: //g')
+CC=gcc
+INSTALL=install
+MKDIR=mkdir -p
+CFLAGS=$(RPM_OPT_FLAGS) -pipe -D_REENTRANT
+ULIBS=#-lefence
+LD=ld
+LD_D=gcc -shared -Xlinker -x
+LD_L=$(LD) -x -shared
+USESONAME=yes
+SOSWITCH=-soname
+LINKLIBS=-lc
+NEEDSONAME=yes
+LDCONFIG=/sbin/ldconfig
+AR=ar -cr
+RANLIB=ranlib
+FAKEROOT=$(RPM_BUILD_ROOT)
+PREFIX=
+SUPLEMENTED=$(PREFIX)/sbin
+LIBDIR=$(PREFIX)/lib
+SECUREDIR=$(LIBDIR)/security
+INCLUDED=/usr/include/security
+CONFIGED=/etc
+SCONFIGED=/etc/security
+EXTRALS=-lcrypt
diff --git a/doc/.cvsignore b/doc/.cvsignore
new file mode 100644
index 00000000..7ac74f9d
--- /dev/null
+++ b/doc/.cvsignore
@@ -0,0 +1,2 @@
+pam.sgml
+MODULES-SGML
diff --git a/doc/CREDITS b/doc/CREDITS
new file mode 100644
index 00000000..059bb5f2
--- /dev/null
+++ b/doc/CREDITS
@@ -0,0 +1,48 @@
+<!--
+ an sgml list of people to credit for their contributions to Linux-PAM
+ $Id$
+ -->
+Chris Adams,
+Peter Allgeyer,
+Tim Baverstock,
+Tim Berger,
+Craig S. Bell,
+Derrick J. Brashear,
+Ben Buxton,
+Seth Chaiklin,
+Oliver Crow,
+Chris Dent,
+Marc Ewing,
+Cristian Gafton,
+Emmanuel Galanos,
+Brad M. Garcia,
+Eric Hester,
+Roger Hu,
+Eric Jacksch,
+Michael K. Johnson,
+David Kinchlea,
+Olaf Kirch,
+Marcin Korzonek,
+Stephen Langasek,
+Nicolai Langfeldt,
+Elliot Lee,
+Luke Kenneth Casson Leighton,
+Al Longyear,
+Ingo Luetkebohle,
+Marek Michalkiewicz,
+Robert Milkowski,
+Aleph One,
+Martin Pool,
+Sean Reifschneider,
+Jan Rekorajski,
+Erik Troan,
+Theodore Ts'o,
+Jeff Uphoff,
+Myles Uyema,
+Savochkin Andrey Vladimirovich,
+Ronald Wahl,
+David Wood,
+John Wilmes,
+Joseph S. D. Yao
+and
+Alex O. Yuriev.
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 00000000..965f24b8
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,101 @@
+
+### $Id$
+
+TXTER=sgml2txt
+HTMLER=sgml2html
+# older distributions use, sgml2ps
+# newer distributions than the one I use (AGM :^) need the following line:
+#PSER=sgml2latex -o ps
+# until I upgrade this is the default
+PSER=sgml2latex -p
+
+FILES=pam pam_appl pam_modules
+FSRCS=pam.sgml pam_appl.sgml pam_modules.sgml
+
+TEXTS=txts/pam.txt txts/pam_appl.txt txts/pam_modules.txt
+HTMLS=html/pam.html html/pam_appl.html html/pam_modules.html
+PSFILES=ps/pam.ps ps/pam_appl.ps ps/pam_modules.ps
+
+MODULES=$(shell ls modules/*.sgml)
+
+#######################################################
+
+dummy:
+ @echo "Making the documentation..."
+ @make all
+
+all: htmls texts postscript
+
+htmls: $(HTMLS)
+
+$(HTMLS) : $(FSRCS)
+ @for i in $(FILES) ; do \
+ if [ ! -f "html/$$i.html" ] || [ "$$i.sgml" -nt "html/$$i.html" ]; \
+ then \
+ cd html ; $(HTMLER) ../$$i ; \
+ if [ $$? -ne 0 ]; then exit 1 ; fi ; \
+ cd .. ; \
+ fi ; \
+ done
+
+texts: $(TEXTS)
+
+$(TEXTS) : $(FSRCS)
+ @for i in $(FILES) ; do \
+ if [ ! -f "txts/$$i.txt" ] \
+ || [ "$$i.sgml" -nt "txts/$$i.txt" ]; then \
+ cd txts ; $(TXTER) ../$$i ; cd .. ; \
+ fi ; \
+ done
+
+postscript: $(PSFILES)
+
+$(PSFILES): $(FSRCS)
+ @for i in $(FILES) ; do \
+ if [ ! -f "ps/$$i.ps" ] || [ "$$i.sgml" -nt "ps/$$i.ps" ]; then \
+ cd ps ; $(PSER) ../$$i ; cd .. ; \
+ fi ; \
+ done
+
+pam.sgml: pam_source.sgml MODULES-SGML
+ @sed -e '/^<!\-\- insert\-file MODULES\-SGML \-\->/r MODULES-SGML' pam_source.sgml > pam.sgml
+
+MODULES-SGML: $(MODULES)
+ @echo 'Building module text from files in modules/*.sgml'
+ @rm -f MODULES-SGML
+ @echo '<!-- modules included:' > MODULES-SGML
+ @ls modules/*.sgml >> MODULES-SGML
+ @echo ' and that is all -->' >> MODULES-SGML
+ @cat modules/*.sgml >> MODULES-SGML
+
+extraclean: clean
+
+DOCDIR=/usr/doc/Linux-PAM
+MANDIR=/usr/man
+install: all
+ mkdir -p $(FAKEROOT)$(DOCDIR)/{html,ps,text}
+ for file in txts/*.txt; do \
+ install -o root -g root -m 644 $$file $(FAKEROOT)$(DOCDIR)/text ; \
+ done
+ for file in ps/*.ps; do \
+ install -o root -g root -m 644 $$file $(FAKEROOT)$(DOCDIR)/ps ; \
+ done
+ for file in html/*.html; do \
+ install -o root -g root -m 644 $$file $(FAKEROOT)$(DOCDIR)/html ; \
+ done
+ mkdir -p $(FAKEROOT)$(MANDIR)/man{3,8}
+ for file in man/*.3 ; do \
+ install -o root -g root -m 644 $$file $(FAKEROOT)$(MANDIR)/man3 ; \
+ done
+ for file in man/*.8 ; do \
+ install -o root -g root -m 644 $$file $(FAKEROOT)$(MANDIR)/man8 ; \
+ done
+
+clean:
+ rm -f *~ *.bak
+ rm -f html/pam*.html
+ rm -f man/*~
+ rm -f $(TEXTS)
+ rm -f $(PSFILES)
+ rm -f MODULES-SGML pam.sgml
+
diff --git a/doc/NOTES b/doc/NOTES
new file mode 100644
index 00000000..b0f40d47
--- /dev/null
+++ b/doc/NOTES
@@ -0,0 +1,16 @@
+Things to be added:
+
+@ modules:
+@ application:
+
+ use of
+ 'user' = user to become,
+ 'uid' = user requesting service
+ 'euid' = privilege of current process.
+
+@ sysadmin:
+
+ included modules:
+ behavior
+ non-included modules:
+ behavior/pointers.
diff --git a/doc/figs/pam_orient.txt b/doc/figs/pam_orient.txt
new file mode 100644
index 00000000..a8b745a1
--- /dev/null
+++ b/doc/figs/pam_orient.txt
@@ -0,0 +1,23 @@
+
+
+
+ +----------------+
+ | application: X |
+ +----------------+ / +----------+ +================+
+ | authentication-[---->--\--] Linux- |--<--| /etc/pam.conf |
+ | + [----<--/--] PAM | |================|
+ |[conversation()][--+ \ | | | X auth .. a.so |
+ +----------------+ | / +-n--n-----+ | X auth .. b.so |
+ | | | __| | | _____/
+ | service user | A | | |____,-----'
+ | | | V A
+ +----------------+ +------|-----|---------+ -----+------+
+ +---u-----u----+ | | |
+ | auth.... |--[ a ]--[ b ]--[ c ]
+ +--------------+
+ | acct.... |--[ b ]--[ d ]
+ +--------------+
+ | password |--[ b ]--[ c ]
+ +--------------+
+ | session |--[ e ]--[ c ]
+ +--------------+ \ No newline at end of file
diff --git a/doc/html/.cvsignore b/doc/html/.cvsignore
new file mode 100644
index 00000000..3b358a3a
--- /dev/null
+++ b/doc/html/.cvsignore
@@ -0,0 +1 @@
+pam*.html \ No newline at end of file
diff --git a/doc/html/index.html b/doc/html/index.html
new file mode 100644
index 00000000..1ffd7e38
--- /dev/null
+++ b/doc/html/index.html
@@ -0,0 +1,21 @@
+
+<HTML>
+<HEAD>
+<TITLE>Linux-PAM - Pluggable Authentication Modules for Linux</TITLE>
+</HEAD>
+<BODY>
+
+<p>
+Here is the documentation for Linux-PAM. As you will see it is
+currently not complete. However, in order of decreasing length:
+
+<ul>
+<li> <a href="pam.html">The System Administrators' Guide</a>
+<li> <a href="pam_modules.html">The Module Writers' Manual</a>
+<li> <a href="pam_appl.html">The Application developers' Manual</a>
+</ul>
+
+<hr>
+<p>
+REVISION: <tt>$Id$</tt>
+</BODY>
diff --git a/doc/man/pam.8 b/doc/man/pam.8
new file mode 100644
index 00000000..68280737
--- /dev/null
+++ b/doc/man/pam.8
@@ -0,0 +1,279 @@
+.\" Hey Emacs! This file is -*- nroff -*- source.
+.\" $Id$
+.\" Copyright (c) Andrew G. Morgan 1996-7 <morgan@linux.kernel.org>
+.TH PAM 8 "1997 Feb 9" "Linux-PAM 0.56" "Linux-PAM Manual"
+.SH NAME
+
+Linux-PAM \- Pluggable Authentication Modules for Linux
+
+.SH SYNOPSIS
+.B /etc/pam.conf
+.sp 2
+.SH DESCRIPTION
+
+This manual is intended to offer a quick introduction to
+.BR Linux-PAM ". "
+For more information the reader is directed to the
+.BR "Linux-PAM system administrators' guide".
+
+.sp
+.BR Linux-PAM
+Is a system of libraries that handle the authentication tasks of
+applications (services) on the system. The library provides a stable
+general interface (Application Programming Interface - API) that
+privilege granting programs (such as
+.BR login "(1) "
+and
+.BR su "(1)) "
+defer to to perform standard authentication tasks.
+
+.sp
+The principal feature of the PAM approach is that the nature of the
+authentication is dynamically configurable. In other words, the
+system administrator is free to choose how individual
+service-providing applications will authenticate users. This dynamic
+configuration is set by the contents of the single
+.BR Linux-PAM
+configuration file
+.BR /etc/pam.conf "."
+Alternatively, the configuration can be set by individual
+configuration files located in the
+.B /etc/pam.d/
+directory.
+.IB "The presence of this directory will cause " Linux-PAM " to ignore"
+.BI /etc/pam.conf "."
+
+.sp
+From the point of view of the system administrator, for whom this
+manual is provided, it is not of primary importance to understand the
+internal behavior of the
+.BR Linux-PAM
+library. The important point to recognize is that the configuration
+file(s)
+.I define
+the connection between applications
+.BR "" "(" services ")"
+and the pluggable authentication modules
+.BR "" "(" PAM "s)"
+that perform the actual authentication tasks.
+
+.sp
+.BR Linux-PAM
+separates the tasks of
+.I authentication
+into four independent management groups:
+.BR "account" " management; "
+.BR "auth" "entication management; "
+.BR "password" " management; "
+and
+.BR "session" " management."
+(We highlight the abbreviations used for these groups in the
+configuration file.)
+
+.sp
+Simply put, these groups take care of different aspects of a typical
+user's request for a restricted service:
+
+.sp
+.BR account " - "
+provide account verification types of service: has the user's password
+expired?; is this user permitted access to the requested service?
+
+.br
+.BR auth "entication - "
+establish the user is who they claim to be. Typically this is via some
+challenge-response request that the user must satisfy: if you are who
+you claim to be please enter your password. Not all authentications
+are of this type, there exist hardware based authentication schemes
+(such as the use of smart-cards and biometric devices), with suitable
+modules, these may be substituted seamlessly for more standard
+approaches to authentication - such is the flexibility of
+.BR Linux-PAM "."
+
+.br
+.BR password " - "
+this group's responsibility is the task of updating authentication
+mechanisms. Typically, such services are strongly coupled to those of
+the
+.BR auth
+group. Some authentication mechanisms lend themselves well to being
+updated with such a function. Standard UN*X password-based access is
+the obvious example: please enter a replacement password.
+
+.br
+.BR session " - "
+this group of tasks cover things that should be done prior to a
+service being given and after it is withdrawn. Such tasks include the
+maintenance of audit trails and the mounting of the user's home
+directory. The
+.BR session
+management group is important as it provides both an opening and
+closing hook for modules to affect the services available to a user.
+
+.SH The configuration file(s)
+
+When a
+.BR Linux-PAM
+aware privilege granting application is started, it activates its
+attachment to the PAM-API. This activation performs a number of
+tasks, the most important being the reading of the configuration file(s):
+.BR /etc/pam.conf "."
+Alternatively, this may be the contents of the
+.BR /etc/pam.d/
+directory.
+
+These files list the
+.BR PAM "s"
+that will do the authentication tasks required by this service, and
+the appropriate behavior of the PAM-API in the event that individual
+.BR PAM "s "
+fail.
+
+.sp
+The syntax of the
+.B /etc/pam.conf
+configuration file is as follows. The file is made
+up of a list of rules, each rule is typically placed on a single line,
+but may be extended with an escaped end of line: `\\<LF>'. Comments
+are preceded with `#' marks and extend to the next end of line.
+
+.sp
+The format of each rule is a space separated collection of tokens, the
+first three being case-insensitive:
+
+.sp
+.br
+.BR " service type control module-path module-arguments"
+
+.sp
+The syntax of files contained in the
+.B /etc/pam.d/
+directory, are identical except for the absence of any
+.I service
+field. In this case, the
+.I service
+is the name of the file in the
+.B /etc/pam.d/
+directory. This filename must be in lower case.
+
+.sp
+An important feature of
+.BR Linux-PAM ", "
+is that a number of rules may be
+.I stacked
+to combine the services of a number of PAMs for a given authentication
+task.
+
+.sp
+The
+.BR service
+is typically the familiar name of the corresponding application:
+.BR login
+and
+.BR su
+are good examples. The
+.BR service "-name, " other ", "
+is reserved for giving
+.I default
+rules. Only lines that mention the current service (or in the absence
+of such, the
+.BR other
+entries) will be associated with the given service-application.
+
+.sp
+The
+.BR type
+is the management group that the rule corresponds to. It is used to
+specify which of the management groups the subsequent module is to
+be associated with. Valid entries are:
+.BR account "; "
+.BR auth "; "
+.BR password "; "
+and
+.BR session "."
+The meaning of each of these tokens was explained above.
+
+.sp
+The third field,
+.BR control ", "
+indicates the behavior of the PAM-API should the module fail to
+succeed in its authentication task. Valid
+.BR control
+values are:
+.BR requisite
+- failure of such a PAM results in the immediate termination of the
+authentication process;
+.BR required
+- failure of such a PAM will ultimately lead to the PAM-API returning
+failure but only after the remaining
+.I stacked
+modules (for this
+.BR service
+and
+.BR type ")"
+have been invoked;
+.BR sufficient
+- success of such a module is enough to satisfy the authentication
+requirements of the stack of modules (if a prior
+.BR required
+module has failed the success of this one is
+.IR ignored "); "
+.BR optional
+- the success or failure of this module is only important if it is the
+only module in the stack associated with this
+.BR service "+" type "."
+
+.sp
+.BR module-path
+- this is the full filename of the PAM to be used by the application
+
+.sp
+.BR module-arguments
+- these are a space separated list of tokens that can be used to
+modify the specific behavior of the given PAM. Such arguments will be
+documented for each individual module.
+
+.SH "FILES"
+.BR /etc/pam.conf " - the configuration file"
+.br
+.BR /etc/pam.d/ " - the"
+.BR Linux-PAM
+configuration directory. If this directory is present, the
+.B /etc/pam.conf
+file is ignored.
+.br
+.BR /usr/lib/libpam.so.X " - the dynamic library"
+.br
+.BR /usr/lib/security/*.so " - the PAMs
+
+.sp
+Note, to conform to the Linux File-system standard, the libraries and
+modules in your system may be located in
+.BR /lib " and " /lib/security
+respectively.
+
+.SH ERRORS
+Typically errors generated by the
+.BR Linux-PAM
+system of libraries, will be written to
+.BR syslog "(3)."
+
+.SH "CONFORMING TO"
+DCE-RFC 86.0, October 1995.
+.br
+Contains additional features, currently under consideration by the
+DCE-RFC committee.
+
+.SH BUGS
+.sp 2
+None known.
+
+.SH "SEE ALSO"
+
+The three
+.BR Linux-PAM
+Guides, for
+.BR "System administrators" ", "
+.BR "module developers" ", "
+and
+.BR "application developers" ". "
diff --git a/doc/man/pam.conf.8 b/doc/man/pam.conf.8
new file mode 100644
index 00000000..ea2dd98b
--- /dev/null
+++ b/doc/man/pam.conf.8
@@ -0,0 +1 @@
+.so man8/pam.8
diff --git a/doc/man/pam.d.8 b/doc/man/pam.d.8
new file mode 100644
index 00000000..ea2dd98b
--- /dev/null
+++ b/doc/man/pam.d.8
@@ -0,0 +1 @@
+.so man8/pam.8
diff --git a/doc/man/pam_authenticate.3 b/doc/man/pam_authenticate.3
new file mode 100644
index 00000000..ba1bc52e
--- /dev/null
+++ b/doc/man/pam_authenticate.3
@@ -0,0 +1,91 @@
+.\" Hey Emacs! This file is -*- nroff -*- source.
+.\" $Id$
+.\" Copyright (c) Andrew G. Morgan 1996-7 <morgan@parc.power.net>
+.TH PAM_AUTHENTICATE 3 "1996 Dec 9" "Linux-PAM 0.55" "App. Programmers' Manual"
+.SH NAME
+
+pam_authenticate \- authenticate a user
+
+.SH SYNOPSIS
+.B #include <security/pam_appl.h>
+.sp
+.BI "int pam_authenticate(pam_handle_t " *pamh ", int " flags ");"
+.sp 2
+.SH DESCRIPTION
+.B pam_authenticate
+
+.br
+Use this function to authenticate an applicant user. It is linked
+.I dynamically
+to the authentication modules by
+.BR Linux-PAM ". "
+It is the task of these module to perform such an authentication. The
+specific nature of the authentication is not the concern of the
+application.
+
+.br
+Following successful completion, the
+.BR name
+of the authenticated user will be present in the
+.BR Linux-PAM
+item
+.BR PAM_USER ". "
+This item may be recovered with a call to
+.BR pam_get_item "(3)."
+
+.br
+The application developer should note that the modules may request
+that the user enter their username via the conversation mechanism (see
+.BR pam_start "(3))."
+Should this be the case, the user-prompt string can be set via
+the
+.BR PAM_USER_PROMPT
+item (see
+.BR pam_set_item "(3))."
+
+.SH "RETURN VALUE"
+On success
+.BR PAM_SUCCESS
+is returned. All other returns should be considered
+authentication failures and will be
+.I delayed
+by an amount specified with prior calls to
+.BR pam_fail_delay "(3). "
+Specific failures that demand special attention are the following:
+.TP
+.B PAM_ABORT
+the application should exit immediately. Of course,
+.BR pam_end "(3)"
+should be called first.
+
+.TP
+.B PAM_MAXTRIES
+the application has tried too many times to authenticate the
+user, authentication should not be attempted again.
+
+.SH ERRORS
+May be translated to text with
+.BR pam_strerror "(3). "
+
+.SH "CONFORMING TO"
+DCE-RFC 86.0, October 1995.
+
+.SH BUGS
+.sp 2
+none known.
+
+.SH "SEE ALSO"
+
+.BR pam_start "(3), "
+.BR pam_get_item "(3) "
+.BR pam_fail_delay "(3) "
+and
+.BR pam_strerror "(3). "
+
+Also, see the three
+.BR Linux-PAM
+Guides, for
+.BR "System administrators" ", "
+.BR "module developers" ", "
+and
+.BR "application developers" ". "
diff --git a/doc/man/pam_chauthtok.3 b/doc/man/pam_chauthtok.3
new file mode 100644
index 00000000..63904da3
--- /dev/null
+++ b/doc/man/pam_chauthtok.3
@@ -0,0 +1,101 @@
+.\" Hey Emacs! This file is -*- nroff -*- source.
+.\" $Id$
+.\" Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net>
+.TH PAM_CHAUTHTOK 3 "1997 Jan 4" "Linux-PAM 0.55" "App. Programmers' Manual"
+.SH NAME
+
+pam_chauthtok \- updating authentication tokens
+
+.SH SYNOPSIS
+.B #include <security/pam_appl.h>
+.sp
+.BI "int pam_chauthtok(pam_handle_t " *pamh ", int " flags ");"
+.sp 2
+.SH DESCRIPTION
+.B pam_chauthtok
+
+.br
+Use this function to rejuvenate the authentication tokens (passwords
+etc.) of an applicant user.
+
+.br
+Note, the application should not pre-authenticate the user, as this is
+performed (if required) by the
+.BR Linux-PAM
+framework.
+
+.br
+The
+.I flags
+argument can
+.I optionally
+take the value,
+.BR PAM_CHANGE_EXPIRED_AUTHTOK "."
+In such cases the framework is only required to update those
+authentication tokens that have expired. Without this argument, the
+framework will attempt to obtain new tokens for all configured
+authentication mechanisms. The details of the types and number of such
+schemes should not concern the calling application.
+
+.SH RETURN VALUE
+A successful return from this function will be indicated with
+.BR PAM_SUCCESS "."
+
+.br
+Specific errors of special interest when calling this function are
+
+.br
+.BR PAM_AUTHTOK_ERROR
+- a valid new token was not obtained
+
+.br
+.BR PAM_AUTHTOK_RECOVERY_ERR
+- old authentication token was not available
+
+.br
+.BR PAM_AUTHTOK_LOCK_BUSY
+- a resource needed to update the token was locked (try again later)
+
+.br
+.BR PAM_AUTHTOK_DISABLE_AGING
+- one or more of the authentication modules does not honor
+authentication token aging
+
+.br
+.BR PAM_TRY_AGAIN
+- one or more authentication mechanism is not prepared to update a
+token at this time
+
+.br
+In general other return values may be returned. They should be treated
+as indicating failure.
+
+.SH ERRORS
+May be translated to text with
+.BR pam_strerror "(3). "
+
+.SH "CONFORMING TO"
+DCE-RFC 86.0, October 1995.
+
+.SH BUGS
+.sp 2
+none known.
+
+.SH "SEE ALSO"
+
+.BR pam_start "(3), "
+.BR pam_authenticate "(3), "
+.BR pam_setcred "(3), "
+.BR pam_get_item "(3), "
+.BR pam_strerror "(3) "
+and
+.BR pam "(8)."
+
+.br
+Also, see the three
+.BR Linux-PAM
+Guides, for
+.BR "System administrators" ", "
+.BR "module developers" ", "
+and
+.BR "application developers" ". "
diff --git a/doc/man/pam_close_session.3 b/doc/man/pam_close_session.3
new file mode 100644
index 00000000..c809a0e4
--- /dev/null
+++ b/doc/man/pam_close_session.3
@@ -0,0 +1 @@
+.so man3/pam_open_session.3
diff --git a/doc/man/pam_end.3 b/doc/man/pam_end.3
new file mode 100644
index 00000000..06fdabb9
--- /dev/null
+++ b/doc/man/pam_end.3
@@ -0,0 +1 @@
+.so man3/pam_start.3
diff --git a/doc/man/pam_fail_delay.3 b/doc/man/pam_fail_delay.3
new file mode 100644
index 00000000..f6cd238a
--- /dev/null
+++ b/doc/man/pam_fail_delay.3
@@ -0,0 +1,130 @@
+.\" Hey Emacs! This file is -*- nroff -*- source.
+.\" $Id$
+.\" Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net>
+.TH PAM_FAIL_DELAY 3 "1997 Jan 12" "Linux-PAM 0.56" "Programmers' Manual"
+.SH NAME
+
+pam_fail_delay \- request a delay on failure
+
+.SH SYNOPSIS
+.B #include <security/pam_appl.h>
+.br
+or,
+.br
+.B #include <security/pam_modules.h>
+.sp
+.BI "int pam_fail_delay(pam_handle_t " "*pamh" ", unsigned int " "usec" ");"
+.sp 2
+.SH DESCRIPTION
+.br
+It is often possible to attack an authentication scheme by exploiting
+the time it takes the scheme to deny access to an applicant user. In
+cases of
+.I short
+timeouts, it may prove possible to attempt a
+.I brute force
+dictionary attack -- with an automated process, the attacker tries all
+possible passwords to gain access to the system. In other cases,
+where individual failures can take measurable amounts of time
+(indicating the nature of the failure), an attacker can obtain useful
+information about the authentication process. These latter attacks
+make use of procedural delays that constitute a
+.I covert channel
+of useful information.
+
+.br
+To minimize the effectiveness of such attacks, it is desirable to
+introduce a random delay in a failed authentication process.
+.B Linux-PAM
+provides such a facility. The delay occurs upon failure of the
+.BR pam_authenticate "(3) "
+and
+.BR pam_chauthtok "(3) "
+functions. It occurs
+.I after
+all authentication modules have been called, but
+.I before
+control is returned to the service application.
+
+.br
+The function,
+.BR pam_fail_delay "(3),"
+is used to specify a required minimum for the length of the
+failure-delay; the
+.I usec
+argument. This function can be called by the service application
+and/or the authentication modules, both may have an interest in
+delaying a reapplication for service by the user. The length of the
+delay is computed at the time it is required. Its length is
+pseudo-gausianly distributed about the
+.I maximum
+requested value; the resultant delay will differ by as much as 25% of
+this maximum requested value (both up and down).
+
+.br
+On return from
+.BR pam_authenticate "(3) or " pam_chauthtok "(3),"
+independent of success or failure, the new requested delay is reset to
+its default value: zero.
+
+.SH EXAMPLE
+.br
+For example, a
+.B login
+application may require a failure delay of roughly 3 seconds. It will
+contain the following code:
+.sp
+.br
+.B " pam_fail_delay(pamh, 3000000 /* micro-seconds */ );"
+.br
+.B " pam_authenticate(pamh, 0);"
+.sp
+.br
+if the modules do not request a delay, the failure delay will be
+between 2.25 and 3.75 seconds.
+
+.br
+However, the modules, invoked in the authentication process, may
+also request delays:
+.sp
+.br
+.RB " (module #1) " "pam_fail_delay(pamh, 2000000);"
+.sp
+.br
+.RB " (module #2) " "pam_fail_delay(pamh, 4000000);"
+.sp
+.br
+in this case, it is the largest requested value that is used to
+compute the actual failed delay: here between 3 and 5 seconds.
+
+.SH "RETURN VALUE"
+Following a successful call to
+.BR pam_fail_delay "(3), " PAM_SUCCESS
+is returned. All other returns should be considered serious failures.
+
+.SH ERRORS
+May be translated to text with
+.BR pam_strerror "(3). "
+
+.SH "CONFORMING TO"
+Under consideration by the X/Open group for future inclusion in the
+PAM RFC. 1996/1/10
+
+.SH BUGS
+.sp 2
+none known.
+
+.SH "SEE ALSO"
+
+.BR pam_start "(3), "
+.BR pam_get_item "(3) "
+and
+.BR pam_strerror "(3). "
+
+Also, see the three
+.BR Linux-PAM
+Guides, for
+.BR "System administrators" ", "
+.BR "module developers" ", "
+and
+.BR "application developers" ". "
diff --git a/doc/man/pam_open_session.3 b/doc/man/pam_open_session.3
new file mode 100644
index 00000000..4e63b5c4
--- /dev/null
+++ b/doc/man/pam_open_session.3
@@ -0,0 +1,99 @@
+.\" Hey Emacs! This file is -*- nroff -*- source.
+.\" $Id$
+.\" Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net>
+.TH PAM_OPEN_SESSION 3 "1997 Jan 4" "Linux-PAM 0.55" "App. Programmers' Manual"
+.SH NAME
+
+pam_open/close_session \- PAM session management
+
+.SH SYNOPSIS
+.B #include <security/pam_appl.h>
+.sp
+.BI "int pam_open_session(pam_handle_t " *pamh ", int " flags ");"
+.sp
+.BI "int pam_close_session(pam_handle_t " *pamh ", int " flags ");"
+.sp 2
+.SH DESCRIPTION
+
+PAM provides management-hooks for the initialization and termination
+of a session.
+
+.TP
+.B pam_open_session
+.br
+Use this function to signal that an authenticated user session has
+begun. It should be called only after the user is properly identified
+and (where necessary) has been granted their credentials with
+.BR pam_authenticate "(3)"
+and
+.BR pam_setcred "(3)"
+respectively.
+
+.br
+Some types of functions associated with session
+initialization are logging for the purposes of system-audit and
+mounting directories (the user's home directory for example). These
+should not concern the application. It should be noted that the
+.I effective
+uid,
+.BR geteuid "(2),"
+of the application should be of sufficient privilege to perform such
+tasks.
+
+.TP
+.B pam_close_session
+.br
+Use this function to signal that a user session has
+terminated. In general this function may not need to be located in the
+same application as the initialization function,
+.BR pam_open_session "."
+
+.br
+Typically, this function will undo the actions of
+.BR pam_open_session "."
+That is, log audit information concerning the end of the user session
+or unmount the user's home directory. Apart from having sufficient
+privilege the details of the session termination should not concern
+the calling application. It is good programming practice, however, to
+cease acting on behalf of the user on returning from this call.
+
+.SH RETURN VALUE
+A successful return from the session management functions will be
+indicated with
+.BR PAM_SUCCESS "."
+
+.br
+The specific error indicating a failure to open or close a session is
+.BR PAM_SESSION_ERR "."
+In general other return values may be returned. They should be treated
+as indicating failure.
+
+.SH ERRORS
+May be translated to text with
+.BR pam_strerror "(3). "
+
+.SH "CONFORMING TO"
+OSF-RFC 86.0, October 1995.
+
+.SH BUGS
+.sp 2
+none known.
+
+.SH "SEE ALSO"
+
+.BR pam_start "(3), "
+.BR pam_authenticate "(3), "
+.BR pam_setcred "(3), "
+.BR pam_get_item "(3), "
+.BR pam_strerror "(3) "
+and
+.BR pam "(3)."
+
+.br
+Also, see the three
+.BR Linux-PAM
+Guides, for
+.BR "System administrators" ", "
+.BR "module developers" ", "
+and
+.BR "application developers" ". "
diff --git a/doc/man/pam_setcred.3 b/doc/man/pam_setcred.3
new file mode 100644
index 00000000..8c00fe71
--- /dev/null
+++ b/doc/man/pam_setcred.3
@@ -0,0 +1,79 @@
+.\" Hey Emacs! This file is -*- nroff -*- source.
+.\" $Id$
+.\" Copyright (c) Andrew G. Morgan 1996,1997 <morgan@parc.power.net>
+.TH PAM_SETCRED 3 "1997 July 6" "Linux-PAM 0.58" "App. Programmers' Manual"
+.SH NAME
+
+pam_setcred \- set the credentials for the user
+
+.SH SYNOPSIS
+.B #include <security/pam_appl.h>
+.sp
+.BI "int pam_setcred(pam_handle_t " *pamh ", int " flags ");"
+.sp 2
+.SH DESCRIPTION
+.B pam_setcred
+
+This function is used to establish, maintain and delete the
+credentials of a user. It should be called after a user has been
+authenticated and before a session is opened for the user (with
+.BR pam_open_session "(3))."
+
+It should be noted that credentials come in many forms. Examples
+include: group memberships; ticket-files; and Linux-PAM environment
+variables. For this reason, it is important that the basic identity
+of the user is established, by the application, prior to a call to
+this function. For example, the default
+.BR Linux-PAM
+environment variables should be set and also
+.BR initgroups "(2) "
+(or equivalent) should have been performed.
+
+.SH "VALID FLAGS"
+.TP
+.BR PAM_ESTABLISH_CRED
+initialize the credentials for the user.
+
+.TP
+.BR PAM_DELETE_CRED
+delete the user's credentials.
+
+.TP
+.BR PAM_REINITIALIZE_CRED
+delete and then initialize the user's credentials.
+
+.TP
+.BR PAM_REFRESH_CRED
+extend the lifetime of the existing credentials.
+
+.SH "RETURN VALUE"
+
+On success
+.BR PAM_SUCCESS
+is returned, all other return values should be treated as errors.
+
+.SH ERRORS
+May be translated to text with
+.BR pam_strerror "(3). "
+
+.SH "CONFORMING TO"
+DCE-RFC 86.0, October 1995.
+
+.SH BUGS
+.sp 2
+none known.
+
+.SH "SEE ALSO"
+
+.BR pam_authenticate "(3), "
+.BR pam_strerror "(3)"
+and
+.BR pam_open_session "(3). "
+
+Also, see the three
+.BR Linux-PAM
+Guides, for
+.BR "System administrators" ", "
+.BR "module developers" ", "
+and
+.BR "application developers" ". "
diff --git a/doc/man/pam_start.3 b/doc/man/pam_start.3
new file mode 100644
index 00000000..9c11fd73
--- /dev/null
+++ b/doc/man/pam_start.3
@@ -0,0 +1,98 @@
+.\" Hey Emacs! This file is -*- nroff -*- source.
+.\" $Id$
+.\" Copyright (c) Andrew G. Morgan 1996-7 <morgan@parc.power.net>
+.TH PAM_START 3 "1997 Feb 15" "Linux-PAM 0.56" "Application Programmers' Manual"
+.SH NAME
+
+pam_start, pam_end \- activating Linux-PAM
+
+.SH SYNOPSIS
+.B #include <security/pam_appl.h>
+.sp
+.BI "int pam_start(const char " *service ", const char " *user ", const struct pam_conv " *conv ", pam_handle_t " **pamh_p ");"
+.sp
+.BI "int pam_end(pam_handle_t " *pamh ", int " pam_status ");"
+.sp 2
+.SH DESCRIPTION
+.TP
+.B pam_start
+Initialize the
+.I Linux-PAM
+library. Identifying the application with a particular
+.IR service
+name. The
+.IR user "name"
+can take the value
+.IR NULL ", "
+if not known at the time the interface is initialized. The
+conversation structure is passed to the library via the
+.IR conv
+argument. (For a complete description of this and other structures
+the reader is directed to the more verbose
+.IR Linux-PAM
+application developers' guide). Upon successful initialization, an
+opaque pointer-handle for future access to the library is returned
+through the contents of the
+.IR pamh_p
+pointer.
+
+.TP
+.B pam_end
+Terminate the
+.B Linux-PAM
+library. The service application associated with the
+.IR pamh
+handle, is terminated. The argument,
+.IR pam_status ", "
+passes the value most recently returned to the application from the
+library; it indicates the manner in which the library should be
+shutdown. Besides carrying a return value, this argument may be
+logically OR'd with
+.IR PAM_DATA_SILENT
+to indicate that the module should not treat the call too
+seriously. It is generally used to indicate that the current closing
+of the library is in a
+.IR fork "(2)ed"
+process, and that the parent will take care of cleaning up things that
+exist outside of the current process space (files etc.).
+
+.SH "RETURN VALUE"
+.TP
+.B pam_start
+.TP
+.B pam_end
+On success,
+.BR PAM_SUCCESS
+is returned
+
+.SH ERRORS
+May be translated to text with
+.BR pam_strerror "(3). "
+
+.SH "CONFORMING TO"
+DCE-RFC 86.0, October 1995.
+.sp
+Note, the
+.BR PAM_DATA_SILENT
+flag is pending acceptance with the DCE (as of 1996/12/4).
+
+.SH BUGS
+.sp 2
+None known.
+
+.SH "SEE ALSO"
+
+.BR fork "(2), "
+.BR pam_authenticate "(3), "
+.BR pam_acct_mgmt "(3), "
+.BR pam_open_session "(3), "
+and
+.BR pam_chauthtok "(3)."
+
+Also, see the three
+.BR Linux-PAM
+Guides, for
+.BR "System administrators" ", "
+.BR "module developers" ", "
+and
+.BR "application developers" ". "
diff --git a/doc/man/pam_strerror.3 b/doc/man/pam_strerror.3
new file mode 100644
index 00000000..01ee0635
--- /dev/null
+++ b/doc/man/pam_strerror.3
@@ -0,0 +1,51 @@
+.\" Hey Emacs! This file is -*- nroff -*- source.
+.\" ripped off from Rick Faith's getgroups man page
+.\" $Id$
+.\" Copyright (c) Andrew G. Morgan 1996-7 <morgan@linux.kernel.org>
+.TH PAM_STRERROR 3 "1999 Oct 4" "Linux-PAM 0.70" "Programmers' Manual"
+.SH NAME
+
+pam_strerror \- return a textual description of a Linux-PAM error
+
+.SH SYNOPSIS
+.B #include <security/pam_appl.h>
+.br
+or,
+.br
+.B #include <security/pam_modules.h>
+.sp
+.BI "const char * pam_strerror( pam_handle_t " "*pamh" ", int " pam_error ");"
+.sp 2
+.SH DESCRIPTION
+.B pam_strerror
+
+This function returns some text describing the
+.BR Linux-PAM
+error associated with the
+.B pam_error
+argument.
+
+.SH "RETURN VALUE"
+
+On success this function returns a description of the indicated
+error. Should the function not recognize the error, ``Unknown
+Linux-PAM error'' is returned.
+
+.SH "CONFORMING TO"
+DCE-RFC 86.0, October 1995.
+
+.SH BUGS
+.sp 2
+This function should be internationalized.
+
+.SH "SEE ALSO"
+
+.BR pam "(8). "
+
+Also, see the three
+.BR Linux-PAM
+Guides, for
+.BR "System administrators" ", "
+.BR "module developers" ", "
+and
+.BR "application developers" ". "
diff --git a/doc/man/template-man b/doc/man/template-man
new file mode 100644
index 00000000..b8159eb6
--- /dev/null
+++ b/doc/man/template-man
@@ -0,0 +1,52 @@
+.\" Hey Emacs! This file is -*- nroff -*- source.
+.\" $Id$
+.\" Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net>
+.TH PAM_???? 2 "1997 Jan 4" "Linux-PAM 0.55" "Application Programmers' Manual"
+.SH NAME
+
+function names \- brief summary of function
+
+.SH SYNOPSIS
+.B #include <security/pam_????.h>
+.sp
+.BI "int pam_???(pam_handle_t " pamh ", int " flags);
+.sp 2
+.SH DESCRIPTION
+.TP
+.B pam_???
+Here goes the
+.I explanation
+it may be quite
+.IR long .
+.TP
+.SH "RETURN VALUE"
+.B pam_???
+On success...
+.BR PAM_SUCCESS
+is returned
+.TP
+.SH ERRORS
+May be translated to text with
+.BR pam_strerror "(2). "
+
+.SH "CONFORMING TO"
+.B pam_???
+DCE-RFC 86.0, October 1995.
+
+.SH BUGS
+.sp 2
+none known.
+
+.SH "SEE ALSO"
+
+.BR pam_??? "(2), "
+and
+.BR pam_??? "(2). "
+
+Also, see the three
+.BR Linux-PAM
+Guides, for
+.BR "System administrators" ", "
+.BR "module developers" ", "
+and
+.BR "application developers" ". "
diff --git a/doc/modules/README b/doc/modules/README
new file mode 100644
index 00000000..df091939
--- /dev/null
+++ b/doc/modules/README
@@ -0,0 +1,13 @@
+$Id$
+
+This directory contains a number of sgml sub-files. One for each
+documented module. They contain a description of each module and give
+some indication of its reliability.
+
+Additionally, there is a 'module.sgml-template' file which should be
+used as a blank form for new module descriptions.
+
+Please feel free to submit amendments/comments etc. regarding these
+files to:
+
+ Andrew G. Morgan <morgan@parc.power.net>
diff --git a/doc/modules/module.sgml-template b/doc/modules/module.sgml-template
new file mode 100644
index 00000000..d0b0e3c6
--- /dev/null
+++ b/doc/modules/module.sgml-template
@@ -0,0 +1,170 @@
+<!--
+
+ $Id$
+
+ This template file was written by Andrew G. Morgan
+ <morgan@parc.power.net>
+
+[
+ Text that should be deleted/replaced, is enclosed within
+ '[' .. ']'
+ marks. For example, this text should be deleted!
+]
+
+-->
+
+<sect1> [*Familiar full name of module*, eg. The "allow all" module.]
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+[
+ insert the name of the module
+
+ Blank is not permitted.
+]
+
+<tag><bf>Author[s]:</bf></tag>
+
+[
+ Insert author names here
+
+ Blank is not permitted. If in doubt, put "unknown" if the
+ author wishes to remain anonymous, put "anonymous".
+]
+
+<tag><bf>Maintainer:</bf></tag>
+
+[
+ Insert names and date-begun of most recent maintainer.
+]
+
+<tag><bf>Management groups provided:</bf></tag>
+
+[
+ list the subset of four management groups supported by the
+ module. Choose from: account; authentication; password;
+ session.
+
+ Blank entries are not permitted. Explicitly list all of the
+ management groups. In the future more may be added to libpam!
+]
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+[
+ Indicate whether this module contains code that can perform
+ reversible (strong) encryption. This field is primarily to
+ ensure that people redistributing it are not unwittingly
+ breaking laws...
+
+ Modules may also require the presence of some local library
+ that performs the necessary encryption via some standard API.
+ In this case "uses API" can be included in this field. The
+ library in question should be added to the system requirements
+ below.
+
+ Blank = no cryptography is used by module.
+]
+
+<tag><bf>Security rating:</bf></tag>
+
+[
+ Initially, this field should be left blank. If someone takes
+ it upon themselves to test the strength of the module, it can
+ later be filled.
+
+ Blank = unknown.
+]
+
+<tag><bf>Clean code base:</bf></tag>
+
+[
+ This will probably be filled by the libpam maintainer.
+ It can be considered to be a public humiliation list. :*)
+
+ I am of the opinion that "gcc -with_all_those_flags" is
+ trying to tell us something about whether the program
+ works as intended. Since there is currently no Security
+ evaluation procedure for modules IMHO this is not a
+ completely unreasonable indication (a lower bound anyway)
+ of the reliability of a module.
+
+ This field would indicate the number and flavor of
+ warnings that gcc barfs up when trying to compile the
+ module as part of the tree. Is this too tyrannical?
+
+ Blank = Linux-PAM maintainer has not tested it :)
+]
+
+<tag><bf>System dependencies:</bf></tag>
+
+[
+ here we list config files, dynamic libraries needed, system
+ resources, kernel options.. etc.
+
+ Blank = nothing more than libc required.
+]
+
+<tag><bf>Network aware:</bf></tag>
+
+[
+ Does the module base its behavior on probing a network
+ connection? Does it expect to be protected by the
+ application?
+
+ Blank = Ignorance of network.
+]
+
+</descrip>
+
+<sect2>Overview of module
+
+[
+ some text describing the intended actions of the module
+ general comments mainly (specifics in sections
+ below).
+]
+
+[
+
+ [ now we have a <sect2> level subsection for each of the
+ management groups. Include as many as there are groups
+ listed above in the synopsis ]
+
+<sect2>[ Account | Authentication | Password | Session ] component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+[
+ List the supported arguments (leave their description for the
+ description below.
+
+ Blank = no arguments are read and nothing is logged to syslog
+ about any arguments that are passed. Note, this
+ behavior is contrary to the RFC!
+]
+
+<tag><bf>Description:</bf></tag>
+
+[
+ This component of the module performs the task of ...
+]
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+[
+ Here we list some doos and don'ts for this module.
+]
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_access.sgml b/doc/modules/pam_access.sgml
new file mode 100644
index 00000000..e192d12e
--- /dev/null
+++ b/doc/modules/pam_access.sgml
@@ -0,0 +1,93 @@
+<!--
+
+ pam_access module docs added by Tim Berger <timb@transmeta.com>
+
+-->
+
+<sect1> The access module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+
+<tt>pam_access</tt>
+
+
+<tag><bf>Author[s]:</bf></tag>
+
+Alexei Nogin &lt;alexei@nogin.dnttm.ru&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+
+Author
+
+<tag><bf>Management groups provided:</bf></tag>
+
+account
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+Requires a configuration file <tt>/etc/security/access.conf</tt>
+<tag><bf>Network aware:</bf></tag>
+
+Through <tt/PAM_TTY/ if set, otherwise attempts getting tty name of
+the stdin file descriptor with <tt/ttyname()/. Standard
+gethostname(), <tt/yp_get_default_domain()/, <tt/gethostbyname()/
+calls. <bf/NIS/ is used for netgroup support.
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+Provides logdaemon style login access control.
+
+<sect2> Account component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+This module provides logdaemon style login access control based on
+login names and on host (or domain) names, internet addresses (or
+network numbers), or on terminal line names in case of non-networked
+logins. Diagnostics are reported through <tt/syslog(3)/. Wietse
+Venema's <tt/login_access.c/ from <em/logdaemon-5.6/ is used with
+several changes by A. Nogin.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+Use of module is recommended, for example, on administrative machines
+such as <bf/NIS/ servers and mail servers where you need several accounts
+active but don't want them all to have login capability.
+
+For <tt>/etc/pam.d</tt> style configurations where your modules live
+in <tt>/lib/security</tt>, start by adding the following line to
+<tt>/etc/pam.d/login</tt>, <tt>/etc/pam.d/rlogin</tt>,
+<tt>/etc/pam.d/rsh</tt> and <tt>/etc/pam.d/ftp</tt>:
+
+<tscreen>
+<verb>
+account required /lib/security/pam_access.so
+</verb>
+</tscreen>
+
+Note that use of this module is not effective unless your system ignores
+<tt>.rhosts</tt> files. See the the pam_rhosts_auth documentation.
+
+A sample <tt>access.conf</tt> configuration file is included with the
+distribution.
+
+</descrip>
+
diff --git a/doc/modules/pam_chroot.sgml b/doc/modules/pam_chroot.sgml
new file mode 100644
index 00000000..ec739c18
--- /dev/null
+++ b/doc/modules/pam_chroot.sgml
@@ -0,0 +1,86 @@
+<!--
+ $Id$
+
+ This file was written by Bruce Campbell <brucec@humbug.org.au>
+-->
+
+<sect1>Chroot
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_chroot/
+
+<tag><bf>Author:</bf></tag>
+Bruce Campbell &lt;brucec@humbug.org.au&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author; proposed on 20/11/96 - email for status
+
+<tag><bf>Management groups provided:</bf></tag>
+account; session; authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+Unwritten.
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+Expects localhost.
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module is intended to provide a transparent wrapper around the
+average user, one that puts them in a fake file-system (eg, their
+'<tt>/</tt>' is really <tt>/some/where/else</tt>).
+
+<p>
+Useful if you have several classes of users, and are slightly paranoid
+about security. Can be used to limit who else users can see on the
+system, and to limit the selection of programs they can run.
+
+<sect2>Account component:
+
+<p>
+<em/Need more info here./
+
+<sect2>Authentication component:
+
+<p>
+<em/Need more info here./
+
+<sect2>Session component:
+
+<p>
+<em/Need more info here./
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+Arguments and logging levels for the PAM version are being worked on.
+
+<tag><bf>Description:</bf></tag>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+Do provide a reasonable list of programs - just tossing 'cat', 'ls', 'rm',
+'cp' and 'ed' in there is a bit...
+<p>
+Don't take it to extremes (eg, you can set up a separate environment for
+each user, but its a big waste of your disk space.)
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_cracklib.sgml b/doc/modules/pam_cracklib.sgml
new file mode 100644
index 00000000..f5b2359a
--- /dev/null
+++ b/doc/modules/pam_cracklib.sgml
@@ -0,0 +1,259 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+ long password amendments are from Philip W. Dalrymple III <pwd@mdtsoft.com>
+-->
+
+<sect1>Cracklib pluggable password strength-checker
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+
+pam_cracklib
+
+<tag><bf>Author:</bf></tag>
+
+Cristian Gafton &lt;gafton@redhat.com&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+
+Author.
+
+<tag><bf>Management groups provided:</bf></tag>
+
+password
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+
+Requires the system library <tt/libcrack/ and a system dictionary:
+<tt>/usr/lib/cracklib_dict</tt>.
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module can be plugged into the <tt/password/ stack of a given
+application to provide some plug-in strength-checking for passwords.
+(XXX - note this does not necessarily work with the pam_unix module,
+although it is known to work with the pam_pwdb replacement for the
+unix module -- see example and pam_pwdb write up for more
+information).
+
+<p>
+This module works in the following manner: it first calls the
+<em>Cracklib</em> routine to check the strength of the password; if
+crack likes the password, the module does an additional set of
+strength checks. These checks are:
+<itemize>
+
+<item> <bf/Palindrome/ -
+
+Is the new password a palindrome of the old one?
+
+<item> <bf/Case Change Only/ -
+
+Is the new password the the old one with only a change of case?
+
+<item> <bf/Similar/ -
+
+Is the new password too much like the old one? This is controlled
+by one argument, <tt/difok/ which is a number of characters that if
+different between the old and new are enough to accept the new
+password, this defaults to 10 or 1/2 the size of the new password
+whichever is smaller.
+
+<item> <bf/Simple/ -
+
+Is the new password too small? This is controlled by 5 arguments
+<tt/minlen/, <tt/dcredit/, <tt/ucredit/, <tt/lcredit/, and
+<tt/ocredit/. See the section on the arguments for the details of how
+these work and there defaults.
+
+<item> <bf/Rotated/ -
+
+Is the new password a rotated version of the old password?
+
+<item> <bf/Already used/ -
+
+Was the password used in the past? Previously used passwords are to
+be found in /etc/security/opasswd.
+
+</itemize>
+
+<p>
+This module with no arguments will work well for standard unix
+password encryption. With md5 encryption, passwords can be longer
+than 8 characters and the default settings for this module can make it
+hard for the user to choose a satisfactory new password. Notably, the
+requirement that the new password contain no more than 1/2 of the
+characters in the old password becomes a non-trivial constraint. For
+example, an old password of the form "the quick brown fox jumped over
+the lazy dogs" would be difficult to change... In addition, the
+default action is to allow passwords as small as 5 characters in
+length. For a md5 systems it can be a good idea to increase the
+required minimum size of a password. One can then allow more credit
+for different kinds of characters but accept that the new password may
+share most of these characters with the old password.
+
+<sect2>Password component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tt/debug/; <tt/type=XXX/; <tt/retry=N/; <tt/difok=N/; <tt/minlen=N/;
+<tt/dcredit=N/; <tt/ucredit=N/; <tt/lcredit=N/; <tt/ocredit=N/;
+
+<tag><bf>Description:</bf></tag>
+
+The action of this module is to prompt the user for a password and
+check its strength against a system dictionary and a set of rules for
+identifying poor choices.
+
+<p>
+The default action is to prompt for a single password, check its
+strength and then, if it is considered strong, prompt for the password
+a second time (to verify that it was typed correctly on the first
+occasion). All being well, the password is passed on to subsequent
+modules to be installed as the new authentication token.
+
+<p>
+The default action may be modified in a number of ways using the
+arguments recognized by the module:
+<itemize>
+
+<item> <tt/debug/ -
+
+this option makes the module write information to syslog(3) indicating
+the behavior of the module (this option does <bf/not/ write password
+information to the log file).
+
+<item> <tt/type=XXX/ -
+
+the default action is for the module to use the following prompts when
+requesting passwords: ``New UNIX password: '' and ``Retype UNIX
+password: ''. Using this option you can replace the word UNIX with
+<tt/XXX/.
+
+<item> <tt/retry=N/ -
+
+the default number of times this module will request a new password
+(for strength-checking) from the user is 1. Using this argument this
+can be increased to <tt/N/.
+
+<item> <tt/difok=N/ -
+
+This argument will change the default of 10 for the number of
+characters in the new password that must not be present in the old
+password. In addition, if 1/2 of the characters in the new password
+are different then the new password will be accepted anyway.
+
+<item> <tt/minlen=N/ -
+
+The minimum acceptable size for the new password plus one. In
+addition to the number of characters in the new password, credit (of
++1 in length) is given for each different kind of character (<em>other,
+upper, lower</em> and <em/digit/). The default for this parameter is
+9 which is good for a old style UNIX password all of the same type of
+character but may be too low to exploit the added security of a md5
+system. Note that there is a pair of length limits in
+<em>Cracklib</em> itself, a "way too short" limit of 4 which is hard
+coded in and a defined limit (6) that will be checked without
+reference to <tt>minlen</tt>. If you want to allow passwords as short
+as 5 characters you should either not use this module or recompile
+the crack library and then recompile this module.
+
+<item> <tt/dcredit=N/ -
+
+This is the maximum credit for having digits in the new password. If
+you have less than or <tt/N/ digits, each digit will count +1 towards
+meeting the current <tt/minlen/ value. The default for <tt/dcredit/
+is 1 which is the recommended value for <tt/minlen/ less than 10.
+
+<item> <tt/ucredit=N/ -
+
+This is the maximum credit for having upper case letters in the new
+password. If you have less than or <tt/N/ upper case letters each
+letter will count +1 towards meeting the current <tt/minlen/ value.
+The default for <tt/ucredit/ is 1 which is the recommended value for
+<tt/minlen/ less than 10.
+
+<item> <tt/lcredit=N/ -
+
+This is the maximum credit for having lower case letters in the new
+password. If you have less than or <tt/N/ lower case letters, each
+letter will count +1 towards meeting the current <tt/minlen/ value.
+The default for <tt/lcredit/ is 1 which is the recommended value for
+<tt/minlen/ less than 10.
+
+<item> <tt/ocredit=N/ -
+
+This is the maximum credit for having other characters in the new
+password. If you have less than or <tt/N/ other characters, each
+character will count +1 towards meeting the current <tt/minlen/ value.
+The default for <tt/ocredit/ is 1 which is the recommended value for
+<tt/minlen/ less than 10.
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+(At the time of writing, this module can only be stacked before the
+<tt/pam_pwdb/ module. Cracklib strength checking may be compiled by
+default into the <tt/pam_unix/ module.)
+
+<p>
+For an example of the use of this module, we show how it may be
+stacked with the password component of <tt/pam_pwdb/:
+<tscreen>
+<verb>
+#
+# These lines stack two password type modules. In this example the
+# user is given 3 opportunities to enter a strong password. The
+# "use_authtok" argument ensures that the pam_pwdb module does not
+# prompt for a password, but instead uses the one provided by
+# pam_cracklib.
+#
+passwd password required pam_cracklib.so retry=3
+passwd password required pam_pwdb.so use_authtok
+</verb>
+</tscreen>
+
+<p>
+Another example (in the <tt>/etc/pam.d/passwd</tt> format) is for the
+case that you want to use md5 password encryption:
+<tscreen>
+<verb>
+#%PAM-1.0
+#
+# These lines allow a md5 systems to support passwords of at least 14
+# bytes with extra credit of 2 for digits and 2 for others the new
+# password must have at least three bytes that are not present in the
+# old password
+#
+password required pam_cracklib.so \
+ difok=3 minlen=15 dcredit= 2 ocredit=2
+password required pam_pwdb.so use_authtok nullok md5
+</verb>
+</tscreen>
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_deny.sgml b/doc/modules/pam_deny.sgml
new file mode 100644
index 00000000..6e1f2992
--- /dev/null
+++ b/doc/modules/pam_deny.sgml
@@ -0,0 +1,179 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+-->
+
+<sect1>The locking-out module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+pam_deny
+
+<tag><bf>Author:</bf></tag>
+Andrew G. Morgan &lt;morgan@parc.power.net&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+current <bf/Linux-PAM/ maintainer
+
+<tag><bf>Management groups provided:</bf></tag>
+account; authentication; password; session
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+clean.
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module can be used to deny access. It always indicates a failure
+to the application through the PAM framework. As is commented in the
+overview section <ref id="overview-section" name="above">, this module
+might be suitable for using for default (the <tt/OTHER/) entries.
+
+<sect2>Account component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+This component does nothing other than return a failure. The
+failure type is <tt/PAM_ACCT_EXPIRED/.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+Stacking this module with type <tt/account/ will prevent the user from
+gaining access to the system via applications that refer to
+<bf/Linux-PAM/'s account management function <tt/pam_acct_mgmt()/.
+
+<p>
+The following example would make it impossible to login:
+<tscreen>
+<verb>
+#
+# add this line to your other login entries to disable all accounts
+#
+login account required pam_deny.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+This component does nothing other than return a failure. The failure
+type is <tt/PAM_AUTH_ERR/ in the case that <tt/pam_authenticate()/ is
+called (when the application tries to authenticate the user), and is
+<tt/PAM_CRED_UNAVAIL/ when the application calls <tt/pam_setcred()/
+(to establish and set the credentials of the user -- it is unlikely
+that this function will ever be called in practice).
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+To deny access to default applications with this component of the
+<tt/pam_deny/ module, you might include the following line in your
+<bf/Linux-PAM/ configuration file:
+<tscreen>
+<verb>
+#
+# add this line to your existing OTHER entries to prevent
+# authentication succeeding with default applications.
+#
+OTHER auth required pam_deny.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<sect2>Password component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+This component of the module denies the user the opportunity to change
+their password. It always responds with <tt/PAM_AUTHTOK_ERR/ when
+invoked.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+This module should be used to prevent an application from updating the
+applicant user's password. For example, to prevent <tt/login/ from
+automatically prompting for a new password when the old one has
+expired you should include the following line in your configuration
+file:
+<tscreen>
+<verb>
+#
+# add this line to your other login entries to prevent the login
+# application from being able to change the user's password.
+#
+login password required pam_deny.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<sect2>Session component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+This aspect of the module prevents an application from starting a
+session on the host computer.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+Together with another session module, that displays a message of the
+day perhaps (XXX - such a module needs to be written),
+this module can be used to block a user from starting a shell. Given
+the presence of a <tt/pam_motd/ module, we might use the following
+entries in the configuration file to inform the user it is system
+time:
+<tscreen>
+<verb>
+#
+# An example to see how to configure login to refuse the user a
+# session (politely)
+#
+login session required pam_motd.so \
+ file=/etc/system_time
+login session required pam_deny.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_env.sgml b/doc/modules/pam_env.sgml
new file mode 100644
index 00000000..8057b38d
--- /dev/null
+++ b/doc/modules/pam_env.sgml
@@ -0,0 +1,141 @@
+<!--
+ $Id$
+
+ This file was written by Dave Kinchlea <kinch@kinch.ark.com>
+ Ed. AGM
+-->
+
+<sect1>Set/unset environment variables
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_env/
+
+<tag><bf>Author:</bf></tag>
+Dave Kinchlea &lt;kinch@kinch.ark.com&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author
+
+<tag><bf>Management groups provided:</bf></tag>
+Authentication (setcred)
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+<tt>/etc/security/pam_env.conf</tt>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module allows the (un)setting of environment variables. Supported
+is the use of previously set environment variables as well as
+<em>PAM_ITEM</em>s such as <tt>PAM_RHOST</tt>.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/; <tt/conffile=/<em/configuration-file-name/;
+<tt/envfile/=/<em/env-file-name/; <tt/readenv/=/<em/0|1/
+
+<tag><bf>Description:</bf></tag>
+This module allows you to (un)set arbitrary environment variables
+using fixed strings, the value of previously set environment variables
+and/or <em/PAM_ITEM/s.
+
+<p>
+All is controlled via a configuration file (by default,
+<tt>/etc/security/pam_env.conf</tt> but can be overriden with
+<tt>connfile</tt> argument). Each line starts with the variable name,
+there are then two possible options for each variable <bf>DEFAULT</bf>
+and <bf>OVERRIDE</bf>. <bf>DEFAULT</bf> allows and administrator to
+set the value of the variable to some default value, if none is
+supplied then the empty string is assumed. The <bf>OVERRIDE</bf>
+option tells pam_env that it should enter in its value (overriding the
+default value) if there is one to use. <bf>OVERRIDE</bf> is not used,
+<tt>""</tt> is assumed and no override will be done.
+
+<p>
+<tscreen>
+<verb>
+VARIABLE [DEFAULT=[value]] [OVERRIDE=[value]]
+</verb>
+</tscreen>
+
+<p>
+(Possibly non-existent) environment variables may be used in values
+using the <tt>&dollar;&lcub;string&rcub;</tt> syntax and (possibly
+non-existent) <em/PAM_ITEM/s may be used in values using the
+<tt>&commat;&lcub;string&rcub;</tt> syntax. Both the <tt>&dollar;</tt>
+and <tt>&commat;</tt> characters can be backslash-escaped to be used
+as literal values (as in <tt>&bsol;&dollar;</tt>. Double quotes may
+be used in values (but not environment variable names) when white
+space is needed <bf>the full value must be delimited by the quotes and
+embedded or escaped quotes are not supported</bf>.
+
+<p>
+This module can also parse a file with simple KEY=VAL pairs on seperate
+lines (/etc/environment by default). You can change the default file to
+parse, with the <em/envfile/ flag and turn it on or off by setting the
+<em/readenv/ flag to 1 or 0 respectively.
+
+<p>
+The behavior of this module can be modified with one of the following
+flags:
+
+<p>
+<itemize>
+
+<item><tt/debug/
+- write more information to <tt/syslog(3)/.
+
+<item><tt/conffile=/<em/filename/
+- by default the file <tt>/etc/security/pam_env.conf</tt> is used as
+the configuration file. This option overrides the default. You must
+supply a complete path + file name.
+
+<item><tt/envfile=/<em/filename/
+- by default the file <tt>/etc/environment</tt> is used to load KEY=VAL
+pairs directly into the env. This option overrides the default. You must
+supply a complete path + file name.
+
+<item><tt/readenv=/<em/0|1/
+- turns on or off the reading of the file specified by envfile (0 is off,
+1 is on). By default this option is on.
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+See sample <tt>pam_env.conf</tt> for more information and examples.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/modules/pam_filter.sgml b/doc/modules/pam_filter.sgml
new file mode 100644
index 00000000..598279b8
--- /dev/null
+++ b/doc/modules/pam_filter.sgml
@@ -0,0 +1,150 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+-->
+
+<sect1>The filter module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+
+pam_filter
+
+<tag><bf>Author:</bf></tag>
+
+Andrew G. Morgan &lt;morgan@parc.power.net&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+
+Author.
+
+<tag><bf>Management groups provided:</bf></tag>
+
+account; authentication; password; session
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+Not yet.
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+This module compiles cleanly on Linux based systems.
+
+<tag><bf>System dependencies:</bf></tag>
+
+To function it requires <em/filters/ to be installed on the system.
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module was written to offer a plug-in alternative to programs
+like ttysnoop (XXX - need a reference). Since writing a filter that
+performs this function has not occurred, it is currently only a toy.
+The single filter provided with the module simply transposes upper and
+lower case letters in the input and output streams. (This can be very
+annoying and is not kind to termcap based editors).
+
+<sect2>Account+Authentication+Password+Session components
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tt/debug/; <tt/new_term/; <tt/non_term/; <tt/runX/
+
+<tag><bf>Description:</bf></tag>
+
+Each component of the module has the potential to invoke the desired
+filter. The filter is always <tt/execv(2)/d with the privilege of the
+calling application and <bf/not/ that of the user. For this reason it
+cannot usually be killed by the user without closing their session.
+
+<p>
+The behavior of the module can be significantly altered by the
+arguments passed to it in the <bf/Linux-PAM/ configuration file:
+<itemize>
+<item><tt/debug/ -
+
+this option increases the amount of information logged to
+<tt/syslog(3)/ as the module is executed.
+
+<item><tt/new_term/ -
+
+the default action of the filter is to set the <tt/PAM_TTY/ item to
+indicate the terminal that the user is using to connect to the
+application. This argument indicates that the filter should set
+<tt/PAM_TTY/ to the filtered pseudo-terminal.
+
+<item><tt/non_term/ -
+don't try to set the <tt/PAM_TTY/ item.
+
+<item><tt/runX/ -
+
+in order that the module can invoke a filter it should know when to
+invoke it. This argument is required to tell the filter when to do
+this. The arguments that follow this one are respectively the full
+pathname of the filter to be run and any command line arguments that
+the filter might expect.
+
+<p>
+Permitted values for <tt/X/ are <tt/1/ and <tt/2/. These indicate the
+precise time the that filter is to be run. To explain this concept it
+will be useful to have read the Linux-PAM Module developer's
+guide. Basically, for each management group there are up to two ways
+of calling the module's functions.
+
+In the case of the <em/authentication/ and <em/session/ components
+there are actually two separate functions. For the case of
+authentication, these functions are <tt/_authenticate/ and
+<tt/_setcred/ -- here <tt/run1/ means run the filter from the
+<tt/_authenticate/ function and <tt/run2/ means run the filter from
+<tt/_setcred/. In the case of the session modules, <tt/run1/ implies
+that the filter is invoked at the <tt/_open_session/ stage, and
+<tt/run2/ for <tt/_close_session/.
+
+<p>
+For the case of the account component. Either <tt/run1/ or <tt/run2/
+may be used.
+
+<p>
+For the case of the password component, <tt/run1/ is used to indicate
+that the filter is run on the first occasion <tt/_chauthtok/ is run
+(the <tt/PAM_PRELIM_CHECK/ phase) and <tt/run2/ is used to indicate
+that the filter is run on the second occasion (the
+<tt/PAM_UPDATE_AUTHTOK/ phase).
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+At the time of writing there is little real use to be made of this
+module. For fun you might try adding the following line to your
+login's configuration entries
+<tscreen>
+<verb>
+#
+# An example to see how to configure login to transpose upper and
+# lower case letters once the user has logged in(!)
+#
+login session required pam_filter.so \
+ run1 /usr/sbin/pam_filter/upperLOWER
+</verb>
+</tscreen>
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_ftp.sgml b/doc/modules/pam_ftp.sgml
new file mode 100644
index 00000000..3c26a5f0
--- /dev/null
+++ b/doc/modules/pam_ftp.sgml
@@ -0,0 +1,93 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@linux.kernel.org>
+-->
+
+<sect1>Anonymous access module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_ftp.so/
+
+<tag><bf>Author:</bf></tag>
+Andrew G. Morgan &lt;morgan@linux.kernel.org&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author.
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+prompts for email address of user; easily spoofed (XXX - needs work)
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+The purpose of this module is to provide a pluggable anonymous ftp
+mode of access.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/;
+<tt/users=XXX,YYY,.../;
+<tt/ignore/
+
+<tag><bf>Description:</bf></tag>
+
+This module intercepts the user's name and password. If the name is
+``<tt/ftp/'' or ``<tt/anonymous/'', the user's password is broken up
+at the `<tt/@/' delimiter into a <tt/PAM_RUSER/ and a <tt/PAM_RHOST/
+part; these pam-items being set accordingly. The username is set to
+``<tt/ftp/''. In this case the module succeeds. Alternatively, the
+module sets the <tt/PAM_AUTHTOK/ item with the entered password and
+fails.
+
+<p>
+The behavior of the module can be modified with the following flags:
+<itemize>
+<item><tt/debug/ -
+log more information to with <tt/syslog(3)/.
+
+<item><tt/users=XXX,YYY,.../ -
+instead of ``<tt/ftp/'' or ``<tt/anonymous/'', provide anonymous login
+to the comma separated list of users; ``<tt/XXX,YYY,.../''. Should the
+applicant enter one of these usernames the returned username is set to
+the first in the list; ``<tt/XXX/''.
+
+<item><tt/ignore/ -
+pay no attention to the email address of the user (if supplied).
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+An example of the use of this module is provided in the configuration
+file section <ref id="configuration" name="above">. With care, this
+module could be used to provide new/temporary account anonymous
+login.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_group.sgml b/doc/modules/pam_group.sgml
new file mode 100644
index 00000000..8251e3dd
--- /dev/null
+++ b/doc/modules/pam_group.sgml
@@ -0,0 +1,108 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+-->
+
+<sect1>The group access module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_group/
+
+<tag><bf>Author:</bf></tag>
+Andrew G. Morgan &lt;morgan@parc.power.net&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author.
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+Sensitive to <em/setgid/ status of file-systems accessible to users.
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+Requires an <tt>/etc/security/group.conf</tt> file. Can be compiled
+with or without <tt/libpwdb/.
+
+<tag><bf>Network aware:</bf></tag>
+Only through correctly set <tt/PAM_TTY/ item.
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module provides group-settings based on the user's name and the
+terminal they are requesting a given service from. It takes note of
+the time of day.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+This module does not authenticate the user, but instead it grants
+group memberships (in the credential setting phase of the
+authentication module) to the user. Such memberships are based on the
+service they are applying for. The group memberships are listed in
+text form in the <tt>/etc/security/group.conf</tt> file.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+For this module to function correctly there must be a correctly
+formatted <tt>/etc/security/groups.conf</tt> file present. The format
+of this file is as follows. Group memberships are given based on the
+service application satisfying any combination of lines in the
+configuration file. Each line (barring comments which are preceded by
+`<tt/#/' marks) has the following
+syntax:
+<tscreen>
+<verb>
+services ; ttys ; users ; times ; groups
+</verb>
+</tscreen>
+Here the first four fields share the syntax of the <tt>pam_time</tt>
+configuration file; <tt>/etc/security/pam_time.conf</tt>, and the last
+field, the <tt/groups/ field, is a comma (or space) separated list of
+the text-names of a selection of groups. If the users application for
+service satisfies the first four fields, the user is granted membership
+of the listed groups.
+
+<p>
+As stated in above this module's usefulness relies on the file-systems
+accessible to the user. The point being that once granted the
+membership of a group, the user may attempt to create a <em/setgid/
+binary with a restricted group ownership. Later, when the user is not
+given membership to this group, they can recover group membership with
+the precompiled binary. The reason that the file-systems that the user
+has access to are so significant, is the fact that when a system is
+mounted <em/nosuid/ the user is unable to create or execute such a
+binary file. For this module to provide any level of security, all
+file-systems that the user has write access to should be mounted
+<em/nosuid/.
+
+<p>
+The <tt>pam_group</tt> module fuctions in parallel with the
+<tt>/etc/group</tt> file. If the user is granted any groups based on
+the behavior of this module, they are granted <em>in addition</em> to
+those entries <tt>/etc/group</tt> (or equivalent).
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_issue.sgml b/doc/modules/pam_issue.sgml
new file mode 100644
index 00000000..1f617e3b
--- /dev/null
+++ b/doc/modules/pam_issue.sgml
@@ -0,0 +1,120 @@
+<!--
+
+Ben Collins <bcollins@debian.org>
+
+-->
+
+<sect1>Add issue file to user prompt
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_issue/
+
+<tag><bf>Author:</bf></tag>
+Ben Collins &lt;bcollins@debian.org&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author
+
+<tag><bf>Management groups provided:</bf></tag>
+Authentication (pam_sm_authenticate)
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module prepends the issue file (<em>/etc/issue</em> by default) when
+prompting for a username.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/issue=issue-file-name/; <tt/noesc/;
+
+<tag><bf>Description:</bf></tag>
+This module allows you to prepend an issue file to the username prompt. It
+also by default parses escape codes in the issue file similar to some
+common getty's (using &bsol;x format).
+<p>
+Recognized escapes:
+<itemize>
+
+<item><tt/d/
+- current date
+
+<item><tt/s/
+- operating system name
+
+<item><tt/l/
+- name of this tty
+
+<item><tt/m/
+- architecture of this system (i686, sparc, powerpc, ...)
+
+<item><tt/n/
+- hostname of this system
+
+<item><tt/o/
+- domainname of this system
+
+<item><tt/r/
+- release number of the operation system (eg. 2.2.12)
+
+<item><tt/t/
+- current time
+
+<item><tt/u/
+- number of users currently logged in
+
+<item><tt/U/
+- same as <tt/u/, except it is suffixed with "user" or "users" (eg. "1
+user" or "10 users"
+
+<item><tt/v/
+- version/build-date of the operating system (eg. "&num;3 Mon Aug 23 14:38:16
+EDT 1999" on Linux).
+
+</itemize>
+
+<p>
+The behavior of this module can be modified with one of the following
+flags:
+
+<p>
+<itemize>
+
+<item><tt/issue/
+- the file to output if not using the default
+
+<item><tt/noesc/
+- turns off escape code parsing
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+login auth pam_issue.so issue=/etc/issue
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_krb4.sgml b/doc/modules/pam_krb4.sgml
new file mode 100644
index 00000000..16ce8183
--- /dev/null
+++ b/doc/modules/pam_krb4.sgml
@@ -0,0 +1,126 @@
+<!--
+ $Id$
+
+ This file was written by Derrick J. Brashear <shadow@DEMENTIA.ORG>
+-->
+
+<sect1>The Kerberos 4 module.
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_krb4/
+
+<tag><bf>Author:</bf></tag>
+Derrick J. Brashear &lt;shadow@dementia.org&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author.
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication; password; session
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+uses API
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+libraries - <tt/libkrb/, <tt/libdes/, <tt/libcom_err/, <tt/libkadm/;
+and a set of Kerberos include files.
+
+<tag><bf>Network aware:</bf></tag>
+Gets Kerberos ticket granting ticket via a Kerberos key distribution
+center reached via the network.
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module provides an interface for doing Kerberos verification of a
+user's password, getting the user a Kerberos ticket granting ticket
+for use with the Kerberos ticket granting service, destroying the
+user's tickets at logout time, and changing a Kerberos password.
+
+<sect2> Session component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+This component of the module currently sets the user's <tt/KRBTKFILE/
+environment variable (although there is currently no way to export
+this), as well as deleting the user's ticket file upon logout (until
+<tt/PAM_CRED_DELETE/ is supported by <em/login/).
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+This part of the module won't be terribly useful until we can change
+the environment from within a <tt/Linux-PAM/ module.
+
+</descrip>
+
+<sect2> Password component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/use_first_pass/; <tt/try_first_pass/
+
+<tag><bf>Description:</bf></tag>
+
+This component of the module changes a user's Kerberos password
+by first getting and using the user's old password to get
+a session key for the password changing service, then sending
+a new password to that service.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+This should only be used with a real Kerberos v4 <tt/kadmind/. It
+cannot be used with an AFS kaserver unless special provisions are
+made. Contact the module author for more information.
+
+</descrip>
+
+<sect2> Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/use_first_pass/; <tt/try_first_pass/
+
+<tag><bf>Description:</bf></tag>
+
+This component of the module verifies a user's Kerberos password
+by requesting a ticket granting ticket from the Kerberos server
+and optionally using it to attempt to retrieve the local computer's
+host key and verifying using the key file on the local machine if
+one exists.
+
+It also writes out a ticket file for the user to use later, and
+deletes the ticket file upon logout (not until <tt/PAM_CRED_DELETE/
+is called from <em/login/).
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+This module can be used with a real Kerberos server using MIT
+v4 Kerberos keys. The module or the system Kerberos libraries
+may be modified to support AFS style Kerberos keys. Currently
+this is not supported to avoid cryptography constraints.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_lastlog.sgml b/doc/modules/pam_lastlog.sgml
new file mode 100644
index 00000000..2ade5baa
--- /dev/null
+++ b/doc/modules/pam_lastlog.sgml
@@ -0,0 +1,119 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+-->
+
+<sect1>The last login module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_lastlog/
+
+<tag><bf>Author:</bf></tag>
+Andrew G. Morgan &lt;morgan@parc.power.net&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author
+
+<tag><bf>Management groups provided:</bf></tag>
+auth
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+uses information contained in the <tt>/var/log/lastlog</tt> file.
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This session module maintains the <tt>/var/log/lastlog</tt> file. Adding
+an open entry when called via the <tt>pam_open_seesion()</tt> function
+and completing it when <tt>pam_close_session()</tt> is called. This
+module can also display a line of information about the last login of
+the user. If an application already performs these tasks, it is not
+necessary to use this module.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/; <tt/nodate/; <tt/noterm/; <tt/nohost/; <tt/silent/;
+<tt/never/
+
+<tag><bf>Description:</bf></tag>
+
+<p>
+This module can be used to provide a ``Last login on ...''
+message. when the user logs into the system from what ever application
+uses the PAM libraries. In addition, the module maintains the
+<tt>/var/log/lastlog</tt> file.
+
+<p>
+The behavior of this module can be modified with one of the following
+flags:
+
+<p>
+<itemize>
+<item><tt/debug/
+- write more information to <tt/syslog(3)/.
+
+<item><tt/nodate/
+- neglect to give the date of the last login when displaying
+information about the last login on the system.
+
+<item><tt/noterm/
+- neglect to diplay the terminal name on which the last login was
+attempt.
+
+<item><tt/nohost/
+- neglect to indicate from which host the last login was attempted.
+
+<item><tt/silent/
+- neglect to inform the user about any previous login: just update
+the <tt>/var/log/lastlog</tt> file.
+
+<item><tt/never/
+- if the <tt>/var/log/lastlog</tt> file does not contain any old entries
+for the user, indicate that the user has never previously logged in
+with a ``welcome..." message.
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+This module can be used to indicate that the user has new mail when
+they <em/login/ to the system. Here is a sample entry for your
+<tt>/etc/pam.conf</tt> file:
+<tscreen>
+<verb>
+#
+# do we have any mail?
+#
+login session optional pam_lastlog.so
+</verb>
+</tscreen>
+
+<p>
+Note, some applications may perform this function themselves. In such
+cases, this module is not necessary.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_limits.sgml b/doc/modules/pam_limits.sgml
new file mode 100644
index 00000000..f7a2245e
--- /dev/null
+++ b/doc/modules/pam_limits.sgml
@@ -0,0 +1,197 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+ from information compiled by Cristian Gafton (author of module)
+-->
+
+<sect1>The resource limits module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_limits/
+
+<tag><bf>Authors:</bf></tag>
+Cristian Gafton &lt;gafton@redhat.com&gt; <newline>
+Thanks are also due to Elliot Lee &lt;sopwith@redhat.com&gt;
+for his comments on improving this module.
+
+<tag><bf>Maintainer:</bf></tag>
+Cristian Gafton - 1996/11/20
+
+<tag><bf>Management groups provided:</bf></tag>
+session
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+requires an <tt>/etc/security/limits.conf</tt> file and kernel support
+for resource limits. Also uses the library, <tt/libpwdb/.
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module, through the <bf/Linux-PAM/ <em/open/-session hook, sets
+limits on the system resources that can be obtained in a
+user-session. Its actions are dictated more explicitly through the
+configuration file discussed below.
+
+<sect2>Session component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/; <tt>conf=/path/to/file.conf</tt>
+
+<tag><bf>Description:</bf></tag>
+
+Through the contents of the configuration file,
+<tt>/etc/security/limits.conf</tt>, resource limits are placed on
+users' sessions. Users of <tt/uid=0/ are not affected by this
+restriction.
+
+<p>
+The behavior of this module can be modified with the following
+arguments:
+<itemize>
+
+<item><tt/debug/ -
+verbose logging to <tt/syslog(3)/.
+
+<item><tt>conf=/path/to/file.conf</tt> -
+indicate an alternative <em/limits/ configuration file to the default.
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+In order to use this module the system administrator must first create
+a <em/root-only-readable/ file (default is
+<tt>/etc/security/limits.conf</tt>). This file describes the resource
+limits the superuser wishes to impose on users and groups. No limits
+are imposed on <tt/uid=0/ accounts.
+
+<p>
+Each line of the configuration file describes a limit for a user in
+the form:
+<tscreen>
+<verb>
+<domain> <type> <item> <value>
+</verb>
+</tscreen>
+
+<p>
+The fields listed above should be filled as follows...<newline>
+<tt>&lt;domain&gt;</tt> can be:
+<itemize>
+<item> a username
+<item> a groupname, with <tt>@group</tt> syntax
+<item> the wild-card <tt/*/, for default entry
+</itemize>
+
+<p>
+<tt>&lt;type&gt;</tt> can have the two values:
+<itemize>
+
+<item> <tt/hard/ for enforcing <em/hard/ resource limits. These limits
+are set by the superuser and enforced by the Linux Kernel. The user
+cannot raise his requirement of system resources above such values.
+
+<item> <tt/soft/ for enforcing <em/soft/ resource limits. These limits
+are ones that the user can move up or down within the permitted range
+by any pre-exisiting <em/hard/ limits. The values specified with this
+token can be thought of as <em/default/ values, for normal system
+usage.
+
+</itemize>
+
+<p>
+<tt>&lt;item&gt;</tt> can be one of the following:
+<itemize>
+<item><tt/core/ - limits the core file size (KB)
+<item><tt/data/ - max data size (KB)
+<item><tt/fsize/ - maximum filesize (KB)
+<item><tt/memlock/ - max locked-in-memory address space (KB)
+<item><tt/nofile/ - max number of open files
+<item><tt/rss/ - max resident set size (KB)
+<item><tt/stack/ - max stack size (KB)
+<item><tt/cpu/ - max CPU time (MIN)
+<item><tt/nproc/ - max number of processes
+<item><tt/as/ - address space limit
+<item><tt/maxlogins/ - max number of logins for this user.
+<item><tt/priority/ - the priority to run user process with
+</itemize>
+
+<p>
+To completely disable limits for a user (or a group), a single dash
+(-) will do (Example: ``<tt/bin -/'', ``<tt/@admin -/''). Please
+remember that individual limits have priority over group limits, so if
+you impose no limits for <tt/admin/ group, but one of the members in this
+group have a limits line, the user will have its limits set according
+to this line.
+
+<p>
+Also, please note that all limit settings are set <em/per login/.
+They are not global, nor are they permanent; existing only for the
+duration of the session.
+
+<p>
+In the <em/limits/ configuration file, the ``<tt/#/'' character
+introduces a comment - after which the rest of the line is ignored.
+
+<p>
+The <tt/pam_limits/ module does its best to report configuration
+problems found in its configuration file via <tt/syslog(3)/.
+
+<p>
+The following is an example configuration file:
+<tscreen>
+<verb>
+# EXAMPLE /etc/security/limits.conf file:
+# =======================================
+# <domain> <type> <item> <value>
+* soft core 0
+* hard rss 10000
+@student hard nproc 20
+@faculty soft nproc 20
+@faculty hard nproc 50
+ftp hard nproc 0
+@student - maxlogins 4
+</verb>
+</tscreen>
+Note, the use of <tt/soft/ and <tt/hard/ limits for the same resource
+(see <tt/@faculty/) -- this establishes the <em/default/ and permitted
+<em/extreme/ level of resources that the user can can obtain in a
+given service-session.
+
+<p>
+For the services that need resources limits (login for example) put a
+the following line in <tt>/etc/pam.conf</tt> as the last line for that
+service (usually after the pam_unix session line:
+<tscreen>
+<verb>
+#
+# Resource limits imposed on login sessions via pam_limits
+#
+login session required pam_limits.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_listfile.sgml b/doc/modules/pam_listfile.sgml
new file mode 100644
index 00000000..98589a3b
--- /dev/null
+++ b/doc/modules/pam_listfile.sgml
@@ -0,0 +1,138 @@
+<!--
+ $Id$
+
+ This file was written by Michael K. Johnson <johnsonm@redhat.com>
+-->
+
+<sect1>The list-file module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_listfile/
+
+<tag><bf>Author:</bf></tag>
+Elliot Lee <tt>&lt;sopwith@cuc.edu&gt;</tt>
+
+<tag><bf>Maintainer:</bf></tag>
+Red Hat Software:<newline>
+Michael K. Johnson &lt;johnsonm@redhat.com&gt; 1996/11/18<newline>
+(if unavailable, contact Elliot Lee &lt;sopwith@cuc.edu&gt;).
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+clean
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+The list-file module provides a way to deny or allow services based on
+an arbitrary file.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tt>onerr=succeed|fail</tt>;
+<tt>sense=allow|deny</tt>;
+<tt>file=</tt><it>filename</it>;
+<tt>item=user|tty|rhost|ruser|group|shell</tt>
+<tt>apply=user|@group</tt>
+
+<tag><bf>Description:</bf></tag>
+
+The module gets the item of the type specified -- <tt>user</tt> specifies
+the username, <tt>PAM_USER</tt>; tty specifies the name of the terminal
+over which the request has been made, <tt>PAM_TTY</tt>; rhost specifies
+the name of the remote host (if any) from which the request was made,
+<tt>PAM_RHOST</tt>; and ruser specifies the name of the remote user
+(if available) who made the request, <tt>PAM_RUSER</tt> -- and looks for
+an instance of that item in the file <it>filename</it>. <it>filename</it>
+contains one line per item listed. If the item is found, then if
+<tt>sense=allow</tt>, <tt>PAM_SUCCESS</tt> is returned, causing the
+authorization request to succeed; else if <tt>sense=deny</tt>,
+<tt>PAM_AUTH_ERR</tt> is returned, causing the authorization
+request to fail.
+
+<p>
+If an error is encountered (for instance, if <it>filename</it>
+does not exist, or a poorly-constructed argument is encountered),
+then if <tt>onerr=succeed</tt>, <tt>PAM_SUCCESS</tt> is returned,
+otherwise if <tt>onerr=fail</tt>, <tt>PAM_AUTH_ERR</tt> or
+<tt>PAM_SERVICE_ERR</tt> (as appropriate) will be returned.
+
+<p>
+An additional argument, <tt>apply=</tt>, can be used to restrict the
+application of the above to a specific user
+(<tt>apply=</tt><em>username</em>) or a given group
+(<tt>apply=@</tt><em>groupname</em>). This added restriction is only
+meaningful when used with the <tt/tty/, <tt/rhost/ and <tt/shell/
+<em/items/.
+
+<p>
+Besides this last one, all arguments should be specified; do not count
+on any default behavior, as it is subject to change.
+
+<p>
+No credentials are awarded by this module.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+Classic ``ftpusers'' authentication can be implemented with this entry
+in <tt>/etc/pam.conf</tt>:
+<tscreen>
+<verb>
+#
+# deny ftp-access to users listed in the /etc/ftpusers file
+#
+ftp auth required pam_listfile.so \
+ onerr=succeed item=user sense=deny file=/etc/ftpusers
+</verb>
+</tscreen>
+Note, users listed in <tt>/etc/ftpusers</tt> file are
+(counterintuitively) <bf/not/ allowed access to the ftp service.
+
+<p>
+To allow login access only for certain users, you can use an
+pam.conf entry like this:
+<tscreen>
+<verb>
+#
+# permit login to users listed in /etc/loginusers
+#
+login auth required pam_listfile.so \
+ onerr=fail item=user sense=allow file=/etc/loginusers
+</verb>
+</tscreen>
+
+<p>
+For this example to work, all users who are allowed to use the login
+service should be listed in the file <tt>/etc/loginusers</tt>. Unless
+you are explicitly trying to lock out root, make sure that when you do
+this, you leave a way for root to log in, either by listing root in
+<tt>/etc/loginusers</tt>, or by listing a user who is able to <em/su/
+to the root account.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_mail.sgml b/doc/modules/pam_mail.sgml
new file mode 100644
index 00000000..064b9ffa
--- /dev/null
+++ b/doc/modules/pam_mail.sgml
@@ -0,0 +1,137 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@linux.kernel.org>
+-->
+
+<sect1>The mail module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_mail/
+
+<tag><bf>Author:</bf></tag>
+Andrew G. Morgan &lt;morgan@linux.kernel.org&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author
+
+<tag><bf>Management groups provided:</bf></tag>
+Authentication (credential)
+Session (open)
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+Default mail directory <tt>/var/spool/mail/</tt>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module looks at the user's mail directory and indicates
+whether the user has any mail in it.
+
+<sect2>Session component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/; <tt/dir=/<em/direcory-name/; <tt/nopen/; <tt/close/;
+<tt/noenv/; <tt/empty/; <tt/hash=/<em/hashcount/; <tt/standard/;
+<tt/quiet/;
+
+<tag><bf>Description:</bf></tag>
+
+This module provides the ``you have new mail'' service to the user. It
+can be plugged into any application that has credential hooks. It gives a
+single message indicating the <em/newness/ of any mail it finds in the
+user's mail folder. This module also sets the <bf/Linux-PAM/
+environment variable, <tt/MAIL/, to the user's mail directory.
+
+<p>
+The behavior of this module can be modified with one of the following
+flags:
+
+<p>
+<itemize>
+<item><tt/debug/
+- write more information to <tt/syslog(3)/.
+
+<item><tt/dir=/<em/pathname/
+- look for the users' mail in an alternative directory given by
+<em/pathname/. The default location for mail is
+<tt>/var/spool/mail</tt>. Note, if the supplied <em/pathname/ is
+prefixed by a `<tt/&tilde;/', the directory is interpreted as
+indicating a file in the user's home directory.
+
+<item><tt/nopen/
+- instruct the module to <em/not/ print any mail information when the
+user's credentials are acquired. This flag is useful to get the <tt/MAIL/
+environment variable set, but to not display any information about it.
+
+<item><tt/close/
+- instruct the module to indicate if the user has any mail at the as
+the user's credentials are revoked.
+
+<item><tt/noenv/
+- do not set the <tt/MAIL/ environment variable.
+
+<item><tt/empty/
+- indicate that the user's mail directory is empty if this is found to
+be the case.
+
+<item><tt/hash=/<em/hashcount/
+- mail directory hash depth. For example, a <em/hashcount/ of 2 would
+make the mailfile be <tt>/var/spool/mail/u/s/user</tt>.
+
+<item><tt/standard/
+- old style "You have..." format which doesn't show the mail spool being used.
+ this also implies "empty"
+
+<item><tt/quiet/
+- only report when there is new mail.
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+This module can be used to indicate that the user has new mail when
+they <em/login/ to the system. Here is a sample entry for your
+<tt>/etc/pam.conf</tt> file:
+<tscreen>
+<verb>
+#
+# do we have any mail?
+#
+login session optional pam_mail.so
+</verb>
+</tscreen>
+
+<p>
+Note, some applications may perform this function themselves. In such
+cases, this module is not necessary.
+
+</descrip>
+
+<sect2>Authentication compent
+
+<p>
+Then authentication companent works the same as the session component,
+expect that everything is done during the pam_setcred() phase.
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_mkhomedir.sgml b/doc/modules/pam_mkhomedir.sgml
new file mode 100644
index 00000000..075e16f9
--- /dev/null
+++ b/doc/modules/pam_mkhomedir.sgml
@@ -0,0 +1,83 @@
+<!--
+
+Ben Collins <bcollins@debian.org>
+
+-->
+
+<sect1>Create home directories on initial login
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_mkhomedir/
+
+<tag><bf>Author:</bf></tag>
+Jason Gunthorpe &lt;jgg@ualberta.ca&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Ben Collins &lt;bcollins@debian.org&gt;
+
+<tag><bf>Management groups provided:</bf></tag>
+Session
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+Creates home directories on the fly for authenticated users.
+
+<sect2>Session component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/; <tt/skel=skeleton-dir/; <tt/umask=octal-umask/;
+
+<tag><bf>Description:</bf></tag>
+This module is useful for distributed systems where the user account is
+managed in a central database (such as NIS, NIS+, or LDAP) and accessed
+through miltiple systems. It frees the administrator from having to create
+a default home directory on each of the systems by creating it upon the
+first succesfully authenticated login of that user. The skeleton directory
+(usually /etc/skel/) is used to copy default files and also set's a umask
+for the creation.
+
+<p>
+The behavior of this module can be modified with one of the following
+flags:
+
+<p>
+<itemize>
+
+<item><tt/skel/
+- The skeleton directory for default files to copy to the new home directory.
+
+<item><tt/umask/
+- An octal for of the same format as you would pass to the shells umask command.
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+session required pam_mkhomedir.so skel=/etc/skel/ umask=0022
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_motd.sgml b/doc/modules/pam_motd.sgml
new file mode 100644
index 00000000..1f8fc393
--- /dev/null
+++ b/doc/modules/pam_motd.sgml
@@ -0,0 +1,77 @@
+<!--
+
+Ben Collins <bcollins@debian.org>
+
+-->
+
+<sect1>Output the motd file
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_motd/
+
+<tag><bf>Author:</bf></tag>
+Ben Collins &lt;bcollins@debian.org&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author
+
+<tag><bf>Management groups provided:</bf></tag>
+Session (open)
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module outputs the motd file (<em>/etc/motd</em> by default) upon succesful
+login.
+
+<sect2>Session component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/; <tt/motd=motd-file-name/;
+
+<tag><bf>Description:</bf></tag>
+This module allows you to have arbitrary motd's (message of the day)
+output after a succesful login. By default this file is <em>/etc/motd</em>,
+but is configurable to any file.
+
+<p>
+The behavior of this module can be modified with one of the following
+flags:
+
+<p>
+<itemize>
+
+<item><tt/motd/
+- the file to output if not using the default.
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+login session pam_motd.so motd=/etc/motd
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_nologin.sgml b/doc/modules/pam_nologin.sgml
new file mode 100644
index 00000000..90564d89
--- /dev/null
+++ b/doc/modules/pam_nologin.sgml
@@ -0,0 +1,75 @@
+<!--
+ $Id$
+
+ This file was written by Michael K. Johnson <johnsonm@redhat.com>
+-->
+
+<sect1>The no-login module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_nologin/
+
+<tag><bf>Author:</bf></tag>
+Written by Michael K. Johnson &lt;johnsonm@redhat.com&gt;<newline>
+(based on code taken from a module written by Andrew G. Morgan
+&lt;morgan@parc.power.net&gt;).
+
+<tag><bf>Maintainer:</bf></tag>
+Michael K. Johnson &lt;johnsonm@redhat.com&gt;
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+1 warning about dropping const
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+Provides standard Unix <em/nologin/ authentication.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+Provides standard Unix <em/nologin/ authentication. If the file
+<tt>/etc/nologin</tt> exists, only root is allowed to log in; other
+users are turned away with an error message. All users (root or
+otherwise) are shown the contents of <tt>/etc/nologin</tt>.
+
+<p>
+If the file <tt>/etc/nologin</tt> does not exist, this module succeeds
+silently.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+In order to make this module effective, all login methods should
+be secured by it. It should be used as a <tt>required</tt>
+method listed before any <tt>sufficient</tt> methods in order to
+get standard Unix nologin semantics.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_permit.sgml b/doc/modules/pam_permit.sgml
new file mode 100644
index 00000000..8b201b7c
--- /dev/null
+++ b/doc/modules/pam_permit.sgml
@@ -0,0 +1,83 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+-->
+
+<sect1>The promiscuous module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+pam_permit
+
+<tag><bf>Author:</bf></tag>
+Andrew G. Morgan, &lt;morgan@parc.power.net&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Linux-PAM maintainer.
+
+<tag><bf>Management groups provided:</bf></tag>
+account; authentication; password; session
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+VERY LOW. Use with extreme caution.
+
+<tag><bf>Clean code base:</bf></tag>
+Clean.
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module is very dangerous. It should be used with extreme
+caution. Its action is always to permit access. It does nothing else.
+
+<sect2>Account+Authentication+Password+Session components
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+No matter what management group, the action of this module is to
+simply return <tt/PAM_SUCCESS/ -- operation successful.
+
+<p>
+In the case of authentication, the user's name will be acquired. Many
+applications become confused if this name is unknown.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+It is seldom a good idea to use this module. However, it does have
+some legitimate uses. For example, if the system-administrator wishes
+to turn off the account management on a workstation, and at the same
+time continue to allow logins, then she might use the following
+configuration file entry for login:
+<tscreen>
+<verb>
+#
+# add this line to your other login entries to disable account
+# management, but continue to permit users to log in...
+#
+login account required pam_permit.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_pwdb.sgml b/doc/modules/pam_pwdb.sgml
new file mode 100644
index 00000000..022cfe57
--- /dev/null
+++ b/doc/modules/pam_pwdb.sgml
@@ -0,0 +1,252 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@linux.kernel.org>
+-->
+
+<sect1>The Password-Database module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+pam_pwdb
+
+<tag><bf>Author:</bf></tag>
+Cristian Gafton &lt;gafton@redhat.com&gt; <newline>
+and Andrew G. Morgan &lt;morgan@linux.kernel.org&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Authors.
+
+<tag><bf>Management groups provided:</bf></tag>
+account; authentication; password; session
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+Requires properly configured <tt/libpwdb/
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module is a pluggable replacement for the <tt/pam_unix_../
+modules. It uses the generic interface of the <em/Password Database/
+library
+<tt><htmlurl
+url="http://linux.kernel.org/morgan/libpwdb/index.html"
+name="http://linux.kernel.org/morgan/libpwdb/index.html"></tt>.
+
+<sect2>Account component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/
+
+<tag><bf>Description:</bf></tag>
+
+The <tt/debug/ argument makes the accounting functions of this module
+<tt/syslog(3)/ more information on its actions. (Remaining arguments
+supported by the other functions of this module are silently ignored,
+but others are logged as errors through <tt/syslog(3)/).
+
+Based on the following <tt/pwdb_element/s:
+<tt/expire/;
+<tt/last_change/;
+<tt/max_change/;
+<tt/defer_change/;
+<tt/warn_change/,
+this module performs the task of establishing the status of the user's
+account and password. In the case of the latter, it may offer advice
+to the user on changing their password or, through the
+<tt/PAM_AUTHTOKEN_REQD/ return, delay giving service to the user until
+they have established a new password. The entries listed above are
+documented in the <em/Password Database Library Guide/ (see pointer
+above). Should the user's record not contain one or more of these
+entries, the corresponding <em/shadow/ check is not performed.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+In its accounting mode, this module can be inserted as follows:
+<tscreen>
+<verb>
+#
+# Ensure users account and password are still active
+#
+login account required pam_pwdb.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/;
+<tt/use_first_pass/;
+<tt/try_first_pass/;
+<tt/nullok/;
+<tt/nodelay/;
+<tt/likeauth/
+
+<tag><bf>Description:</bf></tag>
+
+The <tt/debug/ argument makes the authentication functions of this
+module <tt/syslog(3)/ more information on its actions.
+
+<p>
+The default action of this module is to not permit the user access to
+a service if their <em/official/ password is blank. The <tt/nullok/
+argument overrides this default.
+
+<p>
+When given the argument <tt/try_first_pass/, before prompting the user
+for their password, the module first tries the previous stacked
+<tt/auth/-module's password in case that satisfies this module as
+well. The argument <tt/use_first_pass/ forces the module to use such a
+recalled password and will never prompt the user - if no password is
+available or the password is not appropriate, the user will be denied
+access.
+
+<p>
+The argument, <tt>nodelay</tt>, can be used to discourage the
+authentication component from requesting a delay should the
+authentication as a whole fail. The default action is for the module
+to request a delay-on-failure of the order of one second.
+
+<p>
+Remaining arguments, supported by the other functions of this module,
+are silently ignored. Other arguments are logged as errors through
+<tt/syslog(3)/.
+
+<p>
+A helper binary, <tt>pwdb_chkpwd</tt>, is provided to check the user's
+password when it is stored in a read protected database. This binary
+is very simple and will only check the password of the user invoking
+it. It is called transparently on behalf of the user by the
+authenticating component of this module. In this way it is possible
+for applications like <em>xlock</em> to work without being setuid-root.
+
+<p>
+The <tt>likeauth</tt> argument makes the module return the same value
+when called as a credential setting module and an authentication
+module. This will help libpam take a sane path through the auth
+component of your configuration file.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+The correct functionality of this module is dictated by having an
+appropriate <tt>/etc/pwdb.conf</tt> file, the user
+databases specified there dictate the source of the authenticated
+user's record.
+
+</descrip>
+
+<sect2>Password component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/; <tt/nullok/; <tt/not_set_pass/; <tt/use_authtok/;
+<tt/try_first_pass/; <tt/use_first_pass/; <tt/md5/; <tt/bigcrypt/;
+<tt/shadow/; <tt/radius/; <tt/unix/
+
+<tag><bf>Description:</bf></tag>
+
+This part of the <tt/pam_pwdb/ module performs the task of updating
+the user's password. Thanks to the flexibility of <tt/libpwdb/ this
+module is able to move the user's password from one database to
+another, perhaps securing the user's database entry in a dynamic
+manner (<em/this is very ALPHA code at the moment!/) - this is the
+purpose of the <tt/shadow/, <tt/radius/ and <tt/unix/ arguments.
+
+<p>
+In the case of conventional unix databases (which store the password
+encrypted) the <tt/md5/ argument is used to do the encryption with the
+MD5 function as opposed to the <em/conventional/ <tt/crypt(3)/ call.
+As an alternative to this, the <tt/bigcrypt/ argument can be used to
+encrypt more than the first 8 characters of a password with DEC's
+(Digital Equipment Cooperation) `C2' extension to the standard UNIX
+<tt/crypt()/ algorithm.
+
+<p>
+The <tt/nullok/ module is used to permit the changing of a password
+<em/from/ an empty one. Without this argument, empty passwords are
+treated as account-locking ones.
+
+<p>
+The argument <tt/use_first_pass/ is used to lock the choice of old and
+new passwords to that dictated by the previously stacked <tt/password/
+module. The <tt/try_first_pass/ argument is used to avoid the user
+having to re-enter an old password when <tt/pam_pwdb/ follows a module
+that possibly shared the user's old password - if this old password is
+not correct the user will be prompted for the correct one. The
+argument <tt/use_authtok/ is used to <em/force/ this module to set the
+new password to the one provided by the previously stacked
+<tt/password/ module (this is used in an example of the stacking of
+the <em/Cracklib/ module documented above).
+
+<p>
+The <tt/not_set_pass/ argument is used to inform the module that it is
+not to pay attention to/make available the old or new passwords from/to
+other (stacked) password modules.
+
+<p>
+The <tt/debug/ argument makes the password functions of this module
+<tt/syslog(3)/ more information on its actions. Other arguments may be
+logged as erroneous to <tt/syslog(3)/.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+An example of the stacking of this module with respect to the
+pluggable password checking module, <tt/pam_cracklib/, is given in
+that modules section above.
+</descrip>
+
+<sect2>Session component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+No arguments are recognized by this module component. Its action is
+simply to log the username and the service-type to
+<tt/syslog(3)/. Messages are logged at the beginning and end of the
+user's session.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+The use of the session modules is straightforward:
+<tscreen>
+<verb>
+#
+# pwdb - unix like session opening and closing
+#
+login session required pam_pwdb.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_radius.sgml b/doc/modules/pam_radius.sgml
new file mode 100644
index 00000000..fb442ee3
--- /dev/null
+++ b/doc/modules/pam_radius.sgml
@@ -0,0 +1,117 @@
+<!--
+ $Id$
+
+ This file was written by Cristian Gafton <gafton@redhat.com>
+-->
+
+<sect1>The RADIUS session module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_radius/
+
+<tag><bf>Author:</bf></tag>
+Cristian Gafton &lt;gafton@redhat.com&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author.
+
+<tag><bf>Management groups provided:</bf></tag>
+session
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+This module does not deal with passwords
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+gcc reports 1 warning when compiling <tt>/usr/include/rpc/clnt.h</tt>.
+Hey, is not my fault !
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+yes; this is a network module (independent of application).
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module is intended to provide the session service for users
+autheticated with a RADIUS server. At the present stage, the only
+option supported is the use of the RADIUS server as an accounting
+server.
+
+<sect2>Session component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tt/debug/ - verbose logging to <tt/syslog(3)/.
+
+<tag><bf>Description:</bf></tag>
+
+This module is intended to provide the session service for users
+autheticated with a RADIUS server. At the present stage, the only
+option supported is the use of the RADIUS server as an <em/accounting/
+server.
+
+<p>
+(There are few things which needs to be cleared out first in
+the PAM project until one will be able to use this module and expect
+it to magically start pppd in response to a RADIUS server command to
+use PPP for this user, or to initiate a telnet connection to another
+host, or to hang and call back the user using parameters provided in
+the RADIUS server response. Most of these things are better suited for
+the radius login application. I hope to make available Real Soon (tm)
+patches for the login apps to make it work this way.)
+
+<p>
+When opening a session, this module sends an ``Accounting-Start''
+message to the RADIUS server, which will log/update/whatever a
+database for this user. On close, an ``Accounting-Stop'' message is
+sent to the RADIUS server.
+
+<p>
+This module has no other prerequisites for making it work. One can
+install a RADIUS server just for fun and use it as a centralized
+accounting server and forget about wtmp/last/sac etc. .
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+For the services that need this module (<em/login/ for example) put
+the following line in <tt>/etc/pam.conf</tt> as the last line for that
+service (usually after the pam_unix session line):
+<tscreen>
+<verb>
+login session required pam_radius.so
+</verb>
+</tscreen>
+Replace <tt/login/ for each service you are using this module.
+
+<p>
+This module make extensive use of the API provided in libpwdb
+0.54preB or later. By default, it will read the radius server
+configuration (hostname and secret) from <tt>/etc/raddb/server</tt>.
+This is a default compiled into libpwdb, and curently there is no way to
+modify this default without recompiling libpwdb. I am working on
+extending the radius support from libpwdb to provide a possibility
+to make this runtime-configurable.
+
+Also please note that libpwdb will require also the RADIUS
+dictionary to be present (<tt>/etc/raddb/dictionary</tt>).
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
+
diff --git a/doc/modules/pam_rhosts.sgml b/doc/modules/pam_rhosts.sgml
new file mode 100644
index 00000000..00e55a9d
--- /dev/null
+++ b/doc/modules/pam_rhosts.sgml
@@ -0,0 +1,164 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+-->
+
+<sect1>The rhosts module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_rhosts_auth/
+
+<tag><bf>Author:</bf></tag>
+Al Longyear &lt;longyear@netcom.com&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+Clean.
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+Standard <tt/inet_addr()/, <tt/gethostbyname()/ function calls.
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module performs the standard network authentication for services,
+as used by traditional implementations of <em/rlogin/ and <em/rsh/
+etc.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/no_hosts_equiv/; <tt/no_rhosts/; <tt/debug/; <tt/no_warn/;
+<tt/privategroup/; <tt/promiscuous/; <tt/suppress/
+
+<tag><bf>Description:</bf></tag>
+
+The authentication mechanism of this module is based on the contents
+of two files; <tt>/etc/hosts.equiv</tt> (or <tt/_PATH_HEQUIV/ in
+<tt>#include &lt;netdb.h&gt;</tt>) and <tt>~/.rhosts</tt>. Firstly,
+hosts listed in the former file are treated as equivalent to the
+localhost. Secondly, entries in the user's own copy of the latter file
+is used to map "<tt/remote-host remote-user/" pairs to that user's
+account on the current host. Access is granted to the user if their
+host is present in <tt>/etc/hosts.equiv</tt> and their remote account
+is identical to their local one, or if their remote account has an
+entry in their personal configuration file.
+
+<p>
+Some restrictions are applied to the attributes of the user's personal
+configuration file: it must be a regular file (as defined by
+<tt/S_ISREG(x)/ of POSIX.1); it must be owned by the <em/superuser/ or
+the user; it must not be writable by any user besides its owner.
+
+<p>
+The module authenticates a remote user (internally specified by the
+item <tt/PAM_RUSER/) connecting from the remote host (internally
+specified by the item <tt/PAM_RHOST/). Accordingly, for applications
+to be compatible this authentication module they must set these items
+prior to calling <tt/pam_authenticate()/. The module is not capable
+of independently probing the network connection for such information.
+
+<p>
+In the case of <tt/root/-access, the <tt>/etc/host.equiv</tt> file is
+<em/ignored/ unless the <tt>hosts_equiv_rootok</tt> option
+should be used. Instead, the superuser must have a correctly configured
+personal configuration file.
+
+<p>
+The behavior of the module is modified by flags:
+<itemize>
+<item>
+<tt/debug/ -
+log more information to <tt/syslog(3)/. (XXX - actually, this module
+does not do any logging currently, please volunteer to fix this!)
+
+<item>
+<tt/no_warn/ -
+do not give verbal warnings to the user about failures etc. (XXX -
+this module currently does not issue any warnings, please volunteer to
+fix this!)
+
+<item>
+<tt/no_hosts_equiv/ -
+ignore the contents of the <tt>/etc/hosts.equiv</tt> file.
+
+<item>
+<tt/hosts_equiv_rootok/ -
+allow the use of <tt>/etc/hosts.equiv</tt> for superuser. Without this
+option <tt>/etc/hosts.equiv</tt> is not consulted for the superuser account.
+This option has no effect if the <tt>no_hosts_equiv</tt> option is used.
+
+<item>
+<tt/no_rhosts/ -
+ignore the contents of all user's personal configuration file
+<tt>~/.rhosts</tt>.
+
+<item>
+<tt/privategroup/ -
+normally, the <tt>~/.rhosts</tt> file must not be writable by anyone
+other than its owner. This option overlooks group write access in the
+case that the group owner of this file has the same name as the
+user being authenticated. To lessen the security problems associated
+with this option, the module also checks that the user is the only
+member of their private group.
+
+<item>
+<tt/promiscuous/ -
+A host entry of `+' will lead to all hosts being granted
+access. Without this option, '+' entries will be ignored. Note, that
+the <tt/debug/ option will syslog a warning in this latter case.
+
+<item>
+<tt/suppress/ -
+This will prevent the module from <tt/syslog(3)/ing a warning message
+when this authentication fails. This option is mostly for keeping
+logs free of meaningless errors, in particular when the module is used
+with the <tt/sufficient/ control flag.
+
+</itemize>
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+To allow users to login from trusted remote machines, you should try
+adding the following line to your <tt>/etc/pam.conf</tt> file
+<em/before/ the line that would otherwise prompt the user for a
+password:
+<tscreen>
+<verb>
+#
+# No passwords required for users from hosts listed above.
+#
+login auth sufficient pam_rhosts_auth.so no_rhosts
+</verb>
+</tscreen>
+Note, in this example, the system administrator has turned off all
+<em/personal/ <em/rhosts/ configuration files. Also note, that this module
+can be used to <em/only/ allow remote login from hosts specified in
+the <tt>/etc/host.equiv</tt> file, by replacing <tt/sufficient/ in the
+above example with <tt/required/.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_rootok.sgml b/doc/modules/pam_rootok.sgml
new file mode 100644
index 00000000..e362a2a5
--- /dev/null
+++ b/doc/modules/pam_rootok.sgml
@@ -0,0 +1,85 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+-->
+
+<sect1>The root access module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+pam_rootok
+
+<tag><bf>Author:</bf></tag>
+Andrew G. Morgan &lt;morgan@parc.power.net&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+<bf>Linux-PAM</bf> maintainer
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+Clean.
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module is for use in situations where the superuser wishes
+to gain access to a service without having to enter a password.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/
+
+<tag><bf>Description:</bf></tag>
+
+This module authenticates the user if their <tt/uid/ is <tt/0/.
+Applications that are created <em/setuid/-root generally retain the
+<tt/uid/ of the user but run with the authority of an enhanced
+<em/effective-/<tt/uid/. It is the real <tt/uid/ that is checked.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+In the case of the <tt/su/ application the historical usage is to
+permit the superuser to adopt the identity of a lesser user without
+the use of a password. To obtain this behavior under <tt/Linux-PAM/
+the following pair of lines are needed for the corresponding entry in
+the configuration file:
+<tscreen>
+<verb>
+#
+# su authentication. Root is granted access by default.
+#
+su auth sufficient pam_rootok.so
+su auth required pam_unix_auth.so
+</verb>
+</tscreen>
+
+<p>
+Note. For programs that are run by the superuser (or started when the
+system boots) this module should not be used to authenticate users.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_securetty.sgml b/doc/modules/pam_securetty.sgml
new file mode 100644
index 00000000..ceb1358c
--- /dev/null
+++ b/doc/modules/pam_securetty.sgml
@@ -0,0 +1,72 @@
+<!--
+ $Id$
+
+ This file was written by Michael K. Johnson <johnsonm@redhat.com>
+-->
+
+<sect1>The securetty module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_securetty/
+
+<tag><bf>Author[s]:</bf></tag>
+Elliot Lee &lt;sopwith@cuc.edu&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Red Hat Software:<newline>
+<em/currently/ Michael K. Johnson &lt;johnsonm@redhat.com&gt;<newline>
+(if unavailable, contact Elliot Lee &lt;sopwith@cuc.edu&gt;).
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+<tt>/etc/securetty</tt> file
+
+<tag><bf>Network aware:</bf></tag>
+
+Requires the application to fill in the <tt>PAM_TTY</tt> item
+correctly in order to act meaningfully.
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+Provides standard Unix securetty checking.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+Provides standard Unix securetty checking, which causes authentication
+for root to fail unless <tt>PAM_TTY</tt> is set to a string listed in
+the <tt>/etc/securetty</tt> file. For all other users, it succeeds.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+For canonical usage, should be listed as a <tt>required</tt>
+authentication method before any <tt>sufficient</tt> authentication
+methods.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_time.sgml b/doc/modules/pam_time.sgml
new file mode 100644
index 00000000..4104aad1
--- /dev/null
+++ b/doc/modules/pam_time.sgml
@@ -0,0 +1,166 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+-->
+
+<sect1>Time control
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_time/
+
+<tag><bf>Author:</bf></tag>
+Andrew G. Morgan <tt>&lt;morgan@parc.power.net&gt;</tt>
+
+<tag><bf>Maintainer:</bf></tag>
+Author
+
+<tag><bf>Management groups provided:</bf></tag>
+account
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+Requires a configuration file <tt>/etc/security/time.conf</tt>
+
+<tag><bf>Network aware:</bf></tag>
+Through the <tt/PAM_TTY/ item only
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+Running a well regulated system occasionally involves restricting
+access to certain services in a selective manner. This module offers
+some time control for access to services offered by a system. Its
+actions are determined with a configuration file. This module can be
+configured to deny access to (individual) users based on their name,
+the time of day, the day of week, the service they are applying for
+and their terminal from which they are making their request.
+
+<sect2>Account component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+This module bases its actions on the rules listed in its configuration
+file: <tt>/etc/security/pam.conf</tt>. Each rule has the following
+form,
+<tscreen>
+<em/services/<tt/;/<em/ttys/<tt/;/<em/users/<tt/;/<em/times/
+</tscreen>
+In words, each rule occupies a line, terminated with a newline or the
+beginning of a comment; a `<tt/#/'. It contains four fields separated
+with semicolons, `<tt/;/'. The fields are as follows:
+
+<p>
+<itemize>
+<item><em/services/ -
+a logic list of service names that are affected by this rule.
+
+<item><em/ttys/ -
+a logic list of terminal names indicating those terminals covered by
+the rule.
+
+<item><em/user/ -
+a logic list of usernames to which this rule applies
+
+<p>
+By a logic list we mean a sequence of tokens (associated with the
+appropriate <tt/PAM_/ item), containing no more than one wildcard
+character; `<tt/*/', and optionally prefixed with a negation operator;
+`<tt/!/'. Such a sequence is concatenated with one of two logical
+operators: <tt/&amp;/ (logical AND) and <tt/|/ (logical OR). Two
+examples are: <tt>!morgan&amp;!root</tt>, indicating that this rule
+does not apply to the user <tt>morgan</tt> nor to <tt>root</tt>; and
+<tt>tty*&amp;!ttyp*</tt>, which indicates that the rule applies only
+to console terminals but not pseudoterminals.
+
+<item><em/times/ - a logic list of times at which this rule
+applies. The format of each element is a day/time-range. The days are
+specified by a sequence of two character entries. For example,
+<tt/MoTuSa/, indicates Monday Tuesday and Saturday. Note that
+repeated days are <em/unset/; <tt/MoTuMo/ indicates Tuesday, and
+<tt/MoWk/ means all weekdays bar Monday. The two character
+combinations accepted are,
+<tscreen>
+<verb>
+Mo Tu We Th Fr Sa Su Wk Wd Al
+</verb>
+</tscreen>
+The last two of these being <em/weekend/ days and <em/all 7 days/ of
+the week respectively.
+
+<p>
+The time range part is a pair of 24-hour times, <em/HHMM/, separated
+by a hyphen -- indicating the start and finish time for the rule. If
+the finsish time is smaller than the start time, it is assumed to
+apply on the following day. For an example, <tt/Mo1800-0300/ indicates
+that the permitted times are Monday night from 6pm to 3am the
+following morning.
+
+</itemize>
+
+<p>
+Note, that the given time restriction is only applied when the first
+three fields are satisfied by a user's application for service.
+
+<p>
+For convenience and readability a rule can be extended beyond a single
+line with a `<tt>&bsol;</tt><em/newline/'.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+The use of this module is initiated with an entry in the
+<bf/Linux-PAM/ configuration file of the following type:
+<tscreen>
+<verb>
+#
+# apply pam_time accounting to login requests
+#
+login account required pam_time.so
+</verb>
+</tscreen>
+where, here we are applying the module to the <em/login/ application.
+
+<p>
+Some examples of rules that can be placed in the
+<tt>/etc/security/time.conf</tt> configuration file are the following:
+<descrip>
+
+<tag><tt>login ; tty* &amp ; !ttyp* ; !root ; !Al0000-2400</tt></tag>
+all users except for <tt/root/ are denied access to console-login at
+all times.
+
+<tag><tt>games ; * ; !waster ; Wd0000-2400 | Wk1800-0800</tt></tag>
+games (configured to use Linux-PAM) are only to be accessed out of
+working hours. This rule does not apply to the user <tt/waster/.
+
+</descrip>
+
+<p>
+Note, currently there is no daemon enforcing the end of a session.
+This needs to be remedied.
+
+<p>
+Poorly formatted rules are logged as errors using <tt/syslog(3)/.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_unix.sgml b/doc/modules/pam_unix.sgml
new file mode 100644
index 00000000..792362ed
--- /dev/null
+++ b/doc/modules/pam_unix.sgml
@@ -0,0 +1,289 @@
+<!--
+ This file was written by Andrew G. Morgan <morgan@linux.kernel.org>
+
+ Converted from the pam_pwdb.sgml file for pam_unix by Ben Collins <bcollins@debian.org>
+-->
+
+<sect1>The Unix Password module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+pam_unix
+
+<tag><bf>Author:</bf></tag>
+
+<tag><bf>Maintainer:</bf></tag>
+Authors.
+
+<tag><bf>Management groups provided:</bf></tag>
+account; authentication; password; session
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This is the standard Unix authentication module. It uses standard calls
+from the system's libraries to retrieve and set account information as
+well as authentication. Usually this is obtained from the /etc/passwd
+and the /etc/shadow file aswell if shadow is enabled.
+
+<sect2>Account component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/; <tt/audit/
+
+<tag><bf>Description:</bf></tag>
+
+The <tt/debug/ argument makes the accounting functions of this module
+<tt/syslog(3)/ more information on its actions. (Remaining arguments
+supported by the other functions of this module are silently ignored,
+but others are logged as errors through <tt/syslog(3)/). The <tt/audit/
+argument causes even more logging.
+
+Based on the following <tt/shadow/ elements:
+<tt/expire/;
+<tt/last_change/;
+<tt/max_change/;
+<tt/min_change/;
+<tt/warn_change/,
+this module performs the task of establishing the status of the user's
+account and password. In the case of the latter, it may offer advice
+to the user on changing their password or, through the
+<tt/PAM_AUTHTOKEN_REQD/ return, delay giving service to the user until
+they have established a new password. The entries listed above are
+documented in the <em/GNU Libc/ info documents. Should the user's record
+not contain one or more of these entries, the corresponding <em/shadow/
+check is not performed.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+In its accounting mode, this module can be inserted as follows:
+<tscreen>
+<verb>
+#
+# Ensure users account and password are still active
+#
+login account required pam_unix.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/;
+<tt/audit/;
+<tt/use_first_pass/;
+<tt/try_first_pass/;
+<tt/nullok/;
+<tt/nodelay/
+
+<tag><bf>Description:</bf></tag>
+
+The <tt/debug/ argument makes the authentication functions of this
+module <tt/syslog(3)/ more information on its actions. The <tt/audit/
+causes even more information to be logged.
+
+<p>
+The default action of this module is to not permit the user access to
+a service if their <em/official/ password is blank. The <tt/nullok/
+argument overrides this default.
+
+<p>
+When given the argument <tt/try_first_pass/, before prompting the user
+for their password, the module first tries the previous stacked
+<tt/auth/-module's password in case that satisfies this module as
+well. The argument <tt/use_first_pass/ forces the module to use such a
+recalled password and will never prompt the user - if no password is
+available or the password is not appropriate, the user will be denied
+access.
+
+<p>
+The argument, <tt>nodelay</tt>, can be used to discourage the
+authentication component from requesting a delay should the
+authentication as a whole fail. The default action is for the module
+to request a delay-on-failure of the order of one second.
+
+<p>
+Remaining arguments, supported by the other functions of this module,
+are silently ignored. Other arguments are logged as errors through
+<tt/syslog(3)/.
+
+<p>
+A helper binary, <tt>unix_chkpwd</tt>, is provided to check the user's
+password when it is stored in a read protected database. This binary
+is very simple and will only check the password of the user invoking
+it. It is called transparently on behalf of the user by the
+authenticating component of this module. In this way it is possible
+for applications like <em>xlock</em> to work without being setuid-root.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+The correct functionality of this module is dictated by having an
+appropriate <tt>/etc/nsswitch.conf</tt> file, the user
+databases specified there dictate the source of the authenticated
+user's record.
+<p>
+In its authentication mode, this module can be inserted as follows:
+<tscreen>
+<verb>
+#
+# Authenticate the user
+#
+login auth required pam_unix.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<sect2>Password component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/;
+<tt/audit/;
+<tt/nullok/;
+<tt/not_set_pass/;
+<tt/use_authtok/;
+<tt/try_first_pass/;
+<tt/use_first_pass/;
+<tt/md5/;
+<tt/bigcrypt/;
+<tt/shadow/;
+<tt/nis/;
+<tt/remember/
+
+<tag><bf>Description:</bf></tag>
+
+This part of the <tt/pam_unix/ module performs the task of updating
+the user's password.
+
+<p>
+In the case of conventional unix databases (which store the password
+encrypted) the <tt/md5/ argument is used to do the encryption with the
+MD5 function as opposed to the <em/conventional/ <tt/crypt(3)/ call.
+As an alternative to this, the <tt/bigcrypt/ argument can be used to
+encrypt more than the first 8 characters of a password with DEC's
+(Digital Equipment Cooperation) `C2' extension to the standard UNIX
+<tt/crypt()/ algorithm.
+
+<p>
+The <tt/nullok/ argument is used to permit the changing of a password
+<em/from/ an empty one. Without this argument, empty passwords are
+treated as account-locking ones.
+
+<p>
+The argument <tt/use_first_pass/ is used to lock the choice of old and
+new passwords to that dictated by the previously stacked <tt/password/
+module. The <tt/try_first_pass/ argument is used to avoid the user
+having to re-enter an old password when <tt/pam_unix/ follows a module
+that possibly shared the user's old password - if this old password is
+not correct the user will be prompted for the correct one. The
+argument <tt/use_authtok/ is used to <em/force/ this module to set the
+new password to the one provided by the previously stacked
+<tt/password/ module (this is used in an example of the stacking of
+the <em/Cracklib/ module documented above).
+
+<p>
+The <tt/not_set_pass/ argument is used to inform the module that it is
+not to pay attention to/make available the old or new passwords from/to
+other (stacked) password modules.
+
+<p>
+The <tt/debug/ argument makes the password functions of this module
+<tt/syslog(3)/ more information on its actions. Other arguments may be
+logged as erroneous to <tt/syslog(3)/. The <tt/audit/ argument causes
+even more information to be logged.
+
+<p>
+With the <tt/nis/ argument, <tt/pam_unix/ will attempt to use NIS RPC
+for setting new passwords.
+
+<p>
+The <tt/remember/ argument takes one value. This is the number of most
+recent passwords to save for each user. These are saved in
+<tt>/etc/security/opasswd</tt> in order to force password change history
+and keep the user from alternating between the same password too frequently.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+Standard usage:
+<tscreen>
+<verb>
+#
+# Change the users password
+#
+passwd password required pam_unix.so
+</verb>
+</tscreen>
+
+<p>
+An example of the stacking of this module with respect to the
+pluggable password checking module, <tt/pam_cracklib/:
+<tscreen>
+<verb>
+#
+# Change the users password
+#
+passwd password required pam_cracklib.so retry=3 minlen=6 difok=3
+passwd password required pam_unix.so use_authtok nullok md5
+</verb>
+</tscreen>
+
+</descrip>
+
+<sect2>Session component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+No arguments are recognized by this module component. Its action is
+simply to log the username and the service-type to
+<tt/syslog(3)/. Messages are logged at the beginning and end of the
+user's session.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+The use of the session modules is straightforward:
+<tscreen>
+<verb>
+#
+# session opening and closing
+#
+login session required pam_unix.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_userdb.sgml b/doc/modules/pam_userdb.sgml
new file mode 100644
index 00000000..bdbf80b8
--- /dev/null
+++ b/doc/modules/pam_userdb.sgml
@@ -0,0 +1,112 @@
+<!--
+ This file was written by Cristian Gafton <gafton@redhat.com>
+-->
+
+<sect1>The userdb module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_userdb/
+
+<tag><bf>Author:</bf></tag>
+Cristian Gafton &lt;gafton@redhat.com&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author.
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+Requires Berkeley DB.
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+Look up users in a .db database and verify their password against
+what is contained in that database.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/;
+<tt/icase/;
+<tt/dump/;
+<tt/db=XXXX/;
+
+<tag><bf>Description:</bf></tag>
+
+This module is used to verify a username/password pair against values stored in
+a Berkeley DB database. The database is indexed by the username, and the data
+fields corresponding to the username keys are the passwords, in unencrypted form,
+so caution must be exercised over the access rights to the DB database itself..
+
+The module will read the password from the user using the conversation mechanism. If
+you are using this module on top of another authetication module (like <tt/pam_pwdb/;)
+then you should tell that module to read the entered password from the PAM_AUTHTOK field, which is set by this module.
+
+<p>
+The action of the module may be modified from this default by one or
+more of the following flags in the <tt>/etc/pam.d/&lt;service&gt;</tt> file.
+<itemize>
+<item>
+<tt/debug/ -
+Supply more debugging information to <tt/syslog(3)/.
+
+<item>
+<tt/icase/ -
+Perform the password comparisons case insensitive.
+
+<item>
+<tt/dump/ -
+dump all the entries in the database to the log (eek,
+don't do this by default!)
+
+<item>
+<tt/db=XXXX/ -
+use the database found on pathname XXXX. Note that Berkeley DB usually adds the
+needed filename extension for you, so you should use something like <tt>/etc/foodata</tt>
+instead of <tt>/etc/foodata.db</tt>.
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+This is a normal ftp configuration file (usually placed as <tt>/etc/pam.d/ftp</tt>
+on most systems) that will accept for login users whose username/password pairs are
+provided in the <tt>/tmp/dbtest.db</tt> file:
+
+<tscreen>
+<verb>
+#%PAM-1.0
+auth required pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
+auth sufficient pam_userdb.so icase db=/tmp/dbtest
+auth required pam_pwdb.so shadow nullok try_first_pass
+auth required pam_shells.so
+account required pam_pwdb.so
+session required pam_pwdb.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_warn.sgml b/doc/modules/pam_warn.sgml
new file mode 100644
index 00000000..2ca4cc82
--- /dev/null
+++ b/doc/modules/pam_warn.sgml
@@ -0,0 +1,67 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+-->
+
+<sect1>Warning logger module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_warn/
+
+<tag><bf>Author:</bf></tag>
+Andrew G. Morgan &lt;morgan@parc.power.net&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author.
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication; password
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+
+<tag><bf>Network aware:</bf></tag>
+logs information about the remote user and host (if pam-items are known)
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+This module is principally for logging information about a
+proposed authentication or application to update a password.
+
+<sect2>Authentication+Password component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+
+<tag><bf>Description:</bf></tag>
+
+Log the service, terminal, user, remote user and remote host to
+<tt/syslog(3)/. The items are not probed for, but instead obtained
+from the standard pam-items.
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+an example is provided in the configuration file section <ref
+id="configuration" name="above">.
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/modules/pam_wheel.sgml b/doc/modules/pam_wheel.sgml
new file mode 100644
index 00000000..1eb62743
--- /dev/null
+++ b/doc/modules/pam_wheel.sgml
@@ -0,0 +1,125 @@
+<!--
+ $Id$
+
+ This file was written by Andrew G. Morgan <morgan@parc.power.net>
+ from notes provided by Cristian Gafton.
+-->
+
+<sect1>The wheel module
+
+<sect2>Synopsis
+
+<p>
+<descrip>
+
+<tag><bf>Module Name:</bf></tag>
+<tt/pam_wheel/
+
+<tag><bf>Author:</bf></tag>
+Cristian Gafton &lt;gafton@redhat.com&gt;
+
+<tag><bf>Maintainer:</bf></tag>
+Author.
+
+<tag><bf>Management groups provided:</bf></tag>
+authentication
+
+<tag><bf>Cryptographically sensitive:</bf></tag>
+
+<tag><bf>Security rating:</bf></tag>
+
+<tag><bf>Clean code base:</bf></tag>
+
+<tag><bf>System dependencies:</bf></tag>
+Requires libpwdb.
+
+<tag><bf>Network aware:</bf></tag>
+
+</descrip>
+
+<sect2>Overview of module
+
+<p>
+Only permit root access to members of the wheel (<tt/gid=0/) group.
+
+<sect2>Authentication component
+
+<p>
+<descrip>
+
+<tag><bf>Recognized arguments:</bf></tag>
+<tt/debug/;
+<tt/use_uid/;
+<tt/trust/;
+<tt/deny/;
+<tt/group=XXXX/
+
+<tag><bf>Description:</bf></tag>
+
+This module is used to enforce the so-called <em/wheel/ group. By
+default, it permits root access to the system if the applicant user is
+a member of the <tt/wheel/ group (first, the module checks for the
+existence of a '<tt/wheel/' group. Otherwise the module defines the
+group with group-id <tt/0/ to be the <em/wheel/ group).
+
+<p>
+The action of the module may be modified from this default by one or
+more of the following flags in the <tt>/etc/pam.conf</tt> file.
+<itemize>
+<item>
+<tt/debug/ -
+Supply more debugging information to <tt/syslog(3)/.
+
+<item>
+<tt/use_uid/ -
+This option modifies the behavior of the module by using the current
+<tt/uid/ of the process and not the <tt/getlogin(3)/ name of the user.
+This option is useful for being able to jump from one account to
+another, for example with 'su'.
+
+<item>
+<tt/trust/ -
+This option instructs the module to return <tt/PAM_SUCCESS/ should it
+find the user applying for root privilege is a member of the wheel
+group. The default action is to return <tt/PAM_IGNORE/ in this
+situation. By using the <tt/trust/ option it is possible to arrange
+for <tt/wheel/-group members to become root without typing a
+password. <bf/USE WITH CARE/.
+
+<item>
+<tt/deny/ -
+This is used to reverse the logic of the module's behavior.
+If the user is trying to get <tt/uid=0/ access and is a member of the wheel
+group, deny access (for the wheel group, this is perhaps nonsense!):
+it is intended for use in conjunction with the <tt/group=/ argument...
+
+<item>
+<tt/group=XXXX/ -
+Instead of checking the <tt/gid=0/ group, use the user's <tt/XXXX/
+group membership for the authentication. Here, <tt/XXXX/ is the name
+of the group and <bf/not/ its numeric identifier.
+
+</itemize>
+
+<tag><bf>Examples/suggested usage:</bf></tag>
+
+To restrict access to superuser status to the members of the
+<tt/wheel/ group, use the following entries in your configuration
+file:
+<tscreen>
+<verb>
+#
+# root gains access by default (rootok), only wheel members can
+# become root (wheel) but Unix authenticate non-root applicants.
+#
+su auth sufficient pam_rootok.so
+su auth required pam_wheel.so
+su auth required pam_unix_auth.so
+</verb>
+</tscreen>
+
+</descrip>
+
+<!--
+End of sgml insert for this module.
+-->
diff --git a/doc/pam_appl.sgml b/doc/pam_appl.sgml
new file mode 100644
index 00000000..d1be8c15
--- /dev/null
+++ b/doc/pam_appl.sgml
@@ -0,0 +1,1643 @@
+<!doctype linuxdoc system>
+
+<!--
+
+ $Id$
+
+ Copyright (C) Andrew G. Morgan 1996-9. All rights reserved.
+
+Redistribution and use in source (sgml) and binary (derived) forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, and the entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ALTERNATIVELY, this product may be distributed under the terms of the
+GNU General Public License, in which case the provisions of the GNU
+GPL are required INSTEAD OF the above restrictions. (This clause is
+necessary due to a potential bad interaction between the GNU GPL and
+the restrictions contained in a BSD-style copyright.)
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+ -->
+
+<article>
+
+<title>The Linux-PAM Application Developers' Guide
+<author>Andrew G. Morgan, <tt>morgan@linux.kernel.org</tt>
+<date>DRAFT v0.71 1999/11/8
+<abstract>
+This manual documents what an application developer needs to know
+about the <bf>Linux-PAM</bf> library. It describes how an application
+might use the <bf>Linux-PAM</bf> library to authenticate users. In
+addition it contains a description of the funtions to be found in
+<tt/libpam_misc/ library, that can be used in general applications.
+Finally, it contains some comments on PAM related security issues for
+the application developer.
+</abstract>
+
+<toc>
+
+<sect>Introduction
+
+<sect1>Synopsis
+
+<p>
+For general applications that wish to use the services provided by
+<bf/Linux-PAM/ the following is a summary of the relevant linking
+information:
+<tscreen>
+<verb>
+#include <security/pam_appl.h>
+
+cc -o application .... -lpam -ldl
+</verb>
+</tscreen>
+
+<p>
+In addition to <tt/libpam/, there is a library of miscellaneous
+functions that make the job of writing <em/PAM-aware/ applications
+easier (this library is not covered in the DCE-RFC for PAM and is
+specific to the Linux-PAM distribution):
+<tscreen>
+<verb>
+...
+#include <security/pam_misc.h>
+
+cc -o application .... -lpam -lpam_misc -ldl
+</verb>
+</tscreen>
+
+<sect1> Description
+
+<p>
+<bf>Linux-PAM</bf> (Pluggable Authentication Modules for Linux) is a
+library that enables the local system administrator to choose how
+individual applications authenticate users. For an overview of the
+<bf>Linux-PAM</bf> library see the <bf/Linux-PAM/ System
+Administrators' Guide.
+
+<p>
+It is the purpose of the <bf>Linux-PAM</bf> project to liberate the
+development of privilege granting software from the development of
+secure and appropriate authentication schemes. This is accomplished
+by providing a documented library of functions that an application may
+use for all forms of user authentication management. This library
+dynamically loads locally configured authentication modules that
+actually perform the authentication tasks.
+
+<p>
+From the perspective of an application developer the information
+contained in the local configuration of the PAM library should not be
+important. Indeed it is intended that an application treat the
+functions documented here as a ``black box'' that will deal with all
+aspects of user authentication. ``All aspects'' includes user
+verification, account management, session initialization/termination
+and also the resetting of passwords (<em/authentication tokens/).
+
+<sect>Overview
+
+<p>
+Most service-giving applications are restricted. In other words,
+their service is not available to all and every prospective client.
+Instead, the applying client must jump through a number of hoops to
+convince the serving application that they are authorized to obtain
+service.
+
+The process of <em/authenticating/ a client is what PAM is designed to
+manage. In addition to authentication, PAM provides account
+management, credential management, session management and
+authentication-token (password changing) management services. It is
+important to realize when writing a PAM based application that these
+services are provided in a manner that is <bf>transparent</bf> to the
+the application. That is to say, when the application is written, no
+assumptions can be made about <em>how</em> the client will be
+authenticated.
+
+<p>
+The process of authentication is performed by the PAM library via a
+call to <tt>pam_authenticate()</tt>. The return value of this
+function will indicate whether a named client (the <em>user</em>) has
+been authenticated. If the PAM library needs to prompt the user for
+any information, such as their <em>name</em> or a <em>password</em>
+then it will do so. If the PAM library is configured to authenticate
+the user using some silent protocol, it will do this too. (This
+latter case might be via some hardware interface for example.)
+
+<p>
+It is important to note that the application must leave all decisions
+about when to prompt the user at the discretion of the PAM library.
+
+<p>
+The PAM library, however, must work equally well for different styles
+of application. Some applications, like the familiar <tt>login</tt>
+and <tt>passwd</tt> are terminal based applications, exchanges of
+information with the client in these cases is as plain text messages.
+Graphically based applications, however, have a more sophisticated
+interface. They generally interact with the user via specially
+constructed dialogue boxes. Additionally, network based services
+require that text messages exchanged with the client are specially
+formatted for automated processing: one such example is <tt>ftpd</tt>
+which prefixes each exchanged message with a numeric identifier.
+
+<p>
+The presentation of simple requests to a client is thus something very
+dependent on the protocol that the serving application will use. In
+spite of the fact that PAM demands that it drives the whole
+authentication process, it is not possible to leave such protocol
+subtleties up to the PAM library. To overcome this potential problem,
+the application provides the PAM library with a <em>conversation</em>
+function. This function is called from <bf>within</bf> the PAM
+library and enables the PAM to directly interact with the client. The
+sorts of things that this conversation function must be able to do are
+prompt the user with text and/or obtain textual input from the user
+for processing by the PAM library. The details of this function are
+provided in a later section.
+
+<p>
+For example, the conversation function may be called by the PAM library
+with a request to prompt the user for a password. Its job is to
+reformat the prompt request into a form that the client will
+understand. In the case of <tt>ftpd</tt>, this might involve prefixing
+the string with the number <tt>331</tt> and sending the request over
+the network to a connected client. The conversation function will
+then obtain any reply and, after extracting the typed password, will
+return this string of text to the PAM library. Similar concerns need
+to be addressed in the case of an X-based graphical server.
+
+<p>
+There are a number of issues that need to be addressed when one is
+porting an existing application to become PAM compliant. A section
+below has been devoted to this: Porting legacy applications.
+
+<p>
+Besides authentication, PAM provides other forms of management.
+Session management is provided with calls to
+<tt>pam_open_session()</tt> and <tt>pam_close_session()</tt>. What
+these functions actually do is up to the local administrator. But
+typically, they could be used to log entry and exit from the system or
+for mounting and unmounting the user's home directory. If an
+application provides continuous service for a period of time, it
+should probably call these functions, first open after the user is
+authenticated and then close when the service is terminated.
+
+<p>
+Account management is another area that an application developer
+should include with a call to <tt/pam_acct_mgmt()/. This call will
+perform checks on the good health of the user's account (has it
+expired etc.). One of the things this function may check is whether
+the user's authentication token has expired - in such a case the
+application may choose to attempt to update it with a call to
+<tt/pam_chauthtok()/, although some applications are not suited to
+this task (<em>ftp</em> for example) and in this case the application
+should deny access to the user.
+
+<p>
+PAM is also capable of setting and deleting the users credentials with
+the call <tt>pam_setcred()</tt>. This function should always be
+called after the user is authenticated and before service is offered
+to the user. By convention, this should be the last call to the PAM
+library before service is given to the user. What exactly a
+credential is, is not well defined. However, some examples are given
+in the glossary below.
+
+<sect>The public interface to <bf>Linux-PAM</bf>
+
+<p>
+Firstly, the relevant include file for the <bf>Linux-PAM</bf> library
+is <tt>&lt;security/pam_appl.h&gt;</tt>. It contains the definitions
+for a number of functions. After listing these functions, we collect
+some guiding remarks for programmers.
+
+<sect1>What can be expected by the application
+
+<p>
+Below we document those functions in the <bf/Linux-PAM/ library that
+may be called from an application.
+
+<sect2>Initialization of Linux-PAM
+<label id="pam-start-section">
+
+<p>
+<tscreen>
+<verb>
+extern int pam_start(const char *service_name, const char *user,
+ const struct pam_conv *pam_conversation,
+ pam_handle_t **pamh);
+</verb>
+</tscreen>
+
+<p>
+This is the first of the <bf>Linux-PAM</bf> functions that must be
+called by an application. It initializes the interface and reads the
+system configuration file, <tt>/etc/pam.conf</tt> (see the
+<bf/Linux-PAM/ System Administrators' Guide). Following a successful
+return (<tt/PAM_SUCCESS/) the contents of <tt/*pamh/ is a handle that
+provides continuity for successive calls to the <bf/Linux-PAM/
+library. The arguments expected by <tt/pam_start/ are as follows: the
+<tt/service_name/ of the program, the <tt/user/name of the individual
+to be authenticated, a pointer to an application-supplied
+<tt/pam_conv/ structure and a pointer to a <tt/pam_handle_t/
+<em/pointer/.
+
+<p>
+The <tt>pam_conv</tt> structure is discussed more fully in the section
+<ref id="the-conversation-function" name="below">. The
+<tt>pam_handle_t</tt> is a <em>blind</em> structure and the
+application should not attempt to probe it directly for information.
+Instead the <bf>Linux-PAM</bf> library provides the functions
+<tt>pam_set_item</tt> and <tt>pam_get_item</tt>. These functions are
+documented below.
+
+<sect2>Termination of the library
+<label id="pam-end-section">
+
+<p>
+<tscreen>
+<verb>
+extern int pam_end(pam_handle_t *pamh, int pam_status);
+</verb>
+</tscreen>
+
+<p>
+This function is the last function an application should call in the
+<bf>Linux-PAM</bf> library. Upon return the handle <tt/pamh/ is no
+longer valid and all memory associated with it will be invalid (likely
+to cause a segmentation fault if accessed).
+
+<p>
+Under normal conditions the argument <tt/pam_status/ has the value
+PAM_SUCCESS, but in the event of an unsuccessful service application
+the approprite <bf/Linux-PAM/ error-return value should be used
+here.
+attempt its purpose is to be passed as an argument to the
+module specific function <tt/cleanup()/ (see the <bf/Linux-PAM/
+<htmlurl url="pam_modules.html" name="Module Developers' Guide">).
+
+<sect2>Setting PAM items
+<label id="pam-set-item-section">
+
+<p>
+<tscreen>
+<verb>
+extern int pam_set_item(pam_handle_t *pamh, int item_type,
+ const void *item);
+</verb>
+</tscreen>
+
+<p>This function is used to (re)set the value of one of the following
+<bf/item_type/s:
+
+<p><descrip>
+<tag><tt/PAM_SERVICE/</tag>
+ The service name
+
+<tag><tt/PAM_USER/</tag>
+ The user name
+
+<tag><tt/PAM_TTY/</tag>
+ The terminal name: prefixed by <tt>/dev/</tt> if it is a
+device file; for graphical, X-based, applications the value for this
+item should be the <tt/&dollar;DISPLAY/ variable.
+
+<tag><tt/PAM_RHOST/</tag>
+ The remote host name
+
+<tag><tt/PAM_CONV/</tag>
+ The conversation structure (see section <ref
+id="the-conversation-function" name="below">)
+
+<tag><tt/PAM_RUSER/</tag>
+ The remote user name
+
+<tag><tt/PAM_USER_PROMPT/</tag>
+ The string used when prompting for a user's name. The default
+value for this string is ``Please enter username: ''.
+
+</descrip>
+
+<p>
+For all <tt/item_type/s, other than <tt/PAM_CONV/, <tt/item/ is a
+pointer to a <tt>&lt;NUL&gt;</tt> terminated character string. In the
+case of <tt/PAM_CONV/, <tt/item/ points to an initialized
+<tt/pam_conv/ structure (see section <ref
+id="the-conversation-function" name="below">).
+
+<p>
+A successful call to this function returns <tt/PAM_SUCCESS/. However,
+the application should expect one of the following errors:
+
+<p>
+<descrip>
+<tag><tt/PAM_PERM_DENIED/</tag>
+ An attempt was made to replace the conversation structure with
+a <tt/NULL/ value.
+<tag><tt/PAM_BUF_ERR/</tag>
+ The function ran out of memory making a copy of the item.
+<tag><tt/PAM_BAD_ITEM/</tag>
+ The application attempted to set an undefined item.
+</descrip>
+
+<sect2>Getting PAM items
+<label id="pam-get-item-section">
+
+<p>
+<tscreen>
+<verb>
+extern int pam_get_item(const pam_handle_t *pamh, int item_type,
+ const void **item);
+</verb>
+</tscreen>
+
+<p>
+This function is used to obtain the value of the indicated
+<tt/item_type/. Upon successful return, <tt/*item/ contains a pointer
+to the value of the corresponding item. Note, this is a pointer to
+the <em/actual/ data and should <em/not/ be <tt/free()/'ed or
+over-written! A successful call is signaled by a return value of
+<tt/PAM_SUCCESS/. If an attempt is made to get an undefined item,
+<tt/PAM_BAD_ITEM/ is returned.
+
+<sect2>Understanding errors
+<label id="pam-strerror-section">
+
+<p>
+<tscreen>
+<verb>
+extern const char *pam_strerror(pam_handle_t *pamh, int errnum);
+</verb>
+</tscreen>
+
+<p>
+This function returns some text describing the <bf>Linux-PAM</bf>
+error associated with the argument <tt/errnum/. If the error is not
+recognized ``<tt/Unknown Linux-PAM error/'' is returned.
+
+<sect2>Planning for delays
+
+<p>
+<tscreen>
+<verb>
+extern int pam_fail_delay(pam_handle_t *pamh, unsigned int micro_sec);
+</verb>
+</tscreen>
+
+<p>
+This function is offered by <bf/Linux-PAM/ to facilitate time delays
+following a failed call to <tt/pam_authenticate()/ and before control
+is returned to the application. When using this function the
+application programmer should check if it is available with,
+<tscreen>
+<verb>
+#ifdef HAVE_PAM_FAIL_DELAY
+ ....
+#endif /* HAVE_PAM_FAIL_DELAY */
+</verb>
+</tscreen>
+
+
+<p>
+Generally, an application requests that a user is authenticated by
+<bf/Linux-PAM/ through a call to <tt/pam_authenticate()/ or
+<tt/pam_chauthtok()/. These functions call each of the <em/stacked/
+authentication modules listed in the relevant <bf/Linux-PAM/
+configuration file. As directed by this file, one of more of the
+modules may fail causing the <tt/pam_...()/ call to return an error.
+It is desirable for there to also be a pause before the application
+continues. The principal reason for such a delay is security: a delay
+acts to discourage <em/brute force/ dictionary attacks primarily, but
+also helps hinder <em/timed/ (covert channel) attacks.
+
+<p>
+The <tt/pam_fail_delay()/ function provides the mechanism by which an
+application or module can suggest a minimum delay (of <tt/micro_sec/
+<em/micro-seconds/). <bf/Linux-PAM/ keeps a record of the longest time
+requested with this function. Should <tt/pam_authenticate()/ fail,
+the failing return to the application is delayed by an amount of time
+randomly distributed (by up to 25%) about this longest value.
+
+<p>
+Independent of success, the delay time is reset to its zero default
+value when <bf/Linux-PAM/ returns control to the application.
+
+<p>
+For applications written with a single thread that are event driven in
+nature, <tt/libpam/ generating this dalay may be undesirable. Instead,
+the application may want to register the delay in some other way. For
+example, in a single threaded server that serves multiple
+authentication requests from a single event loop, the application
+might want to simply mark a given connection as blocked until an
+application timer expires. For this reason, <bf/Linux-PAM/ supplies
+the <tt/PAM_FAIL_DELAY/ item. It can be queried and set with
+<tt/pam_get_item()/ and <tt/pam_set_item()/ respectively. The value
+used to set it should be a function pointer of the following
+prototype:
+
+<tscreen>
+<verb>
+void (*delay_fn)(int retval, unsigned usec_delay, void *appdata_ptr);
+</verb>
+</tscreen>
+
+The arguments being the <tt/retval/ return code of the module stack,
+the <tt/usec_delay/ micro-second delay that libpam is requesting and
+the <tt/appdata_ptr/ that the application has associated with the
+current <tt/pamh/ (/tt/pam_handle_t/). This last value was set by the
+application when it called <tt/pam_start/ or explicitly with
+<tt/pam_set_item(... , PAM_CONV, ...)/. Note, if <tt/PAM_FAIL_DELAY/
+is unset (or set to <tt/NULL/), then <tt/libpam/ will perform any
+delay.
+
+<sect2>Authenticating the user
+
+<p>
+<tscreen>
+<verb>
+extern int pam_authenticate(pam_handle_t *pamh, int flags);
+</verb>
+</tscreen>
+
+<p>
+This function serves as an interface to the authentication mechanisms
+of the loaded modules. The single <em/optional/ flag, which may be
+logically OR'd with <tt/PAM_SILENT/, takes the following value,
+
+<p><descrip>
+
+<tag><tt/PAM_DISALLOW_NULL_AUTHTOK/</tag>
+ Instruct the authentication modules to return
+<tt/PAM_AUTH_ERR/ if the user does not have a registered
+authorization token---it is set to <tt/NULL/ in the system database.
+</descrip>
+
+<p>
+The value returned by this function is one of the following:
+
+<p><descrip>
+
+<tag><tt/PAM_AUTH_ERR/</tag>
+ The user was not authenticated
+<tag><tt/PAM_CRED_INSUFFICIENT/</tag>
+ For some reason the application does not have sufficient
+credentials to authenticate the user.
+<tag><tt/PAM_AUTHINFO_UNAVAIL/</tag>
+ The modules were not able to access the authentication
+information. This might be due to a network or hardware failure etc.
+<tag><tt/PAM_USER_UNKNOWN/</tag>
+ The supplied username is not known to the authentication
+service
+<tag><tt/PAM_MAXTRIES/</tag>
+ One or more of the authentication modules has reached its
+limit of tries authenticating the user. Do not try again.
+
+</descrip>
+
+<p>
+If one or more of the authentication modules fails to load, for
+whatever reason, this function will return <tt/PAM_ABORT/.
+
+<sect2>Setting user credentials
+<label id="pam-setcred-section">
+
+<p>
+<tscreen>
+<verb>
+extern int pam_setcred(pam_handle_t *pamh, int flags);
+</verb>
+</tscreen>
+
+<p>
+This function is used to set the module-specific credentials of the
+user. It is usually called after the user has been authenticated,
+after the account management function has been called and after a
+session has been opened for the user.
+
+<p>
+A credential is something that the user possesses. It is some
+property, such as a <em>Kerberos</em> ticket, or a supplementary group
+membership that make up the uniqueness of a given user. On a Linux
+(or UN*X system) the user's <tt>UID</tt> and <tt>GID</tt>'s are
+credentials too. However, it has been decided that these properties
+(along with the default supplementary groups of which the user is a
+member) are credentials that should be set directly by the application
+and not by PAM.
+
+<p>
+This function simply calls the <tt/pam_sm_setcred/ functions of each
+of the loaded modules. Valid <tt/flags/, any one of which, may be
+logically OR'd with <tt/PAM_SILENT/, are:
+
+<p><descrip>
+<tag><tt/PAM_ESTABLISH_CRED/</tag>
+ Set the credentials for the authentication service,
+<tag><tt/PAM_DELETE_CRED/</tag>
+ Delete the credentials associated with the authentication service,
+<tag><tt/PAM_REINITIALIZE_CRED/</tag>
+ Reinitialize the user credentials, and
+<tag><tt/PAM_REFRESH_CRED/</tag>
+ Extend the lifetime of the user credentials.
+</descrip>
+
+<p>
+A successful return is signalled with <tt/PAM_SUCCESS/. Errors that
+are especially relevant to this function are the following:
+
+<p><descrip>
+<tag><tt/PAM_CRED_UNAVAIL/</tag>
+ A module cannot retrieve the user's credentials.
+<tag><tt/PAM_CRED_EXPIRED/</tag>
+ The user's credentials have expired.
+<tag><tt/PAM_USER_UNKNOWN/</tag>
+ The user is not known to an authentication module.
+<tag><tt/PAM_CRED_ERR/</tag>
+ A module was unable to set the credentials of the user.
+</descrip>
+
+<sect2>Account management
+
+<p>
+<tscreen>
+<verb>
+extern int pam_acct_mgmt(pam_handle_t *pamh, int flags);
+</verb>
+</tscreen>
+
+<p>
+This function is typically called after the user has been
+authenticated. It establishes whether the user's account is healthy.
+That is to say, whether the user's account is still active and whether
+the user is permitted to gain access to the system at this time.
+Valid flags, any one of which, may be logically OR'd with
+<tt/PAM_SILENT/, and are the same as those applicable to the
+<tt/flags/ argument of <tt/pam_authenticate/.
+
+<p>
+This function simply calls the corresponding functions of each of the
+loaded modules, as instructed by the configuration file,
+<tt>/etc/pam.conf</tt>.
+
+<p>
+The normal response from this function is <tt/PAM_SUCCESS/, however,
+specific failures are indicated by the following error returns:
+
+<descrip>
+<tag><tt/PAM_AUTHTOKEN_REQD/</tag>
+The user <bf/is/ valid but their authentication token has
+<em/expired/. The correct response to this return-value is to require
+that the user satisfies the <tt/pam_chauthtok()/ function before
+obtaining service. It may not be possible for some applications to do
+this. In such cases, the user should be denied access until such time
+as they can update their password.
+
+<tag><tt/PAM_ACCT_EXPIRED/</tag>
+ The user is no longer permitted access to the system.
+<tag><tt/PAM_AUTH_ERR/</tag>
+ There was an authentication error.
+
+<tag><tt/PAM_PERM_DENIED/</tag>
+ The user is not permitted to gain access at this time.
+<tag><tt/PAM_USER_UNKNOWN/</tag>
+ The user is not known to a module's account management
+component.
+
+</descrip>
+
+<sect2>Updating authentication tokens
+<label id="pam-chauthtok-section">
+
+<p>
+<tscreen>
+<verb>
+extern int pam_chauthtok(pam_handle_t *pamh, const int flags);
+</verb>
+</tscreen>
+
+<p>
+This function is used to change the authentication token for a given
+user (as indicated by the state associated with the handle,
+<tt/pamh/). The following is a valid but optional flag which may be
+logically OR'd with <tt/PAM_SILENT/,
+
+<descrip>
+<tag><tt/PAM_CHANGE_EXPIRED_AUTHTOK/</tag>
+ This argument indicates to the modules that the users
+authentication token (password) should only be changed if it has
+expired.
+</descrip>
+
+<p>
+Note, if this argument is not passed, the application requires that
+<em/all/ authentication tokens are to be changed.
+
+<p>
+<tt/PAM_SUCCESS/ is the only successful return value, valid
+error-returns are:
+
+<descrip>
+<tag><tt/PAM_AUTHTOK_ERR/</tag>
+ A module was unable to obtain the new authentication token.
+
+<tag><tt/PAM_AUTHTOK_RECOVERY_ERR/</tag>
+ A module was unable to obtain the old authentication token.
+
+<tag><tt/PAM_AUTHTOK_LOCK_BUSY/</tag>
+ One or more of the modules was unable to change the
+authentication token since it is currently locked.
+
+<tag><tt/PAM_AUTHTOK_DISABLE_AGING/</tag>
+ Authentication token aging has been disabled for at least one
+of the modules.
+
+<tag><tt/PAM_PERM_DENIED/</tag>
+ Permission denied.
+
+<tag><tt/PAM_TRY_AGAIN/</tag>
+ Not all of the modules were in a position to update the
+authentication token(s). In such a case none of the user's
+authentication tokens are updated.
+
+<tag><tt/PAM_USER_UNKNOWN/</tag>
+ The user is not known to the authentication token changing
+service.
+
+</descrip>
+
+<sect2>Session initialization
+<label id="pam-open-session-section">
+
+<p>
+<tscreen>
+<verb>
+extern int pam_open_session(pam_handle_t *pamh, int flags);
+</verb>
+</tscreen>
+
+<p>
+This function is used to indicate that an authenticated session has
+begun. It is used to inform the module that the user is currently in
+a session. It should be possible for the <bf>Linux-PAM</bf> library
+to open a session and close the same session (see section <ref
+id="pam-close-session-section" name="below">) from different
+applications.
+
+<p>
+Currently, this function simply calls each of the corresponding
+functions of the loaded modules. The only valid flag is
+<tt/PAM_SILENT/ and this is, of course, <em/optional/.
+
+<p>
+If any of the <em/required/ loaded modules are unable to open a
+session for the user, this function will return <tt/PAM_SESSION_ERR/.
+
+<sect2>Terminating sessions
+<label id="pam-close-session-section">
+
+<p>
+<tscreen>
+<verb>
+extern int pam_close_session(pam_handle_t *pamh, int flags);
+</verb>
+</tscreen>
+
+<p>
+This function is used to indicate that an authenticated session has
+ended. It is used to inform the module that the user is exiting a
+session. It should be possible for the <bf>Linux-PAM</bf> library to
+open a session and close the same session from different applications.
+
+<p>
+Currently, this function simply calls each of the corresponding
+functions of the loaded modules. The only valid flag is
+<tt/PAM_SILENT/ and this is, of course, <em/optional/.
+
+<p>
+If any of the <em/required/ loaded modules are unable to close a
+session for the user, this function will return <tt/PAM_SESSION_ERR/.
+
+<sect2>Setting PAM environment variables
+<label id="pam-putenv-section">
+
+<p>
+<tscreen>
+<verb>
+extern int pam_putenv(pam_handle_t *pamh, const char *name_value);
+</verb>
+</tscreen>
+
+<p>
+<em>
+Warning, the environment support in <bf/Linux-PAM/ is based solely
+on a six line email from the developers at Sun. Its interface is
+likely to be generally correct, however, the details are likely to be
+changed as more information becomes available.
+</em>
+
+<p>
+This function attempts to (re)set a <bf/Linux-PAM/ environment
+variable. The <tt/name_value/ argument is a single <tt/NUL/ terminated
+string of one of the following forms:
+<descrip>
+<tag>``<tt/NAME=value of variable/''</tag>
+
+In this case the environment variable of the given <tt/NAME/ is set to
+the indicated value: ``<tt/value of variable/''. If this variable is
+already known, it is overwritten. Otherwise it is added to the
+<bf/Linux-PAM/ environment.
+
+<tag>``<tt/NAME=/''</tag>
+
+This function sets the variable to an empty value. It is listed
+separately to indicate that this is the correct way to achieve such a
+setting.
+
+<tag>``<tt/NAME/''</tag>
+
+Without an `<tt/=/' the <tt/pam_putenv()/ function will delete the
+correspoding variable from the <bf/Linux-PAM/ environment.
+
+</descrip>
+
+<p>
+Success is indicated with a return value of <tt/PAM_SUCCESS/. Failure
+is indicated by one of the following returns:
+
+<descrip>
+<tag><tt/PAM_PERM_DENIED/</tag>
+ name given is a <tt/NULL/ pointer
+
+<tag><tt/PAM_BAD_ITEM/</tag>
+ variable requested (for deletion) is not currently set
+
+<tag><tt/PAM_ABORT/</tag>
+ the <bf/Linux-PAM/ handle, <tt/pamh/, is corrupt
+
+<tag><tt/PAM_BUF_ERR/</tag>
+ failed to allocate memory when attempting update
+
+</descrip>
+
+<sect2>Getting a PAM environment variable
+<label id="pam-getenv-section">
+
+<p>
+<tscreen>
+<verb>
+extern const char *pam_getenv(pam_handle_t *pamh, const char *name);
+</verb>
+</tscreen>
+
+<p>
+<em>
+Warning, the environment support in <bf/Linux-PAM/ is based solely
+on a six-line email from the developers at Sun. Its interface is
+likely to be generally correct, however, the details are likely to be
+changed as more information becomes available.
+</em>
+
+<p>
+Obtain the value of the indicated <bf/Linux-PAM/ environment
+variable. On error, internal failure or the unavailability of the
+given variable (unspecified), this function simply returns <tt/NULL/.
+
+<sect2>Getting the PAM environment
+<label id="pam-getenvlist-section">
+
+<p>
+<tscreen>
+<verb>
+extern const char * const *pam_getenvlist(pam_handle_t *pamh);
+</verb>
+</tscreen>
+
+<p>
+<em>
+Warning, the environment support in <bf/Linux-PAM/ is based solely
+on a six line email from the developers at Sun. Its interface is
+likely to be generally correct, however, the details are likely to be
+changed as more information becomes available.
+</em>
+
+<p>
+This function returns a pointer to the complete <tt/Linux-PAM/
+environment. It is a pointer to a <em/read-only/ list of
+<em/read-only/ environment variables. It should be noted that this
+memory will become invalid after a call to <tt/pam_end()/ (see the
+section <ref id="pam-end-section" name="above">). If application
+wishes to make use of this list after such a call, it should first
+make a copy of all the set variables. (A function that performs such a
+transcription is to be found in <tt/libpam_misc/.)
+
+<sect1>What is expected of an application
+
+<sect2>The conversation function
+<label id="the-conversation-function">
+
+<p>
+An application must provide a ``conversation function''. It is used
+for direct communication between a loaded module and the application
+and will typically provide a means for the module to prompt the user
+for a password etc. . The structure, <tt/pam_conv/, is defined by
+including <tt>&lt;security/pam_appl.h&gt</tt>; to be,
+
+<p>
+<tscreen>
+<verb>
+struct pam_conv {
+ int (*conv)(int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr);
+ void *appdata_ptr;
+};
+</verb>
+</tscreen>
+
+<p>
+It is initialized by the application before it is passed to the
+library. The <em/contents/ of this structure are attached to the
+<tt/*pamh/ handle. The point of this argument is to provide a
+mechanism for any loaded module to interact directly with the
+application program. This is why it is called a <em/conversation/
+structure.
+
+<p>
+When a module calls the referenced <tt/conv()/ function, the argument
+<tt/*appdata_ptr/ is set to the second element of this structure.
+
+<p>
+The other arguments of a call to <tt/conv()/ concern the information
+exchanged by module and application. That is to say, <tt/num_msg/
+holds the length of the array of pointers, <tt/msg/. After a
+successful return, the pointer <tt/*resp/ points to an array of
+<tt/pam_response/ structures, holding the application supplied text.
+Note, <tt/*resp/ is an <tt/struct pam_response/ array and <em/not/ an
+array of pointers.
+
+<p>
+The message (from the module to the application) passing structure is
+defined by <tt>&lt;security/pam_appl.h&gt;</tt> as:
+
+<p>
+<tscreen>
+<verb>
+struct pam_message {
+ int msg_style;
+ const char *msg;
+};
+</verb>
+</tscreen>
+
+<p>
+Valid choices for <tt/msg_style/ are:
+
+<p><descrip>
+<tag><tt/PAM_PROMPT_ECHO_OFF/</tag>
+ Obtain a string without echoing any text
+<tag><tt/PAM_PROMPT_ECHO_ON/</tag>
+ Obtain a string whilst echoing text
+<tag><tt/PAM_ERROR_MSG/</tag>
+ Display an error
+<tag><tt/PAM_TEXT_INFO/</tag>
+ Display some text.
+</descrip>
+
+<p>
+The point of having an array of messages is that it becomes possible
+to pass a number of things to the application in a single call from
+the module. It can also be convenient for the application that related
+things come at once: a windows based application can then present a
+single form with many messages/prompts on at once.
+
+<p>
+The response (from the application to the module) passing structure is
+defined by including <tt>&lt;security/pam_appl.h&gt;</tt> as:
+
+<p><tscreen><verb>
+struct pam_response {
+ char *resp;
+ int resp_retcode;
+};
+</verb></tscreen>
+
+<p>
+Currently, there are no definitions for <tt/resp_retcode/ values; the
+normal value is <tt/0/.
+
+<p>
+Prior to the 0.59 release of Linux-PAM, the length of the returned
+<tt/pam_response/ array was equal to the number of <em/prompts/ (types
+<tt/PAM_PROMPT_ECHO_OFF/ and <tt/PAM_PROMPT_ECHO_ON/) in the
+<tt/pam_message/ array with which the conversation function was
+called. This meant that it was not always necessary for the module to
+<tt/free(3)/ the responses if the conversation function was only used
+to display some text.
+
+<p>
+Post Linux-PAM-0.59 (and in the interests of compatibility with
+Sunsoft). The number of resposes is always equal to the <tt/num_msg/
+conversation function argument. This is slightly easier to program
+but does require that the response array is <tt/free(3)/'d after every
+call to the conversation function. The index of the responses
+corresponds directly to the prompt index in the <tt/pam_message/
+array.
+
+<p>
+The maximum length of the <tt/pam_msg.msg/ and <tt/pam_response.resp/
+character strings is <tt/PAM_MAX_MSG_SIZE/. (This is not enforced by
+Linux-PAM.)
+
+<p>
+<tt/PAM_SUCCESS/ is the expected return value of this
+function. However, should an error occur the application should not
+set <tt/*resp/ but simply return <tt/PAM_CONV_ERR/.
+
+<p>
+Note, if an application wishes to use two conversation functions, it
+should activate the second with a call to <tt/pam_set_item()/.
+
+<p>
+<bf>Notes:</bf> New item types are being added to the conversation
+protocol. Currently Linux-PAM supports: <tt>PAM_BINARY_PROMPT</tt>
+and <tt>PAM_BINARY_MSG</tt>. These two are intended for server-client
+hidden information exchange and may be used as an interface for
+maching-machine authentication.
+
+<sect1>Programming notes
+
+<p>
+Note, all of the authentication service function calls accept the
+token <tt/PAM_SILENT/, which instructs the modules to not send
+messages to the application. This token can be logically OR'd with any
+one of the permitted tokens specific to the individual function calls.
+<tt/PAM_SILENT/ does not override the prompting of the user for
+passwords etc., it only stops informative messages from being
+generated.
+
+<sect>Security issues of <bf>Linux-PAM</bf>
+
+<p>
+PAM, from the perspective of an application, is a convenient API for
+authenticating users. PAM modules generally have no increased
+privilege over that posessed by the application that is making use of
+it. For this reason, the application must take ultimate responsibility
+for protecting the environment in which PAM operates.
+
+<p>
+A poorly (or maliciously) written application can defeat any
+<bf/Linux-PAM/ module's authentication mechanisms by simply ignoring
+it's return values. It is the applications task and responsibility to
+grant privileges and access to services. The <bf/Linux-PAM/ library
+simply assumes the responsibility of <em/authenticating/ the user;
+ascertaining that the user <em/is/ who they say they are. Care should
+be taken to anticipate all of the documented behavior of the
+<bf/Linux-PAM/ library functions. A failure to do this will most
+certainly lead to a future security breach.
+
+<sect1>Care about standard library calls
+
+<p>
+In general, writers of authorization-granting applications should
+assume that each module is likely to call any or <em/all/ `libc'
+functions. For `libc' functions that return pointers to
+static/dynamically allocated structures (ie. the library allocates the
+memory and the user is not expected to `<tt/free()/' it) any module
+call to this function is likely to corrupt a pointer previously
+obtained by the application. The application programmer should either
+re-call such a `libc' function after a call to the <bf/Linux-PAM/
+library, or copy the structure contents to some safe area of memory
+before passing control to the <bf/Linux-PAM/ library.
+
+<p>
+Two important function classes that fall into this category are
+<tt>getpwnam(3)</tt> and <tt>syslog(3)</tt>.
+
+<sect1>Choice of a service name
+
+<p>
+When picking the <em/service-name/ that corresponds to the first entry
+in the <bf/Linux-PAM/ configuration file, the application programmer
+should <bf/avoid/ the temptation of choosing something related to
+<tt/argv[0]/. It is a trivial matter for any user to invoke any
+application on a system under a different name and this should not be
+permitted to cause a security breach.
+
+<p>
+To invoke some <tt/target/ application by another name, the user may
+symbolically link the target application with the desired name. To be
+precise all the user need do is,
+<tscreen>
+<verb>
+ln -s /target/application ./preferred_name
+</verb>
+</tscreen>
+and then <em/run/ <tt>./preferred_name</tt>
+
+<p>
+By studying the <bf/Linux-PAM/ configuration file(s), an attacker can
+choose the <tt/preferred_name/ to be that of a service enjoying
+minimal protection; for example a game which uses <bf/Linux-PAM/ to
+restrict access to certain hours of the day. If the service-name were
+to be linked to the filename under which the service was invoked, it
+is clear that the user is effectively in the position of dictating
+which authentication scheme the service uses. Needless to say, this
+is not a secure situation.
+
+<p>
+The conclusion is that the application developer should carefully
+define the service-name of an application. The safest thing is to make
+it a single hard-wired name.
+
+<sect1>The conversation function
+
+<p>
+Care should be taken to ensure that the <tt/conv()/ function is
+robust. Such a function is provided in the library <tt/libpam_misc/
+(see <ref id="libpam-misc-section" name="below">).
+
+<sect1>The identity of the user
+
+<p>
+The <bf/Linux-PAM/ modules will need to determine the identity of the
+user who requests a service, and the identity of the user who grants
+the service. These two users will seldom be the same. Indeed there
+is generally a third user identity to be considered, the new (assumed)
+identity of the user once the service is granted.
+
+<p>
+The need for keeping tabs on these identities is clearly an issue of
+security. One convention that is actively used by some modules is
+that the identity of the user requesting a service should be the
+current <tt/uid/ (userid) of the running process; the identity of the
+privilege granting user is the <tt/euid/ (effective userid) of the
+running process; the identity of the user, under whose name the
+service will be executed, is given by the contents of the
+<tt/PAM_USER/ <tt/pam_get_item(3)/.
+
+<p>
+For network-serving databases and other applications that provide
+their own security model (independent of the OS kernel) the above
+scheme is insufficient to identify the requesting user.
+
+<p>
+A more portable solution to storing the identity of the requesting
+user is to use the <tt/PAM_RUSER/ <tt/pam_get_item(3)/. The
+application should supply this value before attempting to authenticate
+the user with <tt/pam_authenticate()/. How well this name can be
+trusted will ultimately be at the discretion of the local
+administrator (who configures PAM for your application) and a selected
+module may attempt to override the value where it can obtain more
+reliable data. If an application is unable to determine the identity
+of the requesting entity/user, it should not call <tt/pam_set_item(3)/
+to set <tt/PAM_RUSER/.
+
+<p>
+In addition to the <tt/PAM_RUSER/ item, the application should supply
+the <tt/PAM_RHOST/ (<em/requesting host/) item. As a general rule, the
+following convention for its value can be assumed: <tt/&lt;unset&gt;/
+= unknown; <tt/localhost/ = invoked directly from the local system;
+<em/other.place.xyz/ = some component of the user's connection
+originates from this remote/requesting host. At present, PAM has no
+established convention for indicating whether the application supports
+a trusted path to communication from this host.
+
+<sect1>Sufficient resources
+
+<p>
+Care should be taken to ensure that the proper execution of an
+application is not compromised by a lack of system resources. If an
+application is unable to open sufficient files to perform its service,
+it should fail gracefully, or request additional resources.
+Specifically, the quantities manipulated by the <tt/setrlimit(2)/
+family of commands should be taken into consideration.
+
+<p>
+This is also true of conversation prompts. The application should not
+accept prompts of arbitrary length with out checking for resource
+allocation failure and dealing with such extreme conditions gracefully
+and in a mannor that preserves the PAM API. Such tolerance may be
+especially important when attempting to track a malicious adversary.
+
+<sect>A library of miscellaneous helper functions
+<label id="libpam-misc-section">
+
+<p>
+To aid the work of the application developer a library of
+miscellaneous functions is provided. It is called <tt/libpam_misc/,
+and contains functions for allocating memory (securely), a text based
+conversation function, and routines for enhancing the standard
+PAM-environment variable support.
+
+<sect1>Requirements
+
+<p>
+The functions, structures and macros, made available by this library
+can be defined by including <tt>&lt;security/pam_misc.h&gt;</tt>. It
+should be noted that this library is specific to <bf/Linux-PAM/ and is
+not referred to in the defining DCE-RFC (see <ref id="bibliography"
+name="the bibliography">) below.
+
+<sect1>Functions supplied
+
+<sect2>Safe string duplication
+
+<p>
+<tscreen>
+<verb>
+extern char *xstrdup(const char *s)
+</verb>
+</tscreen>
+Return a duplicate copy of the <tt/NUL/ terminated string,
+<tt/s/. <tt/NULL/ is returned if there is insufficient memory
+available for the duplicate or if <tt/s=NULL/.
+
+<sect2>A text based conversation function
+
+<p>
+<tscreen>
+<verb>
+extern int misc_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response, void *appdata_ptr);
+</verb>
+</tscreen>
+
+<p>
+This is a function that will prompt the user with the appropriate
+comments and obtain the appropriate inputs as directed by
+authentication modules.
+
+<p>
+In addition to simply slotting into the appropriate <tt/struct
+pam_conv/, this function provides some time-out facilities. The
+function exports five variables that can be used by an application
+programmer to limit the amount of time this conversation function will
+spend waiting for the user to type something.
+
+<p>
+The five variables are as follows:
+<descrip>
+<tag><tt>extern time_t pam_misc_conv_warn_time;</tt></tag>
+
+This variable contains the <em/time/ (as returned by <tt/time()/) that
+the user should be first warned that the clock is ticking. By default
+it has the value <tt/0/, which indicates that no such warning will be
+given. The application may set its value to sometime in the future,
+but this should be done prior to passing control to the <bf/Linux-PAM/
+library.
+
+<tag><tt>extern const char *pam_misc_conv_warn_line;</tt></tag>
+
+Used in conjuction with <tt/pam_misc_conv_warn_time/, this variable is
+a pointer to the string that will be displayed when it becomes time to
+warn the user that the timeout is approaching. Its default value is
+``..&bsol;a.Time is running out...&bsol;n'', but this can be changed
+by the application prior to passing control to <bf/Linux-PAM/.
+
+<tag><tt>extern time_t pam_misc_conv_die_time;</tt></tag>
+
+This variable contains the <em/time/ (as returned by <tt/time()/) that
+the conversation will time out. By default it has the value <tt/0/,
+which indicates that the conversation function will not timeout. The
+application may set its value to sometime in the future, this should
+be done prior to passing control to the <bf/Linux-PAM/ library.
+
+<tag><tt>extern const char *pam_misc_conv_die_line;</tt></tag>
+
+Used in conjuction with <tt/pam_misc_conv_die_time/, this variable is
+a pointer to the string that will be displayed when the conversation
+times out. Its default value is ``..&bsol;a.Sorry, your time is
+up!&bsol;n'', but this can be changed by the application prior to
+passing control to <bf/Linux-PAM/.
+
+<tag><tt>extern int pam_misc_conv_died;</tt></tag>
+
+Following a return from the <bf/Linux-PAM/ libraray, the value of this
+variable indicates whether the conversation has timed out. A value of
+<tt/1/ indicates the time-out occurred.
+
+<tag><tt>extern int (*pam_binary_handler_fn)(const union pam_u_packet_p send,
+ union pam_u_packet_p *receive);</tt></tag>
+
+This function pointer is initialized to <tt/NULL/ but can be filled
+with a function that provides machine-machine (hidden) message
+exchange. It is intended for use with hidden authentication protocols
+such as RSA or Diffie-Hellman key exchanges. (This is still under
+development.)
+
+</descrip>
+
+<sect2>Transcribing an environment to that of Linux-PAM
+<p>
+<tscreen>
+<verb>
+extern int pam_misc_paste_env(pam_handle_t *pamh,
+ const char * const * user_env);
+</verb>
+</tscreen>
+
+This function takes the supplied list of environment pointers and
+<em/uploads/ its contents to the <bf/Linux-PAM/ environment. Success
+is indicated by <tt/PAM_SUCCESS/.
+
+<sect2>Saving the Linux-PAM environment for later use
+<p>
+<tscreen>
+<verb>
+extern char **pam_misc_copy_env(pam_handle_t *pamh);
+</verb>
+</tscreen>
+
+This function returns a pointer to a list of environment variables
+that are a direct copy of the <bf/Linux-PAM/ environment. The memory
+associated with these variables are the responsibility of the
+application and should be liberated with a call to
+<tt/pam_misc_drop_env()/.
+
+<sect2>Liberating a locally saved environment
+<p>
+<tscreen>
+<verb>
+extern char **pam_misc_drop_env(char **env);
+</verb>
+</tscreen>
+
+This function is defined to complement the <tt/pam_misc_copy_env()/
+function. It liberates the memory associated with <tt/env/,
+<em/overwriting/ with <tt/0/ all memory before <tt/free()/ing it.
+
+<sect2>BSD like Linux-PAM environment variable setting
+<p>
+<tscreen>
+<verb>
+extern int pam_misc_setenv(pam_handle_t *pamh, const char *name,
+ const char *value, int readonly);
+</verb>
+</tscreen>
+
+This function performs a task equivalent to <tt/pam_putenv()/, its
+syntax is, however, more like the BSD style function; <tt/setenv()/.
+The <tt/name/ and <tt/value/ are concatenated with an ``<tt/=/'' to
+form a <tt/name_value/ and passed to <tt/pam_putenv()/. If, however,
+the <bf/Linux-PAM/ variable is already set, the replacement will only
+be applied if the last argument, <tt/readonly/, is zero.
+
+<sect>Porting legacy applications
+
+<p>
+The following is extracted from an email. I'll tidy it up later.
+
+<p>
+The point of PAM is that the application is not supposed to have any
+idea how the attatched authentication modules will choose to
+authenticate the user. So all they can do is provide a conversation
+function that will talk directly to the user(client) on the modules'
+behalf.
+
+<p>
+Consider the case that you plug a retinal scanner into the login
+program. In this situation the user would be prompted: "please look
+into the scanner". No username or password would be needed - all this
+information could be deduced from the scan and a database lookup. The
+point is that the retinal scanner is an ideal task for a "module".
+
+<p>
+While it is true that a pop-daemon program is designed with the POP
+protocol in mind and no-one ever considered attatching a retinal
+scanner to it, it is also the case that the "clean" PAM'ification of
+such a daemon would allow for the possibility of a scanner module
+being be attatched to it. The point being that the "standard"
+pop-authentication protocol(s) [which will be needed to satisfy
+inflexible/legacy clients] would be supported by inserting an
+appropriate pam_qpopper module(s). However, having rewritten popd
+once in this way any new protocols can be implemented in-situ.
+
+<p>
+One simple test of a ported application would be to insert the
+<tt/pam_permit/ module and see if the application demands you type a
+password... In such a case, <tt/xlock/ would fail to lock the
+terminal - or would at best be a screen-saver, ftp would give password
+free access to all etc.. Neither of these is a very secure thing to
+do, but they do illustrate how much flexibility PAM puts in the hands
+of the local admin.
+
+<p>
+The key issue, in doing things correctly, is identifying what is part
+of the authentication procedure (how many passwords etc..) the
+exchange protocol (prefixes to prompts etc., numbers like 331 in the
+case of ftpd) and what is part of the service that the application
+delivers. PAM really needs to have total control in the
+authentication "proceedure", the conversation function should only
+deal with reformatting user prompts and extracting responses from raw
+input.
+
+<sect>Glossary of PAM related terms
+
+<p>
+The following are a list of terms used within this document.
+
+<p>
+<descrip>
+
+<tag>Authentication token</tag>
+Generally, this is a password. However, a user can authenticate
+him/herself in a variety of ways. Updating the user's authentication
+token thus corresponds to <em>refreshing</em> the object they use to
+authenticate themself with the system. The word password is avoided
+to keep open the possibility that the authentication involves a
+retinal scan or other non-textual mode of challenge/response.
+
+<tag>Credentials</tag>
+Having successfully authenticated the user, PAM is able to establish
+certain characteristics/attributes of the user. These are termed
+<em>credentials</em>. Examples of which are group memberships to
+perform privileged tasks with, and <em>tickets</em> in the form of
+environment variables etc. . Some user-credentials, such as the
+user's UID and GID (plus default group memberships) are not deemed to
+be PAM-credentials. It is the responsibility of the application to
+grant these directly.
+
+</descrip>
+
+<sect>An example application
+
+<p>
+To get a flavor of the way a <tt/Linux-PAM/ application is written we
+include the following example. It prompts the user for their password
+and indicates whether their account is valid on the standard output,
+its return code also indicates the success (<tt/0/ for success; <tt/1/
+for failure).
+
+<p>
+<tscreen>
+<verb>
+/*
+ This program was contributed by Shane Watts
+ [modifications by AGM]
+
+ You need to add the following (or equivalent) to the /etc/pam.conf file.
+ # check authorization
+ check_user auth required /usr/lib/security/pam_unix_auth.so
+ check_user account required /usr/lib/security/pam_unix_acct.so
+ */
+
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+#include <stdio.h>
+
+static struct pam_conv conv = {
+ misc_conv,
+ NULL
+};
+
+int main(int argc, char *argv[])
+{
+ pam_handle_t *pamh=NULL;
+ int retval;
+ const char *user="nobody";
+
+ if(argc == 2) {
+ user = argv[1];
+ }
+
+ if(argc > 2) {
+ fprintf(stderr, "Usage: check_user [username]\n");
+ exit(1);
+ }
+
+ retval = pam_start("check_user", user, &ero;conv, &ero;pamh);
+
+ if (retval == PAM_SUCCESS)
+ retval = pam_authenticate(pamh, 0); /* is user really user? */
+
+ if (retval == PAM_SUCCESS)
+ retval = pam_acct_mgmt(pamh, 0); /* permitted access? */
+
+ /* This is where we have been authorized or not. */
+
+ if (retval == PAM_SUCCESS) {
+ fprintf(stdout, "Authenticated\n");
+ } else {
+ fprintf(stdout, "Not Authenticated\n");
+ }
+
+ if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */
+ pamh = NULL;
+ fprintf(stderr, "check_user: failed to release authenticator\n");
+ exit(1);
+ }
+
+ return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */
+}
+</verb>
+</tscreen>
+
+<sect>Files
+
+<p><descrip>
+
+<tag><tt>/usr/include/security/pam_appl.h</tt></tag>
+
+header file for <bf/Linux-PAM/ applications interface
+
+<tag><tt>/usr/include/security/pam_misc.h</tt></tag>
+
+header file for useful library functions for making applications
+easier to write
+
+<tag><tt>/usr/lib/libpam.so.*</tt></tag>
+
+the shared library providing applications with access to
+<bf/Linux-PAM/.
+
+<tag><tt>/etc/pam.conf</tt></tag>
+
+the <bf/Linux-PAM/ configuration file.
+
+<tag><tt>/usr/lib/security/pam_*.so</tt></tag>
+
+the primary location for <bf/Linux-PAM/ dynamically loadable object
+files; the modules.
+
+</descrip>
+
+<sect>See also
+<label id="bibliography">
+
+<p><itemize>
+
+<item>The <bf/Linux-PAM/
+<htmlurl url="pam.html" name="System Administrators' Guide">.
+
+<item>The <bf/Linux-PAM/
+<htmlurl url="pam_modules.html" name="Module Writers' Guide">.
+
+<item>The V. Samar and R. Schemers (SunSoft), ``UNIFIED LOGIN WITH
+PLUGGABLE AUTHENTICATION MODULES'', Open Software Foundation Request
+For Comments 86.0, October 1995.
+
+</itemize>
+
+<sect>Notes
+
+<p>
+I intend to put development comments here... like ``at the moment
+this isn't actually supported''. At release time what ever is in
+this section will be placed in the Bugs section below! :)
+
+<p>
+<itemize>
+
+<item> <tt/pam_strerror()/ should be internationalized....
+
+<item>
+Note, the <tt/resp_retcode/ of struct <tt/pam_message/, has no
+purpose at the moment. Ideas/suggestions welcome!
+
+<item> more security issues are required....
+
+</itemize>
+
+<sect>Author/acknowledgments
+
+<p>
+This document was written by Andrew G. Morgan
+(morgan@transmeta.com) with many contributions from
+<!-- insert credits here -->
+<!--
+ an sgml list of people to credit for their contributions to Linux-PAM
+ $Id$
+ -->
+Chris Adams,
+Peter Allgeyer,
+Tim Baverstock,
+Tim Berger,
+Craig S. Bell,
+Derrick J. Brashear,
+Ben Buxton,
+Seth Chaiklin,
+Oliver Crow,
+Chris Dent,
+Marc Ewing,
+Cristian Gafton,
+Emmanuel Galanos,
+Brad M. Garcia,
+Eric Hester,
+Roger Hu,
+Eric Jacksch,
+Michael K. Johnson,
+David Kinchlea,
+Olaf Kirch,
+Marcin Korzonek,
+Stephen Langasek,
+Nicolai Langfeldt,
+Elliot Lee,
+Luke Kenneth Casson Leighton,
+Al Longyear,
+Ingo Luetkebohle,
+Marek Michalkiewicz,
+Robert Milkowski,
+Aleph One,
+Martin Pool,
+Sean Reifschneider,
+Jan Rekorajski,
+Erik Troan,
+Theodore Ts'o,
+Jeff Uphoff,
+Myles Uyema,
+Savochkin Andrey Vladimirovich,
+Ronald Wahl,
+David Wood,
+John Wilmes,
+Joseph S. D. Yao
+and
+Alex O. Yuriev.
+
+<p>
+Thanks are also due to Sun Microsystems, especially to Vipin Samar and
+Charlie Lai for their advice. At an early stage in the development of
+<bf/Linux-PAM/, Sun graciously made the documentation for their
+implementation of PAM available. This act greatly accelerated the
+development of <bf/Linux-PAM/.
+
+<sect>Bugs/omissions
+
+<p>
+This manual is hopelessly unfinished. Only a partial list of people is
+credited for all the good work they have done.
+
+<sect>Copyright information for this document
+
+<p>
+Copyright (c) Andrew G. Morgan 1996-9. All rights reserved.
+<newline>
+Email: <tt>&lt;morgan@transmeta.com&gt;</tt>
+
+<p>
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+<p>
+<itemize>
+
+<item>
+1. Redistributions of source code must retain the above copyright
+ notice, and the entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+<item>
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+<item>
+3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+</itemize>
+
+<p>
+<bf/Alternatively/, this product may be distributed under the terms of
+the GNU General Public License (GPL), in which case the provisions of
+the GNU GPL are required <bf/instead of/ the above restrictions.
+(This clause is necessary due to a potential bad interaction between
+the GNU GPL and the restrictions contained in a BSD-style copyright.)
+
+<p>
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+<p>
+<tt>$Id$</tt>
+
+</article>
diff --git a/doc/pam_modules.sgml b/doc/pam_modules.sgml
new file mode 100644
index 00000000..e76e3d7a
--- /dev/null
+++ b/doc/pam_modules.sgml
@@ -0,0 +1,1476 @@
+<!doctype linuxdoc system>
+
+<!--
+
+ $Id$
+
+ Copyright (c) Andrew G. Morgan 1996-9. All rights reserved.
+
+ ** some sections, in this document, were contributed by other
+ ** authors. They carry individual copyrights.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, and the entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ALTERNATIVELY, this product may be distributed under the terms of the
+GNU General Public License, in which case the provisions of the GNU
+GPL are required INSTEAD OF the above restrictions. (This clause is
+necessary due to a potential bad interaction between the GNU GPL and
+the restrictions contained in a BSD-style copyright.)
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+ -->
+
+<article>
+
+<title>The Linux-PAM Module Writers' Guide
+<author>Andrew G. Morgan, <tt>morgan@linux.kernel.org</tt>
+<date>DRAFT v0.71 1999/11/8
+<abstract>
+This manual documents what a programmer needs to know in order to
+write a module that conforms to the <bf/Linux-PAM/ standard. It also
+discusses some security issues from the point of view of the module
+programmer.
+</abstract>
+
+<toc>
+
+<sect>Introduction
+
+<sect1> Synopsis
+<p>
+<tscreen>
+<verb>
+#include <security/pam_modules.h>
+
+gcc -fPIC -c pam_module-name.c
+ld -x --shared -o pam_module-name.so pam_module-name.o -lpam
+</verb>
+</tscreen>
+
+<sect1> Description
+
+<p>
+<bf/Linux-PAM/ (Pluggable Authentication Modules for Linux) is a
+library that enables the local system administrator to choose how
+individual applications authenticate users. For an overview of the
+<bf/Linux-PAM/ library see the <bf/Linux-PAM/ System Administrators'
+Guide.
+
+<p>
+A <bf/Linux-PAM/ module is a single executable binary file that can be
+loaded by the <bf/Linux-PAM/ interface library. This PAM library is
+configured locally with a system file, <tt>/etc/pam.conf</tt>, to
+authenticate a user request via the locally available authentication
+modules. The modules themselves will usually be located in the
+directory <tt>/usr/lib/security</tt> and take the form of dynamically
+loadable object files (see dlopen(3)). Alternatively, the modules can
+be statically linked into the <bf/Linux-PAM/ library; this is mostly to
+allow <bf/Linux-PAM/ to be used on platforms without dynamic linking
+available, but the two forms can be used together. It is the
+<bf/Linux-PAM/ interface that is called by an application and it is
+the responsibility of the library to locate, load and call the
+appropriate functions in a <bf/Linux-PAM/-module.
+
+<p>
+Except for the immediate purpose of interacting with the user
+(entering a password etc..) the module should never call the
+application directly. This exception requires a "conversation
+mechanism" which is documented below.
+
+<sect>What can be expected by the module
+
+<p>
+Here we list the interface that the conventions that all
+<bf/Linux-PAM/ modules must adhere to.
+
+<sect1>Getting and setting <tt/PAM_ITEM/s and <em/data/
+
+<p>
+First, we cover what the module should expect from the <bf/Linux-PAM/
+library and a <bf/Linux-PAM/ <em/aware/ application. Essesntially this
+is the <tt/libpam.*/ library.
+
+<sect2>
+Setting data
+
+<p>
+Synopsis:
+<tscreen>
+<verb>
+extern int pam_set_data(pam_handle_t *pamh,
+ const char *module_data_name,
+ void *data,
+ void (*cleanup)(pam_handle_t *pamh,
+ void *data, int error_status) );
+</verb>
+</tscreen>
+
+<p>
+The modules may be dynamically loadable objects. In general such files
+should not contain <tt/static/ variables. This and the subsequent
+function provide a mechanism for a module to associate some data with
+the handle <tt/pamh/. Typically a module will call the
+<tt/pam_set_data()/ function to register some data under a (hopefully)
+unique <tt/module_data_name/. The data is available for use by other
+modules too but <em/not/ by an application.
+
+<p>
+The function <tt/cleanup()/ is associated with the <tt/data/ and, if
+non-<tt/NULL/, it is called when this data is over-written or
+following a call to <tt/pam_end()/ (see the Linux-PAM Application
+Developers' Guide).
+
+<p>
+The <tt/error_status/ argument is used to indicate to the module the
+sort of action it is to take in cleaning this data item. As an
+example, Kerberos creates a ticket file during the authentication
+phase, this file might be associated with a data item. When
+<tt/pam_end()/ is called by the module, the <tt/error_status/
+carries the return value of the <tt/pam_authenticate()/ or other
+<tt/libpam/ function as appropriate. Based on this value the Kerberos
+module may choose to delete the ticket file (<em/authentication
+failure/) or leave it in place.
+
+<p>
+The <tt/error_status/ may have been logically OR'd with either of the
+following two values:
+
+<p>
+<descrip>
+<tag><tt/PAM_DATA_REPLACE/</tag>
+ When a data item is being replaced (through a second call to
+<tt/pam_set_data()/) this mask is used. Otherwise, the call is assumed
+to be from <tt/pam_end()/.
+
+<tag><tt/PAM_DATA_SILENT/</tag>
+ Which indicates that the process would prefer to perform the
+<tt/cleanup()/ quietly. That is, discourages logging/messages to the
+user.
+
+</descrip>
+
+
+<sect2>
+Getting data
+
+<p>
+Synopsis:
+<tscreen>
+<verb>
+extern int pam_get_data(const pam_handle_t *pamh,
+ const char *module_data_name,
+ const void **data);
+</verb>
+</tscreen>
+
+<p>
+This function together with the previous one provides a method of
+associating module-specific data with the handle <tt/pamh/. A
+successful call to <tt/pam_get_data/ will result in <tt/*data/
+pointing to the data associated with the <tt/module_data_name/. Note,
+this data is <em/not/ a copy and should be treated as <em/constant/
+by the module.
+
+<p>
+Note, if there is an entry but it has the value <tt/NULL/, then this
+call returns <tt/PAM_NO_MODULE_DATA/.
+
+<sect2>
+Setting items
+
+<p>
+Synopsis:
+<tscreen>
+<verb>
+extern int pam_set_item(pam_handle_t *pamh
+ , int item_type
+ , const void *item
+ );
+</verb>
+</tscreen>
+
+<p>
+This function is used to (re)set the value of one of the
+<tt/item_type/s. The reader is urged to read the entry for this
+function in the <bf/Linux-PAM/ application developers' manual.
+
+<p>
+In addition to the <tt/item/s listed there, the module can set the
+following two <tt/item_type/s:
+
+<p>
+<descrip>
+<tag><tt/PAM_AUTHTOK/</tag>
+
+The authentication token (password). This token should be ignored by
+all module functions besides <tt/pam_sm_authenticate()/ and
+<tt/pam_sm_chauthtok()/. In the former function it is used to pass the
+most recent authentication token from one stacked module to
+another. In the latter function the token is used for another
+purpose. It contains the currently active authentication token.
+
+<tag><tt/PAM_OLDAUTHTOK/</tag>
+
+The old authentication token. This token should be ignored by all
+module functions except <tt/pam_sm_chauthtok()/.
+
+</descrip>
+
+<p>
+Both of these items are reset before returning to the application.
+When resetting these items, the <bf/Linux-PAM/ library first writes
+<tt/0/'s to the current tokens and then <tt/free()/'s the associated
+memory.
+
+<p>
+The return values for this function are listed in the
+<bf>Linux-PAM</bf> Application Developers' Guide.
+
+<sect2>
+Getting items
+
+<p>
+Synopsis:
+<tscreen>
+<verb>
+extern int pam_get_item(const pam_handle_t *pamh
+ , int item_type
+ , const void **item
+ );
+</verb>
+</tscreen>
+
+<p>
+This function is used to obtain the value of the specified
+<tt/item_type/. It is better documented in the <bf/Linux-PAM/
+Application Developers' Guide. However, there are three things worth
+stressing here:
+<itemize>
+
+<item>
+Generally, if the module wishes to obtain the name of the user, it
+should not use this function, but instead perform a call to
+<tt/pam_get_user()/ (see section <ref id="pam-get-user"
+name="below">).
+
+<item>
+The module is additionally privileged to read the authentication
+tokens, <tt/PAM_AUTHTOK/ and <tt/PAM_OLDAUTHTOK/ (see the section
+above on <tt/pam_set_data()/).
+
+<item>
+The module should <em/not/ <tt/free()/ or alter the data pointed to by
+<tt/*item/ after a successful return from <tt/pam_get_item()/. This
+pointer points directly at the data contained within the <tt/*pamh/
+structure. Should a module require that a change is made to the this
+<tt/ITEM/ it should make the appropriate call to <tt/pam_set_item()/.
+</itemize>
+
+<sect2>The <em/conversation/ mechanism
+
+<p>
+Following the call <tt>pam_get_item(pamh,PAM_CONV,&amp;item)</tt>, the
+pointer <tt/item/ points to a <em/conversation/-function that provides
+limited but direct access to the application. The purpose of this
+function is to allow the module to prompt the user for their password
+and pass other information in a manner consistent with the
+application. For example, an X-windows based program might pop up a
+dialog box to report a login failure. Just as the application should
+not be concerned with the method of authentication, so the module
+should not dictate the manner in which input (output) is
+obtained from (presented to) to the user.
+
+<p>
+The reader is strongly urged to read the more complete description of
+the <tt/pam_conv/ structure, written from the perspective of the
+application developer, in the <bf/Linux-PAM/ Application Developers'
+Guide.
+
+<p>
+The <tt/pam_response/ structure returned after a call to the
+<tt/pam_conv/ function must be <tt/free()/'d by the module. Since the
+call to the conversation function originates from the module, it is
+clear that either this <tt/pam_response/ structure could be either
+statically or dynamically (using <tt/malloc()/ etc.) allocated within
+the application. Repeated calls to the conversation function would
+likely overwrite static memory, so it is required that for a
+successful return from the conversation function the memory for the
+response structure is dynamically allocated by the application with
+one of the <tt/malloc()/ family of commands and <em/must/ be
+<tt/free()/'d by the module.
+
+<p>
+If the <tt/pam_conv/ mechanism is used to enter authentication tokens,
+the module should either pass the result to the <tt/pam_set_item()/
+library function, or copy it itself. In such a case, once the token
+has been stored (by one of these methods or another one), the memory
+returned by the application should be overwritten with <tt/0/'s, and
+then <tt/free()/'d.
+
+<p>
+The return values for this function are listed in the
+<bf>Linux-PAM</bf> Application Developers' Guide.
+
+<sect2>Getting the name of a user<label id="pam-get-user">
+
+<p>
+Synopsis:
+<tscreen>
+<verb>
+extern int pam_get_user(pam_handle_t *pamh,
+ const char **user,
+ const char *prompt);
+</verb>
+</tscreen>
+
+<p>
+This is a <bf/Linux-PAM/ library function that returns the
+(prospective) name of the user. To determine the username it does the
+following things, in this order:
+<itemize>
+
+<item> checks what <tt/pam_get_item(pamh, PAM_USER, ... );/ would have
+returned. If this is not <tt/NULL/ this is what it returns. Otherwise,
+
+<item> obtains a username from the application via the <tt/pam_conv/
+mechanism, it prompts the user with the first non-<tt/NULL/ string in
+the following list:
+<itemize>
+
+<item> The <tt/prompt/ argument passed to the function
+<item> What is returned by <tt/pam_get_item(pamh,PAM_USER_PROMPT, ... );/
+<item> The default prompt: ``Please enter username: ''
+
+</itemize>
+</itemize>
+
+<p>
+By whatever means the username is obtained, a pointer to it is
+returned as the contents of <tt/*user/. Note, this memory should
+<em/not/ be <tt/free()/'d by the module. Instead, it will be liberated
+on the next call to <tt/pam_get_user()/, or by <tt/pam_end()/ when the
+application ends its interaction with <bf/Linux-PAM/.
+
+<p>
+Also, in addition, it should be noted that this function sets the
+<tt/PAM_USER/ item that is associated with the <tt/pam_[gs]et_item()/
+function.
+
+<p>
+The return value of this function is one of the following:
+<itemize>
+
+<item> <tt/PAM_SUCCESS/ - username obtained.
+
+<item> <tt/PAM_CONV_AGAIN/ - converstation did not complete and the
+caller is required to return control to the application, until such
+time as the application has completed the conversation process. A
+module calling <tt/pam_get_user()/ that obtains this return code,
+should return <tt/PAM_INCOMPLETE/ and be prepared (when invoked the
+next time) to recall <tt/pam_get_user()/ to fill in the user's name,
+and then pick up where it left off as if nothing had happened. This
+procedure is needed to support an event-driven application programming
+model.
+
+<item> <tt/PAM_CONV_ERR/ - the conversation method supplied by the
+application failed to obtain the username.
+
+</itemize>
+
+<sect2>Setting a Linux-PAM environment variable
+
+<p>
+Synopsis:
+<tscreen>
+<verb>
+extern int pam_putenv(pam_handle_t *pamh, const char *name_value);
+</verb>
+</tscreen>
+
+<p>
+<bf/Linux-PAM/ (0.54+) comes equipped with a series of functions for
+maintaining a set of <em/environment/ variables. The environment is
+initialized by the call to <tt/pam_start()/ and is <bf/erased/ with a
+call to <tt/pam_end()/. This <em/environment/ is associated with the
+<tt/pam_handle_t/ pointer returned by the former call.
+
+<p>
+The default environment is all but empty. It contains a single
+<tt/NULL/ pointer, which is always required to terminate the
+variable-list. The <tt/pam_putenv()/ function can be used to add a
+new environment variable, replace an existing one, or delete an old
+one.
+
+<p>
+<itemize>
+<item>Adding/replacing a variable<newline>
+
+To add or overwrite a <bf/Linux-PAM/ environment variable the value of
+the argument <tt/name_value/, should be of the following form:
+<tscreen>
+<verb>
+name_value="VARIABLE=VALUE OF VARIABLE"
+</verb>
+</tscreen>
+Here, <tt/VARIABLE/ is the environment variable's name and what
+follows the `<tt/=/' is its (new) value. (Note, that <tt/"VARIABLE="/
+is a valid value for <tt/name_value/, indicating that the variable is
+set to <tt/""/.)
+
+<item> Deleting a variable<newline>
+
+To delete a <bf/Linux-PAM/ environment variable the value of
+the argument <tt/name_value/, should be of the following form:
+<tscreen>
+<verb>
+name_value="VARIABLE"
+</verb>
+</tscreen>
+Here, <tt/VARIABLE/ is the environment variable's name and the absence
+of an `<tt/=/' indicates that the variable should be removed.
+
+</itemize>
+
+<p>
+In all cases <tt/PAM_SUCCESS/ indicates success.
+
+<sect2>Getting a Linux-PAM environment variable
+
+<p>
+Synopsis:
+<tscreen>
+<verb>
+extern const char *pam_getenv(pam_handle_t *pamh, const char *name);
+</verb>
+</tscreen>
+
+<p>
+This function can be used to return the value of the given
+variable. If the returned value is <tt/NULL/, the variable is not
+known.
+
+<sect2>Listing the Linux-PAM environment
+
+<p>
+Synopsis:
+<tscreen>
+<verb>
+extern char * const *pam_getenvlist(pam_handle_t *pamh);
+</verb>
+</tscreen>
+
+<p>
+This function returns a pointer to the entire <bf/Linux-PAM/
+environment array. At first sight the <em/type/ of the returned data
+may appear a little confusing. It is basically a <em/read-only/ array
+of character pointers, that lists the <tt/NULL/ terminated list of
+environment variables set so far.
+
+<p>
+Although, this is not a concern for the module programmer, we mention
+here that an application should be careful to copy this entire array
+before executing <tt/pam_end()/ otherwise all the variable information
+will be lost. (There are functions in <tt/libpam_misc/ for this
+purpose: <tt/pam_misc_copy_env()/ and <tt/pam_misc_drop_env()/.)
+
+<sect1>Other functions provided by <tt/libpam/
+
+<sect2>Understanding errors
+
+<p>
+<itemize>
+
+<item>
+<tt>extern const char *pam_strerror(pam_handle_t *pamh, int errnum);</tt>
+
+<p>
+This function returns some text describing the <bf/Linux-PAM/ error
+associated with the argument <tt/errnum/. If the error is not
+recognized <tt/``Unknown Linux-PAM error''/ is returned.
+
+</itemize>
+
+<sect2>Planning for delays
+
+<p>
+<itemize>
+
+<item>
+<tt>extern int pam_fail_delay(pam_handle_t *pamh, unsigned int
+micro_sec)</tt>
+
+<p>
+This function is offered by <bf/Linux-PAM/ to facilitate time delays
+following a failed call to <tt/pam_authenticate()/ and before control
+is returned to the application. When using this function the module
+programmer should check if it is available with,
+<tscreen>
+<verb>
+#ifdef HAVE_PAM_FAIL_DELAY
+ ....
+#endif /* HAVE_PAM_FAIL_DELAY */
+</verb>
+</tscreen>
+
+<p>
+Generally, an application requests that a user is authenticated by
+<bf/Linux-PAM/ through a call to <tt/pam_authenticate()/ or
+<tt/pam_chauthtok()/. These functions call each of the <em/stacked/
+authentication modules listed in the <bf/Linux-PAM/ configuration
+file. As directed by this file, one of more of the modules may fail
+causing the <tt/pam_...()/ call to return an error. It is desirable
+for there to also be a pause before the application continues. The
+principal reason for such a delay is security: a delay acts to
+discourage <em/brute force/ dictionary attacks primarily, but also
+helps hinder <em/timed/ (cf. covert channel) attacks.
+
+<p>
+The <tt/pam_fail_delay()/ function provides the mechanism by which an
+application or module can suggest a minimum delay (of <tt/micro_sec/
+<em/micro-seconds/). <bf/Linux-PAM/ keeps a record of the longest time
+requested with this function. Should <tt/pam_authenticate()/ fail,
+the failing return to the application is delayed by an amount of time
+randomly distributed (by up to 25%) about this longest value.
+
+<p>
+Independent of success, the delay time is reset to its zero default
+value when <bf/Linux-PAM/ returns control to the application.
+
+</itemize>
+
+<sect>What is expected of a module
+
+<p>
+The module must supply a sub-set of the six functions listed
+below. Together they define the function of a <bf/Linux-PAM
+module/. Module developers are strongly urged to read the comments on
+security that follow this list.
+
+<sect1> Overview
+
+<p>
+The six module functions are grouped into four independent management
+groups. These groups are as follows: <em/authentication/,
+<em/account/, <em/session/ and <em/password/. To be properly defined,
+a module must define all functions within at least one of these
+groups. A single module may contain the necessary functions for
+<em/all/ four groups.
+
+<sect2> Functional independence
+
+<p>
+The independence of the four groups of service a module can offer
+means that the module should allow for the possibility that any one of
+these four services may legitimately be called in any order. Thus, the
+module writer should consider the appropriateness of performing a
+service without the prior success of some other part of the module.
+
+<p>
+As an informative example, consider the possibility that an
+application applies to change a user's authentication token, without
+having first requested that <bf/Linux-PAM/ authenticate the user. In
+some cases this may be deemed appropriate: when <tt/root/ wants to
+change the authentication token of some lesser user. In other cases it
+may not be appropriate: when <tt/joe/ maliciously wants to reset
+<tt/alice/'s password; or when anyone other than the user themself
+wishes to reset their <em/KERBEROS/ authentication token. A policy for
+this action should be defined by any reasonable authentication scheme,
+the module writer should consider this when implementing a given
+module.
+
+<sect2> Minimizing administration problems
+
+<p>
+To avoid system administration problems and the poor construction of a
+<tt>/etc/pam.conf</tt> file, the module developer may define all
+six of the following functions. For those functions that would not be
+called, the module should return <tt/PAM_SERVICE_ERR/ and write an
+appropriate message to the system log. When this action is deemed
+inappropriate, the function would simply return <tt/PAM_IGNORE/.
+
+<sect2> Arguments supplied to the module
+
+<p>
+The <tt/flags/ argument of each of the following functions can be
+logically OR'd with <tt/PAM_SILENT/, which is used to inform the
+module to not pass any <em/text/ (errors or warnings) to the
+application.
+
+<p>
+The <tt/argc/ and <tt/argv/ arguments are taken from the line
+appropriate to this module---that is, with the <em/service_name/
+matching that of the application---in the configuration file (see the
+<bf/Linux-PAM/ System Administrators' Guide). Together these two
+parameters provide the number of arguments and an array of pointers to
+the individual argument tokens. This will be familiar to C programmers
+as the ubiquitous method of passing command arguments to the function
+<tt/main()/. Note, however, that the first argument (<tt/argv[0]/) is
+a true argument and <bf/not/ the name of the module.
+
+<sect1> Authentication management
+
+<p>
+To be correctly initialized, <tt/PAM_SM_AUTH/ must be <tt/#define/'d
+prior to including <tt>&lt;security/pam_modules.h&gt;</tt>. This will
+ensure that the prototypes for static modules are properly declared.
+
+<p>
+<itemize>
+
+<item>
+<tt>PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+int argc, const char **argv);</tt>
+
+<p>
+This function performs the task of authenticating the user.
+
+<p>
+The <tt/flags/ argument can be a logically OR'd with <tt/PAM_SILENT/
+and optionally take the following value:
+
+<p><descrip>
+<tag><tt/PAM_DISALLOW_NULL_AUTHTOK/</tag>
+ return <tt/PAM_AUTH_ERR/ if the database of authentication
+tokens for this authentication mechanism has a <tt/NULL/ entry for the
+user. Without this flag, such a <tt/NULL/ token will lead to a success
+without the user being prompted.
+</descrip>
+
+<p>
+Besides <tt/PAM_SUCCESS/ return values that can be sent by this
+function are one of the following:
+
+<descrip>
+
+<tag><tt/PAM_AUTH_ERR/</tag>
+ The user was not authenticated
+<tag><tt/PAM_CRED_INSUFFICIENT/</tag>
+ For some reason the application does not have sufficient
+credentials to authenticate the user.
+<tag><tt/PAM_AUTHINFO_UNAVAIL/</tag>
+ The modules were not able to access the authentication
+information. This might be due to a network or hardware failure etc.
+<tag><tt/PAM_USER_UNKNOWN/</tag>
+ The supplied username is not known to the authentication
+service
+<tag><tt/PAM_MAXTRIES/</tag>
+ One or more of the authentication modules has reached its
+limit of tries authenticating the user. Do not try again.
+
+</descrip>
+
+<item>
+<tt>PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int
+argc, const char **argv);</tt>
+
+<p>
+This function performs the task of altering the credentials of the
+user with respect to the corresponding authorization
+scheme. Generally, an authentication module may have access to more
+information about a user than their authentication token. This
+function is used to make such information available to the
+application. It should only be called <em/after/ the user has been
+authenticated and after a session has been established.
+
+<p>
+Permitted flags, one of which, may be logically OR'd with
+<tt/PAM_SILENT/ are,
+
+<p><descrip>
+<tag><tt/PAM_ESTABLISH_CRED/</tag>
+ Set the credentials for the authentication service,
+<tag><tt/PAM_DELETE_CRED/</tag>
+ Delete the credentials associated with the authentication service,
+<tag><tt/PAM_REINITIALIZE_CRED/</tag>
+ Reinitialize the user credentials, and
+<tag><tt/PAM_REFRESH_CRED/</tag>
+ Extend the lifetime of the user credentials.
+</descrip>
+
+<p>
+Generally, the module should attempt to return the same error code as
+<tt/pam_sm_authenticate/ did. This will preserve the logic followed
+by libpam as it executes the stack of <em/authentication/ modules,
+when the application calls <tt/pam_authenticate()/ and
+<tt/pam_setcred()/. Failing to do this, will lead to much confudion
+on the part of the System administrator.
+
+<p>
+<bf>The following text is depreciated. Some thought needs to be given to
+how the credential setting modules are supposed to be stacked...</bf>
+
+<p>
+Besides <tt/PAM_SUCCESS/, the module may return one of the following
+errors:
+
+<p><descrip>
+<tag><tt/PAM_CRED_UNAVAIL/</tag>
+ This module cannot retrieve the user's credentials.
+<tag><tt/PAM_CRED_EXPIRED/</tag>
+ The user's credentials have expired.
+<tag><tt/PAM_USER_UNKNOWN/</tag>
+ The user is not known to this authentication module.
+<tag><tt/PAM_CRED_ERR/</tag>
+ This module was unable to set the credentials of the user.
+</descrip>
+
+</itemize>
+
+<sect1> Account management
+
+<p>
+To be correctly initialized, <tt/PAM_SM_ACCOUNT/ must be
+<tt/#define/'d prior to including <tt>&lt;security/pam_modules.h&gt;</tt>.
+This will ensure that the prototype for a static module is properly
+declared.
+
+<p>
+<itemize>
+
+<item>
+<tt>PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int
+argc, const char **argv);</tt>
+
+<p>
+This function performs the task of establishing whether the user is
+permitted to gain access at this time. It should be understood that
+the user has previously been validated by an authentication
+module. This function checks for other things. Such things might be:
+the time of day or the date, the terminal line, remote
+hostname, etc. .
+
+<p>
+This function may also determine things like the expiration on
+passwords, and respond that the user change it before continuing.
+
+<p>
+Valid flags, which may be logically OR'd with <tt/PAM_SILENT/, are the
+same as those applicable to the <tt/flags/ argument of
+<tt/pam_sm_authenticate/.
+
+<p>
+This function may return one of the following errors,
+
+<descrip>
+
+<tag><tt/PAM_ACCT_EXPIRED/</tag>
+ The user is no longer permitted access to the system.
+<tag><tt/PAM_AUTH_ERR/</tag>
+ There was an authentication error.
+<tag><tt/PAM_AUTHTOKEN_REQD/</tag>
+ The user's authentication token has expired. Before calling
+this function again the application will arrange for a new one to be
+given. This will likely result in a call to <tt/pam_sm_chauthtok()/.
+<tag><tt/PAM_USER_UNKNOWN/</tag>
+ The user is not known to the module's account management
+component.
+
+</descrip>
+
+</itemize>
+
+<sect1> Session management
+
+<p>
+To be correctly initialized, <tt/PAM_SM_SESSION/ must be
+<tt/#define/'d prior to including
+<tt>&lt;security/pam_modules.h&gt;</tt>. This will ensure that the
+prototypes for static modules are properly declared.
+
+<p>
+The following two functions are defined to handle the
+initialization/termination of a session. For example, at the beginning
+of a session the module may wish to log a message with the system
+regarding the user. Similarly, at the end of the session the module
+would inform the system that the user's session has ended.
+
+<p>
+It should be possible for sessions to be opened by one application and
+closed by another. This either requires that the module uses only
+information obtained from <tt/pam_get_item()/, or that information
+regarding the session is stored in some way by the operating system
+(in a file for example).
+
+<p>
+<itemize>
+
+<item>
+<tt>PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int
+argc, const char **argv);</tt>
+
+<p>
+This function is called to commence a session. The only valid, but
+optional, flag is <tt/PAM_SILENT/.
+
+<p>
+As a return value, <tt/PAM_SUCCESS/ signals success and
+<tt/PAM_SESSION_ERR/ failure.
+
+<item>
+<tt>PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int
+argc, const char **argv);</tt>
+
+<p>
+This function is called to terminate a session. The only valid, but
+optional, flag is <tt/PAM_SILENT/.
+
+<p>
+As a return value, <tt/PAM_SUCCESS/ signals success and
+<tt/PAM_SESSION_ERR/ failure.
+
+</itemize>
+
+<sect1> Password management
+
+<p>
+To be correctly initialized, <tt/PAM_SM_PASSWORD/ must be
+<tt/#define/'d prior to including <tt>&lt;security/pam_modules.h&gt;</tt>.
+This will ensure that the prototype for a static module is properly
+declared.
+
+<p>
+<itemize>
+
+<item>
+<tt>PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int
+argc, const char **argv);</tt>
+
+<p>
+This function is used to (re-)set the authentication token of the
+user. A valid flag, which may be logically OR'd with <tt/PAM_SILENT/,
+can be built from the following list,
+
+<descrip>
+<tag><tt/PAM_CHANGE_EXPIRED_AUTHTOK/</tag>
+ This argument indicates to the module that the users
+authentication token (password) should only be changed if it has
+expired. This flag is optional and <em/must/ be combined with one of
+the following two flags. Note, however, the following two options are
+<em/mutually exclusive/.
+
+<tag><tt/PAM_PRELIM_CHECK/</tag>
+ This indicates that the modules are being probed as to their
+ready status for altering the user's authentication token. If the
+module requires access to another system over some network it should
+attempt to verify it can connect to this system on receiving this
+flag. If a module cannot establish it is ready to update the user's
+authentication token it should return <tt/PAM_TRY_AGAIN/, this
+information will be passed back to the application.
+
+<tag><tt/PAM_UPDATE_AUTHTOK/</tag>
+ This informs the module that this is the call it should change
+the authorization tokens. If the flag is logically OR'd with
+<tt/PAM_CHANGE_EXPIRED_AUTHTOK/, the token is only changed if it has
+actually expired.
+
+</descrip>
+
+<p>
+Note, the <bf/Linux-PAM/ library calls this function twice in
+succession. The first time with <tt/PAM_PRELIM_CHECK/ and then, if the
+module does not return <tt/PAM_TRY_AGAIN/, subsequently with
+<tt/PAM_UPDATE_AUTHTOK/. It is only on the second call that the
+authorization token is (possibly) changed.
+
+<p>
+<tt/PAM_SUCCESS/ is the only successful return value, valid
+error-returns are:
+
+<descrip>
+<tag><tt/PAM_AUTHTOK_ERR/</tag>
+ The module was unable to obtain the new authentication token.
+
+<tag><tt/PAM_AUTHTOK_RECOVERY_ERR/</tag>
+ The module was unable to obtain the old authentication token.
+
+<tag><tt/PAM_AUTHTOK_LOCK_BUSY/</tag>
+ Cannot change the authentication token since it is currently
+locked.
+
+<tag><tt/PAM_AUTHTOK_DISABLE_AGING/</tag>
+ Authentication token aging has been disabled.
+
+<tag><tt/PAM_PERM_DENIED/</tag>
+ Permission denied.
+
+<tag><tt/PAM_TRY_AGAIN/</tag>
+ Preliminary check was unsuccessful. Signals an immediate return
+to the application is desired.
+
+<tag><tt/PAM_USER_UNKNOWN/</tag>
+ The user is not known to the authentication token changing
+service.
+
+</descrip>
+
+</itemize>
+
+<sect>Generic optional arguments
+
+<p>
+Here we list the generic arguments that all modules can expect to
+be passed. They are not mandatory, and their absence should be
+accepted without comment by the module.
+
+<p>
+<descrip>
+<tag><tt/debug/</tag>
+
+Use the <tt/syslog(3)/ call to log debugging information to the system
+log files.
+
+<tag><tt/no_warn/</tag>
+
+Instruct module to not give warning messages to the application.
+
+<tag><tt/use_first_pass/</tag>
+
+The module should not prompt the user for a password. Instead, it
+should obtain the previously typed password (by a call to
+<tt/pam_get_item()/ for the <tt/PAM_AUTHTOK/ item), and use that. If
+that doesn't work, then the user will not be authenticated. (This
+option is intended for <tt/auth/ and <tt/passwd/ modules only).
+
+<tag><tt/try_first_pass/</tag>
+
+The module should attempt authentication with the previously typed
+password (by a call to <tt/pam_get_item()/ for the <tt/PAM_AUTHTOK/
+item). If that doesn't work, then the user is prompted for a
+password. (This option is intended for <tt/auth/ modules only).
+
+<tag><tt/use_mapped_pass/</tag>
+
+<bf/WARNING:/ coding this functionality may cause the module writer to
+break <em/local/ encryption laws. For example, in the U.S. there are
+restrictions on the export computer code that is capable of strong
+encryption. It has not been established whether this option is
+affected by this law, but one might reasonably assume that it does
+until told otherwise. For this reason, this option is not supported
+by any of the modules distributed with <bf/Linux-PAM/.
+
+The intended function of this argument, however, is that the module
+should take the existing authentication token from a previously
+invoked module and use it as a key to retrieve the authentication
+token for this module. For example, the module might create a strong
+hash of the <tt/PAM_AUTHTOK/ item (established by a previously
+executed module). Then, with logical-exclusive-or, use the result as a
+<em/key/ to safely store/retrieve the authentication token for this
+module in/from a local file <em/etc/. .
+
+<tag><tt/expose_account/</tag>
+
+<p>
+In general the leakage of some information about user accounts is not
+a secure policy for modules to adopt. Sometimes information such as
+users names or home directories, or preferred shell, can be used to
+attack a user's account. In some circumstances, however, this sort of
+information is not deemed a threat: displaying a user's full name when
+asking them for a password in a secured environment could also be
+called being 'friendly'. The <tt/expose_account/ argument is a
+standard module argument to encourage a module to be less discrete
+about account information as it is deemed appropriate by the local
+administrator.
+
+</descrip>
+
+<sect>Programming notes
+
+<p>
+Here we collect some pointers for the module writer to bear in mind
+when writing/developing a <bf/Linux-PAM/ compatible module.
+
+<sect1>Security issues for module creation
+
+<sect2>Sufficient resources
+
+<p>
+Care should be taken to ensure that the proper execution of a module
+is not compromised by a lack of system resources. If a module is
+unable to open sufficient files to perform its task, it should fail
+gracefully, or request additional resources. Specifically, the
+quantities manipulated by the <tt/setrlimit(2)/ family of commands
+should be taken into consideration.
+
+<sect2>Who's who?
+
+<p>
+Generally, the module may wish to establish the identity of the user
+requesting a service. This may not be the same as the username
+returned by <tt/pam_get_user()/. Indeed, that is only going to be the
+name of the user under whose identity the service will be given. This
+is not necessarily the user that requests the service.
+
+<p>
+In other words, user X runs a program that is setuid-Y, it grants the
+user to have the permissions of Z. A specific example of this sort of
+service request is the <em/su/ program: user <tt/joe/ executes
+<em/su/ to become the user <em/jane/. In this situation X=<tt/joe/,
+Y=<tt/root/ and Z=<tt/jane/. Clearly, it is important that the module
+does not confuse these different users and grant an inappropriate
+level of privilege.
+
+<p>
+The following is the convention to be adhered to when juggling
+user-identities.
+
+<p>
+<itemize>
+<item>X, the identity of the user invoking the service request.
+This is the user identifier; returned by the function <tt/getuid(2)/.
+
+<item>Y, the privileged identity of the application used to grant the
+requested service. This is the <em/effective/ user identifier;
+returned by the function <tt/geteuid(2)/.
+
+<item>Z, the user under whose identity the service will be granted.
+This is the username returned by <tt/pam_get_user(2)/ and also stored
+in the <bf/Linux-PAM/ item, <tt/PAM_USER/.
+
+<item><bf/Linux-PAM/ has a place for an additional user identity that
+a module may care to make use of. This is the <tt/PAM_RUSER/ item.
+Generally, network sensitive modules/applications may wish to set/read
+this item to establish the identity of the user requesting a service
+from a remote location.
+
+</itemize>
+
+<p>
+Note, if a module wishes to modify the identity of either the <tt/uid/
+or <tt/euid/ of the running process, it should take care to restore
+the original values prior to returning control to the <bf/Linux-PAM/
+library.
+
+<sect2>Using the conversation function
+<p>
+Prior to calling the conversation function, the module should reset
+the contents of the pointer that will return the applications
+response. This is a good idea since the application may fail to fill
+the pointer and the module should be in a position to notice!
+
+<p>
+The module should be prepared for a failure from the conversation. The
+generic error would be <tt/PAM_CONV_ERR/, but anything other than
+<tt/PAM_SUCCESS/ should be treated as indicating failure.
+
+<sect2>Authentication tokens
+
+<p>
+To ensure that the authentication tokens are not left lying around the
+items, <tt/PAM_AUTHTOK/ and <tt/PAM_OLDAUTHTOK/, are not available to
+the application: they are defined in
+<tt>&lt;security/pam_modules.h&gt;</tt>. This is ostensibly for
+security reasons, but a maliciously programmed application will always
+have access to all memory of the process, so it is only superficially
+enforced. As a general rule the module should overwrite
+authentication tokens as soon as they are no longer needed.
+Especially before <tt/free()/'ing them. The <bf/Linux-PAM/ library is
+required to do this when either of these authentication token items
+are (re)set.
+
+<p>
+Not to dwell too little on this concern; should the module store the
+authentication tokens either as (automatic) function variables or
+using <tt/pam_[gs]et_data()/ the associated memory should be
+over-written explicitly before it is released. In the case of the
+latter storage mechanism, the associated <tt/cleanup()/ function
+should explicitly overwrite the <tt/*data/ before <tt/free()/'ing it:
+for example,
+
+<tscreen>
+<verb>
+/*
+ * An example cleanup() function for releasing memory that was used to
+ * store a password.
+ */
+
+int cleanup(pam_handle_t *pamh, void *data, int error_status)
+{
+ char *xx;
+
+ if ((xx = data)) {
+ while (*xx)
+ *xx++ = '\0';
+ free(data);
+ }
+ return PAM_SUCCESS;
+}
+</verb>
+</tscreen>
+
+<sect1>Use of <tt/syslog(3)/
+
+<p>
+Only rarely should error information be directed to the user. Usually,
+this is to be limited to ``<em/sorry you cannot login now/'' type
+messages. Information concerning errors in the configuration file,
+<tt>/etc/pam.conf</tt>, or due to some system failure encountered by
+the module, should be written to <tt/syslog(3)/ with
+<em/facility-type/ <tt/LOG_AUTHPRIV/.
+
+<p>
+With a few exceptions, the level of logging is, at the discretion of
+the module developer. Here is the recommended usage of different
+logging levels:
+
+<p>
+<itemize>
+
+<item>
+As a general rule, errors encountered by a module should be logged at
+the <tt/LOG_ERR/ level. However, information regarding an unrecognized
+argument, passed to a module from an entry in the
+<tt>/etc/pam.conf</tt> file, is <bf/required/ to be logged at the
+<tt/LOG_ERR/ level.
+
+<item>
+Debugging information, as activated by the <tt/debug/ argument to the
+module in <tt>/etc/pam.conf</tt>, should be logged at the
+<tt/LOG_DEBUG/ level.
+
+<item>
+If a module discovers that its personal configuration file or some
+system file it uses for information is corrupted or somehow unusable,
+it should indicate this by logging messages at level, <tt/LOG_ALERT/.
+
+<item>
+Shortages of system resources, such as a failure to manipulate a file
+or <tt/malloc()/ failures should be logged at level <tt/LOG_CRIT/.
+
+<item>
+Authentication failures, associated with an incorrectly typed password
+should be logged at level, <tt/LOG_NOTICE/.
+
+</itemize>
+
+<sect1> Modules that require system libraries
+
+<p>
+Writing a module is much like writing an application. You have to
+provide the "conventional hooks" for it to work correctly, like
+<tt>pam_sm_authenticate()</tt> etc., which would correspond to the
+<tt/main()/ function in a normal function.
+
+<p>
+Typically, the author may want to link against some standard system
+libraries. As when one compiles a normal program, this can be done for
+modules too: you simply append the <tt>-l</tt><em>XXX</em> arguments
+for the desired libraries when you create the shared module object. To
+make sure a module is linked to the <tt>lib<em>whatever</em>.so</tt>
+library when it is <tt>dlopen()</tt>ed, try:
+<tscreen>
+<verb>
+% gcc -shared -Xlinker -x -o pam_module.so pam_module.o -lwhatever
+</verb>
+</tscreen>
+
+<sect1> Added requirements for <em/statically/ loaded modules.
+
+<!--
+ Copyright (C) Michael K. Johnson 1996.
+ Last modified: AGM 1996/5/31.
+ -->
+
+<p>
+Modules may be statically linked into libpam. This should be true of
+all the modules distributed with the basic <bf/Linux-PAM/
+distribution. To be statically linked, a module needs to export
+information about the functions it contains in a manner that does not
+clash with other modules.
+
+The extra code necessary to build a static module should be delimited
+with <tt/#ifdef PAM_STATIC/ and <tt/#endif/. The static code should do
+the following:
+<itemize>
+<item> Define a single structure, <tt/struct pam_module/, called
+<tt>_pam_<it>modname</it>_modstruct</tt>, where
+<tt><it>modname</it></tt> is the name of the module <bf/as used in the
+filesystem/ but without the leading directory name (generally
+<tt>/usr/lib/security/</tt> or the suffix (generally <tt/.so/).
+
+</itemize>
+
+<p>
+As a simple example, consider the following module code which defines
+a module that can be compiled to be <em/static/ or <em/dynamic/:
+
+<p>
+<tscreen>
+<verb>
+#include <stdio.h> /* for NULL define */
+
+#define PAM_SM_PASSWORD /* the only pam_sm_... function declared */
+#include <security/pam_modules.h>
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC /* for the case that this module is static */
+
+struct pam_module _pam_modname_modstruct = { /* static module data */
+ "pam_modname",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok,
+};
+
+#endif /* end PAM_STATIC */
+</verb>
+</tscreen>
+
+<p>
+To be linked with <em/libpam/, staticly-linked modules must be built
+from within the <tt>Linux-PAM-X.YY/modules/</tt> subdirectory of the
+<bf/Linux-PAM/ source directory as part of a normal build of the
+<bf/Linux-PAM/ system.
+
+The <em/Makefile/, for the module in question, must execute the
+<tt/register_static/ shell script that is located in the
+<tt>Linux-PAM-X.YY/modules/</tt> subdirectory. This is to ensure that
+the module is properly registered with <em/libpam/.
+
+The <bf/two/ manditory arguments to <tt/register_static/ are the
+title, and the pathname of the object file containing the module's
+code. The pathname is specified relative to the
+<tt>Linux-PAM-X.YY/modules</tt> directory. The pathname may be an
+empty string---this is for the case that a single object file needs to
+register more than one <tt/struct pam_module/. In such a case, exactly
+one call to <tt/register_static/ must indicate the object file.
+
+<p>
+Here is an example; a line in the <em/Makefile/ might look like this:
+<tscreen>
+<verb>
+register:
+ifdef STATIC
+ (cd ..; ./register_static pam_modname pam_modname/pam_modname.o)
+endif
+</verb>
+</tscreen>
+
+For some further examples, see the <tt>modules</tt> subdirectory of
+the current <bf/Linux-PAM/ distribution.
+
+<p>
+<sect>An example module file
+
+<p>
+<em>
+perhaps this should point to a place in the file structure!?
+</em>
+
+<sect>Files
+
+<p><descrip>
+
+<tag><tt>/usr/lib/libpam.so.*</tt></tag>
+
+the shared library providing applications with access to
+<bf/Linux-PAM/.
+
+<tag><tt>/etc/pam.conf</tt></tag>
+
+the <bf/Linux-PAM/ configuration file.
+
+<tag><tt>/usr/lib/security/pam_*.so</tt></tag>
+
+the primary location for <bf/Linux-PAM/ dynamically loadable object
+files; the modules.
+
+</descrip>
+
+<sect>See also
+
+<p><itemize>
+<item>The <bf/Linux-PAM/ System Administrators' Guide.
+<item>The <bf/Linux-PAM/ Application Writers' Guide.
+<item>
+V. Samar and R. Schemers (SunSoft), ``UNIFIED LOGIN WITH PLUGGABLE
+AUTHENTICATION MODULES'', Open Software Foundation Request For
+Comments 86.0, October 1995.
+</itemize>
+
+<sect>Notes
+
+<p>
+I intend to put development comments here... like ``at the moment
+this isn't actually supported''. At release time what ever is in
+this section will be placed in the Bugs section below! :)
+
+<p>
+<itemize>
+<item>
+Perhaps we should keep a registry of data-names as used by
+<tt/pam_[gs]et_data()/ so there are no unintentional problems due to
+conflicts?
+
+<item>
+<tt/pam_strerror()/ should be internationalized....
+
+<item>
+There has been some debate about whether <tt/initgroups()/ should be
+in an application or in a module. It was settled by Sun who stated
+that initgroups is an action of the <em/application/. The modules are
+permitted to add additional groups, however.
+
+<item>
+Refinements/futher suggestions to <tt/syslog(3)/ usage by modules are
+needed.
+
+</itemize>
+
+<sect>Author/acknowledgments
+
+<p>
+This document was written by Andrew G. Morgan
+(<tt/morgan@transmeta.com/) with many contributions from
+<!-- insert credits here -->
+<!--
+ an sgml list of people to credit for their contributions to Linux-PAM
+ $Id$
+ -->
+Chris Adams,
+Peter Allgeyer,
+Tim Baverstock,
+Tim Berger,
+Craig S. Bell,
+Derrick J. Brashear,
+Ben Buxton,
+Seth Chaiklin,
+Oliver Crow,
+Chris Dent,
+Marc Ewing,
+Cristian Gafton,
+Emmanuel Galanos,
+Brad M. Garcia,
+Eric Hester,
+Roger Hu,
+Eric Jacksch,
+Michael K. Johnson,
+David Kinchlea,
+Olaf Kirch,
+Marcin Korzonek,
+Stephen Langasek,
+Nicolai Langfeldt,
+Elliot Lee,
+Luke Kenneth Casson Leighton,
+Al Longyear,
+Ingo Luetkebohle,
+Marek Michalkiewicz,
+Robert Milkowski,
+Aleph One,
+Martin Pool,
+Sean Reifschneider,
+Jan Rekorajski,
+Erik Troan,
+Theodore Ts'o,
+Jeff Uphoff,
+Myles Uyema,
+Savochkin Andrey Vladimirovich,
+Ronald Wahl,
+David Wood,
+John Wilmes,
+Joseph S. D. Yao
+and
+Alex O. Yuriev.
+
+<p>
+Thanks are also due to Sun Microsystems, especially to Vipin Samar and
+Charlie Lai for their advice. At an early stage in the development of
+<bf/Linux-PAM/, Sun graciously made the documentation for their
+implementation of PAM available. This act greatly accelerated the
+development of <bf/Linux-PAM/.
+
+<sect>Bugs/omissions
+
+<p>
+Few PAM modules currently exist. Few PAM-aware applications exist.
+This document is hopelessly unfinished. Only a partial list of people is
+credited for all the good work they have done.
+
+<sect>Copyright information for this document
+
+<p>
+Copyright (c) Andrew G. Morgan 1996, 1997. All rights reserved.
+<newline>
+Email: <tt>&lt;morgan@transmeta.com&gt;</tt>
+
+<p>
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+<p>
+<itemize>
+
+<item>
+1. Redistributions of source code must retain the above copyright
+ notice, and the entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+<item>
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+<item>
+3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+</itemize>
+
+<p>
+<bf/Alternatively/, this product may be distributed under the terms of
+the GNU General Public License (GPL), in which case the provisions of
+the GNU GPL are required <bf/instead of/ the above restrictions.
+(This clause is necessary due to a potential bad interaction between
+the GNU GPL and the restrictions contained in a BSD-style copyright.)
+
+<p>
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+<p>
+<tt>$Id$</tt>
+
+</article>
diff --git a/doc/pam_source.sgml b/doc/pam_source.sgml
new file mode 100644
index 00000000..1c369403
--- /dev/null
+++ b/doc/pam_source.sgml
@@ -0,0 +1,1173 @@
+<!doctype linuxdoc system>
+
+<!--
+
+ $Id$
+
+ Copyright (c) Andrew G. Morgan 1996-9. All rights reserved.
+
+Redistribution and use in source (sgml) and binary (derived) forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, and the entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ALTERNATIVELY, this product may be distributed under the terms of the
+GNU General Public License, in which case the provisions of the GNU
+GPL are required INSTEAD OF the above restrictions. (This clause is
+necessary due to a potential bad interaction between the GNU GPL and
+the restrictions contained in a BSD-style copyright.)
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+ -->
+
+<article>
+
+<title>The Linux-PAM System Administrators' Guide
+<author>Andrew G. Morgan, <tt>morgan@linux.kernel.org</tt>
+<date>DRAFT v0.71 1999/11/8
+<abstract>
+This manual documents what a system-administrator needs to know about
+the <bf>Linux-PAM</bf> library. It covers the correct syntax of the
+PAM configuration file and discusses strategies for maintaining a
+secure system.
+</abstract>
+
+<!-- Table of contents -->
+<toc>
+
+<!-- Begin the document -->
+
+<sect>Introduction
+
+<p><bf/Linux-PAM/ (Pluggable Authentication Modules for Linux) is a
+suite of shared libraries that enable the local system administrator
+to choose how applications authenticate users.
+
+<p>In other words, without (rewriting and) recompiling a PAM-aware
+application, it is possible to switch between the authentication
+mechanism(s) it uses. Indeed, one may entirely upgrade the local
+authentication system without touching the applications themselves.
+
+<p>Historically an application that has required a given user to be
+authenticated, has had to be compiled to use a specific authentication
+mechanism. For example, in the case of traditional UN*X systems, the
+identity of the user is verified by the user entering a correct
+password. This password, after being prefixed by a two character
+``salt'', is encrypted (with crypt(3)). The user is then authenticated
+if this encrypted password is identical to the second field of the
+user's entry in the system password database (the <tt>/etc/passwd</tt>
+file). On such systems, most if not all forms of privileges are
+granted based on this single authentication scheme. Privilege comes in
+the form of a personal user-identifier (<tt/uid/) and membership of
+various groups. Services and applications are available based on the
+personal and group identity of the user. Traditionally, group
+membership has been assigned based on entries in the
+<tt>/etc/group</tt> file.
+
+<p>
+Unfortunately, increases in the speed of computers and the
+widespread introduction of network based computing, have made once
+secure authentication mechanisms, such as this, vulnerable to
+attack. In the light of such realities, new methods of authentication
+are continuously being developed.
+
+<p>
+It is the purpose of the <bf/Linux-PAM/ project to separate the
+development of privilege granting software from the development of
+secure and appropriate authentication schemes. This is accomplished
+by providing a library of functions that an application may use to
+request that a user be authenticated. This PAM library is configured
+locally with a system file, <tt>/etc/pam.conf</tt> (or a series of
+configuration files located in <tt>/etc/pam.d/</tt>) to authenticate a
+user request via the locally available authentication modules. The
+modules themselves will usually be located in the directory
+<tt>/usr/lib/security</tt> and take the form of dynamically loadable
+object files (see <tt/dlopen(3)/).
+
+<sect>Some comments on the text<label id="text-conventions">
+
+<p>
+Before proceeding to read the rest of this document, it should be
+noted that the text assumes that certain files are placed in certain
+directories. Where they have been specified, the conventions we adopt
+here for locating these files are those of the relevant RFC (RFC-86.0,
+see <ref id="see-also-sec" name="bibliography">). If you are using a
+distribution of Linux (or some other operating system) that supports
+PAM but chooses to distribute these files in a diferent way (Red Hat
+is one such distribution), you should be careful when copying examples
+directly from the text.
+
+<p>
+As an example of the above, where it is explicit, the text assumes
+that PAM loadable object files (the <em/modules/) are to be located in
+the following directory: <tt>/usr/lib/security/</tt>. However, Red Hat
+Linux, in agreement with the Linux File System Standard (the FSSTND),
+places these files in <tt>/lib/security</tt>. Please be careful to
+perform the necessary transcription when using the examples from the
+text.
+
+<sect>Overview<label id="overview-section">
+
+<p>
+For the uninitiated, we begin by considering an example. We take an
+application that grants some service to users; <em/login/ is one such
+program. <em/Login/ does two things, it first establishes that the
+requesting user is whom they claim to be and second provides them with
+the requested service: in the case of <em/login/ the service is a
+command shell (<em>bash, tcsh, zsh, etc.</em>) running with the
+identity of the user.
+
+<p>
+Traditinally, the former step is achieved by the <em/login/
+application prompting the user for a password and then verifying that
+it agrees with that located on the system; hence verifying that the
+so far as the system is concerned the user is who they claim to be.
+This is the task that is delegated to <bf/Linux-PAM/.
+
+<p>
+From the perspective of the application programmer (in this case the
+person that wrote the <em/login/ application), <bf/Linux-PAM/ takes
+care of this authentication task -- verifying the identity of the user.
+
+<p>
+The flexibility of <bf/Linux-PAM/ is that <em/you/, the system
+administrator, have the freedom to stipulate which authentication
+scheme is to be used. You have the freedom to set the scheme for
+any/all PAM-aware applications on your Linux system. That is, you can
+authenticate from anything as naive as <em/simple trust/
+(<tt/pam_permit/) to something as paranoid as a combination of a
+retinal scan, a voice print and a one-time password!
+
+<p>
+To illustrate the flexibility you face, consider the following
+situation: a system administrator (parent) wishes to improve the
+mathematical ability of her users (children). She can configure their
+favorite ``Shoot 'em up game'' (PAM-aware of course) to authenticate
+them with a request for the product of a couple of random numbers less
+than 12. It is clear that if the game is any good they will soon learn
+their <em/multiplication tables/. As they mature, the authentication
+can be upgraded to include (long) division!
+
+<p>
+<bf/Linux-PAM/ deals with four separate types of (management)
+task. These are: <em/authentication management/; <em/account
+management/; <em/session management/; and <em/password management/.
+The association of the preferred management scheme with the behavior
+of an application is made with entries in the relevant <bf/Linux-PAM/
+configuration file. The management functions are performed by
+<em/modules/ specified in the configuration file. The syntax for this
+file is discussed in the section <ref id="configuration"
+name="below">.
+
+<p>
+Here is a figure that describes the overall organization of
+<bf/Linux-PAM/.
+<tscreen>
+<verb>
+ +----------------+
+ | application: X |
+ +----------------+ / +----------+ +================+
+ | authentication-[---->--\--] Linux- |--<--| PAM config file|
+ | + [----<--/--] PAM | |================|
+ |[conversation()][--+ \ | | | X auth .. a.so |
+ +----------------+ | / +-n--n-----+ | X auth .. b.so |
+ | | | __| | | _____/
+ | service user | A | | |____,-----'
+ | | | V A
+ +----------------+ +------|-----|---------+ -----+------+
+ +---u-----u----+ | | |
+ | auth.... |--[ a ]--[ b ]--[ c ]
+ +--------------+
+ | acct.... |--[ b ]--[ d ]
+ +--------------+
+ | password |--[ b ]--[ c ]
+ +--------------+
+ | session |--[ e ]--[ c ]
+ +--------------+
+</verb>
+</tscreen>
+By way of explanation, the left of the figure represents the
+application; application X. Such an application interfaces with the
+<bf/Linux-PAM/ library and knows none of the specifics of its
+configured authentication method. The <bf/Linux-PAM/ library (in the
+center) consults the contents of the PAM configuration file and loads
+the modules that are appropriate for application-X. These modules fall
+into one of four management groups (lower-center) and are stacked in
+the order they appear in the configuaration file. These modules, when
+called by <bf/Linux-PAM/, perform the various authentication tasks for
+the application. Textual information, required from/or offered to the
+user, can be exchanged through the use of the application-supplied
+<em/conversation/ function.
+
+<sect1>Getting started
+
+<p>
+The following text was contributed by Seth Chaiklin:
+<tscreen>
+<verb>
+To this point, we have described how PAM should work in an
+ideal world, in which all applications are coded properly.
+However, at the present time (October 1998), this is far
+from the case. Therefore, here are some practical considerations
+in trying to use PAM in your system.
+
+Why bother, is it really worth all the trouble?
+
+If you running Linux as a single user system, or in an
+environment where all the users are trusted, then there
+is no real advantage for using PAM.
+</verb>
+</tscreen>
+
+<p>
+<BF>Ed:</BF> there is actually an advantage since you can <em/dummy
+down/ the authentication to the point where you don't have
+any... Almost like Win95.
+<p>
+In a networked environment, it is clear that you need to think a
+little more about how users etc., are authenticated:]
+
+<p>
+<tscreen>
+<verb>
+If you are running Linux as a server, where several different
+services are being provided (e.g., WWW with areas restricted by
+password control, PPP), then there can be some real and interesting
+value for PAM. In particular, through the use of modules, PAM can
+enable a program to search through several different password
+databases, even if that program is not explicitly coded for
+that particular database. Here are some examples of the possibilities
+that this enables.
+
+ o Apache has a module that provides PAM services. Now
+ authentication
+ to use particular directories can be conducted by PAM, which
+ means that the range of modules that are available to PAM can
+ be used, including RADIUS, NIS, NCP (which means that Novell
+ password databases can be used).
+
+ o pppd has a PAMified version (available from RedHat) Now it is
+ possible to use a series of databases to authenticate ppp users.
+ In addition to the normal Linux-based password databases (such
+ as /etc/passwd and /etc/shadow), you can use PAM modules to
+ authenticate against Novell password databases or NT-based
+ password databases.
+
+ o The preceding two examples can be combined. Imagaine that the
+ persons in your office/department are already registered with a
+ username and password in a Novell or NT LAN. If you wanted to
+ use this database on your Linux server (for PPP access, for
+ web access, or even for normal shell access), you can use PAM
+ to authenticate against this existing database, rather than
+ maintain a separate database on both Linux and the LAN server.
+
+
+Can I use PAM for any program that requires authentication?
+
+Yes and no. Yes, if you have access to the source code, and can
+add the appropriate PAM functions. No, if you do not have access
+to the source code, and the binary does not have the PAM functions
+included.
+
+In other words, if a program is going to use PAM, then it has to
+have PAM functions explicitly coded into the program. If they
+are not, then it is not possible to use PAM.
+
+How can I tell whether a program has PAM coded into it or not?
+
+A quick-and-dirty (but not always reliable) method is to ldd
+<programname>
+If libpam and libpam_misc are not among the libraries that the program
+uses, then it is not going to work with PAM. However, it is possible
+that the libraries are included, but there are still problems, because
+the PAM coding in the program does not work as it should. So a
+more reliable method is to make the follow tests.
+
+In the /etc/pam.d directory, one needs to make a configuration file
+for the program that one wants to run. The exact name of the
+configuration
+file is hard-coded into the program. Usually, it is the same name as
+the
+program, but not always. For sake of illustration, let's assume that
+the program is named "pamprog" and the name of the configuration file
+is /etc/pam.d/pamprog.
+
+In the /etc/pam.d/pamprog but the following two lines:
+
+auth required pam_permit.so
+auth required pam_warn.so
+
+
+Now try to use pamprog. The first line in the configuration file
+says that all users are permitted. The second line will write a
+warning to your syslog file (or whether you syslog is writing
+
+messages). If this test succeeds, then you know that you have
+a program that can understand pam, and you can start the more
+interesting work of deciding how to stack modules in your
+/etc/pam.d/pamprog file.
+</verb>
+</tscreen>
+
+<sect>The Linux-PAM configuration file
+<label id="configuration">
+
+<p>
+<bf/Linux-PAM/ is designed to provide the system administrator with a
+great deal of flexibility in configuring the privilege granting
+applications of their system. The local configuration of those aspects
+of system security controlled by <tt/Linux-PAM/ is contained in one of
+two places: either the single system file, <tt>/etc/pam.conf</tt>; or
+the <tt>/etc/pam.d/</tt> directory. In this section we discuss the
+correct syntax of and generic options respected by entries to these
+files.
+
+<sect1>Configuration file syntax
+
+<p>
+The reader should note that the <bf/Linux-PAM/ specific tokens in this
+file are case <em/insensitive/. The module paths, however, are case
+sensitive since they indicate a file's <em/name/ and reflect the case
+dependence of typical Linux file-systems. The case-sensitivity of the
+arguments to any given module is defined for each module in turn.
+
+<p>
+In addition to the lines described below, there are two <em/special/
+characters provided for the convenience of the system administrator:
+comments are preceded by a `<tt/&num;/' and extend to the
+next end-of-line; also, module specification lines may be extended
+with a `<tt/&bsol;/' escaped newline.
+
+<p>
+A general configuration line of the <tt>/etc/pam.conf</tt> file has
+the following form:
+<tscreen>
+<verb>
+service-name module-type control-flag module-path arguments
+</verb>
+</tscreen>
+Below, we explain the meaning of each of these tokens. The second (and
+more recently adopted) way of configuring <bf/Linux-PAM/ is via the
+contents of the <tt>/etc/pam.d/</tt> directory. Once we have explained
+the meaning of the above tokens, we will describe this method.
+
+<p>
+<descrip>
+<tag><tt/service-name/</tag>
+The name of the service associated with this entry. Frequently the
+service name is the conventional name of the given application. For
+example, `<tt/ftpd/', `<tt/rlogind/' and `<tt/su/', <em/etc./ .
+
+<p>
+There is a special <tt/service-name/, reserved for defining a default
+authentication mechanism. It has the name `<tt/OTHER/' and may be
+specified in either lower or upper case characters. Note, when there
+is a module specified for a named service, the `<tt/OTHER/' entries
+are ignored.
+
+<tag><tt/module-type/</tag>
+One of (currently) four types of module. The four types are as
+follows:
+<itemize>
+<item> <tt/auth/; this module type provides two aspects of
+authenticating the user. Firstly, it establishes that the user is who
+they claim to be, by instructing the application to prompt the user
+for a password or other means of identification. Secondly, the module
+can grant <tt/group/ membership (independently of the
+<tt>/etc/groups</tt> file discussed above) or other privileges through
+its <em/credential/ granting properties.
+
+<item> <tt/account/; this module performs non-authentication based
+account management. It is typically used to restrict/permit access to
+a service based on the time of day, currently available system
+resources (maximum number of users) or perhaps the location of the
+applicant user---`<tt/root/' login only on the console.
+
+<item> <tt/session/; primarily, this module is associated with doing
+things that need to be done for the user before/after they can be
+given service. Such things include the logging of information
+concerning the opening/closing of some data exchange with a user,
+mounting directories, etc. .
+
+<item> <tt/password/; this last module type is required for updating the
+authentication token associated with the user. Typically, there is one
+module for each `challenge/response' based authentication (<tt/auth/)
+module-type.
+
+</itemize>
+
+<tag><tt/control-flag/</tag>
+
+The control-flag is used to indicate how the PAM library will react to
+the success or failure of the module it is associated with. Since
+modules can be <em/stacked/ (modules of the same type execute in
+series, one after another), the control-flags determine the relative
+importance of each module. The application is not made aware of the
+individual success or failure of modules listed in the
+`<tt>/etc/pam.conf</tt>' file. Instead, it receives a summary
+<em/success/ or <em/fail/ response from the <bf/Linux-PAM/ library.
+The order of execution of these modules is that of the entries in the
+<tt>/etc/pam.conf</tt> file; earlier entries are executed before later
+ones. As of Linux-PAM v0.60, this <em/control-flag/ can be defined
+with one of two syntaxes.
+
+<p>
+The simpler (and historical) syntax for the control-flag is a single
+keyword defined to indicate the severity of concern associated with
+the success or failure of a specific module. There are four such
+keywords: <tt/required/, <tt/requisite/, <tt/sufficient/ and
+<tt/optional/.
+
+<p>
+The Linux-PAM library interprets these keywords in the following
+manner:
+
+<itemize>
+
+<item> <tt/required/; this indicates that the success of the module is
+required for the <tt/module-type/ facility to succeed. Failure of this
+module will not be apparent to the user until all of the remaining
+modules (of the same <tt/module-type/) have been executed.
+
+<item> <tt/requisite/; like <tt/required/, however, in the case that
+such a module returns a failure, control is directly returned to the
+application. The return value is that associated with the <em/first/
+<tt/required/ or <tt/requisite/ module to fail. Note, this flag can be
+used to protect against the possibility of a user getting the
+opportunity to enter a password over an unsafe medium. It is
+conceivable that such behavior might inform an attacker of valid
+accounts on a system. This possibility should be weighed against the
+not insignificant concerns of exposing a sensitive password in a
+hostile environment.
+
+<item> <tt/sufficient/; the success of this module is deemed
+`<em/sufficient/' to satisfy the <bf/Linux-PAM/ library that this
+module-type has succeeded in its purpose. In the event that no
+previous <tt/required/ module has failed, no more `<em/stacked/'
+modules of this type are invoked. (Note, in this case subsequent
+<tt/required/ modules are <bf/not/ invoked.). A failure of this module
+is not deemed as fatal to satisfying the application that this
+<tt/module-type/ has succeeded.
+
+<item> <tt/optional/; as its name suggests, this <tt/control-flag/
+marks the module as not being critical to the success or failure of
+the user's application for service. In general, <bf/Linux-PAM/
+ignores such a module when determining if the module stack will
+succeed or fail. However, in the absence of any definite successes or
+failures of previous or subsequent stacked modules this module will
+determine the nature of the response to the application. One example
+of this latter case, is when the other modules return something like
+<tt/PAM_IGNORE/.
+
+</itemize>
+
+<p>
+The more elaborate (newer) syntax is much more specific and gives the
+administrator a great deal of control over how the user is
+authenticated. This form of the control flag is delimeted with square
+brackets and consists of a series of <tt/value=action/ tokens:
+<tscreen>
+<verb>
+ [value1=action1 value2=action2 ...]
+</verb>
+</tscreen>
+
+<p>
+Here, <tt/valueI/ is one of the following <em/return values/:
+<tt/success/; <tt/open_err/; <tt/symbol_err/; <tt/service_err/;
+<tt/system_err/; <tt/buf_err/; <tt/perm_denied/; <tt/auth_err/;
+<tt/cred_insufficient/; <tt/authinfo_unavail/; <tt/user_unknown/;
+<tt/maxtries/; <tt/new_authtok_reqd/; <tt/acct_expired/;
+<tt/session_err/; <tt/cred_unavail/; <tt/cred_expired/; <tt/cred_err/;
+<tt/no_module_data/; <tt/conv_err/; <tt/authtok_err/;
+<tt/authtok_recover_err/; <tt/authtok_lock_busy/;
+<tt/authtok_disable_aging/; <tt/try_again/; <tt/ignore/; <tt/abort/;
+<tt/authtok_expired/; <tt/module_unknown/; <tt/bad_item/; and
+<tt/default/. The last of these (<tt/default/) can be used to set the
+action for those return values that are not explicitly defined.
+
+<p>
+The <tt/actionI/ can be a positive integer or one of the following
+tokens: <tt/ignore/; <tt/ok/; <tt/done/; <tt/bad/; <tt/die/; and
+<tt/reset/. A positive integer, <tt/J/, when specified as the action,
+can be used to indicate that the next <em/J/ modules of the current
+type will be skipped. In this way, the administrator can develop a
+moderately sophisticated stack of modules with a number of different
+paths of execution. Which path is taken can be determined by the
+reactions of individual modules.
+
+<p>
+<itemize>
+<item><tt/ignore/ - when used with a stack of modules, the module's
+ return status will not contribute to the return code the application
+ obtains.
+<item><tt/bad/ - this action indicates that the return code should be
+ thought of as indicative of the module failing. If this module is
+ the first in the stack to fail, its status value will be used for
+ that of the whole stack.
+<item><tt/die/ - equivalent to <tt/bad/ with the side effect of
+ terminating the module stack and PAM immediately returning to the
+ application.
+<item><tt/ok/ - this tells <bf/PAM/ that the administrator thinks this
+ return code should contribute directly to the return code of the full
+ stack of modules. In other words, if the former state of the stack
+ would lead to a return of <tt/PAM_SUCCESS/, the module's return code
+ will override this value. Note, if the former state of the stack
+ holds some value that is indicative of a modules failure, this 'ok'
+ value will not be used to override that value.
+<item><tt/done/ - equivalent to <tt/ok/ with the side effect of
+ terminating the module stack and PAM immediately returning to the
+ application.
+<item><tt/reset/ - clear all memory of the state of the module stack and
+ start again with the next stacked module.
+</itemize>
+
+<p>
+Just to get a feel for the power of this new syntax, here is a taste
+of what you can do with it. With <bf/Linux-PAM-0.63/, the notion of
+client plug-in agents was introduced. This is something that makes it
+possible for PAM to support machine-machine authentication using the
+transport protocol inherent to the client/server application. With
+the ``<tt/[ ... value=action ... ]/'' control syntax, it is possible
+for an application to be configured to support binary prompts with
+compliant clients, but to gracefully fall over into an alternative
+authentication mode for older, legacy, applications. Flexible eh?
+
+<tag> <tt/module-path/</tag>
+
+The path-name of the dynamically loadable object file; <em/the
+pluggable module/ itself. If the first character of the module path is
+`<tt>/</tt>', it is assumed to be a complete path. If this is not the
+case, the given module path is appended to the default module path:
+<tt>/usr/lib/security</tt> (but see the notes <ref
+id="text-conventions" name="above">).
+
+<tag> <tt/args/</tag>
+
+The <tt/args/ are a list of tokens that are passed to the module when
+it is invoked. Much like arguments to a typical Linux shell command.
+Generally, valid arguments are optional and are specific to any given
+module. Invalid arguments are ignored by a module, however, when
+encountering an invalid argument, the module is required to write an
+error to <tt/syslog(3)/. For a list of <em/generic/ options see the
+next section.
+
+</descrip>
+
+<p>
+Any line in (one of) the confiuration file(s), that is not formatted
+correctly, will generally tend (erring on the side of caution) to make
+the authentication process fail. A corresponding error is written to
+the system log files with a call to <tt/syslog(3)/.
+
+<sect1>Directory based configuration
+
+<p>
+More flexible than the single configuration file, as of version 0.56,
+it is possible to configure <tt>libpam</tt> via the contents of the
+<tt>/etc/pam.d/</tt> directory. In this case the directory is filled
+with files each of which has a filename equal to a service-name (in
+lower-case): it is the personal configuration file for the named
+service.
+
+<p>
+<bf/Linux-PAM/ can be compiled in one of two modes. The preferred
+mode uses either <tt>/etc/pam.d/</tt> or <tt>/etc/pam.conf</tt>
+configuration but not both. That is to say, if there is a
+<tt>/etc/pam.d/</tt> directory then libpam only uses the files
+contained in this directory. However, in the absence of the
+<tt>/etc/pam.d/</tt> directory the <tt>/etc/pam.conf</tt> file is
+used. The other mode (and the one currently supported by Red Hat 4.2
+and higher) is to use both <tt>/etc/pam.d/</tt> and
+<tt>/etc/pam.conf</tt> in sequence. In this mode, entries in
+<tt>/etc/pam.d/</tt> override those of <tt>/etc/pam.conf</tt>.
+
+The syntax of each file in <tt>/etc/pam.d/</tt> is similar to that of
+the <tt>/etc/pam.conf</tt> file and is made up of lines of the
+following form:
+<tscreen>
+<verb>
+module-type control-flag module-path arguments
+</verb>
+</tscreen>
+The only difference being that the <tt>service-name</tt> is not
+present. The service-name is of course the name of the given
+configuration file. For example, <tt>/etc/pam.d/login</tt> contains
+the configuration for the <em>login</em> service.
+
+<p>
+This method of configuration has a number of advantages over the
+single file approach. We list them here to assist the reader in
+deciding which scheme to adopt:
+
+<p>
+<itemize>
+
+<item>A lower chance of misconfiguring an application. There is one
+less field to mis-type when editing the configuration files by hand.
+
+<item>Easier to maintain. One application may be reconfigured without
+risk of interfering with other applications on the system.
+
+<item>It is possible to symbolically link different services
+configuration files to a single file. This makes it easier to keep the
+system policy for access consistent across different applications.
+(It should be noted, to conserve space, it is equally possible to
+<em>hard</em> link a number of configuration files. However, care
+should be taken when administering this arrangement as editing a hard
+linked file is likely to break the link.)
+
+<item>A potential for quicker configuration file parsing. Only the
+relevant entries are parsed when a service gets bound to its modules.
+
+<item>It is possible to limit read access to individual <bf/Linux-PAM/
+configuration files using the file protections of the filesystem.
+
+<item>Package management becomes simpler. Every time a new
+application is installed, it can be accompanied by an
+<tt>/etc/pam.d/</tt><em>xxxxxx</em> file.
+
+</itemize>
+
+<sect1>Generic optional arguments
+
+<p>
+The following are optional arguments which are likely to be understood
+by any module. Arguments (including these) are in general
+<em/optional/.
+
+<p>
+<descrip>
+<tag><tt/debug/</tag>
+
+Use the <tt/syslog(3)/ call to log debugging information to the system
+log files.
+
+<tag> <tt/no_warn/</tag>
+
+Instruct module to not give warning messages to the application.
+
+<tag> <tt/use_first_pass/</tag>
+
+The module should not prompt the user for a password. Instead, it
+should obtain the previously typed password (from the preceding
+<tt/auth/ module), and use that. If that doesn't work, then the user
+will not be authenticated. (This option is intended for <tt/auth/
+and <tt/password/ modules only).
+
+<tag> <tt/try_first_pass/</tag>
+
+The module should attempt authentication with the previously typed
+password (from the preceding <tt/auth/ module). If that doesn't work,
+then the user is prompted for a password. (This option is intended for
+<tt/auth/ modules only).
+
+<tag> <tt/use_mapped_pass/</tag>
+
+This argument is not currently supported by any of the modules in the
+<bf/Linux-PAM/ distribution because of possible consequences
+associated with U.S. encryption exporting restrictions. Within the
+U.S., module developers are, of course, free to implement it (as are
+developers in other countries). For compatibility reasons we describe
+its use as suggested in the <bf/DCE-RFC 86.0/, see section <ref
+id="see-also-sec" name="bibliography"> for a pointer to this document.
+
+<p>
+The <tt/use_mapped_pass/ argument instructs the module to take the
+clear text authentication token entered by a previous module (that
+requests such a token) and use it to generate an encryption/decryption
+key with which to safely store/retrieve the authentication token
+required for this module. In this way the user can enter a single
+authentication token and be quietly authenticated by a number of
+stacked modules. Obviously a convenient feature that necessarily
+requires some reliably strong encryption to make it secure.
+This argument is intended for the <tt/auth/ and <tt/password/ module
+types only.
+
+<tag><tt/expose_account/</tag>
+
+<p>
+In general the leakage of some information about user accounts is not
+a secure policy for modules to adopt. Sometimes information such as
+users names or home directories, or preferred shell, can be used to
+attack a user's account. In some circumstances, however, this sort of
+information is not deemed a threat: displaying a user's full name when
+asking them for a password in a secured environment could also be
+called being 'friendly'. The <tt/expose_account/ argument is a
+standard module argument to encourage a module to be less discrete
+about account information as it is deemed appropriate by the local
+administrator.
+
+</descrip>
+
+<sect1>Example configuration file entries
+
+<p>
+In this section, we give some examples of entries that can be present
+in the <bf/Linux-PAM/ configuration file. As a first attempt at
+configuring your system you could do worse than to implement these.
+
+<sect2>Default policy
+
+<p>
+If a system is to be considered secure, it had better have a
+reasonably secure `<tt/OTHER/' entry. The following is a paranoid
+setting (which is not a bad place to start!):
+<tscreen>
+<verb>
+#
+# default; deny access
+#
+OTHER auth required /usr/lib/security/pam_deny.so
+OTHER account required /usr/lib/security/pam_deny.so
+OTHER password required /usr/lib/security/pam_deny.so
+OTHER session required /usr/lib/security/pam_deny.so
+</verb>
+</tscreen>
+Whilst fundamentally a secure default, this is not very sympathetic to
+a misconfigured system. For example, such a system is vulnerable to
+locking everyone out should the rest of the file become badly written.
+
+<p>
+The module <tt/pam_deny/ (documented in a later section) is not very
+sophisticated. For example, it logs no information when it is invoked
+so unless the users of a system contact the administrator when failing
+to execute a service application, the administrator may go for a long
+while in ignorance of the fact that his system is misconfigured.
+
+<p>
+The addition of the following line before those in the above example
+would provide a suitable warning to the administrator.
+<tscreen>
+<verb>
+#
+# default; wake up! This application is not configured
+#
+OTHER auth required /usr/lib/security/pam_warn.so
+OTHER password required /usr/lib/security/pam_warn.so
+</verb>
+</tscreen>
+Having two ``<tt/OTHER auth/'' lines is an example of stacking.
+
+<p>
+On a system that uses the <tt>/etc/pam.d/</tt> configuration, the
+corresponding default setup would be achieved with the following file:
+<tscreen>
+<verb>
+#
+# default configuration: /etc/pam.d/other
+#
+auth required /usr/lib/security/pam_warn.so
+auth required /usr/lib/security/pam_deny.so
+account required /usr/lib/security/pam_deny.so
+password required /usr/lib/security/pam_warn.so
+password required /usr/lib/security/pam_deny.so
+session required /usr/lib/security/pam_deny.so
+</verb>
+</tscreen>
+This is the only explicit example we give for an <tt>/etc/pam.d/</tt>
+file. In general, it should be clear how to transpose the remaining
+examples to this configuration scheme.
+
+<p>
+On a less sensitive computer, one on which the system administrator
+wishes to remain ignorant of much of the power of <tt/Linux-PAM/, the
+following selection of lines (in <tt>/etc/pam.conf</tt>) is likely to
+mimic the historically familiar Linux setup.
+<tscreen>
+<verb>
+#
+# default; standard UNIX access
+#
+OTHER auth required /usr/lib/security/pam_unix_auth.so
+OTHER account required /usr/lib/security/pam_unix_acct.so
+OTHER password required /usr/lib/security/pam_unix_passwd.so
+OTHER session required /usr/lib/security/pam_unix_session.so
+</verb>
+</tscreen>
+In general this will provide a starting place for most applications.
+Unfortunately, most is not all. One application that might require
+additional lines is <em/ftpd/ if you wish to enable
+<em/anonymous-ftp/.
+
+<p>
+To enable anonymous-ftp, the following lines might be used to replace
+the default (<tt/OTHER/) ones. (<bf/*WARNING*/ as of 1996/12/28 this
+does not work correctly with any ftpd. Consequently, this description
+may be subject to change or the application will be fixed.)
+<tscreen>
+<verb>
+#
+# ftpd; add ftp-specifics. These lines enable anonymous ftp over
+# standard UNIX access (the listfile entry blocks access to
+# users listed in /etc/ftpusers)
+#
+ftpd auth sufficient /usr/lib/security/pam_ftp.so
+ftpd auth required /usr/lib/security/pam_unix_auth.so use_first_pass
+ftpd auth required /usr/lib/security/pam_listfile.so \
+ onerr=succeed item=user sense=deny file=/etc/ftpusers
+</verb>
+</tscreen>
+Note, the second line is necessary since the default entries are
+ignored by a service application (here <em/ftpd/) if there are
+<em/any/ entries in <tt>/etc/pam.conf</tt> for that specified service.
+Again, this is an example of authentication module stacking. Note the
+use of the <tt/sufficient/ control-flag. It says that ``if this module
+authenticates the user, ignore the subsequent <tt/auth/
+modules''. Also note the use of the ``<tt/use_first_pass/''
+module-argument, this instructs the UNIX authentication module that it
+is not to prompt for a password but rely one already having been
+obtained by the ftp module.
+
+<sect>Security issues of Linux-PAM
+
+<p>
+This section will discuss good practices for using Linux-PAM in a
+secure manner. <em>It is currently sadly lacking...suggestions are
+welcome!</em>
+
+<sect1>If something goes wrong
+
+<p>
+<bf/Linux-PAM/ has the potential to seriously change the security of
+your system. You can choose to have no security or absolute security
+(no access permitted). In general, <bf/Linux-PAM/ errs towards the
+latter. Any number of configuration errors can dissable access to
+your system partially, or completely.
+
+<p>
+The most dramatic problem that is likely to be encountered when
+configuring <bf/Linux-PAM/ is that of <em>deleting</em> the
+configuration file(s): <tt>/etc/pam.d/*</tt> and/or
+<tt>/etc/pam.conf</tt>. This will lock you out of your own system!
+
+<p>
+To recover, your best bet is to reboot the system in single user mode
+and set about correcting things from there. The following has been
+<em>adapted</em> from a life-saving email on the subject from David
+Wood:
+<verb>
+> What the hell do I do now?
+
+OK, don't panic. The first thing you have to realize is that
+this happens to 50% of users who ever do anything with PAM.
+It happened here, not once, not twice, but three times, all
+different, and in the end, the solution was the same every
+time.
+
+First, I hope you installed LILO with a delay. If you can,
+reboot, hit shift or tab or something and type:
+
+ LILO boot: linux single
+
+(Replace 'linux' with 'name-of-your-normal-linux-image').
+This will let you in without logging in. Ever wondered how
+easy it is to break into a linux machine from the console?
+Now you know.
+
+If you can't do that, then get yourself a bootkernel floppy
+and a root disk a-la slackware's rescue.gz. (Red Hat's
+installation disks can be used in this mode too.)
+
+In either case, the point is to get back your root prompt.
+
+Second, I'm going to assume that you haven't completely
+nuked your pam installation - just your configuration files.
+Here's how you make your configs nice again:
+
+ cd /etc
+ mv pam.conf pam.conf.orig
+ mv pam.d pam.d.orig
+ mkdir pam.d
+ cd pam.d
+
+and then use vi to create a file called "other" in this
+directory. It should contain the following four lines:
+
+ auth required pam_unix_auth.so
+ account required pam_unix_acct.so
+ password required pam_unix_passwd.so
+ session required pam_unix_session.so
+
+Now you have the simplest possible PAM configuration that
+will work the way you're used to. Everything should
+magically start to work again. Try it out by hitting ALT-F2
+and logging in on another virtual console. If it doesn't
+work, you have bigger problems, or you've mistyped
+something. One of the wonders of this system (seriously,
+perhaps) is that if you mistype anything in the conf files,
+you usually get no error reporting of any kind on the
+console - just some entries in the log file. So look there!
+(Try 'tail /var/log/messages'.)
+
+From here you can go back and get a real configuration
+going, hopefully after you've tested it first on a machine
+you don't care about screwing up. :/
+
+Some pointers (to make everything "right" with Red Hat...):
+
+ Install the newest pam, pamconfig, and pwdb from the
+ redhat current directory, and do it all on the same
+ command line with rpm...
+
+ rpm -Uvh [maybe --force too] pam-* pamconfig-* pwdb-*
+
+ Then make sure you install (or reinstall) the newest
+ version of libc, util-linux, wuftp, and NetKit. For
+ kicks you might try installing the newest versions of
+ the affected x apps, like xlock, but I haven't gotten
+ those to work at all yet.
+
+</verb>
+
+<sect1>Avoid having a weak `other' configuration
+
+<p>
+It is not a good thing to have a weak default (<tt/OTHER/) entry.
+This service is the default configuration for all PAM aware
+applications and if it is weak, your system is likely to be vulnerable
+to attack.
+
+<p>
+Here is a sample "other" configuration file. The <em/pam_deny/ module will
+deny access and the <em/pam_warn/ module will send a syslog message to
+<tt/auth.notice/:
+
+<p>
+<tscreen>
+<verb>
+#
+# The PAM configuration file for the `other' service
+#
+auth required pam_deny.so
+auth required pam_warn.so
+account required pam_deny.so
+account required pam_warn.so
+password required pam_deny.so
+password required pam_warn.so
+session required pam_deny.so
+session required pam_warn.so
+</verb>
+</tscreen>
+
+<sect>A reference guide for available modules
+
+<p>
+Here, we collect together some descriptions of the various modules
+available for <bf/Linux-PAM/. In general these modules should be
+freely available. Where this is not the case, it will be indicated.
+
+<p>
+Also please note the comments contained in the section <ref
+id="text-conventions" name="on text conventions above"> when copying
+the examples listed below.
+
+<!-- insert-file MODULES-SGML -->
+
+<sect>Files
+
+<p><descrip>
+
+<tag><tt>/usr/lib/libpam.so.*</tt></tag>
+
+the shared library providing applications with access to
+<bf/Linux-PAM/.
+
+<tag><tt>/etc/pam.conf</tt></tag>
+
+the <bf/Linux-PAM/ configuration file.
+
+<tag><tt>/usr/lib/security/pam_*.so</tt></tag>
+
+the primary location for <bf/Linux-PAM/ dynamically loadable object
+files; the modules.
+
+</descrip>
+
+<sect>See also<label id="see-also-sec">
+
+<p><itemize>
+
+<item>The <bf/Linux-PAM/ Application Writers' Guide.
+
+<item>The <bf/Linux-PAM/ Module Writers' Guide.
+
+<item>The V. Samar and R. Schemers (SunSoft), ``UNIFIED LOGIN WITH
+PLUGGABLE AUTHENTICATION MODULES'', Open Software Foundation Request
+For Comments 86.0, October 1995. See this url:
+<tt><htmlurl
+url="http://www.pilgrim.umass.edu/pub/osf_dce/RFC/rfc86.0.txt"
+name="http://www.pilgrim.umass.edu/pub/osf&lowbar;dce/RFC/rfc86.0.txt"></tt>
+
+</itemize>
+
+<sect>Notes
+
+<p>
+I intend to put development comments here... like ``at the moment
+this isn't actually supported''. At release time what ever is in
+this section will be placed in the Bugs section below! :)
+
+<p>
+Are we going to be able to support the <tt/use_mapped_pass/ module
+argument? Anyone know a cheap (free) good lawyer?!
+
+<p>
+<itemize>
+<item>
+This issue may go away, as Sun have investigated adding a new
+management group for mappings. In this way, libpam would have mapping
+modules that could securely store passwords using strong cryptography
+and in such a way that they need not be distributed with Linux-PAM.
+</itemize>
+
+<sect>Author/acknowledgments
+
+<p>
+This document was written by Andrew G. Morgan (morgan@linux.kernel.org)
+with many contributions from
+<!-- insert credits here -->
+<!--
+ an sgml list of people to credit for their contributions to Linux-PAM
+ $Id$
+ -->
+Chris Adams,
+Peter Allgeyer,
+Tim Baverstock,
+Tim Berger,
+Craig S. Bell,
+Derrick J. Brashear,
+Ben Buxton,
+Seth Chaiklin,
+Oliver Crow,
+Chris Dent,
+Marc Ewing,
+Cristian Gafton,
+Emmanuel Galanos,
+Brad M. Garcia,
+Eric Hester,
+Roger Hu,
+Eric Jacksch,
+Michael K. Johnson,
+David Kinchlea,
+Olaf Kirch,
+Marcin Korzonek,
+Stephen Langasek,
+Nicolai Langfeldt,
+Elliot Lee,
+Luke Kenneth Casson Leighton,
+Al Longyear,
+Ingo Luetkebohle,
+Marek Michalkiewicz,
+Robert Milkowski,
+Aleph One,
+Martin Pool,
+Sean Reifschneider,
+Jan Rekorajski,
+Erik Troan,
+Theodore Ts'o,
+Jeff Uphoff,
+Myles Uyema,
+Savochkin Andrey Vladimirovich,
+Ronald Wahl,
+David Wood,
+John Wilmes,
+Joseph S. D. Yao
+and
+Alex O. Yuriev.
+
+<p>
+Thanks are also due to Sun Microsystems, especially to Vipin Samar and
+Charlie Lai for their advice. At an early stage in the development of
+<bf/Linux-PAM/, Sun graciously made the documentation for their
+implementation of PAM available. This act greatly accelerated the
+development of <bf/Linux-PAM/.
+
+<sect>Bugs/omissions
+
+<p>
+More PAM modules are being developed all the time. It is unlikely that
+this document will ever be truely up to date!
+
+<p>
+This manual is unfinished. Only a partial list of people is credited
+for all the good work they have done.
+
+<sect>Copyright information for this document
+
+<p>
+Copyright (c) Andrew G. Morgan 1996-9. All rights reserved.
+<newline>
+Email: <tt>&lt;morgan@linux.kernel.org&gt;</tt>
+
+<p>
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+<p>
+<itemize>
+
+<item>
+1. Redistributions of source code must retain the above copyright
+ notice, and the entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+<item>
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+<item>
+3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+</itemize>
+
+<p>
+<bf/Alternatively/, this product may be distributed under the terms of
+the GNU General Public License (GPL), in which case the provisions of
+the GNU GPL are required <bf/instead of/ the above restrictions.
+(This clause is necessary due to a potential bad interaction between
+the GNU GPL and the restrictions contained in a BSD-style copyright.)
+
+<p>
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+<p>
+<tt>$Id$</tt>
+
+</article>
diff --git a/doc/ps/.cvsignore b/doc/ps/.cvsignore
new file mode 100644
index 00000000..8d83f2d0
--- /dev/null
+++ b/doc/ps/.cvsignore
@@ -0,0 +1 @@
+pam*.ps \ No newline at end of file
diff --git a/doc/ps/README b/doc/ps/README
new file mode 100644
index 00000000..3122478b
--- /dev/null
+++ b/doc/ps/README
@@ -0,0 +1,3 @@
+$Id$
+
+this is the directory for the postscipt documentation
diff --git a/doc/ps/missfont.log b/doc/ps/missfont.log
new file mode 100644
index 00000000..162c37a8
--- /dev/null
+++ b/doc/ps/missfont.log
@@ -0,0 +1,36 @@
+MakeTeXPK cmss17 720 600 magstep\(1.0\) ljfour
+MakeTeXPK cmr10 600 600 1+0/600 ljfour
+MakeTeXPK cmtt10 600 600 1+0/600 ljfour
+MakeTeXPK cmss10 600 600 1+0/600 ljfour
+MakeTeXPK cmbx10 600 600 1+0/600 ljfour
+MakeTeXPK cmbx12 720 600 magstep\(1.0\) ljfour
+MakeTeXPK cmti10 600 600 1+0/600 ljfour
+MakeTeXPK cmtt9 600 600 1+0/600 ljfour
+MakeTeXPK cmbx12 600 600 1+0/600 ljfour
+MakeTeXPK cmsy10 600 600 1+0/600 ljfour
+MakeTeXPK cmmi10 600 600 1+0/600 ljfour
+MakeTeXPK cmti9 600 600 1+0/600 ljfour
+MakeTeXPK cmss17 720 600 magstep\(1.0\) ljfour
+MakeTeXPK cmr10 600 600 1+0/600 ljfour
+MakeTeXPK cmtt10 600 600 1+0/600 ljfour
+MakeTeXPK cmss10 600 600 1+0/600 ljfour
+MakeTeXPK cmbx10 600 600 1+0/600 ljfour
+MakeTeXPK cmbx12 720 600 magstep\(1.0\) ljfour
+MakeTeXPK cmbx12 600 600 1+0/600 ljfour
+MakeTeXPK cmtt9 600 600 1+0/600 ljfour
+MakeTeXPK cmti10 600 600 1+0/600 ljfour
+MakeTeXPK cmmi10 600 600 1+0/600 ljfour
+MakeTeXPK cmsy10 600 600 1+0/600 ljfour
+MakeTeXPK cmss17 720 600 magstep\(1.0\) ljfour
+MakeTeXPK cmr10 600 600 1+0/600 ljfour
+MakeTeXPK cmtt10 600 600 1+0/600 ljfour
+MakeTeXPK cmss10 600 600 1+0/600 ljfour
+MakeTeXPK cmbx10 600 600 1+0/600 ljfour
+MakeTeXPK cmbx12 720 600 magstep\(1.0\) ljfour
+MakeTeXPK cmti10 600 600 1+0/600 ljfour
+MakeTeXPK cmbx12 600 600 1+0/600 ljfour
+MakeTeXPK cmtt9 600 600 1+0/600 ljfour
+MakeTeXPK cmtt12 600 600 1+0/600 ljfour
+MakeTeXPK cmti12 600 600 1+0/600 ljfour
+MakeTeXPK cmsy10 600 600 1+0/600 ljfour
+MakeTeXPK cmmi10 600 600 1+0/600 ljfour
diff --git a/doc/specs/.cvsignore b/doc/specs/.cvsignore
new file mode 100644
index 00000000..d564ba7e
--- /dev/null
+++ b/doc/specs/.cvsignore
@@ -0,0 +1 @@
+draft-morgan-pam-*.txt
diff --git a/doc/specs/draft-morgan-pam.raw b/doc/specs/draft-morgan-pam.raw
new file mode 100644
index 00000000..46db0013
--- /dev/null
+++ b/doc/specs/draft-morgan-pam.raw
@@ -0,0 +1,702 @@
+PAM working group ## A.G. Morgan
+Internet Draft: ## October 6, 1999
+Document: draft-morgan-pam-07.txt ##
+Expires: June 13, 2000 ##
+Obsoletes: draft-morgan-pam-06.txt##
+
+## Pluggable Authentication Modules ##
+
+#$ Status of this memo
+
+This document is an draft specification. The latest version of this
+draft may be obtained from here:
+
+ http://linux.kernel.org/pub/linux/libs/pam/pre/doc/
+
+As
+
+ Linux-PAM-'version'-docs.tar.gz
+
+It is also contained in the Linux-PAM tar ball.
+
+#$ Abstract
+
+This document is concerned with the definition of a general
+infrastructure for module based authentication. The infrastructure is
+named Pluggable Authentication Modules (PAM for short).
+
+#$ Introduction
+
+Computers are tools. They provide services to people and other
+computers (collectively we shall call these _users_ entities). In
+order to provide convenient, reliable and individual service to
+different entities, it is common for entities to be labelled. Having
+defined a label as referring to a some specific entity, the label is
+used for the purpose of protecting and allocating data resources.
+
+All modern operating systems have a notion of labelled entities and
+all modern operating systems face a common problem: how to
+authenticate the association of a predefined label with applicant
+entities.
+
+There are as many authentication methods as one might care to count.
+None of them are perfect and none of them are invulnerable. In
+general, any given authentication method becomes weaker over time. It
+is common then for new authentication methods to be developed in
+response to newly discovered weaknesses in the old authentication
+methods.
+
+The problem with inventing new authentication methods is the fact that
+old applications do not support them. This contributes to an inertia
+that discourages the overhaul of weakly protected systems. Another
+problem is that individuals (people) are frequently powerless to layer
+the protective authentication around their systems. They are forced
+to rely on single (lowest common denominator) authentication schemes
+even in situations where this is far from appropriate.
+
+PAM, as discussed in this document, is a generalization of the
+approach first introduced in [#$R#{OSF_RFC_PAM}]. In short, it is a
+general framework of interfaces that abstract the process of
+authentication. With PAM, a service provider can custom protect
+individual services to the level that they deem is appropriate.
+
+PAM has nothing explicit to say about transport layer encryption.
+Within the context of this document encryption and/or compression of
+data exchanges are application specific (strictly between client and
+server) and orthogonal to the process of authentication.
+
+#$ Definitions
+
+Here we pose the authentication problem as one of configuring defined
+interfaces between two entities.
+
+#$$#{players} Players in the authentication process
+
+PAM reserves the following words to specify unique entities in the
+authentication process:
+
+ applicant
+ the entity (user) initiating an application for service
+ [PAM associates the PAM_RUSER _item_ with this requesting user].
+
+ arbitrator
+ the entity (user) under whose identity the service application
+ is negotiated and with whose authority service is granted.
+
+ user
+ the entity (user) whose identity is being authenticated
+ [PAM associates the PAM_USER _item_ with this identity].
+
+ server
+ the application that provides service, or acts as an
+ authenticated gateway to the requested service. This
+ application is completely responsible for the server end of
+ the transport layer connecting the server to the client.
+ PAM makes no assumptions about how data is encapsulated for
+ exchanges between the server and the client, only that full
+ octet sequences can be freely exchanged without corruption.
+
+ client
+ application providing the direct/primary interface to
+ applicant. This application is completely responsible
+ for the client end of the transport layer connecting the
+ server to the client. PAM makes no assumptions about how data
+ is encapsulated for exchanges between the server and the
+ client, only that full octet sequences can be freely
+ exchanged without corruption.
+
+ module
+ authentication binary that provides server-side support for
+ some (arbitrary) authentication method.
+
+ agent
+ authentication binary that provides client-side support for
+ some (arbitrary) authentication method.
+
+Here is a diagram to help orient the reader:
+
+## +-------+ +--------+ ##
+## . . . . .| agent | .| module | ##
+## . +-------+ .+--------+ ##
+## V | . | ##
+## . | V | ##
+## +---------+ +-------+ . +------+ ##
+## | | |libpamc| . |libpam| ##
+## | | +-------+ . +------+ ##
+## |applicant| | . | ##
+## | | +--------+ +----------+ ##
+## | |---| client |-----------| server | ##
+## +---------+ +--------+ +----------+ ##
+
+Solid lines connecting the boxes represent two-way interaction. The
+dotted-directed lines indicate an optional connection beteween the
+plugin module (agent) and the server (applicant). In the case of the
+module, this represents the module invoking the 'conversation'
+callback function provided to libpam by the server application when it
+inititializes the libpam library. In the case of the agent, this may
+be some out-of-PAM API interaction (for example directly displaying a
+dialog box under X).
+
+#$$ Defined Data Types
+
+In this draft, we define two composite data types, the text string and
+the binary prompt. They are the data types used to communicate
+authentication requests and responses.
+
+#$$$#{text_string} text string
+
+The text string is a simple sequence of non-NUL (NUL = 0x00)
+octets. Terminated with a single NUL (0x00) octet. The character set
+employed in the octet sequence may be negotiated out of band, but
+defaults to utf-8.
+
+## --------------------------- ##
+## [ character data | NUL ] ##
+## [ octet sequence | 0x00 ] ##
+## --------------------------- ##
+
+Within the rest of this text, PAM text strings are delimited with a
+pair of double quotes. Example, "this" = {'t';'h';'i';'s';0x00}.
+
+#$$$#{binary_prompt} binary prompt
+
+A binary prompt consists of a stream of octets arranged as follows:
+
+## ---------------------------------------- ##
+## [ u32 | u8 | (length-5 octets) ] ##
+## [ length | control | data ] ##
+## ---------------------------------------- ##
+
+That is, a 32-bit unsigned integer in network byte order, a single
+unsigned byte of control information and a sequence of octets of
+length (length-5). The composition of the _data_ is context dependent
+but is generally not a concern for either the server or the client. It
+is very much the concern of modules and agents.
+
+For purposes of interoperability, we define the following control
+characters as legal.
+
+## value symbol description ##
+## ------------------------------------------------- ##
+## 0x01 PAM_BPC_OK - continuation packet ##
+## 0x02 PAM_BPC_SELECT - initialization packet ##
+## 0x03 PAM_BPC_DONE - termination packet ##
+## 0x04 PAM_BPC_FAIL - unable to execute ##
+
+The following control characters are only legal for exchanges between
+an agent and a client (it is the responsibility of the client to
+enforce this rule in the face of a rogue server):
+
+## 0x41 PAM_BPC_GETENV - obtain client env.var ##
+## 0x42 PAM_BPC_PUTENV - set client env.var ##
+## 0x43 PAM_BPC_TEXT - display message ##
+## 0x44 PAM_BPC_ERROR - display error message ##
+## 0x45 PAM_BPC_PROMPT - echo'd text prompt ##
+## 0x46 PAM_BPC_PASS - non-echo'd text prompt##
+
+Note, length is always equal to the total length of the binary
+prompt and represented by a network ordered unsigned 32 bit integer.
+
+#$$$$#{agent_ids} PAM_BPC_SELECT binary prompts
+
+Binary prompts of control type PAM_BPC_SELECT have a defined
+data part. It is composed of three elements:
+
+ {agent_id;'/';data}
+
+The agent_id is a sequence of characters satisfying the following
+regexp:
+
+ /^[a-z0-9\_]+(@[a-z0-9\_.]+)?$/
+
+and has a specific form for each independent agent.
+
+o Agent_ids that do not contain an at-sign (@) are reserved to be
+ assigned by IANA (Internet Assigned Numbers Authority). Names of
+ this format MUST NOT be used without first registering with IANA.
+ Registered names MUST NOT contain an at-sign (@).
+
+o Anyone can define additional agents by using names in the format
+ name@domainname, e.g. "ouragent@example.com". The part following
+ the at-sign MUST be a valid fully qualified internet domain name
+ [RFC-1034] controlled by the person or organization defining the
+ name. (Said another way, if you control the email address that
+ your agent has as an identifier, they you are entitled to use
+ this identifier.) It is up to each domain how it manages its local
+ namespace.
+
+The '/' character is a mandatory delimiter, indicating the end of the
+agent_id. The trailing data is of a format specific to the agent with
+the given agent_id.
+
+
+#$$ Special cases
+
+In a previous section (#{players}) we identified the most general
+selection of authentication participants. In the case of network
+authentication, it is straightforward to ascribe identities to the
+defined participants. However, there are also special (less general)
+cases that we recognize here.
+
+The primary authentication step, when a user is directly introduced
+into a computer system (log's on to a workstation) is a special case.
+In this situation, the client and the server are generally one
+application. Before authenticating such a user, the applicant is
+formally unknown: PAM_RUSER is NULL.
+
+Some client-server implementations (telnet for example) provide
+effective full tty connections. In these cases, the four simple text
+string prompting cases (see below) can be handled as in the primary
+login step. In other words, the server absorbs most of the overhead of
+propagating authentication messages. In these cases, there is special
+client/server support for handling binary prompts.
+
+#$ Defined interfaces for information flow
+
+Here, we discuss the information exchange interfaces between the
+players in the authentication process. It should be understood that
+the server side is responsible for driving the authentication of the
+applicant. Notably, every request received by the client from the
+server must be matched with a single response from the client to the
+server.
+
+#$$#{applicant_client} Applicant <-> client
+
+Once the client is invoked, requests to the applicant entity are
+initiated by the client application. General clients are able to make
+the following requests directly to an applicant:
+
+ echo text string
+ echo error text string
+ prompt with text string for echo'd text string input
+ prompt with text string for concealed text string input
+
+the nature of the interface provided by the client for the benefit of
+the applicant entity is client specific and not defined by PAM.
+
+#$$#{client_agent} Client <-> agent
+
+In general, authentication schemes require more modes of exchange than
+the four defined in the previous section (#{applicant_client}). This
+provides a role for client-loadable agents. The client and agent
+exchange binary-messages that can have one of the following forms:
+
+ client -> agent
+ binary prompt agent expecting binary prompt reply to client
+
+ agent -> client
+ binary prompt reply from agent to clients binary prompt
+
+Following the acceptance of a binary prompt by the agent, the agent
+may attempt to exchange information with the client before returning
+its binary prompt reply. Permitted exchanges are binary prompts of the
+following types:
+
+ agent -> client
+ set environment variable (A)
+ get environment variable (B)
+ echo text string (C)
+ echo error text string (D)
+ prompt for echo'd text string input (E)
+ prompt for concealed text string input (F)
+
+In response to these prompts, the client must legitimately respond
+with a corresponding binary prompt reply. We list a complete set of
+example exchanges, including each type of legitimate response (passes
+and a single fail):
+
+## Type | Agent request | Client response ##
+## --------------------------------------------------------------- ##
+## (A) | {13;PAM_BPC_PUTENV;"FOO=BAR"} | {5;PAM_BPC_OK;} ##
+## | {10;PAM_BPC_PUTENV;"FOO="} | {5;PAM_BPC_OK;} ##
+## | {9;PAM_BPC_PUTENV;"FOO"} (*) | {5;PAM_BPC_OK;} ##
+## | {9;PAM_BPC_PUTENV;"BAR"} (*) | {5;PAM_BPC_FAIL;} ##
+## --------------------------------------------------------------- ##
+## (B) | {10;PAM_BPC_GETENV;"TERM"} | {11;PAM_BPC_OK;"vt100"} ##
+## | {9;PAM_BPC_GETENV;"FOO"} | {5;PAM_BPC_FAIL;} ##
+## --------------------------------------------------------------- ##
+## (C) | {12;PAM_BPC_TEXT;"hello!"} | {5;PAM_BPC_OK;} ##
+## | {12;PAM_BPC_TEXT;"hello!"} | {5;PAM_BPC_FAIL;} ##
+## --------------------------------------------------------------- ##
+## (D) | {11;PAM_BPC_TEXT;"ouch!"} | {5;PAM_BPC_OK;} ##
+## | {11;PAM_BPC_TEXT;"ouch!"} | {5;PAM_BPC_FAIL;} ##
+## --------------------------------------------------------------- ##
+## (E) | {13;PAM_BPC_PROMPT;"login: "} | {9;PAM_BPC_OK;"joe"} ##
+## | {13;PAM_BPC_PROMPT;"login: "} | {6;PAM_BPC_OK;""} ##
+## | {13;PAM_BPC_PROMPT;"login: "} | {5;PAM_BPC_FAIL;} ##
+## --------------------------------------------------------------- ##
+## (F) | {16;PAM_BPC_PASS;"password: "} | {9;PAM_BPC_OK;"XYZ"} ##
+## | {16;PAM_BPC_PASS;"password: "} | {6;PAM_BPC_OK;""} ##
+## | {16;PAM_BPC_PASS;"password: "} | {5;PAM_BPC_FAIL;} ##
+
+(*) Used to attempt the removal of a pre-existing environment
+variable.
+
+#$$ Client <-> server
+
+Once the client has established a connection with the server (the
+nature of the transport protocol is not specified by PAM), the server
+is responsible for driving the authentication process.
+
+General servers can request the following from the client:
+
+ (to be forwarded by the client to the applicant)
+ echo text string
+ echo error text string
+ prompt for echo'd text string response
+ prompt for concealed text string response
+
+ (to be forwarded by the client to the appropriate agent)
+ binary prompt for a binary prompt response
+
+Client side agents are required to process binary prompts. The
+agents' binary prompt responses are returned to the server.
+
+#$$ Server <-> module
+
+Modules drive the authentication process. The server provides a
+conversation function with which it encapsulates module-generated
+requests and exchanges them with the client. Every message sent by a
+module should be acknowledged.
+
+General conversation functions can support the following five
+conversation requests:
+
+ echo text string
+ echo error string
+ prompt for echo'd text string response
+ prompt for concealed text string response
+ binary prompt for binary prompt response
+
+The server is responsible for redirecting these requests to the
+client.
+
+#$ C API for application interfaces (client and server)
+
+#$$ Applicant <-> client
+
+No API is defined for this interface. The interface is considered to
+be specific to the client application. Example applications include
+terminal login, (X)windows login, machine file transfer applications.
+
+All that is important is that the client application is able to
+present the applicant with textual output and to receive textual
+input from the applicant. The forms of textual exchange are listed
+in an earlier section (#{applicant_client}). Other methods of
+data input/output are better suited to being handled via an
+authentication agent.
+
+#$$ Client <-> agent
+
+The client makes use of a general API for communicating with
+agents. The client is not required to communicate directly with
+available agents, instead a layer of abstraction (in the form of a
+library: libpamc) takes care of loading and maintaining communication
+with all requested agents. This layer of abstraction will choose which
+agents to interact with based on the content of binary prompts it
+receives that have the control type PAM_BPC_SELECT.
+
+#$$$ Client <-> libpamc
+
+#$$$$ Compilation information
+
+The C-header file provided for client-agent abstraction is included
+with the following source line:
+
+ \#include <security/pam_client.h>
+
+The library providing the corresponding client-agent abstraction
+functions is, libpamc.
+
+ cc .... -lpamc
+
+#$$$$ Initializing libpamc
+
+The libpamc library is initialized with a call to the following
+function:
+
+ pamc_handle_t pamc_start(void);
+
+This function is responsible for configuring the library and
+registering the location of available agents. The location of the
+available agents on the system is implementation specific.
+
+pamc_start() function returns NULL on failure. Otherwise, the return
+value is a pointer to an opaque data type which provides a handle to
+the libpamc library. On systems where threading is available, the
+libpamc libraray is thread safe provided a single (pamc_handler_t *)
+is used by each thread.
+
+#$$$$ Client (Applicant) selection of agents
+
+For the purpose of applicant and client review of available agents,
+the following function is provided.
+
+ char **pamc_list_agents(pamc_handle_t pch);
+
+This returns a list of pointers to the agent_id's of the agents which
+are available on the system. The list is terminated by a NULL pointer.
+It is the clients responsibility to free this memory area by calling
+free() on each agent id and the block of agent_id pointers in the
+result.
+
+PAM represents a server-driven authentication model, so by default
+any available agent may be invoked in the authentication process.
+
+#$$$$$ Client demands agent
+
+If the client requires that a specific authentication agent is
+satisfied during the authentication process, then the client should
+call the following function, immediately after obtaining a
+pamc_handle_t from pamc_start().
+
+ int pamc_load(pamc_handle_t pch, const char *agent_id);
+
+agent_id is a PAM text string (see section #{agent_ids}) and is not
+suffixed with a '/' delimiter. The return value for this function is:
+
+ PAM_BPC_TRUE - agent located and loaded.
+ PAM_BPC_FALSE - agent is not available.
+
+Note, although the agent is loaded, no data is fed to it. The agent's
+opportunity to inform the client that it does not trust the server is
+when the agent is shutdown.
+
+#$$$$$ Client marks agent as unusable
+
+The applicant might prefer that a named agent is marked as not
+available. To do this, the client would invoke the following function
+immediately after obtaining a pamc_handle_t from pam_start().
+
+ int pamc_disable(pamc_handle_t pch, const char *agent_id);
+
+here agent_id is a PAM text string containing an agent_id (section
+#{agent_ids}).
+
+The return value for this function is:
+
+ PAM_BPC_TRUE - agent is disabled. This is the response
+ independent of whether the agent is locally
+ available.
+
+ PAM_BPC_FALSE - agent cannot be disabled (this may be because
+ it has already been invoked).
+
+#$$$$ Allocating and manipulating binary prompts
+
+All conversation between an client and an agent takes place with
+respect to binary prompts. A binary prompt (see section #{binary_prompt}), is
+obtained, resized and deleted via the following C-macro:
+
+ CREATION of a binary prompt with control X1 and data length Y1:
+
+ pamc_bp_t prompt = NULL;
+ PAM_BP_RENEW(&prompt, X1, Y1);
+
+ REPLACEMENT of a binary prompt with a control X2 and data length Y2:
+
+ PAM_BP_RENEW(&prompt, X2, Y2);
+
+ DELETION of a binary prompt (the referenced prompt is scrubbed):
+
+ PAM_BP_RENEW(&prompt, 0, 0);
+
+Note, the PAM_BP_RENEW macro always overwrites any prompt that you
+call it with, deleting and liberating the old contents in a secure
+fashion. Also note that PAM_BP_RENEW, when returning a prompt of data
+size Y1>0, will always append a '\0' byte to the end of the prompt (at
+data offset Y1). It is thus, by definition, acceptable to treat the
+data contents of a binary packet as a text string (see #{text_string}).
+
+ FILLING a binary prompt from a memory pointer U1 from offset O1 of
+ length L1:
+
+ PAM_BP_FILL(prompt, O1, L1, U1);
+
+ the CONTROL type for the packet can be obtained as follows:
+
+ control = PAM_PB_CONTROL(prompt);
+
+ the LENGTH of a data within the prompt (_excluding_ its header
+ information) can be obtained as follows:
+
+ length = PAM_BP_LENGTH(prompt);
+
+ the total SIZE of the prompt (_including_ its header information)
+ can be obtained as follows:
+
+ size = PAM_BP_SIZE(prompt);
+
+ EXTRACTING data from a binary prompt from offset O2 of length L2 to
+ a memory pointer U2:
+
+ PAM_BP_EXTRACT(prompt, O2, L2, U2);
+
+ If you require direct access to the raw prompt DATA, you should use
+ the following macro:
+
+ __u8 *raw_data = PAM_BP_DATA(prompt);
+
+#$$$$ Client<->agent conversations
+
+All exchanges of binary prompts with agents are handled with the
+single function:
+
+ int pamc_converse(pamc_handle_t *pch, pamc_bp_t *prompt_p);
+
+The return value for pamc_converse(...) is PAM_BPC_TRUE when there is
+a response packet and PAM_BPC_FALSE when the client is unable to
+handle the request represented by the original prompt. In this latter
+case, *prompt_p is set to NULL.
+
+This function takes a binary prompt and returns a replacement binary
+prompt that is either a request from an agent to be acted upon by the
+client or the 'result' which should be forwarded to the server. In the
+former case, the following macro will return 1 (PAM_BPC_TRUE) and in
+all other cases, 0 (PAM_BPC_FALSE):
+
+ PAM_BPC_FOR_CLIENT(/* pamc_bp_t */ prompt)
+
+Note, all non-NULL binary prompts returned by pamc_converse(...), are
+terminated with a '\0', even when the full length of the prompt (as
+returned by the agent) does not contain this delimiter. This is a
+defined property of the PAM_BP_RENEW macro, and can be relied upon.
+
+Important security note: in certain implementations, agents are
+implemented by executable binaries, which are transparently loaded and
+managed by the PAM client library. To ensure there is never a leakage
+of elevated privilege to an unprivileged agent, the client application
+should go to some effort to lower its level of privilege. It remains
+the responsibility of the applicant and the client to ensure that it
+is not compromised by a rogue agent.
+
+#$$$$ Termination of agents
+
+When closing the authentication session and severing the connection
+between a client and a selection of agents, the following function is
+used:
+
+ int pamc_end(pamc_handle_t *pch);
+
+Following a call to pamc_end, the pamc_handle_t will be invalid.
+
+The return value for this function is one of the following:
+
+ PAM_BPC_TRUE - all invoked agents are content with
+ authentication (the server is _not_ judged
+ _un_trustworthy by any agent)
+
+ PAM_BPC_FALSE - one or more agents were unsatisfied at
+ being terminated. In general, the client
+ should terminate its connection to the
+ server and indicate to the applicant that
+ the server is untrusted.
+
+#$$$ libpamc <-> agents
+
+The agents are manipulated from within libpamc. Each agent is an
+executable in its own right. This permits the agent to have access to
+sensitive data not accessible directly from the client. The mode of
+communication between libpamc and an agent is through a pair of
+pipes. The agent reads binary prompts (section #{binary_prompt})
+through its standard input file descriptor and writes response (to the
+server) binary prompts and instruction binary prompts (instructions
+for the client) through its standard output file descriptor.
+
+#$$ Client <-> server
+
+This interface is concerned with the exchange of text and binary
+prompts between the client application and the server application. No
+API is provided for this as it is considered specific to the transport
+protocol shared by the client and the server.
+
+#$$ Server <-> modules
+
+The server makes use of a general API for communicating with
+modules. The client is not required to communicate directly with
+available modules. By abstracting the authentication interface, it
+becomes possible for the local administrator to make a run time
+decision about the authentication method adopted by the server.
+
+#$$$ Functions and definitions available to servers and modules
+
+[This section will document the following functions
+
+ pam_set_item()
+ pam_get_item()
+ pam_fail_delay(pam_handle_t *pamh, unsigned int micro_sec)
+ pam_get_env(pam_handle_t *pamh, const char *varname)
+ pam_strerror(pam_handle_t *pamh, int pam_errno)
+]
+
+#$$$ Server <-> libpam
+
+[This section will document the following pam_ calls:
+
+ pam_start
+ pam_end
+ pam_authenticate (*)
+ pam_setcred
+ pam_acct_mgmt
+ pam_open_session
+ pam_close_session
+ pam_chauthtok (*)
+
+The asterisked functions may return PAM_INCOMPLETE. In such cases, the
+application should be aware that the conversation function was called
+and that it returned PAM_CONV_AGAIN to a module. The correct action
+for the application to take in response to receiving PAM_INCOMPLETE,
+is to acquire the replies so that the next time the conversation
+function is called it will be able to provide the desired
+responses. And then recall pam_authenticate (pam_chauthtok) with the
+same arguments. Libpam will arrange that the module stack is resumed
+from the module that returned before. This functionality is required
+for programs whose user interface is maintained by an event loop. ]
+
+#$$$ libpam <-> modules
+
+[This section will document the following pam_ and pam_sm_ calls:
+
+functions provided by libpam
+
+ pam_set_data
+ pam_get_data
+
+functions provided to libpam by each module
+
+ groups:
+ AUTHENTICATION
+ pam_sm_authenticate
+ pam_sm_setcred
+ ACCOUNT
+ pam_sm_acct_mgmt
+ SESSION
+ pam_sm_open_session
+ pam_sm_close_session
+ AUTHENTICATION TOKEN MANAGEMENT
+ pam_sm_chauthtok
+]
+
+#$ Security considerations
+
+This document is devoted to standardizing authentication
+infrastructure: everything in this document has implications for
+security.
+
+#$ Contact
+
+The email list for discussing issues related to this document is
+<pam-list@redhat.com>.
+
+#$ References
+
+[#{OSF_RFC_PAM}] OSF RFC 86.0, "Unified Login with Pluggable Authentication
+ Modules (PAM)", October 1995
+
+#$ Author's Address
+
+Andrew G. Morgan
+Email: morgan@ftp.kernel.org
+
+## $Id$ ##
+
diff --git a/doc/specs/formatter/.cvsignore b/doc/specs/formatter/.cvsignore
new file mode 100644
index 00000000..8af8c897
--- /dev/null
+++ b/doc/specs/formatter/.cvsignore
@@ -0,0 +1,3 @@
+lex.yy.c
+parse.tab.c
+padout
diff --git a/doc/specs/formatter/Makefile b/doc/specs/formatter/Makefile
new file mode 100644
index 00000000..d73258d7
--- /dev/null
+++ b/doc/specs/formatter/Makefile
@@ -0,0 +1,16 @@
+LIBS=-lfl
+
+padout: parse.tab.o
+ $(CC) -o padout parse.tab.o $(LIBS)
+
+parse.tab.o: parse.tab.c lex.yy.c
+ $(CC) -c parse.tab.c
+
+parse.tab.c: parse.y
+ bison parse.y
+
+lex.yy.c: parse.lex
+ flex parse.lex
+
+clean:
+ rm -f parse.tab.o parse.tab.c lex.yy.c padout *~ core
diff --git a/doc/specs/formatter/parse.lex b/doc/specs/formatter/parse.lex
new file mode 100644
index 00000000..1d5c898e
--- /dev/null
+++ b/doc/specs/formatter/parse.lex
@@ -0,0 +1,11 @@
+%%
+
+\#[\$]+[a-zA-Z]*(\=[0-9]+)? return NEW_COUNTER;
+\#\{[a-zA-Z][a-zA-Z0-9\_]*\} return LABEL;
+\# return NO_INDENT;
+\#\# return RIGHT;
+\\\# return HASH;
+[^\n] return CHAR;
+[\n] return NEWLINE;
+
+%%
diff --git a/doc/specs/formatter/parse.y b/doc/specs/formatter/parse.y
new file mode 100644
index 00000000..6da47d17
--- /dev/null
+++ b/doc/specs/formatter/parse.y
@@ -0,0 +1,293 @@
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAXLINE 1000
+#define INDENT_STRING " "
+#define PAPER_WIDTH 74
+
+ int indent=0;
+ int line=1;
+ char *last_label=NULL;
+
+ extern void yyerror(const char *x);
+ extern char *get_label(const char *label);
+ extern void set_label(const char *label, const char *target);
+ char *new_counter(const char *key);
+
+#include "lex.yy.c"
+
+%}
+
+%union {
+ int def;
+ char *string;
+}
+
+%token NEW_COUNTER LABEL HASH CHAR NEWLINE NO_INDENT RIGHT
+%type <string> stuff text
+
+%start doc
+
+%%
+
+doc:
+| doc NEWLINE {
+ printf("\n");
+ ++line;
+}
+| doc stuff NEWLINE {
+ if (strlen($2) > (PAPER_WIDTH-(indent ? strlen(INDENT_STRING):0))) {
+ yyerror("line too long");
+ }
+ printf("%s%s\n", indent ? INDENT_STRING:"", $2);
+ free($2);
+ indent = 1;
+ ++line;
+}
+| doc stuff RIGHT stuff NEWLINE {
+ char fixed[PAPER_WIDTH+1];
+ int len;
+
+ len = PAPER_WIDTH-(strlen($2)+strlen($4));
+
+ if (len >= 0) {
+ memset(fixed, ' ', len);
+ fixed[len] = '\0';
+ } else {
+ yyerror("line too wide");
+ fixed[0] = '\0';
+ }
+ printf("%s%s%s\n", $2, fixed, $4);
+ free($2);
+ free($4);
+ indent = 1;
+ ++line;
+}
+| doc stuff RIGHT stuff RIGHT stuff NEWLINE {
+ char fixed[PAPER_WIDTH+1];
+ int len, l;
+
+ len = PAPER_WIDTH-(strlen($2)+strlen($4));
+
+ if (len < 0) {
+ len = 0;
+ yyerror("line too wide");
+ }
+
+ l = len/2;
+ memset(fixed, ' ', l);
+ fixed[l] = '\0';
+ printf("%s%s%s", $2, fixed, $4);
+ free($2);
+ free($4);
+
+ l = (len+1)/2;
+ memset(fixed, ' ', l);
+ fixed[l] = '\0';
+ printf("%s%s\n", fixed, $6);
+ free($6);
+
+ indent = 1;
+ ++line;
+}
+| doc stuff RIGHT stuff RIGHT stuff NEWLINE {
+ char fixed[PAPER_WIDTH+1];
+ int len, l;
+
+ len = PAPER_WIDTH-(strlen($2)+strlen($4));
+
+ if (len < 0) {
+ len = 0;
+ yyerror("line too wide");
+ }
+
+ l = len/2;
+ memset(fixed, ' ', l);
+ fixed[l] = '\0';
+ printf("%s%s%s", $2, fixed, $4);
+ free($2);
+ free($4);
+
+ l = (len+1)/2;
+ memset(fixed, ' ', l);
+ fixed[l] = '\0';
+ printf("%s%s\n", fixed, $6);
+ free($6);
+
+ indent = 1;
+ ++line;
+}
+;
+
+stuff: {
+ $$ = strdup("");
+}
+| stuff text {
+ $$ = malloc(strlen($1)+strlen($2)+1);
+ sprintf($$,"%s%s", $1, $2);
+ free($1);
+ free($2);
+}
+;
+
+text: CHAR {
+ $$ = strdup(yytext);
+}
+| text CHAR {
+ $$ = malloc(strlen($1)+2);
+ sprintf($$,"%s%s", $1, yytext);
+ free($1);
+}
+| NO_INDENT {
+ $$ = strdup("");
+ indent = 0;
+}
+| HASH {
+ $$ = strdup("#");
+}
+| LABEL {
+ if (($$ = get_label(yytext)) == NULL) {
+ set_label(yytext, last_label);
+ $$ = strdup("");
+ }
+}
+| NEW_COUNTER {
+ $$ = new_counter(yytext);
+}
+;
+
+%%
+
+typedef struct node_s {
+ struct node_s *left, *right;
+ const char *key;
+ char *value;
+} *node_t;
+
+node_t label_root = NULL;
+node_t counter_root = NULL;
+
+const char *find_key(node_t root, const char *key)
+{
+ while (root) {
+ int cmp = strcmp(key, root->key);
+
+ if (cmp > 0) {
+ root = root->right;
+ } else if (cmp) {
+ root = root->left;
+ } else {
+ return root->value;
+ }
+ }
+ return NULL;
+}
+
+node_t set_key(node_t root, const char *key, const char *value)
+{
+ if (root) {
+ int cmp = strcmp(key, root->key);
+ if (cmp > 0) {
+ root->right = set_key(root->right, key, value);
+ } else if (cmp) {
+ root->left = set_key(root->left, key, value);
+ } else {
+ free(root->value);
+ root->value = strdup(value);
+ }
+ } else {
+ root = malloc(sizeof(struct node_s));
+ root->right = root->left = NULL;
+ root->key = strdup(key);
+ root->value = strdup(value);
+ }
+ return root;
+}
+
+void yyerror(const char *x)
+{
+ fprintf(stderr, "line %d: %s\n", line, x);
+}
+
+char *get_label(const char *label)
+{
+ const char *found = find_key(label_root, label);
+
+ if (found) {
+ return strdup(found);
+ }
+ return NULL;
+}
+
+void set_label(const char *label, const char *target)
+{
+ if (target == NULL) {
+ yyerror("no hanging value for label");
+ target = "<??>";
+ }
+ label_root = set_key(label_root, label, target);
+}
+
+char *new_counter(const char *key)
+{
+ int i=0, j, ndollars = 0;
+ const char *old;
+ char *new;
+
+ if (key[i++] != '#') {
+ yyerror("bad index");
+ return strdup("<???>");
+ }
+
+ while (key[i] == '$') {
+ ++ndollars;
+ ++i;
+ }
+
+ key += i;
+ old = find_key(counter_root, key);
+ new = malloc(20*ndollars);
+
+ if (old) {
+ for (j=0; ndollars > 1 && old[j]; ) {
+ if (old[j++] == '.' && --ndollars <= 0) {
+ break;
+ }
+ }
+ if (j) {
+ strncpy(new, old, j);
+ }
+ if (old[j]) {
+ i = atoi(old+j);
+ } else {
+ new[j++] = '.';
+ i = 0;
+ }
+ } else {
+ j=0;
+ while (--ndollars > 0) {
+ new[j++] = '0';
+ new[j++] = '.';
+ }
+ i = 0;
+ }
+ new[j] = '\0';
+ sprintf(new+j, "%d", ++i);
+
+ counter_root = set_key(counter_root, key, new);
+
+ if (last_label) {
+ free(last_label);
+ }
+ last_label = strdup(new);
+
+ return new;
+}
+
+main()
+{
+ yyparse();
+}
diff --git a/doc/specs/rfc86.0.txt b/doc/specs/rfc86.0.txt
new file mode 100644
index 00000000..6dd5e6ea
--- /dev/null
+++ b/doc/specs/rfc86.0.txt
@@ -0,0 +1,1851 @@
+
+
+
+
+
+
+
+
+ Open Software Foundation V. Samar (SunSoft)
+ Request For Comments: 86.0 R. Schemers (SunSoft)
+ October 1995
+
+
+
+ UNIFIED LOGIN WITH
+ PLUGGABLE AUTHENTICATION MODULES (PAM)
+
+
+ 1. INTRODUCTION
+
+ Since low-level authentication mechanisms constantly evolve, it is
+ important to shield the high-level consumers of these mechanisms
+ (system-entry services and users) from such low-level changes. With
+ the Pluggable Authentication Module (PAM) framework, we can provide
+ pluggability for a variety of system-entry services -- not just
+ system authentication _per se_, but also for account, session and
+ password management. PAM's ability to _stack_ authentication modules
+ can be used to integrate `login' with different authentication
+ mechanisms such as RSA, DCE, and Kerberos, and thus unify login
+ mechanisms. The PAM framework can also provide easy integration of
+ smart cards into the system.
+
+ Modular design and pluggability have become important for users who
+ want ease of use. In the PC hardware arena, no one wants to set the
+ interrupt vector numbers or resolve the addressing conflict between
+ various devices. In the software arena, people also want to be able
+ to replace components easily for easy customization, maintenance, and
+ upgrades.
+
+ Authentication software deserves special attention because
+ authentication forms a very critical component of any secure computer
+ system. The authentication infrastructure and its components may
+ have to be modified or replaced either because some deficiencies have
+ been found in the current algorithms, or because sites want to
+ enforce a different security policy than what was provided by the
+ system vendor. The replacement and modification should be done in
+ such a way that the user is not affected by these changes.
+
+ The solution has to address not just how the applications use the new
+ authentication mechanisms in a generic fashion, but also how the user
+ will be authenticated to these mechanisms in a generic way. The
+ former is addressed by GSS-API [Linn 93], while this RFC addresses
+ the later; these two efforts are complementary to each other.
+
+ Since most system-entry services (for example, `login', `dtlogin',
+ `rlogin', `ftp', `rsh') may want to be independent of the specific
+ authentication mechanisms used by the machine, it is important that
+ there be a framework for _plugging_ in various mechanisms. This
+ requires that the system applications use a standard API to interact
+
+
+
+ Samar, Schemers Page 1
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ with the authentication services. If these system-entry services
+ remain independent of the actual mechanism used on that machine, the
+ system administrator can install suitable authentication modules
+ without requiring changes to these applications.
+
+ For any security system to be successful, it has to be easy to use.
+ In the case of authentication, the single most important ease-of-use
+ characteristic is that the user should not be required to learn about
+ various ways of authentication and remember multiple passwords.
+ Ideally, there should be one all-encompassing authentication system
+ where there is only one password, but for heterogeneous sites,
+ multiple authentication mechanisms have to co-exist. The problem of
+ integrating multiple authentication mechanisms such as Kerberos
+ [Steiner 88], RSA [Rivest 78], and Diffie-Hellman [Diffie 76, Taylor
+ 88], is also referred to as _integrated login_, or _unified login_
+ problem. Even if the user has to use multiple authentication
+ mechanisms, the user should not be forced to type multiple passwords.
+ Furthermore, the user should be able to use the new network identity
+ without taking any further actions. The key here is in modular
+ integration of the network authentication technologies with `login'
+ and other system-entry services.
+
+ In this RFC we discuss the architecture and design of pluggable
+ authentication modules. This design gives the capability to use
+ field-replaceable authentication modules along with unified login
+ capability. It thus provides for both _pluggability_ and _ease-of-
+ use_.
+
+ The RFC is organized as follows. We first motivate the need for a
+ generic way to authenticate the user by various system-entry services
+ within the operating system. We describe the goals and constraints
+ of the design. This leads to the architecture, description of the
+ interfaces, and _stacking_ of modules to get unified login
+ functionality. We then describe our experience with the design, and
+ end with a description of future work.
+
+
+ 2. OVERVIEW OF IDENTIFICATION AND AUTHENTICATION MECHANISMS
+
+ An identification and authentication ("I&A") mechanism is used to
+ establish a user's identity the system (i.e., to a local machine's
+ operating system) and to other principals on the network. On a
+ typical UNIX system, there are various ports of entry into the
+ system, such as `login', `dtlogin', `rlogin', `ftp', `rsh', `su', and
+ `telnet'. In all cases, the user has to be identified and
+ authenticated before granting appropriate access rights to the user.
+ The user identification and authentication for all these entry points
+ needs to be coordinated to ensure a secure system.
+
+ In most of the current UNIX systems, the login mechanism is based
+ upon verification of the password using the modified DES algorithm.
+
+
+
+ Samar, Schemers Page 2
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ The security of the implementation assumes that the password cannot
+ be guessed, and that the password does not go over the wire in the
+ clear. These assumptions, however, are not universally valid.
+ Various programs are now available freely on the Internet that can
+ run dictionary attack against the encrypted password. Further, some
+ of the network services (for example, `rlogin', `ftp', `telnet') send
+ the password over in clear, and there are "sniffer" programs freely
+ available to steal these passwords. The classical assumptions may be
+ acceptable on a trusted network, but in an open environment there is
+ a need to use more restrictive and stronger authentication
+ mechanisms. Examples of such mechanisms include Kerberos, RSA,
+ Diffie-Hellman, one-time password [Skey 94], and challenge-response
+ based smart card authentication systems. Since this list will
+ continue to evolve, it is important that the system-entry services do
+ not have hard-coded dependencies on any of these authentication
+ mechanisms.
+
+
+ 3. DESIGN GOALS
+
+ The goals of the PAM framework are as follows:
+
+ (a) The system administrator should be able to choose the default
+ authentication mechanism for the machine. This can range from
+ a simple password-based mechanism to a biometric or a smart
+ card based system.
+
+ (b) It should be possible to configure the user authentication
+ mechanism on a per application basis. For example, a site may
+ require S/Key password authentication for `telnet' access,
+ while allowing machine `login' sessions with just UNIX password
+ authentication.
+
+ (c) The framework should support the display requirements of the
+ applications. For example, for a graphical login session such
+ as `dtlogin', the user name and the password may have to be
+ entered in a new window. For networking system-entry
+ applications such as `ftp' and `telnet', the user name and
+ password has to be transmitted over the network to the client
+ machine.
+
+ (d) It should be possible to configure multiple authentication
+ protocols for each of those applications. For example, one may
+ want the users to get authenticated by both Kerberos and RSA
+ authentication systems.
+
+ (e) The system administrator should be able to _stack_ multiple
+ user authentication mechanisms such that the user is
+ authenticated with all authentication protocols without
+ retyping the password.
+
+
+
+
+ Samar, Schemers Page 3
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ (f) The architecture should allow for multiple passwords if
+ necessary to achieve higher security for users with specific
+ security requirements.
+
+ (g) The system-entry services should not be required to change when
+ the underlying mechanism changes. This can be very useful for
+ third-party developers because they often do not have the
+ source code for these services.
+
+ (h) The architecture should provide for a _pluggable_ model for
+ system authentication, as well as for other related tasks such
+ as password, account, and session management.
+
+ (i) For backward-compatibility reasons, the PAM API should support
+ the authentication requirements of the current system-entry
+ services.
+
+ There are certain issues that the PAM framework does not specifically
+ address:
+
+ (a) We focus only on providing a generic scheme through which users
+ use passwords to establish their identities to the machine.
+ Once the identity is established, how the identity is
+ communicated to other interested parties is outside the scope
+ of this design. There are efforts underway at IETF [Linn 93]
+ to develop a Generic Security Services Application Interface
+ (GSSAPI) that can be used by applications for secure and
+ authenticated communication without knowing the underlying
+ mechanism.
+
+ (b) The _single-signon_ problem of securely transferring the
+ identity of the caller to a remote site is not addressed. For
+ example, the problem of delegating credentials from the
+ `rlogin' client to the other machine without typing the
+ password is not addressed by our work. We also do not address
+ the problem of sending the passwords over the network in the
+ clear.
+
+ (c) We do not address the source of information obtained from the
+ "`getXbyY()'" family of calls (e.g., `getpwnam()'). Different
+ operating systems address this problem differently. For
+ example, Solaris uses the name service switch (NSS) to
+ determine the source of information for the "`getXbyY()'"
+ calls. It is expected that data which is stored in multiple
+ sources (such as passwd entries in NIS+ and the DCE registry)
+ is kept in sync using the appropriate commands (such as
+ `passwd_export').
+
+
+
+
+
+
+
+ Samar, Schemers Page 4
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ 4. OVERVIEW OF THE PAM FRAMEWORK
+
+ We propose that the goals listed above can be met through a framework
+ in which authentication modules can be _plugged_ independently of the
+ application. We call this the _Pluggable Authentication Modules_
+ (PAM) framework.
+
+ The core components of the PAM framework are the authentication
+ library API (the front end) and the authentication mechanism-specific
+ modules (the back end), connected through the Service Provider
+ Interface (SPI). Applications write to the PAM API, while the
+ authentication-system providers write to the PAM SPI and supply the
+ back end modules that are independent of the application.
+
+ ftp telnet login (Applications)
+ | | |
+ | | |
+ +--------+--------+
+ |
+ +-----+-----+
+ | PAM API | <-- pam.conf file
+ +-----+-----+
+ |
+ +--------+--------+
+ UNIX Kerberos Smart Cards (Mechanisms)
+
+ Figure 1: The Basic PAM Architecture
+
+ Figure 1 illustrates the relationship between the application, the
+ PAM library, and the authentication modules. Three applications
+ (`login', `telnet' and `ftp') are shown which use the PAM
+ authentication interfaces. When an application makes a call to the
+ PAM API, it loads the appropriate authentication module as determined
+ by the configuration file, `pam.conf'. The request is forwarded to
+ the underlying authentication module (for example, UNIX password,
+ Kerberos, smart cards) to perform the specified operation. The PAM
+ layer then returns the response from the authentication module to the
+ application.
+
+ PAM unifies system authentication and access control for the system,
+ and allows plugging of associated authentication modules through well
+ defined interfaces. The plugging can be defined through various
+ means, one of which uses a configuration file, such as the one in
+ Table 1. For each of the system applications, the file specifies the
+ authentication module that should be loaded. In the example below,
+ `login' uses the UNIX password module, while `ftp' and `telnet' use
+ the S/Key module.
+
+
+
+
+
+
+
+ Samar, Schemers Page 5
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ Table 1: A Simplified View of a Sample PAM Configuration File.
+
+ service module_path
+ ------- -----------
+ login pam_unix.so
+ ftp pam_skey.so
+ telnet pam_skey.so
+
+ Authentication configuration is only one aspect of this interface.
+ Other critical components include account management, session
+ management, and password management. For example, the `login'
+ program may want to verify not only the password but also whether the
+ account has aged or expired. Generic interfaces also need to be
+ provided so that the password can be changed according to the
+ requirements of the module. Furthermore, the application may want to
+ log information about the current session as determined by the
+ module.
+
+ Not all applications or services may need all of the above
+ components, and not each authentication module may need to provide
+ support for all of the interfaces. For example, while `login' may
+ need access to all four components, `su' may need access to just the
+ authentication component. Some applications may use some specific
+ authentication and password management modules but share the account
+ and session management modules with others.
+
+ This reasoning leads to a partitioning of the entire set of
+ interfaces into four areas of functionality: (1) authentication, (2)
+ account, (3) session, and (4) password. The concept of PAM was
+ extended to these functional areas by implementing each of them as a
+ separate pluggable module.
+
+ Breaking the functionality into four modules helps the module
+ providers because they can use the system-provided libraries for the
+ modules that they are not changing. For example, if a supplier wants
+ to provide a better version of Kerberos, they can just provide that
+ new authentication and password module, and reuse the existing ones
+ for account and session.
+
+ 4.1. Module Description
+
+ More details on specific API's are described in Appendix A. A brief
+ description of four modules follows:
+
+ (a) Authentication management: This set includes the
+ `pam_authenticate()' function to authenticate the user, and the
+ `pam_setcred()' interface to set, refresh or destroy the user
+ credentials.
+
+ (b) Account management: This set includes the `pam_acct_mgmt()'
+ function to check whether the authenticated user should be
+
+
+
+ Samar, Schemers Page 6
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ given access to his/her account. This function can implement
+ account expiration and access hour restrictions.
+
+ (c) Session management: This set includes the `pam_open_session()'
+ and `pam_close_session()' functions for session management and
+ accounting. For example, the system may want to store the
+ total time for the session.
+
+ (d) Password management: This set includes a function,
+ `pam_chauthtok()', to change the password.
+
+
+ 5. FRAMEWORK INTERFACES
+
+ The PAM framework further provides a set of administrative interfaces
+ to support the above modules and to provide for application-module
+ communication. There is no corresponding service provider interface
+ (SPI) for such functions.
+
+ 5.1. Administrative Interfaces
+
+ Each set of PAM transactions starts with `pam_start()' and ends with
+ the `pam_end()' function. The interfaces `pam_get_item()' and
+ `pam_set_item()' are used to read and write the state information
+ associated with the PAM transaction.
+
+ If there is any error with any of the PAM interfaces, the error
+ message can be printed with `pam_strerror()'.
+
+ 5.2. Application-Module Communication
+
+ During application initialization, certain data such as the user name
+ is saved in the PAM framework layer through `pam_start()' so that it
+ can be used by the underlying modules. The application can also pass
+ opaque data to the module which the modules will pass back while
+ communicating with the user.
+
+ 5.3. User-Module Communication
+
+ The `pam_start()' function also passes conversation function that has
+ to be used by the underlying modules to read and write module
+ specific authentication information. For example, these functions
+ can be used to prompt the user for the password in a way determined
+ by the application. PAM can thus be used by graphical, non-
+ graphical, or networked applications.
+
+
+
+
+
+
+
+
+
+ Samar, Schemers Page 7
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ 5.4. Inter-Module Communication
+
+ Though the modules are independent, they can share certain common
+ information about the authentication session such as user name,
+ service name, password, and conversation function through the
+ `pam_get_item()' and `pam_set_item()' interfaces. These API's can
+ also be used by the application to change the state information after
+ having called `pam_start()' once.
+
+ 5.5. Module State Information
+
+ The PAM service modules may want to keep certain module-specific
+ state information about the session. The interfaces `pam_get_data()'
+ and `pam_set_data()' can be used by the service modules to access and
+ update module-specific information as needed from the PAM handle.
+ The modules can also attach a cleanup function with the data. The
+ cleanup function is executed when `pam_end()' is called to indicate
+ the end of the current authentication activity.
+
+ Since the PAM modules are loaded upon demand, there is no direct
+ module initialization support in the PAM framework. If there are
+ certain initialization tasks that the PAM service modules have to do,
+ they should be done upon the first invocation. However, if there are
+ certain clean-up tasks to be done when the authentication session
+ ends, the modules should use `pam_set_data()' to specify the clean-up
+ functions, which would be called when `pam_end()' is called by the
+ application.
+
+
+ 6. MODULE CONFIGURATION MANAGEMENT
+
+ Table 2 shows an example of a configuration file `pam.conf' with
+ support for authentication, session, account, and password management
+ modules. `login' has three entries: one each for authentication
+ processing, session management and account management. Each entry
+ specifies the module name that should be loaded for the given module
+ type. In this example, the `ftp' service uses the authentication and
+ session modules. Note that all services here share the same session
+ management module, while having different authentication modules.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Samar, Schemers Page 8
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ Table 2: Configuration File (pam.conf) with Different Modules
+ and Control Flow
+
+ service module_type control_flag module_path options
+ ------- ----------- ------------ ----------- -------
+ login auth required pam_unix_auth.so nowarn
+ login session required pam_unix_session.so
+ login account required pam_unix_account.so
+ ftp auth required pam_skey_auth.so debug
+ ftp session required pam_unix_session.so
+ telnet session required pam_unix_session.so
+ login password required pam_unix_passwd.so
+ passwd password required pam_unix_passwd.so
+ OTHER auth required pam_unix_auth.so
+ OTHER session required pam_unix_session.so
+ OTHER account required pam_unix_account.so
+
+ The first field, _service_, denotes the service (for example,
+ `login', `passwd', `rlogin'). The name `OTHER' indicates the module
+ used by all other applications that have not been specified in this
+ file. This name can also be used if all services have the same
+ requirements. In the example, since all the services use the same
+ session module, we could have replaced those lines with a single
+ `OTHER' line.
+
+ The second field, _module_type_, indicates the type of the PAM
+ functional module. It can be one of `auth', `account', `session', or
+ `password' modules.
+
+ The third field, _control_flag_ determines the behavior of stacking
+ multiple modules by specifying whether any particular module is
+ _required_, _sufficient_, or _optional_. The next section describes
+ stacking in more detail.
+
+ The fourth field, _module_path_, specifies the location of the
+ module. The PAM framework loads this module upon demand to invoke
+ the required function.
+
+ The fifth field, _options_, is used by the PAM framework layer to
+ pass module specific options to the modules. It is up to the module
+ to parse and interpret the options. This field can be used by the
+ modules to turn on debugging or to pass any module specific
+ parameters such as a timeout value. It is also used to support
+ unified login as described below. The options field can be used by
+ the system administrator to fine-tune the PAM modules.
+
+ If any of the fields are invalid, or if a module is not found, that
+ line is ignored and the error is logged as a critical error via
+ `syslog(3)'. If no entries are found for the given module type, then
+ the PAM framework returns an error to the application.
+
+
+
+
+ Samar, Schemers Page 9
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ 7. INTEGRATING MULTIPLE AUTHENTICATION SERVICES WITH STACKING
+
+ In the world of heterogeneous systems, the system administrator often
+ has to deal with the problem of integrating multiple authentication
+ mechanisms. The user is often required to know about the
+ authentication command of the new authentication module (for example,
+ `kinit', `dce_login') after logging into the system. This is not
+ user-friendly because it forces people to remember to type the new
+ command and enter the new password. This functionality should be
+ invisible instead of burdening the user with it.
+
+ There are two problems to be addressed here:
+
+ (a) Supporting multiple authentication mechanisms.
+
+ (b) Providing unified login in the presence of multiple mechanisms.
+
+ In the previous section, we described how one could replace the
+ default authentication module with any other module of choice. Now
+ we demonstrate how the same model can be extended to provide support
+ for multiple modules.
+
+ 7.1. Design for Stacked Modules
+
+ One possibility was to provide hard-coded rules in `login' or other
+ applications requiring authentication services [Adamson 95]. But
+ this becomes very specific to the particular combination of
+ authentication protocols, and also requires the source code of the
+ application. Digital's Security Integration Architecture [SIA 95]
+ addresses this problem by specifying the same list of authentication
+ modules for all applications. Since requirements for various
+ applications can vary, it is essential that the configuration be on a
+ per-application basis.
+
+ To support multiple authentication mechanisms, the PAM framework was
+ extended to support _stacking_. When any API is called, the back
+ ends for the stacked modules are invoked in the order listed, and the
+ result returned to the caller. In Figure 2, the authentication
+ service of `login' is stacked and the user is authenticated by UNIX,
+ Kerberos, and RSA authentication mechanisms. Note that in this
+ example, there is no stacking for session or account management
+ modules.
+
+
+
+
+
+
+
+
+
+
+
+
+ Samar, Schemers Page 10
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ login
+ |
+ +--------+--------+
+ | | |
+ session auth account
+ | | |
+ +--+--+ +--+--+ +--+--+
+ | PAM | | PAM | | PAM |
+ +--+--+ +--+--+ +--+--+
+ | | |
+ UNIX UNIX UNIX
+ session auth account
+ |
+ Kerberos
+ auth
+ |
+ RSA
+ auth
+
+ Figure 2: Stacking With the PAM Architecture
+
+ Stacking is specified through additional entries in the configuration
+ file shown earlier. As shown in Table 2, for each application (such
+ as `login') the configuration file can specify multiple mechanisms
+ that have to be invoked in the specified order. When mechanisms
+ fail, the _control_flag_ decides which error should be returned to
+ the application. Since the user should not know which authentication
+ module failed when a bad password was typed, the PAM framework
+ continues to call other authentication modules on the stack even on
+ failure. The semantics of the control flag are as follows:
+
+ (a) `required': With this flag, the module failure results in the
+ PAM framework returning the error to the caller _after_
+ executing all other modules on the stack. For the function to
+ be able to return success to the application all `required'
+ modules have to report success. This flag is normally set when
+ authentication by this module is a _must_.
+
+ (b) `optional': With this flag, the PAM framework ignores the
+ module failure and continues with the processing of the next
+ module in sequence. This flag is used when the user is allowed
+ to login even if that particular module has failed.
+
+ (c) `sufficient': With this flag, if the module succeeds the PAM
+ framework returns success to the application immediately
+ without trying any other modules. For failure cases, the
+ _sufficient_ modules are treated as `optional'.
+
+ Table 3 shows a sample configuration file that stacks the `login'
+ command. Here the user is authenticated by UNIX, Kerberos, and RSA
+ authentication services. The `required' key word for _control_flag_
+
+
+
+ Samar, Schemers Page 11
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ enforces that the user is allowed to login only if he/she is
+ authenticated by _both_ UNIX and Kerberos services. RSA
+ authentication is optional by virtue of the `optional' key word in
+ the _control_flag_ field. The user can still log in even if RSA
+ authentication fails.
+
+ Table 3: PAM Configuration File with Support for Stacking
+
+ service module_type control_flag module_path options
+ ------- ----------- ------------ ----------- -------
+ login auth required pam_unix.so debug
+ login auth required pam_kerb.so use_mapped_pass
+ login auth optional pam_rsa.so use_first_pass
+
+ Table 4 illustrates the use of the sufficient flag for the `rlogin'
+ service. The Berkeley `rlogin' protocol specifies that if the remote
+ host is trusted (as specified in the `/etc/hosts.equiv' file or in
+ the `.rhosts' file in the home directory of the user), then the
+ `rlogin' daemon should not require the user to type the password. If
+ this is not the case, then the user is required to type the password.
+ Instead of hard coding this policy in the `rlogin' daemon, this can
+ be expressed with the `pam.conf' file in Table 4. The PAM module
+ `pam_rhosts_auth.so.1' implements the `.rhosts' policy described
+ above. If a site administrator wants to enable remote login with
+ only passwords, then the first line should be deleted.
+
+ Table 4: PAM Configuration File for the rlogin service
+
+ service module_type control_flag module_path options
+ ------- ----------- ------------ ----------- -------
+ rlogin auth sufficient pam_rhosts_auth.so
+ rlogin auth required pam_unix.so
+
+ 7.2. Password-Mapping
+
+ Multiple authentication mechanisms on a machine can lead to multiple
+ passwords that users have to remember. One attractive solution from
+ the ease-of-use viewpoint is to use the same password for all
+ mechanisms. This, however, can also weaken the security because if
+ that password were to be compromised in any of the multiple
+ mechanisms, all mechanisms would be compromised at the same time.
+ Furthermore, different authentication mechanisms may have their own
+ distinctive password requirements in regards to its length, allowed
+ characters, time interval between updates, aging, locking, and so
+ forth. These requirements make it problematic to use the same
+ password for multiple authentication mechanisms.
+
+ The solution we propose, while not precluding use of the same
+ password for every mechanism, allows for a different password for
+ each mechanism through what we call _password-mapping_. This
+ basically means using the user's _primary_ password to encrypt the
+
+
+
+ Samar, Schemers Page 12
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ user's other (_secondary_) passwords, and storing these encrypted
+ passwords in a place where they are available to the user. Once the
+ primary password is verified, the authentication modules would obtain
+ the other passwords for their own mechanisms by decrypting the
+ mechanism-specific encrypted password with the primary password, and
+ passing it to the authentication service. The security of this
+ design for password-mapping assumes that the primary password is the
+ user's strongest password, in terms of its unguessability (length,
+ type and mix of characters used, etc.).
+
+ If there is any error in password-mapping, or if the mapping does not
+ exist, the user will be prompted for the password by each
+ authentication module.
+
+ To support password-mapping, the PAM framework saves the primary
+ password and provides it to stacked authentication modules. The
+ password is cleared out before the `pam_authenticate' function
+ returns.
+
+ How the password is encrypted depends completely on the module
+ implementation. The encrypted secondary password (also called a
+ "mapped password") can be stored in a trusted or untrusted place,
+ such as a smart card, a local file, or a directory service. If the
+ encrypted passwords are stored in an untrusted publicly accessible
+ place, this does provide an intruder with opportunities for potential
+ dictionary attack.
+
+ Though password-mapping is voluntary, it is recommended that all
+ module providers add support for the following four mapping options:
+
+ (a) `use_first_pass': Use the same password used by the first
+ mechanism that asked for a password. The module should not ask
+ for the password if the user cannot be authenticated by the
+ first password. This option is normally used when the system
+ administrator wants to enforce the same password across
+ multiple modules.
+
+ (b) `try_first_pass': This is the same as `use_first_pass', except
+ that if the primary password is not valid, it should prompt the
+ user for the password.
+
+ (c) `use_mapped_pass': Use the password-mapping scheme to get the
+ actual password for this module. One possible implementation
+ is to get the mapped-password using the XFN API [XFN 94], and
+ decrypt it with the primary password to get the module-specific
+ password. The module should not ask for the password if the
+ user cannot be authenticated by the first password. The XFN
+ API allows user-defined attributes (such as _mapped-password_)
+ to be stored in the _user-context_. Using the XFN API is
+ particularly attractive because support for the XFN may be
+ found on many systems in the future.
+
+
+
+ Samar, Schemers Page 13
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ (d) `try_mapped_pass': This is the same as `use_mapped_pass',
+ except that if the primary password is not valid, it should
+ prompt the user for the password.
+
+ When passwords get updated, the PAM framework stores both the old as
+ well as the new password to be able to inform other dependent
+ authentication modules about the change. Other modules can use this
+ information to update the encrypted password without forcing the user
+ to type the sequence of passwords again. The PAM framework clears
+ out the passwords before returning to the application.
+
+ Table 3 illustrates how the same password can be used by `login' for
+ authenticating to the standard UNIX login, Kerberos and RSA services.
+ Once the user has been authenticated to the primary authentication
+ service (UNIX `login' in this example) with the primary password, the
+ option `use_mapped_pass' indicates to the Kerberos module that it
+ should use the primary password to decrypt the stored Kerberos
+ password and then use the Kerberos password to get the ticket for the
+ ticket-granting-service. After that succeeds, the option
+ `use_first_pass' indicates to the RSA module that instead of
+ prompting the user for a password, it should use the primary password
+ typed earlier for authenticating the user. Note that in this
+ scenario, the user has to enter the password just once.
+
+ Note that if a one-time password scheme (e.g., S/Key) is used,
+ password mapping cannot apply.
+
+ 7.3. Implications of Stacking on the PAM Design
+
+ Because of the stacking capability of PAM, we have designed the PAM
+ API's to not return any data to the application, except status. If
+ this were not the case, it would be difficult for the PAM framework
+ to decide which module should return data to the application. When
+ there is any error, the application does not know which of the
+ modules failed. This behavior enables (even requires) the
+ application to be completely independent from the modules.
+
+ Another design decision we have made is that PAM gives only the user
+ name to all the underlying PAM modules, hence it is the
+ responsibility of the PAM modules to convert the name to their own
+ internal format. For example, the Kerberos module may have to
+ convert the UNIX user name to a Kerberos principal name.
+
+ Stacking also forces the modules to be designed such that they can
+ occur anywhere in the stack without any side-effects.
+
+ Since modules such as the authentication and the password module are
+ very closely related, it is important they be configured in the same
+ order and with compatible options.
+
+
+
+
+
+ Samar, Schemers Page 14
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ 8. INTEGRATION WITH SMART CARDS
+
+ Many networking authentication protocols require possession of a long
+ key to establish the user identity. For ease-of-use reasons, that
+ long key is normally encrypted with the user's password so that the
+ user is not required to memorize it. However, weak passwords can be
+ compromised through a dictionary attack and thus undermine the
+ stronger network authentication mechanism. Furthermore, the
+ encrypted data is normally stored in a centrally accessible service
+ whose availability depends upon the reliability of the associated
+ service. Solutions have been proposed to use a pass-phrase or one-
+ time-password, but those are much longer than the regular eight
+ character passwords traditionally used with UNIX `login'. This makes
+ the solution user-unfriendly because it requires longer strings to be
+ remembered and typed.
+
+ For most authentication protocol implementations, the trust boundary
+ is the local machine. This assumption may not be valid in cases
+ where the user is mobile and has to use publicly available networked
+ computers. In such cases, it is required that the clear text of the
+ key or the password never be made available to the machine.
+
+ Smart cards solve the above problems by reducing password exposure by
+ supporting a _two factor_ authentication mechanism: the first with
+ the possession of the card, and the second with the knowledge of the
+ PIN associated with the card. Not only can the smart cards be a
+ secure repository of multiple passwords, they can also provide the
+ encryption and authentication functions such that the long (private)
+ key is never exposed outside the card.
+
+ The PAM framework allows for integrating smart cards to the system by
+ providing a smart card specific module for authentication.
+ Furthermore, the unified login problem is simplified because the
+ multiple passwords for various authentication mechanisms can be
+ stored on the smart card itself. This can be enabled by adding a
+ suitable key-word such as `use_smart_card' in the _options_ field.
+
+
+ 9. SECURITY ISSUES
+
+ It is important to understand the impact of PAM on the security of
+ any system so that the site-administrator can make an informed
+ decision.
+
+ (a) Sharing of passwords with multiple authentication mechanisms.
+
+ If there are multiple authentication modules, one possibility
+ is to use the same password for all of them. If the password
+ for any of the multiple authentication system is compromised,
+ the user's password in all systems would be compromised. If
+ this is a concern, then multiple passwords might be considered
+
+
+
+ Samar, Schemers Page 15
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ at the cost of ease-of-use.
+
+ (b) Password-mapping.
+
+ This technique of encrypting all other passwords with the
+ primary password assumes that it is lot more difficult to crack
+ the primary password and that reasonable steps have been taken
+ to ensure limited availability of the encrypted primary
+ password. If this is not done, an intruder could target the
+ primary password as the first point of dictionary attack. If
+ one of the other modules provide stronger security than the
+ password based security, the site would be negating the strong
+ security by using password-mapping. If this is a concern, then
+ multiple passwords might be considered at the cost of ease-of-
+ use. If smart cards are used, they obviate the need for
+ password-mapping completely.
+
+ (c) Security of the configuration file.
+
+ Since the policy file dictates how the user is authenticated,
+ this file should be protected from unauthorized modifications.
+
+ (d) Stacking various PAM modules.
+
+ The system administrator should fully understand the
+ implications of stacking various modules that will be installed
+ on the system and their respective orders and interactions.
+ The composition of various authentication modules should be
+ carefully examined. The trusted computing base of the machine
+ now includes the PAM modules.
+
+
+ 10. EXPERIENCE WITH PAM
+
+ The PAM framework was first added in Solaris 2.3 release as a private
+ internal interface. PAM is currently being used by several system
+ entry applications such as `login', `passwd', `su', `dtlogin',
+ `rlogind', `rshd', `telnetd', `ftpd', `in.rexecd', `uucpd', `init',
+ `sac', and `ttymon'. We have found that PAM provides an excellent
+ framework to encapsulate the authentication-related tasks for the
+ entire system. The Solaris 2.3 PAM API's were hence enhanced and
+ simplified to support stacking.
+
+ PAM modules have been developed for UNIX, DCE, Kerberos, S/Key,
+ remote user authentication, and dialpass authentication. Other PAM
+ modules are under development, and integration with smart cards is
+ being planned.
+
+ Some third parties have used the PAM interface to extend the security
+ mechanisms offered by the Solaris environment.
+
+
+
+
+ Samar, Schemers Page 16
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ The PAM API has been accepted by Common Desktop Environment (CDE)
+ vendors as the API to be used for integrating the graphical interface
+ for login, `dtlogin' with multiple authentication mechanisms.
+
+
+ 11. FUTURE WORK
+
+ Amongst the various components of PAM, the password component needs
+ to be carefully examined to see whether the stacking semantics are
+ particularly applicable, and how PAM should deal with partial
+ failures when changing passwords.
+
+ The _control_flag_ of the configuration file can be extended to
+ include other semantics. For example, if the error is "name service
+ not available", one may want to retry. It is also possible to offer
+ semantics of "return success if any of the modules return success".
+
+ In an earlier section, we had mentioned integration of smart cards
+ with PAM. Though we feel that integration should be straight forward
+ from the PAM architecture point of view, there may be some issues
+ with implementation because the interfaces to the smart cards have
+ not yet been standardized.
+
+ One possible extension to PAM is to allow the passing of module-
+ specific data between applications and PAM modules. For example, the
+ `login' program likes to build its new environment from a select list
+ of variables, yet the DCE module needs the `KRB5CCNAME' variable to
+ be exported to the child process. For now we have modified the
+ `login' program to explicitly export the `KRB5CCNAME' variable.
+
+ Administrative tools are needed to help system administrators modify
+ `pam.conf', and perform sanity checks on it (i.e., a `pam_check'
+ utility).
+
+
+ 12. CONCLUSION
+
+ The PAM framework and the module interfaces provide pluggability for
+ user authentication, as well as for account, session and password
+ management. The PAM architecture can be used by `login' and by all
+ other system-entry services, and thus ensure that all entry points
+ for the system have been secured. This architecture enables
+ replacement and modification of authentication modules in the field
+ to secure the system against the newly found weaknesses without
+ changing any of the system services.
+
+ The PAM framework can be used to integrate `login' and `dtlogin' with
+ different authentication mechanisms such as RSA and Kerberos.
+ Multiple authentication systems can be accessed with the same
+ password. The PAM framework also provides easy integration of smart
+ cards into the system.
+
+
+
+ Samar, Schemers Page 17
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ PAM provides complementary functionality to GSS-API, in that it
+ provides mechanisms through which the user gets authenticated to any
+ new system-level authentication service on the machine. GSS-API then
+ uses the credentials for authenticated and secure communications with
+ other application-level service entities on the network.
+
+
+ 13. ACKNOWLEDGEMENTS
+
+ PAM development has spanned several release cycles at SunSoft.
+ Shau-Ping Lo, Chuck Hickey, and Alex Choy did the first design and
+ implementation. Bill Shannon and Don Stephenson helped with the PAM
+ architecture. Rocky Wu prototyped stacking of multiple modules.
+ Paul Fronberg, Charlie Lai, and Roland Schemers made very significant
+ enhancements to the PAM interfaces and took the project to completion
+ within a very short time. Kathy Slattery wrote the PAM
+ documentation. John Perry integrated PAM within the CDE framework.
+
+
+ APPENDIX A. PAM API'S
+
+ This appendix gives an informal description of the various interfaces
+ of PAM. Since the goal here is just for the reader to get a working
+ knowledge about the PAM interfaces, not all flags and options have
+ been fully defined and explained. The API's described here are
+ subject to change.
+
+ The PAM Service Provider Interface is very similar to the PAM API,
+ except for one extra parameter to pass module-specific options to the
+ underlying modules.
+
+ A.1. Framework Layer API's
+
+ int
+ pam_start(
+ char *service_name,
+ char *user,
+ struct pam_conv *pam_conversation,
+ pam_handle_t **pamh
+ );
+
+ `pam_start()' is called to initiate an authentication transaction.
+ `pam_start()' takes as arguments the name of the service, the name of
+ the user to be authenticated, the address of the conversation
+ structure. `pamh' is later used as a handle for subsequent calls to
+ the PAM library.
+
+ The PAM modules do not communicate directly with the user; instead
+ they rely on the application to perform all such interaction. The
+ application needs to provide the conversation functions, `conv()',
+ and associated application data pointers through a `pam_conv'
+
+
+
+ Samar, Schemers Page 18
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ structure when it initiates an authentication transaction. The
+ module uses the `conv()' function to prompt the user for data,
+ display error messages, or text information.
+
+ int
+ pam_end(
+ pam_handle_t *pamh,
+ int pam_status
+ );
+
+ `pam_end()' is called to terminate the PAM transaction as specified
+ by `pamh', and to free any storage area allocated by the PAM modules
+ with `pam_set_item()'.
+
+ int
+ pam_set_item(
+ pam_handle_t *pamh,
+ int item_type,
+ void *item
+ );
+
+ int
+ pam_get_item(
+ pam_handle_t *pamh,
+ int item_type,
+ void **item);
+
+ `pam_get_item()' and `pam_set_item()' allow the parameters specified
+ in the initial call to `pam_start()' to be read and updated. This is
+ useful when a particular parameter is not available when
+ `pam_start()' is called or must be modified after the initial call to
+ `pam_start()'. `pam_set_item()' is passed a pointer to the object,
+ `item', and its type, `item_type'. `pam_get_item()' is passed the
+ address of the pointer, `item', which is assigned the address of the
+ requested object.
+
+ The `item_type' is one of the following:
+
+ Table 5: Possible Values for Item_type
+
+ Item Name Description
+ --------- -----------
+ PAM_SERVICE The service name
+ PAM_USER The user name
+ PAM_TTY The tty name
+ PAM_RHOST The remote host name
+ PAM_CONV The pam_conv structure
+ PAM_AUTHTOK The authentication token (password)
+ PAM_OLDAUTHTOK The old authentication token
+ PAM_RUSER The remote user name
+
+
+
+
+ Samar, Schemers Page 19
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ Note that the values of `PAM_AUTHTOK' and `PAM_OLDAUTHTOK' are only
+ available to PAM modules and not to the applications. They are
+ explicitly cleared out by the framework before returning to the
+ application.
+
+ char *
+ pam_strerror(
+ int errnum
+ );
+
+ `pam_strerror()' maps the error number to a PAM error message string,
+ and returns a pointer to that string.
+
+ int
+ pam_set_data(
+ pam_handle_t *pamh,
+ char *module_data_name,
+ char *data,
+ (*cleanup)(pam_handle_t *pamh, char *data,
+ int error_status)
+ );
+
+ The `pam_set_data()' function stores module specific data within the
+ PAM handle. The `module_data_name' uniquely specifies the name to
+ which some data and cleanup callback function can be attached. The
+ cleanup function is called when `pam_end()' is invoked.
+
+ int
+ pam_get_data(
+ pam_handle_t *pamh,
+ char *module_data_name,
+ void **datap
+ );
+
+ The `pam_get_data()' function obtains module-specific data from the
+ PAM handle stored previously by the `pam_get_data()' function. The
+ `module_data_name' uniquely specifies the name for which data has to
+ be obtained. This function is normally used to retrieve module
+ specific state information.
+
+ A.2. Authentication API's
+
+ int
+ pam_authenticate(
+ pam_handle_t *pamh,
+ int flags
+ );
+
+ The `pam_authenticate()' function is called to verify the identity of
+ the current user. The user is usually required to enter a password
+ or similar authentication token, depending upon the authentication
+
+
+
+ Samar, Schemers Page 20
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ module configured with the system. The user in question is specified
+ by a prior call to `pam_start()', and is referenced by the
+ authentication handle, `pamh'.
+
+ int
+ pam_setcred(
+ pam_handle_t *pamh,
+ int flags
+ );
+
+ The `pam_setcred()' function is called to set the credentials of the
+ current process associated with the authentication handle, `pamh'.
+ The actions that can be denoted through `flags' include credential
+ initialization, refresh, reinitialization and deletion.
+
+ A.3. Account Management API
+
+ int
+ pam_acct_mgmt(
+ pam_handle_t *pamh,
+ int flags
+ );
+
+ The function `pam_acct_mgmt()' is called to determine whether the
+ current user's account and password are valid. This typically
+ includes checking for password and account expiration, valid login
+ times, etc. The user in question is specified by a prior call to
+ `pam_start()', and is referenced by the authentication handle,
+ `pamh'.
+
+ A.4. Session Management API's
+
+ int
+ pam_open_session(
+ pam_handle_t *pamh,
+ int flags
+ );
+
+ `pam_open_session()' is called to inform the session modules that a
+ new session has been initialized. All programs which use PAM should
+ invoke `pam_open_session()' when beginning a new session.
+
+ int
+ pam_close_session(
+ pam_handle_t *pamh,
+ int flags
+ );
+
+ Upon termination of this session, the `pam_close_session()' function
+ should be invoked to inform the underlying modules that the session
+ has terminated.
+
+
+
+ Samar, Schemers Page 21
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ A.5. Password Management API's
+
+ int
+ pam_chauthtok(
+ pam_handle_t *pamh,
+ int flags
+ );
+
+ `pam_chauthtok()' is called to change the authentication token
+ associated with the user referenced by the authentication handle
+ `pamh'. After the call, the authentication token of the user will be
+ changed in accordance with the authentication module configured on
+ the system.
+
+
+ APPENDIX B. SAMPLE PAM APPLICATION
+
+ This appendix shows a sample `login' application which uses the PAM
+ API's. It is not meant to be a fully functional login program, as
+ some functionality has been left out in order to emphasize the use of
+ PAM API's.
+
+ #include <security/pam_appl.h>
+
+ static int login_conv(int num_msg, struct pam_message **msg,
+ struct pam_response **response, void *appdata_ptr);
+
+ static struct pam_conv pam_conv = {login_conv, NULL};
+
+ static pam_handle_t *pamh; /* Authentication handle */
+
+ void
+ main(int argc, char *argv[], char **renvp)
+ {
+
+ /*
+ * Call pam_start to initiate a PAM authentication operation
+ */
+
+ if ((pam_start("login", user_name, &pam_conv, &pamh))
+ != PAM_SUCCESS)
+ login_exit(1);
+
+ pam_set_item(pamh, PAM_TTY, ttyn);
+ pam_set_item(pamh, PAM_RHOST, remote_host);
+
+ while (!authenticated && retry < MAX_RETRIES) {
+ status = pam_authenticate(pamh, 0);
+ authenticated = (status == PAM_SUCCESS);
+ }
+
+
+
+
+ Samar, Schemers Page 22
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ if (status != PAM_SUCCESS) {
+ fprintf(stderr,"error: %s\n", pam_strerror(status));
+ login_exit(1);
+ }
+
+ /* now check if the authenticated user is allowed to login. */
+
+ if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
+ if (status == PAM_AUTHTOK_EXPIRED) {
+ status = pam_chauthtok(pamh, 0);
+ if (status != PAM_SUCCESS)
+ login_exit(1);
+ } else {
+ login_exit(1);
+ }
+ }
+
+ /*
+ * call pam_open_session to open the authenticated session
+ * pam_close_session gets called by the process that
+ * cleans up the utmp entry (i.e., init)
+ */
+ if (status = pam_open_session(pamh, 0) != PAM_SUCCESS) {
+ login_exit(status);
+ }
+
+ /* set up the process credentials */
+ setgid(pwd->pw_gid);
+
+ /*
+ * Initialize the supplementary group access list.
+ * This should be done before pam_setcred because
+ * the PAM modules might add groups during the pam_setcred call
+ */
+ initgroups(user_name, pwd->pw_gid);
+
+ status = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+ if (status != PAM_SUCCESS) {
+ login_exit(status);
+ }
+
+ /* set the real (and effective) UID */
+ setuid(pwd->pw_uid);
+
+ pam_end(pamh, PAM_SUCCESS); /* Done using PAM */
+
+ /*
+ * Add DCE/Kerberos cred name, if any.
+ * XXX - The module specific stuff should be removed from login
+ * program eventually. This is better placed in DCE module and
+ * will be once PAM has routines for "exporting" environment
+
+
+
+ Samar, Schemers Page 23
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ * variables.
+ */
+ krb5p = getenv("KRB5CCNAME");
+ if (krb5p != NULL) {
+ ENVSTRNCAT(krb5ccname, krb5p);
+ envinit[basicenv++] = krb5ccname;
+ }
+ environ = envinit; /* Switch to the new environment. */
+ exec_the_shell();
+
+ /* All done */
+ }
+
+ /*
+ * login_exit - Call exit() and terminate.
+ * This function is here for PAM so cleanup can
+ * be done before the process exits.
+ */
+ static void
+ login_exit(int exit_code)
+ {
+ if (pamh)
+ pam_end(pamh, PAM_ABORT);
+ exit(exit_code);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * login_conv():
+ * This is the conv (conversation) function called from
+ * a PAM authentication module to print error messages
+ * or garner information from the user.
+ */
+
+ static int
+ login_conv(int num_msg, struct pam_message **msg,
+ struct pam_response **response, void *appdata_ptr)
+ {
+
+ while (num_msg--) {
+ switch (m->msg_style) {
+
+ case PAM_PROMPT_ECHO_OFF:
+ r->resp = strdup(getpass(m->msg));
+ break;
+
+ case PAM_PROMPT_ECHO_ON:
+ (void) fputs(m->msg, stdout);
+ r->resp = malloc(PAM_MAX_RESP_SIZE);
+ fgets(r->resp, PAM_MAX_RESP_SIZE, stdin);
+ /* add code here to remove \n from fputs */
+
+
+
+ Samar, Schemers Page 24
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ break;
+
+ case PAM_ERROR_MSG:
+ (void) fputs(m->msg, stderr);
+ break;
+
+ case PAM_TEXT_INFO:
+ (void) fputs(m->msg, stdout);
+ break;
+
+ default:
+ /* add code here to log error message, etc */
+ break;
+ }
+ }
+ return (PAM_SUCCESS);
+ }
+
+
+ APPENDIX C. DCE MODULE
+
+ This appendix describes a sample implementation of a DCE PAM module.
+ In order to simplify the description, we do not address the issues
+ raised by password-mapping or stacking. The intent is to show which
+ DCE calls are being made by the DCE module.
+
+ The `pam_sm_*()' functions implement the PAM SPI functions which are
+ called from the PAM API functions.
+
+ C.1. DCE Authentication Management
+
+ The algorithm for authenticating with DCE (not including error
+ checking, prompting for passwords, etc.) is as follows:
+
+ pam_sm_authenticate()
+ {
+ sec_login_setup_identity(...);
+ pam_set_data(...);
+ sec_login_valid_and_cert_ident(...);
+ }
+
+ pam_sm_setcred()
+ {
+ pam_get_data(...);
+ sec_login_set_context(...);
+ }
+
+ The `pam_sm_authenticate()' function for DCE uses the
+ `pam_set_data()' and `pam_get_data()' functions to keep state (like
+ the `sec_login_handle_t' context) between calls. The following
+ cleanup function is also registered and gets called when `pam_end()'
+
+
+
+ Samar, Schemers Page 25
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ is called:
+
+ dce_cleanup()
+ {
+ if (/* PAM_SUCCESS and
+ sec_login_valid_and_cert_ident success */) {
+ sec_login_release_context(...);
+ } else {
+ sec_login_purge_context(...);
+ }
+ }
+
+ If everything was successful we release the login context, but leave
+ the credentials file intact. If the status passed to `pam_end()' was
+ not `PAM_SUCCESS' (i.e., a required module failed) we purge the login
+ context which also removes the credentials file.
+
+ C.2. DCE Account Management
+
+ The algorithm for DCE account management is as follows:
+
+ pam_sm_acct_mgmt()
+ {
+ pam_get_data(...);
+ sec_login_inquire_net_info(...);
+ /* check for expired password and account */
+ sec_login_free_net_info(...);
+ }
+
+ The `sec_login_inquire_net_info()' function is called to obtain
+ information about when the user's account and/or password are going
+ to expire. A warning message is displayed (using the conversation
+ function) if the user's account or password is going to expire in the
+ near future, or has expired. These warning messages can be disabled
+ using the `nowarn' option in the `pam.conf' file.
+
+ C.3. DCE Session Management
+
+ The DCE session management functions are currently empty. They could
+ be modified to optionally remove the DCE credentials file upon
+ logout, etc.
+
+ C.4. DCE Password Management
+
+ The algorithm for DCE password management is as follows:
+
+
+
+
+
+
+
+
+
+ Samar, Schemers Page 26
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ pam_sm_chauthtok
+ {
+ sec_rgy_site_open(...);
+ sec_rgy_acct_lookup(...);
+ sec_rgy_acct_passwd(...);
+ sec_rgy_site_close(...);
+ }
+
+ The `sec_rgy_acct_passwd()' function is called to change the user's
+ password in the DCE registry.
+
+
+ REFERENCES
+
+ [Adamson 95] W. A. Adamson, J. Rees, and P. Honeyman, "Joining
+ Security Realms: A Single Login for Netware and
+ Kerberos", CITI Technical Report 95-1, Center for
+ Information Technology Integration, University of
+ Michigan, Ann Arbor, MI, February 1995.
+
+ [Diffie 76] W. Diffie and M. E. Hellman, "New Directions in
+ Cryptography", IEEE Transactions on Information
+ Theory, November 1976.
+
+ [Linn 93] J. Linn, "Generic Security Service Application
+ Programming Interface", Internet RFC 1508, 1509, 1993.
+
+ [Rivest 78] R. L. Rivest, A. Shamir, and L. Adleman., "A Method
+ for Obtaining Digital Signatures and Pubic-key
+ Cryptosystems", Communications of the ACM, 21(2),
+ 1978.
+
+ [SIA 95] "Digital UNIX Security", Digital Equipment
+ Corporation, Order Number AA-Q0R2C-TE, July 1995.
+
+ [Skey 94] N. M. Haller, "The S/Key One-Time Password System",
+ ISOC Symposium on Network and Distributed Security,
+ 1994.
+
+ [Steiner 88] J.G. Steiner, B. C. Neuman, and J. I. Schiller,
+ "Kerberos, An Authentication Service for Open Network
+ Systems", in Proceedings of the Winter USENIX
+ Conference, Dallas, Jan 1988.
+
+ [Taylor 88] B. Taylor and D. Goldberg, "Secure Networking in the
+ Sun Environment", Sun Microsystems Technical Paper,
+ 1988.
+
+ [XFN 94] "Federated Naming: the XFN Specifications", X/Open
+ Preliminary Specification, X/Open Document #P403,
+ ISBN:1-85912-045-8, X/Open Co. Ltd., July 1994.
+
+
+
+ Samar, Schemers Page 27
+
+
+
+
+
+
+
+ OSF-RFC 86.0 PAM October 1995
+
+
+
+ AUTHOR'S ADDRESS
+
+ Vipin Samar Internet email: vipin@eng.sun.com
+ SunSoft, Inc. Telephone: +1-415-336-1002
+ 2550 Garcia Avenue
+ Mountain View, CA 94043
+ USA
+
+ Roland J. Schemers III Internet email: schemers@eng.sun.com
+ SunSoft, Inc. Telephone: +1-415-336-1035
+ 2550 Garcia Avenue
+ Mountain View, CA 94043
+ USA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Samar, Schemers Page 28
+
+
+
+
+
+
diff --git a/doc/txts/.cvsignore b/doc/txts/.cvsignore
new file mode 100644
index 00000000..f35c3921
--- /dev/null
+++ b/doc/txts/.cvsignore
@@ -0,0 +1 @@
+pam*.txt
diff --git a/doc/txts/README b/doc/txts/README
new file mode 100644
index 00000000..540b09b5
--- /dev/null
+++ b/doc/txts/README
@@ -0,0 +1,3 @@
+$Id$
+
+This is a directory for text versions of the pam documentation
diff --git a/examples/.cvsignore b/examples/.cvsignore
new file mode 100644
index 00000000..2769a41e
--- /dev/null
+++ b/examples/.cvsignore
@@ -0,0 +1,3 @@
+blank
+xsh
+check_user
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644
index 00000000..c6882473
--- /dev/null
+++ b/examples/Makefile
@@ -0,0 +1,42 @@
+#
+# $Id$
+#
+
+dummy:
+
+ @echo "*** This is not a top level Makefile!"
+
+PROGS = blank xsh check_user
+SRCS = blank.c xsh.c check_user.c
+
+# have removed the following pair since they no longer conform to
+# any recognized conventions: vpass test
+# ditto: vpass.c test.c
+
+PROGSUID =
+
+all: $(PROGS)
+
+check_user: check_user.o
+ $(CC) $(CFLAGS) -o $@ $< $(LOADLIBES)
+
+blank: blank.o
+ $(CC) $(CFLAGS) -o $@ $< $(LOADLIBES)
+
+xsh: xsh.o
+ $(CC) $(CFLAGS) -o $@ $< $(LOADLIBES)
+
+install: all
+ if [ -n "$(PROGS)" ]; then cp $(PROGS) ../bin ; fi
+ if [ -n "$(PROGSUID)" ]; then \
+ $(INSTALL) -m 4555 -o root -g bin $(PROGSUID) ../bin ; fi
+
+clean:
+ rm -f *.a *.so *.o *~ $(PROGS) $(PROGSUID)
+
+remove:
+ cd ../bin ; rm -f $(PROGS) $(PROGSUID)
+
+extraclean: clean
+ rm -f *.a *.out *.o *.so
+ for x in $(PROGS) $(PROGSUID) ; do rm -f ../bin/$$x ; done
diff --git a/examples/blank.c b/examples/blank.c
new file mode 100644
index 00000000..33b5056e
--- /dev/null
+++ b/examples/blank.c
@@ -0,0 +1,182 @@
+/*
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:13 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/11/08 05:39:53 morgan
+ * removed void main def which was making gcc complain
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:14 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.7 1996/12/01 03:16:53 morgan
+ * added setcred closing function
+ *
+ * Revision 1.6 1996/11/10 19:51:40 morgan
+ * minor change to avoid gcc warning
+ *
+ * Revision 1.5 1996/07/07 23:53:05 morgan
+ * added optional fail delay (non-standard Linux-PAM)
+ *
+ * Revision 1.4 1996/05/02 04:44:18 morgan
+ * moved conversation to a libmisc library routine.
+ *
+ *
+ */
+
+/* Andrew Morgan (morgan@parc.power.net) -- a self contained `blank'
+ * application
+ *
+ * I am not very proud of this code. It makes use of a possibly ill-
+ * defined pamh pointer to call pam_strerror() with. The reason that
+ * I was sloppy with this is historical (pam_strerror, prior to 0.59,
+ * did not require a pamh argument) and if this program is used as a
+ * model for anything, I should wish that you will take this error into
+ * account.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+
+/* ------ some local (static) functions ------- */
+
+static void bail_out(pam_handle_t *pamh, int really, int code, const char *fn)
+{
+ fprintf(stderr,"==> called %s()\n got: `%s'\n", fn,
+ pam_strerror(pamh, code));
+ if (really && code)
+ exit (1);
+}
+
+/* ------ some static data objects ------- */
+
+static struct pam_conv conv = {
+ misc_conv,
+ NULL
+};
+
+/* ------- the application itself -------- */
+
+int main(int argc, char **argv)
+{
+ pam_handle_t *pamh=NULL;
+ char *username=NULL;
+ int retcode;
+
+ /* did the user call with a username as an argument ? */
+
+ if (argc > 2) {
+ fprintf(stderr,"usage: %s [username]\n",argv[0]);
+ } else if (argc == 2) {
+ username = argv[1];
+ }
+
+ /* initialize the Linux-PAM library */
+ retcode = pam_start("blank", username, &conv, &pamh);
+ bail_out(pamh,1,retcode,"pam_start");
+
+ /* test the environment stuff */
+ {
+#define MAXENV 15
+ const char *greek[MAXENV] = {
+ "a=alpha", "b=beta", "c=gamma", "d=delta", "e=epsilon",
+ "f=phi", "g=psi", "h=eta", "i=iota", "j=mu", "k=nu",
+ "l=zeta", "h=", "d", "k=xi"
+ };
+ char **env;
+ int i;
+
+ for (i=0; i<MAXENV; ++i) {
+ retcode = pam_putenv(pamh,greek[i]);
+ bail_out(pamh,0,retcode,"pam_putenv");
+ }
+ env = pam_getenvlist(pamh);
+ if (env)
+ env = pam_misc_drop_env(env);
+ else
+ fprintf(stderr,"???\n");
+ fprintf(stderr,"a test: c=[%s], j=[%s]\n"
+ , pam_getenv(pamh, "c"), pam_getenv(pamh, "j"));
+ }
+
+ /* to avoid using goto we abuse a loop here */
+ for (;;) {
+ /* authenticate the user --- `0' here, could have been PAM_SILENT
+ * | PAM_DISALLOW_NULL_AUTHTOK */
+
+ retcode = pam_authenticate(pamh, 0);
+ bail_out(pamh,0,retcode,"pam_authenticate");
+
+ /* has the user proved themself valid? */
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: invalid request\n",argv[0]);
+ break;
+ }
+
+ /* the user is valid, but should they have access at this
+ time? */
+
+ retcode = pam_acct_mgmt(pamh, 0); /* `0' could be as above */
+ bail_out(pamh,0,retcode,"pam_acct_mgmt");
+
+ if (retcode == PAM_NEW_AUTHTOK_REQD) {
+ fprintf(stderr,"Application must request new password...\n");
+ retcode = pam_chauthtok(pamh,PAM_CHANGE_EXPIRED_AUTHTOK);
+ bail_out(pamh,0,retcode,"pam_chauthtok");
+ }
+
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: invalid request\n",argv[0]);
+ break;
+ }
+
+ /* `0' could be as above */
+ retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+ bail_out(pamh,0,retcode,"pam_setcred1");
+
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: problem setting user credentials\n"
+ ,argv[0]);
+ break;
+ }
+
+ /* open a session for the user --- `0' could be PAM_SILENT */
+ retcode = pam_open_session(pamh,0);
+ bail_out(pamh,0,retcode,"pam_open_session");
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: problem opening a session\n",argv[0]);
+ break;
+ }
+
+ fprintf(stderr,"The user has been authenticated and `logged in'\n");
+
+ /* close a session for the user --- `0' could be PAM_SILENT
+ * it is possible that this pam_close_call is in another program..
+ */
+
+ retcode = pam_close_session(pamh,0);
+ bail_out(pamh,0,retcode,"pam_close_session");
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: problem closing a session\n",argv[0]);
+ break;
+ }
+
+ retcode = pam_setcred(pamh, PAM_DELETE_CRED);
+ bail_out(pamh,0,retcode,"pam_setcred2");
+
+ break; /* don't go on for ever! */
+ }
+
+ /* close the Linux-PAM library */
+ retcode = pam_end(pamh, PAM_SUCCESS);
+ pamh = NULL;
+
+ bail_out(pamh,1,retcode,"pam_end");
+
+ exit(0);
+}
diff --git a/examples/check_user.c b/examples/check_user.c
new file mode 100644
index 00000000..6d52ccaa
--- /dev/null
+++ b/examples/check_user.c
@@ -0,0 +1,71 @@
+/*
+ $Id$
+
+ This program was contributed by Shane Watts <shane@icarus.bofh.asn.au>
+ slight modifications by AGM.
+
+ You need to add the following (or equivalent) to the /etc/pam.conf file.
+ # check authorization
+ check auth required pam_unix_auth.so
+ check account required pam_unix_acct.so
+
+ $Log$
+ Revision 1.1 2000/06/20 22:11:13 agmorgan
+ Initial revision
+
+ Revision 1.1.1.1 1998/07/12 05:17:14 morgan
+ Linux PAM sources pre-0.66
+
+ Revision 1.1 1996/11/10 21:19:30 morgan
+ Initial revision
+
+ */
+
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+#include <stdio.h>
+
+static struct pam_conv conv = {
+ misc_conv,
+ NULL
+};
+
+int main(int argc, char *argv[])
+{
+ pam_handle_t *pamh=NULL;
+ int retval;
+ const char *user="nobody";
+
+ if(argc == 2) {
+ user = argv[1];
+ }
+
+ if(argc > 2) {
+ fprintf(stderr, "Usage: check_user [username]\n");
+ exit(1);
+ }
+
+ retval = pam_start("check", user, &conv, &pamh);
+
+ if (retval == PAM_SUCCESS)
+ retval = pam_authenticate(pamh, 0); /* is user really user? */
+
+ if (retval == PAM_SUCCESS)
+ retval = pam_acct_mgmt(pamh, 0); /* permitted access? */
+
+ /* This is where we have been authorized or not. */
+
+ if (retval == PAM_SUCCESS) {
+ fprintf(stdout, "Authenticated\n");
+ } else {
+ fprintf(stdout, "Not Authenticated\n");
+ }
+
+ if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */
+ pamh = NULL;
+ fprintf(stderr, "check_user: failed to release authenticator\n");
+ exit(1);
+ }
+
+ return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */
+}
diff --git a/examples/test.c b/examples/test.c
new file mode 100644
index 00000000..8fc5e6cd
--- /dev/null
+++ b/examples/test.c
@@ -0,0 +1,105 @@
+/*
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:13 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:14 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.3 1996/03/10 00:14:20 morgan
+ * made lines less than 80 chars long.
+ *
+ * Revision 1.2 1996/03/09 09:16:26 morgan
+ * changed the header file that it includes.
+ *
+ * Revision 1.1 1996/03/09 09:13:34 morgan
+ * Initial revision
+ */
+
+/* Marc Ewing (marc@redhat.com) - original test code
+ * Alexander O. Yuriev (alex@bach.cis.temple.edu)
+ * Andrew Morgan (morgan@physics.ucla.edu)
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <pwd.h>
+
+#include <security/pam_appl.h>
+
+/* this program is not written to the PAM spec: it tests the
+ * pam_[sg]et_data() functions. Which is usually reserved for modules */
+
+#include <security/pam_modules.h>
+#include <security/pam_misc.h>
+
+#define USERNAMESIZE 1024
+
+static int test_conv( int num_msg,
+ const struct pam_message **msgm,
+ struct pam_response **response,
+ void *appdata_ptr )
+{
+ return 0;
+}
+
+static struct pam_conv conv = {
+ test_conv,
+ NULL
+};
+
+static int cleanup_func(pam_handle_t *pamh, void *data, int error_status)
+{
+ printf("Cleaning up!\n");
+ return PAM_SUCCESS;
+}
+
+void main( void )
+{
+ pam_handle_t *pamh;
+ char *name = ( char *) malloc( USERNAMESIZE + 1 );
+ char *p = NULL;
+ char *s = NULL;
+
+ if (! name )
+ {
+ perror( "Ouch, don't have enough memory");
+ exit( -1 );
+ }
+
+
+
+
+ fprintf( stdout, "Enter a name of a user to authenticate : ");
+ name = fgets( name , USERNAMESIZE, stdin );
+ if ( !name )
+ {
+ perror ( "Hey, how can authenticate "
+ "someone whos name I don't know?" );
+ exit ( -1 );
+ }
+
+ *( name + strlen ( name ) - 1 ) = 0;
+
+ pam_start( "login", name, &conv, &pamh );
+
+ p = x_strdup( getpass ("Password: ") );
+ if ( !p )
+ {
+ perror ( "You love NULL pointers, "
+ "don't you? I don't ");
+ exit ( -1 );
+ }
+ pam_set_item ( pamh, PAM_AUTHTOK, p );
+ pam_get_item ( pamh, PAM_USER, (void**) &s);
+ pam_set_data(pamh, "DATA", "Hi there! I'm data!", cleanup_func);
+ pam_get_data(pamh, "DATA", (void **) &s);
+ printf("%s\n", s);
+
+ fprintf( stdout, "*** Attempting to perform "
+ "PAM authentication...\n");
+ fprintf( stdout, "%s\n",
+ pam_strerror( pam_authenticate( pamh, 0 ) ) ) ;
+
+ pam_end(pamh, PAM_SUCCESS);
+}
diff --git a/examples/vpass.c b/examples/vpass.c
new file mode 100644
index 00000000..9a07ee38
--- /dev/null
+++ b/examples/vpass.c
@@ -0,0 +1,47 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <security/pam_appl.h>
+
+static int test_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response, void *appdata_ptr)
+{
+ return 0;
+}
+
+static struct pam_conv conv = {
+ test_conv,
+ NULL
+};
+
+int main(void)
+{
+ char *user;
+ pam_handle_t *pamh;
+ struct passwd *pw;
+ uid_t uid;
+ int res;
+
+ uid = geteuid();
+ pw = getpwuid(uid);
+ if (pw) {
+ user = pw->pw_name;
+ } else {
+ fprintf(stderr, "Invalid userid: %d\n", uid);
+ exit(1);
+ }
+
+ pam_start("vpass", user, &conv, &pamh);
+ pam_set_item(pamh, PAM_TTY, "/dev/tty");
+ if ((res = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
+ fprintf(stderr, "Oops: %s\n", pam_strerror(pamh, res));
+ exit(1);
+ }
+
+ pam_end(pamh, res);
+ exit(0);
+}
+
+
diff --git a/examples/xsh.c b/examples/xsh.c
new file mode 100644
index 00000000..d4b50b37
--- /dev/null
+++ b/examples/xsh.c
@@ -0,0 +1,148 @@
+/*
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:13 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/11/08 05:39:53 morgan
+ * removed void main def which was making gcc complain
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:14 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.4 1996/11/10 21:09:45 morgan
+ * no gcc warnings
+ *
+ * Revision 1.3 1996/07/07 23:53:36 morgan
+ * added support for non standard pam_fail_delay
+ *
+ * Revision 1.2 1996/05/02 04:44:48 morgan
+ * moved conversaation to a libmisc routine.
+ *
+ * Revision 1.1 1996/04/07 08:18:55 morgan
+ * Initial revision
+ *
+ */
+
+/* Andrew Morgan (morgan@parc.power.net) -- an example application
+ * that invokes a shell, based on blank.c */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+
+/* ------ some local (static) functions ------- */
+
+static void bail_out(pam_handle_t *pamh,int really, int code, const char *fn)
+{
+ fprintf(stderr,"==> called %s()\n got: `%s'\n", fn,
+ pam_strerror(pamh,code));
+ if (really && code)
+ exit (1);
+}
+
+/* ------ some static data objects ------- */
+
+static struct pam_conv conv = {
+ misc_conv,
+ NULL
+};
+
+/* ------- the application itself -------- */
+
+int main(int argc, char **argv, char **envp)
+{
+ pam_handle_t *pamh=NULL;
+ char *username=NULL;
+ int retcode;
+
+ /* did the user call with a username as an argument ? */
+
+ if (argc > 2) {
+ fprintf(stderr,"usage: %s [username]\n",argv[0]);
+ } else if (argc == 2) {
+ username = argv[1];
+ }
+
+ /* initialize the Linux-PAM library */
+ retcode = pam_start("xsh", username, &conv, &pamh);
+ bail_out(pamh,1,retcode,"pam_start");
+
+ /* to avoid using goto we abuse a loop here */
+ for (;;) {
+ /* authenticate the user --- `0' here, could have been PAM_SILENT
+ * | PAM_DISALLOW_NULL_AUTHTOK */
+
+ retcode = pam_authenticate(pamh, 0);
+ bail_out(pamh,0,retcode,"pam_authenticate");
+
+ /* has the user proved themself valid? */
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: invalid request\n",argv[0]);
+ break;
+ }
+
+ /* the user is valid, but should they have access at this
+ time? */
+
+ retcode = pam_acct_mgmt(pamh, 0); /* `0' could be as above */
+ bail_out(pamh,0,retcode,"pam_acct_mgmt");
+
+ if (retcode == PAM_NEW_AUTHTOK_REQD) {
+ fprintf(stderr,"Application must request new password...\n");
+ retcode = pam_chauthtok(pamh,PAM_CHANGE_EXPIRED_AUTHTOK);
+ bail_out(pamh,0,retcode,"pam_chauthtok");
+ }
+
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: invalid request\n",argv[0]);
+ break;
+ }
+
+ /* `0' could be as above */
+ retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+ bail_out(pamh,0,retcode,"pam_setcred");
+
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: problem setting user credentials\n"
+ ,argv[0]);
+ break;
+ }
+
+ /* open a session for the user --- `0' could be PAM_SILENT */
+ retcode = pam_open_session(pamh,0);
+ bail_out(pamh,0,retcode,"pam_open_session");
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: problem opening a session\n",argv[0]);
+ break;
+ }
+
+ fprintf(stderr,"The user has been authenticated and `logged in'\n");
+
+ /* this is always a really bad thing for security! */
+ system("/bin/sh");
+
+ /* close a session for the user --- `0' could be PAM_SILENT
+ * it is possible that this pam_close_call is in another program..
+ */
+
+ retcode = pam_close_session(pamh,0);
+ bail_out(pamh,0,retcode,"pam_close_session");
+ if (retcode != PAM_SUCCESS) {
+ fprintf(stderr,"%s: problem closing a session\n",argv[0]);
+ break;
+ }
+
+ break; /* don't go on for ever! */
+ }
+
+ /* close the Linux-PAM library */
+ retcode = pam_end(pamh, PAM_SUCCESS);
+ pamh = NULL;
+ bail_out(pamh,1,retcode,"pam_end");
+
+ exit(0);
+}
diff --git a/libpam/.cvsignore b/libpam/.cvsignore
new file mode 100644
index 00000000..dd17fdcf
--- /dev/null
+++ b/libpam/.cvsignore
@@ -0,0 +1,2 @@
+dynamic
+static
diff --git a/libpam/Makefile b/libpam/Makefile
new file mode 100644
index 00000000..66b2a705
--- /dev/null
+++ b/libpam/Makefile
@@ -0,0 +1,162 @@
+#
+# $Id$
+#
+#
+
+# need to tell libpam about the default directory for PAMs
+MOREFLAGS=-D"DEFAULT_MODULE_PATH=\"$(SECUREDIR)/\""
+
+# you may uncomment the following to build libpam in modified ways
+
+# lots of debugging information goes to /tmp/pam-debug.log
+#MOREFLAGS += -D"DEBUG"
+
+# pay attention to locked /etc/pam.conf or /etc/pam.d/* files
+#MOREFLAGS += -D"PAM_LOCKING"
+
+# read both the /etc/pam.d/ and pam.conf files specific to the deisred service
+#MOREFLAGS += -D"PAM_READ_BOTH_CONFS"
+
+# make a kludge attempt to be compatible with the old pam_strerror
+# calling convention
+#MOREFLAGS += -D"UGLY_HACK_FOR_PRIOR_BEHAVIOR_SUPPORT"
+
+# libpam.so needs -ldl, too.
+LINKLIBS += $(LIBDL)
+
+ifeq ($(DEBUG_REL),yes)
+ LIBNAME=libpamd
+else
+ LIBNAME=libpam
+endif
+VERSION=.$(MAJOR_REL)
+MODIFICATION=.$(MINOR_REL)
+
+# ---------------------------------------------
+
+dummy:
+ @echo "*** This is not a top-level Makefile!"
+
+# ---------------------------------------------
+
+CFLAGS += $(DYNAMIC) $(STATIC) $(MOREFLAGS)
+
+# dynamic library names
+
+LIBPAM = $(LIBNAME).$(DYNTYPE)
+LIBPAMNAME = $(LIBPAM)$(VERSION)
+LIBPAMFULL = $(LIBPAMNAME)$(MODIFICATION)
+
+# static library name
+
+LIBPAMSTATIC = $(LIBNAME).a
+
+ifdef STATIC
+MODULES = $(shell cat ../modules/_static_module_objects)
+STATICOBJ = pam_static.o
+endif
+
+ifdef MEMORY_DEBUG
+EXTRAS += pam_malloc.o
+endif
+
+LIBOBJECTS = pam_item.o pam_strerror.o pam_end.o pam_start.o pam_data.o \
+ pam_delay.o pam_dispatch.o pam_handlers.o pam_misc.o \
+ pam_account.o pam_auth.o pam_session.o pam_password.o \
+ pam_env.o pam_log.o $(EXTRAS)
+
+ifdef DYNAMIC_LIBPAM
+DLIBOBJECTS = $(addprefix dynamic/,$(LIBOBJECTS) $(STATICOBJ))
+ifdef STATICOBJ
+dynamic/pam_static.o: pam_static.c ../modules/_static_module_objects
+ $(CC) $(CFLAGS) -c pam_static.c -o $@
+endif
+endif
+
+ifdef STATIC_LIBPAM
+SLIBOBJECTS = $(addprefix static/,$(LIBOBJECTS) $(STATICOBJ))
+ifdef STATICOBJ
+static/pam_static.o: pam_static.c ../modules/_static_module_objects
+ $(CC) $(CFLAGS) -c pam_static.c -o $@
+endif
+endif
+
+# ---------------------------------------------
+## rules
+
+all: dirs $(LIBPAM) $(LIBPAMSTATIC)
+
+dirs:
+ifdef DYNAMIC_LIBPAM
+ mkdir -p dynamic
+endif
+ifdef STATIC_LIBPAM
+ mkdir -p static
+endif
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+$(LIBPAM): $(DLIBOBJECTS)
+ifdef DYNAMIC_LIBPAM
+ ifeq ($(USESONAME),yes)
+ $(LD_L) $(SOSWITCH) $(LIBPAMNAME) -o $@ $(DLIBOBJECTS) $(MODULES) $(LINKLIBS)
+ else
+ $(LD_L) -o $@ $(DLIBOBJECTS) $(MODULES)
+ endif
+ ifeq ($(NEEDSONAME),yes)
+ rm -f $(LIBPAMFULL)
+ ln -s $(LIBPAM) $(LIBPAMFULL)
+ rm -f $(LIBPAMNAME)
+ ln -s $(LIBPAM) $(LIBPAMNAME)
+ endif
+endif
+
+$(LIBPAMSTATIC): $(SLIBOBJECTS)
+ifdef STATIC_LIBPAM
+ $(AR) $@ $(SLIBOBJECTS) $(MODULES)
+ $(RANLIB) $@
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(INCLUDED) $(FAKEROOT)$(LIBDIR)
+ $(INSTALL) -m 644 include/security/pam_appl.h $(FAKEROOT)$(INCLUDED)
+ $(INSTALL) -m 644 include/security/pam_modules.h $(FAKEROOT)$(INCLUDED)
+ $(INSTALL) -m 644 include/security/_pam_macros.h $(FAKEROOT)$(INCLUDED)
+ $(INSTALL) -m 644 include/security/_pam_types.h $(FAKEROOT)$(INCLUDED)
+ $(INSTALL) -m 644 include/security/_pam_compat.h $(FAKEROOT)$(INCLUDED)
+ifdef MEMORY_DEBUG
+ $(INSTALL) -m 644 include/security/pam_malloc.h $(FAKEROOT)$(INCLUDED)
+endif
+ifdef DYNAMIC_LIBPAM
+ $(INSTALL) -m $(SHLIBMODE) $(LIBPAM) $(FAKEROOT)$(LIBDIR)/$(LIBPAMFULL)
+ $(LDCONFIG)
+ ifneq ($(DYNTYPE),"sl")
+ ( cd $(FAKEROOT)$(LIBDIR) ; rm -f $(LIBPAM) ; ln -s $(LIBPAMNAME) $(LIBPAM) )
+ endif
+endif
+ifdef STATIC_LIBPAM
+ $(INSTALL) -m 644 $(LIBPAMSTATIC) $(FAKEROOT)$(LIBDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(INCLUDED)/_pam_types.h
+ rm -f $(FAKEROOT)$(INCLUDED)/_pam_macros.h
+ rm -f $(FAKEROOT)$(INCLUDED)/pam_appl.h
+ rm -f $(FAKEROOT)$(INCLUDED)/pam_modules.h
+ rm -f $(FAKEROOT)$(INCLUDED)/pam_malloc.h
+ rm -f $(FAKEROOT)$(LIBDIR)/$(LIBPAM).*
+ rm -f $(FAKEROOT)$(LIBDIR)/$(LIBPAM)
+ $(LDCONFIG)
+ rm -f $(FAKEROOT)$(LIBDIR)/$(LIBPAMSTATIC)
+
+clean:
+ rm -f a.out core *~ static/*.o dynamic/*.o
+
+extraclean: clean
+ rm -f *.a *.out *.o *.so ./include/security/*~
+ if [ -d dynamic ]; then rmdir dynamic ; fi
+ if [ -d static ]; then rmdir static ; fi
diff --git a/libpam/include/security/_pam_compat.h b/libpam/include/security/_pam_compat.h
new file mode 100644
index 00000000..a5f77e7a
--- /dev/null
+++ b/libpam/include/security/_pam_compat.h
@@ -0,0 +1,122 @@
+#ifndef _PAM_COMPAT_H
+#define _PAM_COMPAT_H
+
+/*
+ * $Id$
+ *
+ * This file was contributed by Derrick J Brashear <shadow@dementia.org>
+ * slight modification by Brad M. Garcia <bgarcia@fore.com>
+ *
+ * A number of operating systems have started to implement PAM.
+ * unfortunately, they have a different set of numeric values for
+ * certain constants. This file is included for compatibility's sake.
+ */
+
+/* Solaris uses different constants. We redefine to those here */
+#if defined(solaris) || (defined(__SVR4) && defined(sun))
+
+#ifndef _SECURITY__PAM_TYPES_H
+
+# ifdef _SECURITY_PAM_MODULES_H
+
+/* flags for pam_chauthtok() */
+# undef PAM_PRELIM_CHECK
+# define PAM_PRELIM_CHECK 0x1
+
+# undef PAM_UPDATE_AUTHTOK
+# define PAM_UPDATE_AUTHTOK 0x2
+
+# endif /* _SECURITY_PAM_MODULES_H */
+
+#else /* _SECURITY__PAM_TYPES_H */
+
+/* generic for pam_* functions */
+# undef PAM_SILENT
+# define PAM_SILENT 0x80000000
+
+/* flags for pam_setcred() */
+# undef PAM_ESTABLISH_CRED
+# define PAM_ESTABLISH_CRED 0x1
+
+# undef PAM_DELETE_CRED
+# define PAM_DELETE_CRED 0x2
+
+# undef PAM_REINITIALIZE_CRED
+# define PAM_REINITIALIZE_CRED 0x4
+
+# undef PAM_REFRESH_CRED
+# define PAM_REFRESH_CRED 0x8
+
+/* another binary incompatibility comes from the return codes! */
+
+# undef PAM_CONV_ERR
+# define PAM_CONV_ERR 6
+
+# undef PAM_PERM_DENIED
+# define PAM_PERM_DENIED 7
+
+# undef PAM_MAXTRIES
+# define PAM_MAXTRIES 8
+
+# undef PAM_AUTH_ERR
+# define PAM_AUTH_ERR 9
+
+# undef PAM_NEW_AUTHTOK_REQD
+# define PAM_NEW_AUTHTOK_REQD 10
+
+# undef PAM_CRED_INSUFFICIENT
+# define PAM_CRED_INSUFFICIENT 11
+
+# undef PAM_AUTHINFO_UNAVAIL
+# define PAM_AUTHINFO_UNAVAIL 12
+
+# undef PAM_USER_UNKNOWN
+# define PAM_USER_UNKNOWN 13
+
+# undef PAM_CRED_UNAVAIL
+# define PAM_CRED_UNAVAIL 14
+
+# undef PAM_CRED_EXPIRED
+# define PAM_CRED_EXPIRED 15
+
+# undef PAM_CRED_ERR
+# define PAM_CRED_ERR 16
+
+# undef PAM_ACCT_EXPIRED
+# define PAM_ACCT_EXPIRED 17
+
+# undef PAM_AUTHTOK_EXPIRED
+# define PAM_AUTHTOK_EXPIRED 18
+
+# undef PAM_SESSION_ERR
+# define PAM_SESSION_ERR 19
+
+# undef PAM_AUTHTOK_ERR
+# define PAM_AUTHTOK_ERR 20
+
+# undef PAM_AUTHTOK_RECOVERY_ERR
+# define PAM_AUTHTOK_RECOVERY_ERR 21
+
+# undef PAM_AUTHTOK_LOCK_BUSY
+# define PAM_AUTHTOK_LOCK_BUSY 22
+
+# undef PAM_AUTHTOK_DISABLE_AGING
+# define PAM_AUTHTOK_DISABLE_AGING 23
+
+# undef PAM_NO_MODULE_DATA
+# define PAM_NO_MODULE_DATA 24
+
+# undef PAM_IGNORE
+# define PAM_IGNORE 25
+
+# undef PAM_ABORT
+# define PAM_ABORT 26
+
+# undef PAM_TRY_AGAIN
+# define PAM_TRY_AGAIN 27
+
+#endif /* _SECURITY__PAM_TYPES_H */
+
+#endif /* defined(solaris) || (defined(__SVR4) && defined(sun)) */
+
+#endif /* _PAM_COMPAT_H */
diff --git a/libpam/include/security/_pam_macros.h b/libpam/include/security/_pam_macros.h
new file mode 100644
index 00000000..7c3dde1d
--- /dev/null
+++ b/libpam/include/security/_pam_macros.h
@@ -0,0 +1,166 @@
+#ifndef PAM_MACROS_H
+#define PAM_MACROS_H
+
+/*
+ * All kind of macros used by PAM, but usable in some other
+ * programs too.
+ * Organized by Cristian Gafton <gafton@redhat.com>
+ */
+
+/* a 'safe' version of strdup */
+
+#include <string.h>
+#include <stdlib.h>
+
+#define x_strdup(s) ( (s) ? strdup(s):NULL )
+
+/* Good policy to strike out passwords with some characters not just
+ free the memory */
+
+#define _pam_overwrite(x) \
+do { \
+ register char *__xx__; \
+ if ((__xx__=(x))) \
+ while (*__xx__) \
+ *__xx__++ = '\0'; \
+} while (0)
+
+/*
+ * Don't just free it, forget it too.
+ */
+
+#define _pam_drop(X) \
+do { \
+ if (X) { \
+ free(X); \
+ X=NULL; \
+ } \
+} while (0)
+
+#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+do { \
+ int reply_i; \
+ \
+ for (reply_i=0; reply_i<replies; ++reply_i) { \
+ if (reply[reply_i].resp) { \
+ _pam_overwrite(reply[reply_i].resp); \
+ free(reply[reply_i].resp); \
+ } \
+ } \
+ if (reply) \
+ free(reply); \
+} while (0)
+
+/* some debugging code */
+
+#ifdef DEBUG
+
+/*
+ * This provides the necessary function to do debugging in PAM.
+ * Cristian Gafton <gafton@redhat.com>
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <errno.h>
+
+/*
+ * This is for debugging purposes ONLY. DO NOT use on live systems !!!
+ * You have been warned :-) - CG
+ *
+ * to get automated debugging to the log file, it must be created manually.
+ * _PAM_LOGFILE must exist, mode 666
+ */
+
+#ifndef _PAM_LOGFILE
+#define _PAM_LOGFILE "/tmp/pam-debug.log"
+#endif
+
+static void _pam_output_debug_info(const char *file, const char *fn
+ , const int line)
+{
+ FILE *logfile;
+ int must_close = 1;
+
+ if (!(logfile = fopen(_PAM_LOGFILE,"a"))) {
+ logfile = stderr;
+ must_close = 0;
+ }
+ fprintf(logfile,"[%s:%s(%d)] ",file, fn, line);
+ if (must_close) {
+ fflush(logfile);
+ fclose(logfile);
+ }
+}
+
+static void _pam_output_debug(const char *format, ...)
+{
+ va_list args;
+ FILE *logfile;
+ int must_close = 1;
+
+ va_start(args, format);
+
+ if (!(logfile = fopen(_PAM_LOGFILE,"a"))) {
+ logfile = stderr;
+ must_close = 0;
+ }
+ vfprintf(logfile, format, args);
+ fprintf(logfile, "\n");
+ if (must_close) {
+ fflush(logfile);
+ fclose(logfile);
+ }
+
+ va_end(args);
+}
+
+#define D(x) do { \
+ _pam_output_debug_info(__FILE__, __FUNCTION__, __LINE__); \
+ _pam_output_debug x ; \
+} while (0)
+
+#define _pam_show_mem(X,XS) do { \
+ int i; \
+ register unsigned char *x; \
+ x = (unsigned char *)X; \
+ fprintf(stderr, " <start at %p>\n", X); \
+ for (i = 0; i < XS ; ++x, ++i) { \
+ fprintf(stderr, " %02X. <%p:%02X>\n", i, x, *x); \
+ } \
+ fprintf(stderr, " <end for %p after %d bytes>\n", X, XS); \
+} while (0)
+
+#define _pam_show_reply(/* struct pam_response * */reply, /* int */replies) \
+do { \
+ int reply_i; \
+ setbuf(stderr, NULL); \
+ fprintf(stderr, "array at %p of size %d\n",reply,replies); \
+ fflush(stderr); \
+ if (reply) { \
+ for (reply_i = 0; reply_i < replies; reply_i++) { \
+ fprintf(stderr, " elem# %d at %p: resp = %p, retcode = %d\n", \
+ reply_i, reply+reply_i, reply[reply_i].resp, \
+ reply[reply_i].resp, _retcode); \
+ fflush(stderr); \
+ if (reply[reply_i].resp) { \
+ fprintf(stderr, " resp[%d] = '%s'\n", \
+ strlen(reply[reply_i].resp), reply[reply_i].resp); \
+ fflush(stderr); \
+ } \
+ } \
+ } \
+ fprintf(stderr, "done here\n"); \
+ fflush(stderr); \
+} while (0)
+
+#else
+
+#define D(x) do { } while (0)
+#define _pam_show_mem(X,XS) do { } while (0)
+#define _pam_show_reply(reply, replies) do { } while (0)
+
+#endif /* DEBUG */
+
+#endif /* PAM_MACROS_H */
diff --git a/libpam/include/security/_pam_types.h b/libpam/include/security/_pam_types.h
new file mode 100644
index 00000000..fe4ada39
--- /dev/null
+++ b/libpam/include/security/_pam_types.h
@@ -0,0 +1,316 @@
+/*
+ * <security/_pam_types.h>
+ *
+ * $Id$
+ *
+ * This file defines all of the types common to the Linux-PAM library
+ * applications and modules.
+ *
+ * Note, the copyright+license information is at end of file.
+ *
+ * Created: 1996/3/5 by AGM
+ */
+
+#ifndef _SECURITY__PAM_TYPES_H
+#define _SECURITY__PAM_TYPES_H
+
+/*
+ * include local definition for POSIX - NULL
+ */
+
+#include <locale.h>
+
+/* This is a blind structure; users aren't allowed to see inside a
+ * pam_handle_t, so we don't define struct pam_handle here. This is
+ * defined in a file private to the PAM library. (i.e., it's private
+ * to PAM service modules, too!) */
+
+typedef struct pam_handle pam_handle_t;
+
+/* ----------------- The Linux-PAM return values ------------------ */
+
+#define PAM_SUCCESS 0 /* Successful function return */
+#define PAM_OPEN_ERR 1 /* dlopen() failure when dynamically */
+ /* loading a service module */
+#define PAM_SYMBOL_ERR 2 /* Symbol not found */
+#define PAM_SERVICE_ERR 3 /* Error in service module */
+#define PAM_SYSTEM_ERR 4 /* System error */
+#define PAM_BUF_ERR 5 /* Memory buffer error */
+#define PAM_PERM_DENIED 6 /* Permission denied */
+#define PAM_AUTH_ERR 7 /* Authentication failure */
+#define PAM_CRED_INSUFFICIENT 8 /* Can not access authentication data */
+ /* due to insufficient credentials */
+#define PAM_AUTHINFO_UNAVAIL 9 /* Underlying authentication service */
+ /* can not retrieve authenticaiton */
+ /* information */
+#define PAM_USER_UNKNOWN 10 /* User not known to the underlying */
+ /* authenticaiton module */
+#define PAM_MAXTRIES 11 /* An authentication service has */
+ /* maintained a retry count which has */
+ /* been reached. No further retries */
+ /* should be attempted */
+#define PAM_NEW_AUTHTOK_REQD 12 /* New authentication token required. */
+ /* This is normally returned if the */
+ /* machine security policies require */
+ /* that the password should be changed */
+ /* beccause the password is NULL or it */
+ /* has aged */
+#define PAM_ACCT_EXPIRED 13 /* User account has expired */
+#define PAM_SESSION_ERR 14 /* Can not make/remove an entry for */
+ /* the specified session */
+#define PAM_CRED_UNAVAIL 15 /* Underlying authentication service */
+ /* can not retrieve user credentials */
+ /* unavailable */
+#define PAM_CRED_EXPIRED 16 /* User credentials expired */
+#define PAM_CRED_ERR 17 /* Failure setting user credentials */
+#define PAM_NO_MODULE_DATA 18 /* No module specific data is present */
+#define PAM_CONV_ERR 19 /* Conversation error */
+#define PAM_AUTHTOK_ERR 20 /* Authentication token manipulation error */
+#define PAM_AUTHTOK_RECOVER_ERR 21 /* Authentication information */
+ /* cannot be recovered */
+#define PAM_AUTHTOK_LOCK_BUSY 22 /* Authentication token lock busy */
+#define PAM_AUTHTOK_DISABLE_AGING 23 /* Authentication token aging disabled */
+#define PAM_TRY_AGAIN 24 /* Preliminary check by password service */
+#define PAM_IGNORE 25 /* Ingore underlying account module */
+ /* regardless of whether the control */
+ /* flag is required, optional, or sufficient */
+#define PAM_ABORT 26 /* Critical error (?module fail now request) */
+#define PAM_AUTHTOK_EXPIRED 27 /* user's authentication token has expired */
+#define PAM_MODULE_UNKNOWN 28 /* module is not known */
+
+#define PAM_BAD_ITEM 29 /* Bad item passed to pam_*_item() */
+#define PAM_CONV_AGAIN 30 /* conversation function is event driven
+ and data is not available yet */
+#define PAM_INCOMPLETE 31 /* please call this function again to
+ complete authentication stack. Before
+ calling again, verify that conversation
+ is completed */
+
+/* Add new #define's here */
+
+#define _PAM_RETURN_VALUES 32 /* this is the number of return values */
+
+
+/* ---------------------- The Linux-PAM flags -------------------- */
+
+/* Authentication service should not generate any messages */
+#define PAM_SILENT 0x8000U
+
+/* Note: these flags are used by pam_authenticate{,_secondary}() */
+
+/* The authentication service should return PAM_AUTH_ERROR if the
+ * user has a null authentication token */
+#define PAM_DISALLOW_NULL_AUTHTOK 0x0001U
+
+/* Note: these flags are used for pam_setcred() */
+
+/* Set user credentials for an authentication service */
+#define PAM_ESTABLISH_CRED 0x0002U
+
+/* Delete user credentials associated with an authentication service */
+#define PAM_DELETE_CRED 0x0004U
+
+/* Reinitialize user credentials */
+#define PAM_REINITIALIZE_CRED 0x0008U
+
+/* Extend lifetime of user credentials */
+#define PAM_REFRESH_CRED 0x0010U
+
+/* Note: these flags are used by pam_chauthtok */
+
+/* The password service should only update those passwords that have
+ * aged. If this flag is not passed, the password service should
+ * update all passwords. */
+#define PAM_CHANGE_EXPIRED_AUTHTOK 0x0020U
+
+/* ------------------ The Linux-PAM item types ------------------- */
+
+/* these defines are used by pam_set_item() and pam_get_item() */
+
+#define PAM_SERVICE 1 /* The service name */
+#define PAM_USER 2 /* The user name */
+#define PAM_TTY 3 /* The tty name */
+#define PAM_RHOST 4 /* The remote host name */
+#define PAM_CONV 5 /* The pam_conv structure */
+
+/* missing entries found in <security/pam_modules.h> for modules only! */
+
+#define PAM_RUSER 8 /* The remote user name */
+#define PAM_USER_PROMPT 9 /* the prompt for getting a username */
+#define PAM_FAIL_DELAY 10 /* app supplied function to override failure
+ delays */
+
+/* ---------- Common Linux-PAM application/module PI ----------- */
+
+extern int pam_set_item(pam_handle_t *pamh, int item_type, const void *item);
+extern int pam_get_item(const pam_handle_t *pamh, int item_type,
+ const void **item);
+extern const char *pam_strerror(pam_handle_t *pamh, int errnum);
+
+extern int pam_putenv(pam_handle_t *pamh, const char *name_value);
+extern const char *pam_getenv(pam_handle_t *pamh, const char *name);
+extern char **pam_getenvlist(pam_handle_t *pamh);
+
+/* ---------- Common Linux-PAM application/module PI ----------- */
+
+/*
+ * here are some proposed error status definitions for the
+ * 'error_status' argument used by the cleanup function associated
+ * with data items they should be logically OR'd with the error_status
+ * of the latest return from libpam -- new with .52 and positive
+ * impression from Sun although not official as of 1996/9/4
+ * [generally the other flags are to be found in pam_modules.h]
+ */
+
+#define PAM_DATA_SILENT 0x40000000 /* used to suppress messages... */
+
+/*
+ * here we define an externally (by apps or modules) callable function
+ * that primes the libpam library to delay when a stacked set of
+ * modules results in a failure. In the case of PAM_SUCCESS this delay
+ * is ignored.
+ *
+ * Note, the pam_[gs]et_item(... PAM_FAIL_DELAY ...) can be used to set
+ * a function pointer which can override the default fail-delay behavior.
+ * This item was added to accommodate event driven programs that need to
+ * manage delays more carefully. The function prototype for this data
+ * item is
+ * void (*fail_delay)(int status, unsigned int delay);
+ */
+
+#define HAVE_PAM_FAIL_DELAY
+extern int pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay);
+
+#include <syslog.h>
+#ifndef LOG_AUTHPRIV
+# ifdef LOG_PRIV
+# define LOG_AUTHPRIV LOG_PRIV
+# endif /* LOG_PRIV */
+#endif /* !LOG_AUTHPRIV */
+
+#ifdef MEMORY_DEBUG
+/*
+ * this defines some macros that keep track of what memory has been
+ * allocated and indicates leakage etc... It should not be included in
+ * production application/modules.
+ */
+#include <security/pam_malloc.h>
+#endif
+
+/* ------------ The Linux-PAM conversation structures ------------ */
+
+/* Message styles */
+
+#define PAM_PROMPT_ECHO_OFF 1
+#define PAM_PROMPT_ECHO_ON 2
+#define PAM_ERROR_MSG 3
+#define PAM_TEXT_INFO 4
+
+/* Linux-PAM specific types */
+
+#define PAM_RADIO_TYPE 5 /* yes/no/maybe conditionals */
+
+/* This is for server client non-human interaction.. these are NOT
+ part of the X/Open PAM specification. */
+
+#define PAM_BINARY_PROMPT 7
+
+/* maximum size of messages/responses etc.. (these are mostly
+ arbitrary so Linux-PAM should handle longer values). */
+
+#define PAM_MAX_NUM_MSG 32
+#define PAM_MAX_MSG_SIZE 512
+#define PAM_MAX_RESP_SIZE 512
+
+/* Used to pass prompting text, error messages, or other informatory
+ * text to the user. This structure is allocated and freed by the PAM
+ * library (or loaded module). */
+
+struct pam_message {
+ int msg_style;
+ const char *msg;
+};
+
+/* if the pam_message.msg_style = PAM_BINARY_PROMPT
+ the 'pam_message.msg' is a pointer to a 'const *' for the following
+ pseudo-structure. When used with a PAM_BINARY_PROMPT, the returned
+ pam_response.resp pointer points to an object with the following
+ structure:
+
+ struct {
+ u32 length; # network byte order
+ unsigned char type;
+ unsigned char data[length-5];
+ };
+
+ The 'libpamc' library is designed around this flavor of
+ message and should be used to handle this flavor of msg_style.
+ */
+
+/* Used to return the user's response to the PAM library. This
+ structure is allocated by the application program, and free()'d by
+ the Linux-PAM library (or calling module). */
+
+struct pam_response {
+ char *resp;
+ int resp_retcode; /* currently un-used, zero expected */
+};
+
+/* The actual conversation structure itself */
+
+struct pam_conv {
+ int (*conv)(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata_ptr);
+ void *appdata_ptr;
+};
+
+#ifndef LINUX_PAM
+/*
+ * the following few lines represent a hack. They are there to make
+ * the Linux-PAM headers more compatible with the Sun ones, which have a
+ * less strictly separated notion of module specific and application
+ * specific definitions.
+ */
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#endif
+
+
+/* ... adapted from the pam_appl.h file created by Theodore Ts'o and
+ *
+ * Copyright Theodore Ts'o, 1996. All rights reserved.
+ * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org>, 1996-8
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#endif /* _SECURITY__PAM_TYPES_H */
+
diff --git a/libpam/include/security/pam_appl.h b/libpam/include/security/pam_appl.h
new file mode 100644
index 00000000..2849970a
--- /dev/null
+++ b/libpam/include/security/pam_appl.h
@@ -0,0 +1,92 @@
+/*
+ * <security/pam_appl.h>
+ *
+ * This header file collects definitions for the PAM API --- that is,
+ * public interface between the PAM library and an application program
+ * that wishes to use it.
+ *
+ * Note, the copyright information is at end of file.
+ *
+ * Created: 15-Jan-96 by TYT
+ * Last modified: 1996/3/5 by AGM
+ *
+ * $Id$
+ */
+
+#ifndef _SECURITY_PAM_APPL_H
+#define _SECURITY_PAM_APPL_H
+
+#include <security/_pam_types.h> /* Linux-PAM common defined types */
+
+/* -------------- The Linux-PAM Framework layer API ------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int pam_start(const char *service_name, const char *user,
+ const struct pam_conv *pam_conversation,
+ pam_handle_t **pamh);
+extern int pam_end(pam_handle_t *pamh, int pam_status);
+
+/* Authentication API's */
+
+extern int pam_authenticate(pam_handle_t *pamh, int flags);
+extern int pam_setcred(pam_handle_t *pamh, int flags);
+
+/* Account Management API's */
+
+extern int pam_acct_mgmt(pam_handle_t *pamh, int flags);
+
+/* Session Management API's */
+
+extern int pam_open_session(pam_handle_t *pamh, int flags);
+extern int pam_close_session(pam_handle_t *pamh, int flags);
+
+/* Password Management API's */
+
+extern int pam_chauthtok(pam_handle_t *pamh, int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* take care of any compatibility issues */
+#include <security/_pam_compat.h>
+
+/*
+ * Copyright Theodore Ts'o, 1996. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#endif /* _SECURITY_PAM_APPL_H */
diff --git a/libpam/include/security/pam_malloc.h b/libpam/include/security/pam_malloc.h
new file mode 100644
index 00000000..8daf3f7c
--- /dev/null
+++ b/libpam/include/security/pam_malloc.h
@@ -0,0 +1,79 @@
+/* $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:23 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.1 1996/11/10 21:23:14 morgan
+ * Initial revision
+ *
+ */
+
+/*
+ * This file (via the use of macros) defines a wrapper for the malloc
+ * family of calls. It logs where the memory was requested and also
+ * where it was free()'d and keeps a list of currently requested memory.
+ *
+ * It is hoped that it will provide some help in locating memory leaks.
+ */
+
+#ifndef PAM_MALLOC_H
+#define PAM_MALLOC_H
+
+/* these are the macro definitions for the stdlib.h memory functions */
+
+#define malloc(s) pam_malloc(s,__FILE__,__FUNCTION__,__LINE__)
+#define calloc(n,s) pam_calloc(n,s,__FILE__,__FUNCTION__,__LINE__)
+#define free(x) pam_free(x,__FILE__,__FUNCTION__,__LINE__)
+/* #define memalign(a,s) pam_memalign(a,s,__FILE__,__FUNCTION__,__LINE__) */
+#define realloc(x,s) pam_realloc(x,s,__FILE__,__FUNCTION__,__LINE__)
+/* #define valloc(s) pam_valloc(s,__FILE__,__FUNCTION__,__LINE__) */
+/* #define alloca(s) pam_alloca(s,__FILE__,__FUNCTION__,__LINE__) */
+#define exit(i) pam_exit(i,__FILE__,__FUNCTION__,__LINE__)
+
+/* these are the prototypes for the wrapper functions */
+
+#include <sys/types.h>
+
+extern void *pam_malloc(size_t s,const char *,const char *,const int);
+extern void *pam_calloc(size_t n,size_t s,const char *,const char *,const int);
+extern void pam_free(void *x,const char *,const char *,const int);
+extern void *pam_memalign(size_t a,size_t s
+ ,const char *,const char *,const int);
+extern void *pam_realloc(void *x,size_t s,const char *,const char *,const int);
+extern void *pam_valloc(size_t s,const char *,const char *,const int);
+extern void *pam_alloca(size_t s,const char *,const char *,const int);
+extern void pam_exit(int i,const char *,const char *,const int);
+
+/* these are the flags used to turn on and off diagnostics */
+
+#define PAM_MALLOC_LEAKED 01
+#define PAM_MALLOC_REQUEST 02
+#define PAM_MALLOC_FREE 04
+#define PAM_MALLOC_EXCH (PAM_MALLOC_FREED|PAM_MALLOC_EXCH)
+#define PAM_MALLOC_RESIZE 010
+#define PAM_MALLOC_FAIL 020
+#define PAM_MALLOC_NULL 040
+#define PAM_MALLOC_VERIFY 0100
+#define PAM_MALLOC_FUNC 0200
+#define PAM_MALLOC_PAUSE 0400
+#define PAM_MALLOC_STOP 01000
+
+#define PAM_MALLOC_ALL 0777
+
+#define PAM_MALLOC_DEFAULT \
+ (PAM_MALLOC_LEAKED|PAM_MALLOC_PAUSE|PAM_MALLOC_FAIL)
+
+#include <stdio.h>
+
+extern FILE *pam_malloc_outfile; /* defaults to stdout */
+
+/* how much output do you want? */
+
+extern int pam_malloc_flags;
+extern int pam_malloc_delay_length; /* how long to pause on errors */
+
+#endif /* PAM_MALLOC_H */
diff --git a/libpam/include/security/pam_modules.h b/libpam/include/security/pam_modules.h
new file mode 100644
index 00000000..945c1711
--- /dev/null
+++ b/libpam/include/security/pam_modules.h
@@ -0,0 +1,195 @@
+/*
+ * <security/pam_modules.h>
+ *
+ * $Id$
+ *
+ * This header file documents the PAM SPI --- that is, interface
+ * between the PAM library and a PAM service library which is called
+ * by the PAM library.
+ *
+ * Note, the copyright information is at end of file.
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:23 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.8 1997/01/04 20:14:42 morgan
+ * moved PAM_DATA_SILENT to _pam_types.h so applications can use it too
+ *
+ * Revision 1.7 1996/11/10 19:57:08 morgan
+ * pam_get_user prototype.
+ *
+ * Revision 1.6 1996/09/05 06:18:45 morgan
+ * added some data error_status masks, changed prototype for cleanup()
+ *
+ * Revision 1.5 1996/06/02 07:58:37 morgan
+ * altered the way in which modules obtain static prototypes for
+ * functions
+ *
+ */
+
+#ifndef _SECURITY_PAM_MODULES_H
+#define _SECURITY_PAM_MODULES_H
+
+#include <security/_pam_types.h> /* Linux-PAM common defined types */
+
+/* these defines are used by pam_set_item() and pam_get_item() and are
+ * in addition to those found in <security/_pam_types.h> */
+
+#define PAM_AUTHTOK 6 /* The authentication token (password) */
+#define PAM_OLDAUTHTOK 7 /* The old authentication token */
+
+/* -------------- The Linux-PAM Module PI ------------- */
+
+extern int pam_set_data(pam_handle_t *pamh, const char *module_data_name,
+ void *data,
+ void (*cleanup)(pam_handle_t *pamh, void *data,
+ int error_status));
+extern int pam_get_data(const pam_handle_t *pamh,
+ const char *module_data_name, const void **data);
+
+extern int pam_get_user(pam_handle_t *pamh, const char **user
+ , const char *prompt);
+
+#ifdef PAM_STATIC
+
+#define PAM_EXTERN static
+
+struct pam_module {
+ const char *name; /* Name of the module */
+
+ /* These are function pointers to the module's key functions. */
+
+ int (*pam_sm_authenticate)(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+ int (*pam_sm_setcred)(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+ int (*pam_sm_acct_mgmt)(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+ int (*pam_sm_open_session)(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+ int (*pam_sm_close_session)(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+ int (*pam_sm_chauthtok)(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+};
+
+#else /* !PAM_STATIC */
+
+#define PAM_EXTERN extern
+
+#endif /* PAM_STATIC */
+
+/* Lots of files include pam_modules.h that don't need these
+ * declared. However, when they are declared static, they
+ * need to be defined later. So we have to protect C files
+ * that include these without wanting these functions defined.. */
+
+#if (defined(PAM_STATIC) && defined(PAM_SM_AUTH)) || !defined(PAM_STATIC)
+
+/* Authentication API's */
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+#endif /*(defined(PAM_STATIC) && defined(PAM_SM_AUTH))
+ || !defined(PAM_STATIC)*/
+
+#if (defined(PAM_STATIC) && defined(PAM_SM_ACCOUNT)) || !defined(PAM_STATIC)
+
+/* Account Management API's */
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+#endif /*(defined(PAM_STATIC) && defined(PAM_SM_ACCOUNT))
+ || !defined(PAM_STATIC)*/
+
+#if (defined(PAM_STATIC) && defined(PAM_SM_SESSION)) || !defined(PAM_STATIC)
+
+/* Session Management API's */
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+#endif /*(defined(PAM_STATIC) && defined(PAM_SM_SESSION))
+ || !defined(PAM_STATIC)*/
+
+#if (defined(PAM_STATIC) && defined(PAM_SM_PASSWORD)) || !defined(PAM_STATIC)
+
+/* Password Management API's */
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+#endif /*(defined(PAM_STATIC) && defined(PAM_SM_PASSWORD))
+ || !defined(PAM_STATIC)*/
+
+/* The following two flags are for use across the Linux-PAM/module
+ * interface only. The Application is not permitted to use these
+ * tokens.
+ *
+ * The password service should only perform preliminary checks. No
+ * passwords should be updated. */
+#define PAM_PRELIM_CHECK 0x4000
+
+/* The password service should update passwords Note: PAM_PRELIM_CHECK
+ * and PAM_UPDATE_AUTHTOK can not both be set simultaneously! */
+#define PAM_UPDATE_AUTHTOK 0x2000
+
+
+/*
+ * here are some proposed error status definitions for the
+ * 'error_status' argument used by the cleanup function associated
+ * with data items they should be logically OR'd with the error_status
+ * of the latest return from libpam -- new with .52 and positive
+ * impression from Sun although not official as of 1996/9/4 there are
+ * others in _pam_types.h -- they are for common module/app use.
+ */
+
+#define PAM_DATA_REPLACE 0x20000000 /* used when replacing a data item */
+
+/* take care of any compatibility issues */
+#include <security/_pam_compat.h>
+
+/* Copyright (C) Theodore Ts'o, 1996.
+ * Copyright (C) Andrew Morgan, 1996-8.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU General Public License, in which case the provisions of the
+ * GNU GPL are required INSTEAD OF the above restrictions. (This
+ * clause is necessary due to a potential bad interaction between the
+ * GNU GPL and the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#endif /* _SECURITY_PAM_MODULES_H */
+
diff --git a/libpam/pam_account.c b/libpam/pam_account.c
new file mode 100644
index 00000000..ffc01acd
--- /dev/null
+++ b/libpam/pam_account.c
@@ -0,0 +1,13 @@
+/* pam_account.c - PAM Account Management */
+
+#include <stdio.h>
+
+#include "pam_private.h"
+
+int pam_acct_mgmt(pam_handle_t *pamh, int flags)
+{
+ D(("called"));
+
+ IF_NO_PAMH("pam_acct_mgmt",pamh,PAM_SYSTEM_ERR);
+ return _pam_dispatch(pamh, flags, PAM_ACCOUNT);
+}
diff --git a/libpam/pam_auth.c b/libpam/pam_auth.c
new file mode 100644
index 00000000..c946eaab
--- /dev/null
+++ b/libpam/pam_auth.c
@@ -0,0 +1,67 @@
+/*
+ * pam_auth.c -- PAM authentication
+ *
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:13 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.7 1997/04/05 06:53:52 morgan
+ * fail-delay changes
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pam_private.h"
+
+int pam_authenticate(pam_handle_t *pamh, int flags)
+{
+ int retval;
+
+ D(("pam_authenticate called"));
+
+ if (pamh->former.choice == PAM_NOT_STACKED) {
+ _pam_sanitize(pamh);
+ _pam_start_timer(pamh); /* we try to make the time for a failure
+ independent of the time it takes to
+ fail */
+ }
+
+ IF_NO_PAMH("pam_authenticate",pamh,PAM_SYSTEM_ERR);
+ retval = _pam_dispatch(pamh, flags, PAM_AUTHENTICATE);
+
+ if (retval != PAM_INCOMPLETE) {
+ _pam_sanitize(pamh);
+ _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */
+ D(("pam_authenticate exit"));
+ } else {
+ D(("will resume when ready"));
+ }
+
+ return retval;
+}
+
+int pam_setcred(pam_handle_t *pamh, int flags)
+{
+ int retval;
+
+ IF_NO_PAMH("pam_setcred", pamh, PAM_SYSTEM_ERR);
+
+ D(("pam_setcred called"));
+
+ if (! flags) {
+ flags = PAM_ESTABLISH_CRED;
+ }
+
+ retval = _pam_dispatch(pamh, flags, PAM_SETCRED);
+
+ D(("pam_setcred exit"));
+
+ return retval;
+}
diff --git a/libpam/pam_data.c b/libpam/pam_data.c
new file mode 100644
index 00000000..6422b10e
--- /dev/null
+++ b/libpam/pam_data.c
@@ -0,0 +1,105 @@
+/* pam_data.c */
+
+/*
+ * $Id$
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pam_private.h"
+
+struct pam_data *_pam_locate_data(const pam_handle_t *pamh, const char *name);
+
+int pam_set_data(
+ pam_handle_t *pamh,
+ const char *module_data_name,
+ void *data,
+ void (*cleanup)(pam_handle_t *pamh, void *data, int error_status))
+{
+ struct pam_data *data_entry;
+
+ IF_NO_PAMH("pam_set_data",pamh,PAM_SYSTEM_ERR);
+
+ /* first check if there is some data already. If so clean it up */
+
+ if ((data_entry = _pam_locate_data(pamh, module_data_name))) {
+ if (data_entry->cleanup) {
+ data_entry->cleanup(pamh, data_entry->data,
+ PAM_DATA_REPLACE | PAM_SUCCESS );
+ }
+ } else if ((data_entry = malloc(sizeof(*data_entry)))) {
+ char *tname;
+
+ if ((tname = _pam_strdup(module_data_name)) == NULL) {
+ _pam_system_log(LOG_CRIT, "pam_set_data: no memory for data name");
+ _pam_drop(data_entry);
+ return PAM_BUF_ERR;
+ }
+ data_entry->next = pamh->data;
+ pamh->data = data_entry;
+ data_entry->name = tname;
+ } else {
+ _pam_system_log(LOG_CRIT, "pam_set_data: cannot allocate data entry");
+ return PAM_BUF_ERR;
+ }
+
+ data_entry->data = data; /* note this could be NULL */
+ data_entry->cleanup = cleanup;
+
+ return PAM_SUCCESS;
+}
+
+int pam_get_data(
+ const pam_handle_t *pamh,
+ const char *module_data_name,
+ const void **datap)
+{
+ struct pam_data *data;
+
+ IF_NO_PAMH("pam_get_data",pamh,PAM_SYSTEM_ERR);
+
+ data = _pam_locate_data(pamh, module_data_name);
+ if (data) {
+ *datap = data->data;
+ return PAM_SUCCESS;
+ }
+
+ return PAM_NO_MODULE_DATA;
+}
+
+struct pam_data *_pam_locate_data(const pam_handle_t *pamh, const char *name)
+{
+ struct pam_data *data;
+
+ IF_NO_PAMH("_pam_locate_data",pamh,NULL);
+ data = pamh->data;
+
+ while (data) {
+ if (!strcmp(data->name, name)) {
+ return data;
+ }
+ data = data->next;
+ }
+
+ return NULL;
+}
+
+void _pam_free_data(pam_handle_t *pamh, int status)
+{
+ struct pam_data *last;
+ struct pam_data *data;
+
+ IF_NO_PAMH("_pam_free_data",pamh,/* no return value for void fn */);
+ data = pamh->data;
+
+ while (data) {
+ last = data;
+ data = data->next;
+ if (last->cleanup) {
+ last->cleanup(pamh, last->data, status);
+ }
+ _pam_drop(last->name);
+ _pam_drop(last);
+ }
+}
diff --git a/libpam/pam_delay.c b/libpam/pam_delay.c
new file mode 100644
index 00000000..b7c6604d
--- /dev/null
+++ b/libpam/pam_delay.c
@@ -0,0 +1,168 @@
+/*
+ * pam_delay.c
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org> 1996-9
+ * All rights reserved.
+ *
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:14 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/04 23:23:42 morgan
+ * add appdata_ptr to app callback function
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ */
+
+/*
+ * This is a simple implementation of a delay on failure mechanism; an
+ * attempt to overcome authentication-time attacks in a simple manner.
+ */
+
+#include <unistd.h>
+#include "pam_private.h"
+
+/* **********************************************************************
+ * initialize the time as unset, this is set on the return from the
+ * authenticating pair of of the libpam pam_XXX calls.
+ */
+
+void _pam_reset_timer(pam_handle_t *pamh)
+{
+ D(("setting pamh->fail_delay.set to FALSE"));
+ pamh->fail_delay.set = PAM_FALSE;
+}
+
+/* **********************************************************************
+ * this function sets the start time for possible delayed failing.
+ *
+ * Eventually, it may set the timer so libpam knows how long the program
+ * has already been executing. Currently, this value is used to seed
+ * a pseudo-random number generator...
+ */
+
+void _pam_start_timer(pam_handle_t *pamh)
+{
+ pamh->fail_delay.begin = time(NULL);
+ D(("starting timer..."));
+}
+
+/* *******************************************************************
+ * Compute a pseudo random time. The value is base*(1 +/- 1/5) where
+ * the distribution is pseudo gausian (the sum of three evenly
+ * distributed random numbers -- central limit theorem and all ;^) The
+ * linear random numbers are based on a formulae given in Knuth's
+ * Seminumerical recipies that was reproduced in `Numerical Recipies
+ * in C'. It is *not* a cryptographically strong generator, but it is
+ * probably "good enough" for our purposes here.
+ *
+ * /dev/random might be a better place to look for some numbers...
+ */
+
+static unsigned int _pam_rand(unsigned int seed)
+{
+#define N1 1664525
+#define N2 1013904223
+ return N1*seed + N2;
+}
+
+static unsigned int _pam_compute_delay(unsigned int seed, unsigned int base)
+{
+ int i;
+ double sum;
+ unsigned int ans;
+
+ for (sum=i=0; i<3; ++i) {
+ seed = _pam_rand(seed);
+ sum += (double) ((seed / 10) % 1000000);
+ }
+ sum = (sum/3.)/1e6 - .5; /* rescale */
+ ans = (unsigned int) ( base*(1.+sum) );
+ D(("random number: base=%u -> ans=%u\n", base, ans));
+
+ return ans;
+}
+
+/* **********************************************************************
+ * the following function sleeps for a random time. The actual time
+ * slept is computed above.. It is based on the requested time but will
+ * differ by up to +/- 25%.
+ */
+
+void _pam_await_timer(pam_handle_t *pamh, int status)
+{
+ unsigned int delay;
+ D(("waiting?..."));
+
+ delay = _pam_compute_delay(pamh->fail_delay.begin,
+ pamh->fail_delay.delay);
+ if (pamh->fail_delay.delay_fn_ptr) {
+ union {
+ const void *value;
+ void (*fn)(int, unsigned, void *);
+ } hack_fn_u;
+ void *appdata_ptr;
+
+ if (pamh->pam_conversation) {
+ appdata_ptr = pamh->pam_conversation->appdata_ptr;
+ } else {
+ appdata_ptr = NULL;
+ }
+
+ /* always call the applications delay function, even if
+ the delay is zero - indicate status */
+ hack_fn_u.value = pamh->fail_delay.delay_fn_ptr;
+ hack_fn_u.fn(status, delay, appdata_ptr);
+
+ } else if (status != PAM_SUCCESS && pamh->fail_delay.set) {
+
+ D(("will wait %u usec", delay));
+
+ if (delay > 0) {
+ struct timeval tval;
+
+ tval.tv_sec = delay / 1000000;
+ tval.tv_usec = delay % 1000000;
+ select(0, NULL, NULL, NULL, &tval);
+ }
+ }
+
+ _pam_reset_timer(pamh);
+ D(("waiting done"));
+}
+
+/* **********************************************************************
+ * this function is known to both the module and the application, it
+ * keeps a running score of the largest-requested delay so far, as
+ * specified by either modules or an application.
+ */
+
+int pam_fail_delay(pam_handle_t *pamh, unsigned int usec)
+{
+ int largest;
+
+ IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR);
+
+ D(("setting delay to %u",usec));
+
+ if (pamh->fail_delay.set) {
+ largest = pamh->fail_delay.delay;
+ } else {
+ pamh->fail_delay.set = PAM_TRUE;
+ largest = 0;
+ }
+
+ D(("largest = %u",largest));
+
+ if (largest < usec) {
+ D(("resetting largest delay"));
+ pamh->fail_delay.delay = usec;
+ }
+
+ return PAM_SUCCESS;
+}
+
diff --git a/libpam/pam_dispatch.c b/libpam/pam_dispatch.c
new file mode 100644
index 00000000..285f3316
--- /dev/null
+++ b/libpam/pam_dispatch.c
@@ -0,0 +1,282 @@
+/* pam_dispatch.c - handles module function dispatch */
+
+/*
+ * Copyright (c) 1998 Andrew G. Morgan <morgan@linux.kernel.org>
+ *
+ * $Id$
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "pam_private.h"
+
+/*
+ * this is the return code we return when a function pointer is NULL
+ * or, the handler structure indicates a broken module config line
+ */
+#define PAM_MUST_FAIL_CODE PAM_PERM_DENIED
+
+/* impression codes - this gives some sense to the logical choices */
+#define _PAM_UNDEF 0
+#define _PAM_POSITIVE +1
+#define _PAM_NEGATIVE -1
+
+/*
+ * walk a stack of modules. Interpret the administrator's instructions
+ * when combining the return code of each module.
+ */
+
+static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
+ _pam_boolean resumed)
+{
+ int depth, impression, status, skip_depth;
+
+ IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR);
+
+ if (h == NULL) {
+ const char *service=NULL;
+
+ (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
+ _pam_system_log(LOG_ERR, "no modules loaded for `%s' service",
+ service ? service:"<unknown>" );
+ service = NULL;
+ return PAM_MUST_FAIL_CODE;
+ }
+
+ /* if we are recalling this module stack because a former call did
+ not complete, we restore the state of play from pamh. */
+ if (resumed) {
+ skip_depth = pamh->former.depth;
+ status = pamh->former.status;
+ impression = pamh->former.impression;
+ /* forget all that */
+ pamh->former.impression = _PAM_UNDEF;
+ pamh->former.status = PAM_MUST_FAIL_CODE;
+ pamh->former.depth = 0;
+ } else {
+ skip_depth = 0;
+ impression = _PAM_UNDEF;
+ status = PAM_MUST_FAIL_CODE;
+ }
+
+ /* Loop through module logic stack */
+ for (depth=0 ; h != NULL ; h = h->next, ++depth) {
+ int retval, action;
+
+ /* skip leading modules if they have already returned */
+ if (depth < skip_depth) {
+ continue;
+ }
+
+ /* attempt to call the module */
+ if (h->func == NULL) {
+ D(("module function is not defined, indicating failure"));
+ retval = PAM_MODULE_UNKNOWN;
+ } else {
+ D(("passing control to module..."));
+ retval = h->func(pamh, flags, h->argc, h->argv);
+ D(("module returned: %s", pam_strerror(pamh, retval)));
+ if (h->must_fail) {
+ D(("module poorly listed in pam.conf; forcing failure"));
+ retval = PAM_MUST_FAIL_CODE;
+ }
+ }
+
+ /*
+ * PAM_INCOMPLETE return is special. It indicates that the
+ * module wants to wait for the application before continuing.
+ * In order to return this, the module will have saved its
+ * state so it can resume from an equivalent position when it
+ * is called next time. (This was added as of 0.65)
+ */
+ if (retval == PAM_INCOMPLETE) {
+ pamh->former.impression = impression;
+ pamh->former.status = status;
+ pamh->former.depth = depth;
+
+ D(("module %d returned PAM_INCOMPLETE", depth));
+ return retval;
+ }
+
+ /* verify that the return value is a valid one */
+ if (retval < PAM_SUCCESS || retval >= _PAM_RETURN_VALUES) {
+ retval = PAM_MUST_FAIL_CODE;
+ action = _PAM_ACTION_BAD;
+ } else {
+ action = h->actions[retval];
+ }
+
+ /* decide what to do */
+ switch (action) {
+ case _PAM_ACTION_RESET:
+ impression = _PAM_UNDEF;
+ status = PAM_MUST_FAIL_CODE;
+ break;
+
+ case _PAM_ACTION_OK:
+ case _PAM_ACTION_DONE:
+ if ( impression == _PAM_UNDEF
+ || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) {
+ impression = _PAM_POSITIVE;
+ status = retval;
+ }
+ if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) {
+ goto decision_made;
+ }
+ break;
+
+ case _PAM_ACTION_BAD:
+ case _PAM_ACTION_DIE:
+#ifdef PAM_FAIL_NOW_ON
+ if ( retval == PAM_ABORT ) {
+ impression = _PAM_NEGATIVE;
+ status = PAM_PERM_DENIED;
+ goto decision_made;
+ }
+#endif /* PAM_FAIL_NOW_ON */
+ if ( impression != _PAM_NEGATIVE ) {
+ impression = _PAM_NEGATIVE;
+ status = retval;
+ }
+ if ( action == _PAM_ACTION_DIE ) {
+ goto decision_made;
+ }
+ break;
+
+ case _PAM_ACTION_IGNORE:
+ break;
+
+ /* if we get here, we expect action is a positive number --
+ this is what the ...JUMP macro checks. */
+
+ default:
+ if ( _PAM_ACTION_IS_JUMP(action) ) {
+ /* this means that we need to skip #action stacked modules */
+ do {
+ h = h->next;
+ } while ( --action > 0 && h != NULL );
+
+ /* note if we try to skip too many modules action is
+ still non-zero and we snag the next if. */
+ }
+
+ /* this case is a syntax error: we can't succeed */
+ if (action) {
+ D(("action syntax error"));
+ impression = _PAM_NEGATIVE;
+ status = PAM_MUST_FAIL_CODE;
+ }
+ }
+ }
+
+decision_made: /* by getting here we have made a decision */
+
+ /* Sanity check */
+ if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) {
+ D(("caught on sanity check -- this is probably a config error!"));
+ status = PAM_MUST_FAIL_CODE;
+ }
+
+ /* We have made a decision about the modules executed */
+ return status;
+}
+
+/*
+ * This function translates the module dispatch request into a pointer
+ * to the stack of modules that will actually be run. the
+ * _pam_dispatch_aux() function (above) is responsible for walking the
+ * module stack.
+ */
+
+int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
+{
+ struct handler *h = NULL;
+ int retval;
+ _pam_boolean resumed;
+
+ IF_NO_PAMH("_pam_dispatch",pamh,PAM_SYSTEM_ERR);
+
+ /* Load all modules, resolve all symbols */
+
+ if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) {
+ _pam_system_log(LOG_ERR, "unable to dispatch function");
+ return retval;
+ }
+
+ switch (choice) {
+ case PAM_AUTHENTICATE:
+ h = pamh->handlers.conf.authenticate;
+ break;
+ case PAM_SETCRED:
+ h = pamh->handlers.conf.setcred;
+ break;
+ case PAM_ACCOUNT:
+ h = pamh->handlers.conf.acct_mgmt;
+ break;
+ case PAM_OPEN_SESSION:
+ h = pamh->handlers.conf.open_session;
+ break;
+ case PAM_CLOSE_SESSION:
+ h = pamh->handlers.conf.close_session;
+ break;
+ case PAM_CHAUTHTOK:
+ h = pamh->handlers.conf.chauthtok;
+ break;
+ default:
+ _pam_system_log(LOG_ERR, "undefined fn choice; %d", choice);
+ return PAM_ABORT;
+ }
+
+ if (h == NULL) { /* there was no handlers.conf... entry; will use
+ * handlers.other... */
+ switch (choice) {
+ case PAM_AUTHENTICATE:
+ h = pamh->handlers.other.authenticate;
+ break;
+ case PAM_SETCRED:
+ h = pamh->handlers.other.setcred;
+ break;
+ case PAM_ACCOUNT:
+ h = pamh->handlers.other.acct_mgmt;
+ break;
+ case PAM_OPEN_SESSION:
+ h = pamh->handlers.other.open_session;
+ break;
+ case PAM_CLOSE_SESSION:
+ h = pamh->handlers.other.close_session;
+ break;
+ case PAM_CHAUTHTOK:
+ h = pamh->handlers.other.chauthtok;
+ break;
+ }
+ }
+
+ /* Did a module return an "incomplete state" last time? */
+ if (pamh->former.choice != PAM_NOT_STACKED) {
+ if (pamh->former.choice != choice) {
+ _pam_system_log(LOG_ERR,
+ "application failed to re-exec stack [%d:%d]",
+ pamh->former.choice, choice);
+ return PAM_ABORT;
+ }
+ resumed = PAM_TRUE;
+ } else {
+ resumed = PAM_FALSE;
+ }
+
+ /* call the list of module functions */
+ retval = _pam_dispatch_aux(pamh, flags, h, resumed);
+ resumed = PAM_FALSE;
+
+ /* Should we recall where to resume next time? */
+ if (retval == PAM_INCOMPLETE) {
+ D(("module [%d] returned PAM_INCOMPLETE"));
+ pamh->former.choice = choice;
+ } else {
+ pamh->former.choice = PAM_NOT_STACKED;
+ }
+
+ return retval;
+}
+
diff --git a/libpam/pam_end.c b/libpam/pam_end.c
new file mode 100644
index 00000000..b389038f
--- /dev/null
+++ b/libpam/pam_end.c
@@ -0,0 +1,72 @@
+/* pam_end.c */
+
+/*
+ * $Id$
+ */
+
+#include <stdlib.h>
+
+#include "pam_private.h"
+
+int pam_end(pam_handle_t *pamh, int pam_status)
+{
+ int ret;
+
+ IF_NO_PAMH("pam_end", pamh, PAM_SYSTEM_ERR);
+
+ D(("entering pam_end()"));
+
+ /* first liberate the modules (it is not inconcevible that the
+ modules may need to use the service_name etc. to clean up) */
+
+ _pam_free_data(pamh, pam_status);
+
+ /* now drop all modules */
+
+ if ((ret = _pam_free_handlers(pamh)) != PAM_SUCCESS) {
+ return ret; /* error occurred */
+ }
+
+ /* from this point we cannot call the modules any more. Free the remaining
+ memory used by the Linux-PAM interface */
+
+ _pam_drop_env(pamh); /* purge the environment */
+
+ _pam_overwrite(pamh->authtok); /* blank out old token */
+ _pam_drop(pamh->authtok);
+
+ _pam_overwrite(pamh->oldauthtok); /* blank out old token */
+ _pam_drop(pamh->oldauthtok);
+
+ _pam_overwrite(pamh->former.prompt);
+ _pam_drop(pamh->former.prompt); /* drop saved prompt */
+
+ _pam_overwrite(pamh->service_name);
+ _pam_drop(pamh->service_name);
+
+ _pam_overwrite(pamh->user);
+ _pam_drop(pamh->user);
+
+ _pam_overwrite(pamh->prompt);
+ _pam_drop(pamh->prompt); /* prompt for pam_get_user() */
+
+ _pam_overwrite(pamh->tty);
+ _pam_drop(pamh->tty);
+
+ _pam_overwrite(pamh->rhost);
+ _pam_drop(pamh->rhost);
+
+ _pam_overwrite(pamh->ruser);
+ _pam_drop(pamh->ruser);
+
+ _pam_drop(pamh->pam_conversation);
+ pamh->fail_delay.delay_fn_ptr = NULL;
+
+ /* and finally liberate the memory for the pam_handle structure */
+
+ _pam_drop(pamh);
+
+ D(("exiting pam_end() successfully"));
+
+ return PAM_SUCCESS;
+}
diff --git a/libpam/pam_env.c b/libpam/pam_env.c
new file mode 100644
index 00000000..a1e744b7
--- /dev/null
+++ b/libpam/pam_env.c
@@ -0,0 +1,389 @@
+/*
+ * pam_env.c
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@parc.power.net> 1996,1997
+ * All rights reserved.
+ *
+ * This file was written from a "hint" provided by the people at SUN.
+ * and the X/Open XSSO draft of March 1997.
+ *
+ * $Id$
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#ifdef sunos
+#define memmove(x,y,z) bcopy(y,x,z)
+#endif
+
+#include "pam_private.h"
+
+/* helper functions */
+
+#ifdef DEBUG
+static void _pam_dump_env(pam_handle_t *pamh)
+{
+ int i;
+
+ D(("Listing environment of pamh=%p", pamh));
+ D(("pamh->env = %p", pamh->env));
+ D(("environment entries used = %d [of %d allocated]"
+ , pamh->env->requested, pamh->env->entries));
+
+ for (i=0; i<pamh->env->requested; ++i) {
+ _pam_output_debug(">%-3d [%9p]:[%s]"
+ , i, pamh->env->list[i], pamh->env->list[i]);
+ }
+ _pam_output_debug("*NOTE* the last item should be (nil)");
+}
+#else
+#define _pam_dump_env(x)
+#endif
+
+/*
+ * Create the environment
+ */
+
+int _pam_make_env(pam_handle_t *pamh)
+{
+ D(("called."));
+ IF_NO_PAMH("_pam_make_env", pamh, PAM_ABORT);
+
+ /*
+ * get structure memory
+ */
+
+ pamh->env = (struct pam_environ *) malloc(sizeof(struct pam_environ));
+ if (pamh->env == NULL) {
+ _pam_system_log(LOG_CRIT, "_pam_make_env: out of memory");
+ return PAM_BUF_ERR;
+ }
+
+ /*
+ * get list memory
+ */
+
+ pamh->env->list = (char **)calloc( PAM_ENV_CHUNK, sizeof(char *) );
+ if (pamh->env->list == NULL) {
+ _pam_system_log(LOG_CRIT, "_pam_make_env: no memory for list");
+ _pam_drop(pamh->env);
+ return PAM_BUF_ERR;
+ }
+
+ /*
+ * fill entries in pamh->env
+ */
+
+ pamh->env->entries = PAM_ENV_CHUNK;
+ pamh->env->requested = 1;
+ pamh->env->list[0] = NULL;
+
+ _pam_dump_env(pamh); /* only active when debugging */
+
+ return PAM_SUCCESS;
+}
+
+/*
+ * purge the environment
+ */
+
+void _pam_drop_env(pam_handle_t *pamh)
+{
+ D(("called."));
+ IF_NO_PAMH("_pam_make_env", pamh, /* nothing to return */);
+
+ if (pamh->env != NULL) {
+ int i;
+ /* we will only purge the pamh->env->requested number of elements */
+
+ for (i=pamh->env->requested-1; i-- > 0; ) {
+ D(("dropping #%3d>%s<", i, pamh->env->list[i]));
+ _pam_overwrite(pamh->env->list[i]); /* clean */
+ _pam_drop(pamh->env->list[i]); /* forget */
+ }
+ pamh->env->requested = 0;
+ pamh->env->entries = 0;
+ _pam_drop(pamh->env->list); /* forget */
+ _pam_drop(pamh->env); /* forget */
+ } else {
+ D(("no environment present in pamh?"));
+ }
+}
+
+/*
+ * Return the item number of the given variable = first 'length' chars
+ * of 'name_value'. Since this is a static function, it is safe to
+ * assume its supplied arguments are well defined.
+ */
+
+static int _pam_search_env(const struct pam_environ *env
+ , const char *name_value, int length)
+{
+ int i;
+
+ for (i=env->requested-1; i-- > 0; ) {
+ if (strncmp(name_value,env->list[i],length) == 0
+ && env->list[i][length] == '=') {
+
+ return i; /* Got it! */
+
+ }
+ }
+
+ return -1; /* no luck */
+}
+
+/*
+ * externally visible functions
+ */
+
+/*
+ * pam_putenv(): Add/replace/delete a PAM-environment variable.
+ *
+ * Add/replace:
+ * name_value = "NAME=VALUE" or "NAME=" (for empty value="\0")
+ *
+ * delete:
+ * name_value = "NAME"
+ */
+
+int pam_putenv(pam_handle_t *pamh, const char *name_value)
+{
+ int l2eq, item, retval;
+
+ D(("called."));
+ IF_NO_PAMH("pam_putenv", pamh, PAM_ABORT);
+
+ if (name_value == NULL) {
+ _pam_system_log(LOG_ERR, "pam_putenv: no variable indicated");
+ return PAM_PERM_DENIED;
+ }
+
+ /*
+ * establish if we are setting or deleting; scan for '='
+ */
+
+ for (l2eq=0; name_value[l2eq] && name_value[l2eq] != '='; ++l2eq);
+ if (l2eq <= 0) {
+ _pam_system_log(LOG_ERR, "pam_putenv: bad variable");
+ return PAM_BAD_ITEM;
+ }
+
+ /*
+ * Look first for environment.
+ */
+
+ if (pamh->env == NULL || pamh->env->list == NULL) {
+ _pam_system_log(LOG_ERR, "pam_putenv: no env%s found",
+ pamh->env == NULL ? "":"-list");
+ return PAM_ABORT;
+ }
+
+ /* find the item to replace */
+
+ item = _pam_search_env(pamh->env, name_value, l2eq);
+
+ if (name_value[l2eq]) { /* (re)setting */
+
+ if (item == -1) { /* new variable */
+ D(("adding item: %s", name_value));
+ /* enough space? */
+ if (pamh->env->entries <= pamh->env->requested) {
+ register int i;
+ register char **tmp;
+
+ /* get some new space */
+ tmp = calloc( pamh->env->entries + PAM_ENV_CHUNK
+ , sizeof(char *) );
+ if (tmp == NULL) {
+ /* nothing has changed - old env intact */
+ _pam_system_log(LOG_CRIT,
+ "pam_putenv: cannot grow environment");
+ return PAM_BUF_ERR;
+ }
+
+ /* copy old env-item pointers/forget old */
+ for (i=0; i<pamh->env->requested; ++i) {
+ tmp[i] = pamh->env->list[i];
+ pamh->env->list[i] = NULL;
+ }
+
+ /* drop old list and replace with new */
+ _pam_drop(pamh->env->list);
+ pamh->env->list = tmp;
+ pamh->env->entries += PAM_ENV_CHUNK;
+
+ D(("resized env list"));
+ _pam_dump_env(pamh); /* only when debugging */
+ }
+
+ item = pamh->env->requested-1; /* old last item (NULL) */
+
+ /* add a new NULL entry at end; increase counter */
+ pamh->env->list[pamh->env->requested++] = NULL;
+
+ } else { /* replace old */
+ D(("replacing item: %s\n with: %s"
+ , pamh->env->list[item], name_value));
+ _pam_overwrite(pamh->env->list[item]);
+ _pam_drop(pamh->env->list[item]);
+ }
+
+ /*
+ * now we have a place to put the new env-item, insert at 'item'
+ */
+
+ pamh->env->list[item] = _pam_strdup(name_value);
+ if (pamh->env->list[item] != NULL) {
+ _pam_dump_env(pamh); /* only when debugging */
+ return PAM_SUCCESS;
+ }
+
+ /* something went wrong; we should delete the item - fall through */
+
+ retval = PAM_BUF_ERR; /* an error occurred */
+ } else {
+ retval = PAM_SUCCESS; /* we requested delete */
+ }
+
+ /* getting to here implies we are deleting an item */
+
+ if (item < 0) {
+ _pam_system_log(LOG_ERR, "pam_putenv: delete non-existent entry; %s",
+ name_value);
+ return PAM_BAD_ITEM;
+ }
+
+ /*
+ * remove item: purge memory; reset counter; resize [; display-env]
+ */
+
+ D(("deleting: env#%3d:[%s]", item, pamh->env->list[item]));
+ _pam_overwrite(pamh->env->list[item]);
+ _pam_drop(pamh->env->list[item]);
+ --(pamh->env->requested);
+ D(("mmove: item[%d]+%d -> item[%d]"
+ , item+1, ( pamh->env->requested - item ), item));
+ (void) memmove(&pamh->env->list[item], &pamh->env->list[item+1]
+ , ( pamh->env->requested - item )*sizeof(char *) );
+
+ _pam_dump_env(pamh); /* only when debugging */
+
+ /*
+ * deleted.
+ */
+
+ return retval;
+}
+
+/*
+ * Return the value of the requested environment variable
+ */
+
+const char *pam_getenv(pam_handle_t *pamh, const char *name)
+{
+ int item;
+
+ D(("called."));
+ IF_NO_PAMH("pam_getenv", pamh, NULL);
+
+ if (name == NULL) {
+ _pam_system_log(LOG_ERR, "pam_getenv: no variable indicated");
+ return NULL;
+ }
+
+ if (pamh->env == NULL || pamh->env->list == NULL) {
+ _pam_system_log(LOG_ERR, "pam_getenv: no env%s found",
+ pamh->env == NULL ? "":"-list" );
+ return NULL;
+ }
+
+ /* find the requested item */
+
+ item = _pam_search_env(pamh->env, name, strlen(name));
+ if (item != -1) {
+
+ D(("env-item: %s, found!", name));
+ return (pamh->env->list[item] + 1 + strlen(name));
+
+ } else {
+
+ D(("env-item: %s, not found", name));
+ return NULL;
+
+ }
+}
+
+static char **_copy_env(pam_handle_t *pamh)
+{
+ char **dump;
+ int i = pamh->env->requested; /* reckon size of environment */
+ char *const *env = pamh->env->list;
+
+ D(("now get some memory for dump"));
+
+ /* allocate some memory for this (plus the null tail-pointer) */
+ dump = (char **) calloc(i, sizeof(char *));
+ D(("dump = %p", dump));
+ if (dump == NULL) {
+ return NULL;
+ }
+
+ /* now run through entries and copy the variables over */
+ dump[--i] = NULL;
+ while (i-- > 0) {
+ D(("env[%d]=`%s'", i,env[i]));
+ dump[i] = _pam_strdup(env[i]);
+ D(("->dump[%d]=`%s'", i,dump[i]));
+ if (dump[i] == NULL) {
+ /* out of memory */
+
+ while (dump[++i]) {
+ _pam_overwrite(dump[i]);
+ _pam_drop(dump[i]);
+ }
+ return NULL;
+ }
+ }
+
+ env = NULL; /* forget now */
+
+ /* return transcribed environment */
+ return dump;
+}
+
+char **pam_getenvlist(pam_handle_t *pamh)
+{
+ int i;
+
+ D(("called."));
+ IF_NO_PAMH("pam_getenvlist", pamh, NULL);
+
+ if (pamh->env == NULL || pamh->env->list == NULL) {
+ _pam_system_log(LOG_ERR, "pam_getenvlist: no env%s found",
+ pamh->env == NULL ? "":"-list" );
+ return NULL;
+ }
+
+ /* some quick checks */
+
+ if (pamh->env->requested > pamh->env->entries) {
+ _pam_system_log(LOG_ERR, "pam_getenvlist: environment corruption");
+ _pam_dump_env(pamh); /* only active when debugging */
+ return NULL;
+ }
+
+ for (i=pamh->env->requested-1; i-- > 0; ) {
+ if (pamh->env->list[i] == NULL) {
+ _pam_system_log(LOG_ERR, "pam_getenvlist: environment broken");
+ _pam_dump_env(pamh); /* only active when debugging */
+ return NULL; /* somehow we've broken the environment!? */
+ }
+ }
+
+ /* Seems fine; copy environment */
+
+ _pam_dump_env(pamh); /* only active when debugging */
+
+ return _copy_env(pamh);
+}
diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c
new file mode 100644
index 00000000..569335e6
--- /dev/null
+++ b/libpam/pam_handlers.c
@@ -0,0 +1,896 @@
+/* pam_handlers.c -- pam config file parsing and module loading */
+
+/*
+ * created by Marc Ewing.
+ * Currently maintained by Andrew G. Morgan <morgan@linux.kernel.org>
+ *
+ * $Id$
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef PAM_DYNAMIC
+# ifdef PAM_SHL
+# include <dl.h>
+# else /* PAM_SHL */
+# include <dlfcn.h>
+# endif /* PAM_SHL */
+#endif /* PAM_DYNAMIC */
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "pam_private.h"
+
+/* FreeBSD doesn't define this */
+#ifndef RTLD_NOW
+# define RTLD_NOW 1
+#endif
+
+/* If not required, define as nothing - FreeBSD needs it to be "_"... */
+#ifndef SHLIB_SYM_PREFIX
+# define SHLIB_SYM_PREFIX ""
+#endif
+
+#define BUF_SIZE 1024
+#define MODULE_CHUNK 4
+#define UNKNOWN_MODULE_PATH "<*unknown module path*>"
+
+static int _pam_assemble_line(FILE *f, char *buf, int buf_len);
+
+static void _pam_free_handlers_aux(struct handler **hp);
+
+static int _pam_add_handler(pam_handle_t *pamh
+ , int must_fail, int other, int type
+ , int *actions, const char *mod_path
+ , int argc, char **argv, int argvlen);
+
+/* Values for module type */
+
+#define PAM_T_AUTH 1
+#define PAM_T_SESS 2
+#define PAM_T_ACCT 4
+#define PAM_T_PASS 8
+
+static int _pam_parse_conf_file(pam_handle_t *pamh, FILE *f
+ , const char *known_service /* specific file */
+#ifdef PAM_READ_BOTH_CONFS
+ , int not_other
+#endif /* PAM_READ_BOTH_CONFS */
+ )
+{
+ char buf[BUF_SIZE];
+ int x; /* read a line from the FILE *f ? */
+ /*
+ * read a line from the configuration (FILE *) f
+ */
+ while ((x = _pam_assemble_line(f, buf, BUF_SIZE)) > 0) {
+ char *tok, *nexttok=NULL;
+ const char *this_service;
+ const char *mod_path;
+ int module_type, actions[_PAM_RETURN_VALUES];
+ int other; /* set if module is for PAM_DEFAULT_SERVICE */
+ int res; /* module added successfully? */
+ int must_fail=0; /* a badly formatted line must fail when used */
+ int argc;
+ char **argv;
+ int argvlen;
+
+ D(("_pam_init_handler: LINE: %s", buf));
+ if (known_service != NULL) {
+ nexttok = buf;
+ /* No service field: all lines are for the known service. */
+ this_service = known_service;
+ } else {
+ this_service = tok = _pam_StrTok(buf, " \n\t", &nexttok);
+ }
+
+#ifdef PAM_READ_BOTH_CONFS
+ if (not_other)
+ other = 0;
+ else
+#endif /* PAM_READ_BOTH_CONFS */
+ other = !_pam_strCMP(this_service, PAM_DEFAULT_SERVICE);
+
+ /* accept "service name" or PAM_DEFAULT_SERVICE modules */
+ if (!_pam_strCMP(this_service, pamh->service_name) || other) {
+ /* This is a service we are looking for */
+ D(("_pam_init_handlers: Found PAM config entry for: %s"
+ , this_service));
+
+ tok = _pam_StrTok(NULL, " \n\t", &nexttok);
+ if (!_pam_strCMP("auth", tok)) {
+ module_type = PAM_T_AUTH;
+ } else if (!_pam_strCMP("session", tok)) {
+ module_type = PAM_T_SESS;
+ } else if (!_pam_strCMP("account", tok)) {
+ module_type = PAM_T_ACCT;
+ } else if (!_pam_strCMP("password", tok)) {
+ module_type = PAM_T_PASS;
+ } else {
+ /* Illegal module type */
+ D(("_pam_init_handlers: bad module type: %s", tok));
+ _pam_system_log(LOG_ERR, "(%s) illegal module type: %s",
+ this_service, tok);
+ module_type = PAM_T_AUTH; /* most sensitive */
+ must_fail = 1; /* install as normal but fail when dispatched */
+ }
+ D(("Using %s config entry: %s", must_fail?"BAD ":"", tok));
+
+ /* reset the actions to .._UNDEF's -- this is so that
+ we can work out which entries are not yet set (for default). */
+ {
+ int i;
+ for (i=0; i<_PAM_RETURN_VALUES;
+ actions[i++] = _PAM_ACTION_UNDEF);
+ }
+ tok = _pam_StrTok(NULL, " \n\t", &nexttok);
+ if (!_pam_strCMP("required", tok)) {
+ D(("*PAM_F_REQUIRED*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_OK;
+ actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
+ actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
+ _pam_set_default_control(actions, _PAM_ACTION_BAD);
+ } else if (!_pam_strCMP("requisite", tok)) {
+ D(("*PAM_F_REQUISITE*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_OK;
+ actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
+ actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
+ _pam_set_default_control(actions, _PAM_ACTION_DIE);
+ } else if (!_pam_strCMP("optional", tok)) {
+ D(("*PAM_F_OPTIONAL*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_OK;
+ actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
+ _pam_set_default_control(actions, _PAM_ACTION_IGNORE);
+ } else if (!_pam_strCMP("sufficient", tok)) {
+ D(("*PAM_F_SUFFICIENT*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_DONE;
+ actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_DONE;
+ _pam_set_default_control(actions, _PAM_ACTION_IGNORE);
+ } else {
+ D(("will need to parse %s", tok));
+ _pam_parse_control(actions, tok);
+ /* by default the default is to treat as failure */
+ _pam_set_default_control(actions, _PAM_ACTION_BAD);
+ }
+
+ tok = _pam_StrTok(NULL, " \n\t", &nexttok);
+ if (tok != NULL) {
+ mod_path = tok;
+ D(("mod_path = %s",mod_path));
+ } else {
+ /* no module name given */
+ D(("_pam_init_handlers: no module name supplied"));
+ _pam_system_log(LOG_ERR,
+ "(%s) no module name supplied", this_service);
+ mod_path = NULL;
+ must_fail = 1;
+ }
+
+ /* nexttok points to remaining arguments... */
+
+ if (nexttok != NULL) {
+ D(("list: %s",nexttok));
+ argvlen = _pam_mkargv(nexttok, &argv, &argc);
+ D(("argvlen = %d",argvlen));
+ } else { /* there are no arguments so fix by hand */
+ D(("_pam_init_handlers: empty argument list"));
+ argvlen = argc = 0;
+ argv = NULL;
+ }
+
+#ifdef DEBUG
+ {
+ int y;
+
+ D(("CONF%s: %s%s %d %s %d"
+ , must_fail?"<*will fail*>":""
+ , this_service, other ? "(backup)":""
+ , module_type
+ , mod_path, argc));
+ for (y = 0; y < argc; y++) {
+ D(("CONF: %s", argv[y]));
+ }
+ for (y = 0; y<_PAM_RETURN_VALUES; ++y) {
+ D(("RETURN %s(%d) -> %d %s",
+ _pam_token_returns[y], y, actions[y],
+ actions[y]>0 ? "jump":
+ _pam_token_actions[-actions[y]]));
+ }
+ }
+#endif
+
+ res = _pam_add_handler(pamh, must_fail, other
+ , module_type, actions, mod_path
+ , argc, argv, argvlen);
+ if (res != PAM_SUCCESS) {
+ _pam_system_log(LOG_ERR, "error loading %s", mod_path);
+ D(("failed to load module - aborting"));
+ return PAM_ABORT;
+ }
+ }
+ }
+
+ return ( (x < 0) ? PAM_ABORT:PAM_SUCCESS );
+}
+
+/* Parse config file, allocate handler structures, dlopen() */
+int _pam_init_handlers(pam_handle_t *pamh)
+{
+ FILE *f;
+ int retval;
+
+ D(("_pam_init_handlers called"));
+ IF_NO_PAMH("_pam_init_handlers",pamh,PAM_SYSTEM_ERR);
+
+ /* Return immediately if everything is already loaded */
+ if (pamh->handlers.handlers_loaded) {
+ return PAM_SUCCESS;
+ }
+
+ D(("_pam_init_handlers: initializing"));
+
+ /* First clean the service structure */
+
+ _pam_free_handlers(pamh);
+ if (! pamh->handlers.module) {
+ if ((pamh->handlers.module =
+ malloc(MODULE_CHUNK * sizeof(struct loaded_module))) == NULL) {
+ _pam_system_log(LOG_CRIT,
+ "_pam_init_handlers: no memory loading module");
+ return PAM_BUF_ERR;
+ }
+ pamh->handlers.modules_allocated = MODULE_CHUNK;
+ pamh->handlers.modules_used = 0;
+ }
+
+ if (pamh->service_name == NULL) {
+ return PAM_BAD_ITEM; /* XXX - better error? */
+ }
+
+#ifdef PAM_LOCKING
+ /* Is the PAM subsystem locked? */
+ {
+ int fd_tmp;
+
+ if ((fd_tmp = open( PAM_LOCK_FILE, O_RDONLY )) != -1) {
+ _pam_system_log(LOG_ERR, "_pam_init_handlers: PAM lockfile ("
+ PAM_LOCK_FILE ") exists - aborting");
+ (void) close(fd_tmp);
+ /*
+ * to avoid swamping the system with requests
+ */
+ _pam_start_timer(pamh);
+ pam_fail_delay(pamh, 5000000);
+ _pam_await_timer(pamh, PAM_ABORT);
+
+ return PAM_ABORT;
+ }
+ }
+#endif /* PAM_LOCKING */
+
+ /*
+ * Now parse the config file(s) and add handlers
+ */
+ {
+ struct stat test_d;
+
+ /* Is there a PAM_CONFIG_D directory? */
+ if ( stat(PAM_CONFIG_D, &test_d) == 0 && S_ISDIR(test_d.st_mode) ) {
+ char *filename;
+ int read_something=0;
+
+ D(("searching " PAM_CONFIG_D " for config files"));
+ filename = malloc(sizeof(PAM_CONFIG_DF)
+ +strlen(pamh->service_name));
+ if (filename == NULL) {
+ _pam_system_log(LOG_ERR,
+ "_pam_init_handlers: no memory; service %s",
+ pamh->service_name);
+ return PAM_BUF_ERR;
+ }
+ sprintf(filename, PAM_CONFIG_DF, pamh->service_name);
+ D(("opening %s", filename));
+ f = fopen(filename, "r");
+ if (f != NULL) {
+ /* would test magic here? */
+ retval = _pam_parse_conf_file(pamh, f, pamh->service_name
+#ifdef PAM_READ_BOTH_CONFS
+ , 0
+#endif /* PAM_READ_BOTH_CONFS */
+ );
+ fclose(f);
+ if (retval != PAM_SUCCESS) {
+ _pam_system_log(LOG_ERR,
+ "_pam_init_handlers: error reading %s",
+ filename);
+ _pam_system_log(LOG_ERR, "_pam_init_handlers: [%s]",
+ pam_strerror(pamh, retval));
+ } else {
+ read_something = 1;
+ }
+ } else {
+ D(("unable to open %s", filename));
+#ifdef PAM_READ_BOTH_CONFS
+ D(("checking %s", PAM_CONFIG));
+
+ if ((f = fopen(PAM_CONFIG,"r")) != NULL) {
+ retval = _pam_parse_conf_file(pamh, f, NULL, 1);
+ fclose(f);
+ } else
+#endif /* PAM_READ_BOTH_CONFS */
+ retval = PAM_SUCCESS;
+ /*
+ * XXX - should we log an error? Some people want to always
+ * use "other"
+ */
+ }
+ _pam_drop(filename);
+
+ if (retval == PAM_SUCCESS) {
+ /* now parse the PAM_DEFAULT_SERVICE_FILE */
+
+ D(("opening %s", PAM_DEFAULT_SERVICE_FILE));
+ f = fopen(PAM_DEFAULT_SERVICE_FILE, "r");
+ if (f != NULL) {
+ /* would test magic here? */
+ retval = _pam_parse_conf_file(pamh, f
+ , PAM_DEFAULT_SERVICE
+#ifdef PAM_READ_BOTH_CONFS
+ , 0
+#endif /* PAM_READ_BOTH_CONFS */
+ );
+ fclose(f);
+ if (retval != PAM_SUCCESS) {
+ _pam_system_log(LOG_ERR,
+ "_pam_init_handlers: error reading %s",
+ PAM_DEFAULT_SERVICE_FILE);
+ _pam_system_log(LOG_ERR,
+ "_pam_init_handlers: [%s]",
+ pam_strerror(pamh, retval));
+ } else {
+ read_something = 1;
+ }
+ } else {
+ D(("unable to open %s", PAM_DEFAULT_SERVICE_FILE));
+ _pam_system_log(LOG_ERR,
+ "_pam_init_handlers: no default config %s",
+ PAM_DEFAULT_SERVICE_FILE);
+ }
+ if (!read_something) { /* nothing read successfully */
+ retval = PAM_ABORT;
+ }
+ }
+ } else {
+ if ((f = fopen(PAM_CONFIG, "r")) == NULL) {
+ _pam_system_log(LOG_ERR, "_pam_init_handlers: could not open "
+ PAM_CONFIG );
+ return PAM_ABORT;
+ }
+
+ retval = _pam_parse_conf_file(pamh, f, NULL
+#ifdef PAM_READ_BOTH_CONFS
+ , 0
+#endif /* PAM_READ_BOTH_CONFS */
+ );
+
+ D(("closing configuration file"));
+ fclose(f);
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ /* Read error */
+ _pam_system_log(LOG_ERR, "error reading PAM configuration file");
+ return PAM_ABORT;
+ }
+
+ pamh->handlers.handlers_loaded = 1;
+
+ D(("_pam_init_handlers exiting"));
+ return PAM_SUCCESS;
+}
+
+/*
+ * This is where we read a line of the PAM config file. The line may be
+ * preceeded by lines of comments and also extended with "\\\n"
+ */
+
+int _pam_assemble_line(FILE *f, char *buffer, int buf_len)
+{
+ char *p = buffer;
+ char *s, *os;
+ int used = 0;
+
+ /* loop broken with a 'break' when a non-'\\n' ended line is read */
+
+ D(("called."));
+ for (;;) {
+ if (used >= buf_len) {
+ /* Overflow */
+ D(("_pam_assemble_line: overflow"));
+ return -1;
+ }
+ if (fgets(p, buf_len - used, f) == NULL) {
+ if (used) {
+ /* Incomplete read */
+ return -1;
+ } else {
+ /* EOF */
+ return 0;
+ }
+ }
+
+ /* skip leading spaces --- line may be blank */
+
+ s = p + strspn(p, " \n\t");
+ if (*s && (*s != '#')) {
+ os = s;
+
+ /*
+ * we are only interested in characters before the first '#'
+ * character
+ */
+
+ while (*s && *s != '#')
+ ++s;
+ if (*s == '#') {
+ *s = '\0';
+ used += strlen(os);
+ break; /* the line has been read */
+ }
+
+ s = os;
+
+ /*
+ * Check for backslash by scanning back from the end of
+ * the entered line, the '\n' has been included since
+ * normally a line is terminated with this
+ * character. fgets() should only return one though!
+ */
+
+ s += strlen(s);
+ while (s > os && ((*--s == ' ') || (*s == '\t')
+ || (*s == '\n')));
+
+ /* check if it ends with a backslash */
+ if (*s == '\\') {
+ *s++ = ' '; /* replace backslash with ' ' */
+ *s = '\0'; /* truncate the line here */
+ used += strlen(os);
+ p = s; /* there is more ... */
+ } else {
+ /* End of the line! */
+ used += strlen(os);
+ break; /* this is the complete line */
+ }
+
+ } else {
+ /* Nothing in this line */
+ /* Don't move p */
+ }
+ }
+
+ return used;
+}
+
+typedef int (*servicefn)(pam_handle_t *, int, int, char **);
+
+int _pam_add_handler(pam_handle_t *pamh
+ , int must_fail, int other, int type
+ , int *actions, const char *mod_path
+ , int argc, char **argv, int argvlen)
+{
+ struct loaded_module *mod;
+ int x = 0;
+ struct handler **handler_p;
+ struct handler **handler_p2;
+ struct handlers *the_handlers;
+ const char *sym, *sym2;
+#ifdef PAM_SHL
+ const char *_sym, *_sym2;
+#endif
+ char *mod_full_path=NULL;
+ servicefn func, func2;
+ int success;
+
+ D(("called."));
+ IF_NO_PAMH("_pam_add_handler",pamh,PAM_SYSTEM_ERR);
+
+ /* if NULL set to something that can be searched for */
+ switch (mod_path != NULL) {
+ default:
+ if (mod_path[0] == '/') {
+ break;
+ }
+ mod_full_path = malloc(sizeof(DEFAULT_MODULE_PATH)+strlen(mod_path));
+ if (mod_full_path) {
+ sprintf(mod_full_path, DEFAULT_MODULE_PATH "%s", mod_path);
+ mod_path = mod_full_path;
+ break;
+ }
+ _pam_system_log(LOG_CRIT, "cannot malloc full mod path");
+ case 0:
+ mod_path = UNKNOWN_MODULE_PATH;
+ }
+
+ D(("_pam_add_handler: adding type %d, module `%s'",type,mod_path));
+ mod = pamh->handlers.module;
+
+ /* First, ensure the module is loaded */
+ while (x < pamh->handlers.modules_used) {
+ if (!strcmp(mod[x].name, mod_path)) { /* case sensitive ! */
+ break;
+ }
+ x++;
+ }
+ if (x == pamh->handlers.modules_used) {
+ /* Not found */
+ if (pamh->handlers.modules_allocated == pamh->handlers.modules_used) {
+ /* will need more memory */
+ void *tmp = realloc(pamh->handlers.module,
+ (pamh->handlers.modules_allocated+MODULE_CHUNK)
+ *sizeof(struct loaded_module));
+ if (tmp == NULL) {
+ D(("cannot enlarge module pointer memory"));
+ _pam_system_log(LOG_ERR,
+ "realloc returned NULL in _pam_add_handler");
+ _pam_drop(mod_full_path);
+ return PAM_ABORT;
+ }
+ pamh->handlers.module = tmp;
+ pamh->handlers.modules_allocated += MODULE_CHUNK;
+ }
+ mod = &(pamh->handlers.module[x]);
+ /* Be pessimistic... */
+ success = PAM_ABORT;
+
+#ifdef PAM_DYNAMIC
+ D(("_pam_add_handler: dlopen(%s) -> %lx", mod_path, &mod->dl_handle));
+ mod->dl_handle =
+# ifdef PAM_SHL
+ shl_load(mod_path, BIND_IMMEDIATE, 0L);
+# else /* PAM_SHL */
+ dlopen(mod_path, RTLD_NOW);
+# endif /* PAM_SHL */
+ D(("_pam_add_handler: dlopen'ed"));
+ if (mod->dl_handle == NULL) {
+ D(("_pam_add_handler: dlopen(%s) failed", mod_path));
+ _pam_system_log(LOG_ERR, "unable to dlopen(%s)", mod_path);
+# ifndef PAM_SHL
+ _pam_system_log(LOG_ERR, "[dlerror: %s]", dlerror());
+# endif /* PAM_SHL */
+ /* Don't abort yet; static code may be able to find function.
+ * But defaults to abort if nothing found below... */
+ } else {
+ D(("module added successfully"));
+ success = PAM_SUCCESS;
+ mod->type = PAM_MT_DYNAMIC_MOD;
+ pamh->handlers.modules_used++;
+ }
+#endif
+#ifdef PAM_STATIC
+ /* Only load static function if function was not found dynamically.
+ * This code should work even if no dynamic loading is available. */
+ if (success != PAM_SUCCESS) {
+ D(("_pam_add_handler: open static handler %s", mod_path));
+ mod->dl_handle = _pam_open_static_handler(mod_path);
+ if (mod->dl_handle == NULL) {
+ D(("_pam_add_handler: unable to find static handler %s",
+ mod_path));
+ _pam_system_log(LOG_ERR,
+ "unable to open static handler %s", mod_path);
+ /* Didn't find module in dynamic or static..will mark bad */
+ } else {
+ D(("static module added successfully"));
+ success = PAM_SUCCESS;
+ mod->type = PAM_MT_STATIC_MOD;
+ pamh->handlers.modules_used++;
+ }
+ }
+#endif
+
+ if (success != PAM_SUCCESS) { /* add a malformed module */
+ mod->dl_handle = NULL;
+ mod->type = PAM_MT_FAULTY_MOD;
+ pamh->handlers.modules_used++;
+ _pam_system_log(LOG_ERR, "adding faulty module: %s", mod_path);
+ success = PAM_SUCCESS; /* We have successfully added a module */
+ }
+
+ /* indicate its name - later we will search for it by this */
+ if ((mod->name = _pam_strdup(mod_path)) == NULL) {
+ D(("_pam_handler: couldn't get memory for mod_path"));
+ _pam_system_log(LOG_ERR, "no memory for module path", mod_path);
+ success = PAM_ABORT;
+ }
+
+ } else { /* x != pamh->handlers.modules_used */
+ mod += x; /* the located module */
+ success = PAM_SUCCESS;
+ }
+
+ _pam_drop(mod_full_path);
+ mod_path = NULL; /* no longer needed or trusted */
+
+ /* Now return error if necessary after trying all possible ways... */
+ if (success != PAM_SUCCESS)
+ return(success);
+
+ /*
+ * At this point 'mod' points to the stored/loaded module. If its
+ * dl_handle is unknown, then we must be able to indicate dispatch
+ * failure with 'must_fail'
+ */
+
+ /* Now define the handler(s) based on mod->dlhandle and type */
+
+ /* decide which list of handlers to use */
+ the_handlers = (other) ? &pamh->handlers.other : &pamh->handlers.conf;
+
+ handler_p = handler_p2 = NULL;
+ func = func2 = NULL;
+#ifdef PAM_SHL
+ _sym2 =
+#endif /* PAM_SHL */
+ sym2 = NULL;
+
+ /* point handler_p's at the root addresses of the function stacks */
+ switch (type) {
+ case PAM_T_AUTH:
+ handler_p = &the_handlers->authenticate;
+ sym = SHLIB_SYM_PREFIX "pam_sm_authenticate";
+ handler_p2 = &the_handlers->setcred;
+ sym2 = SHLIB_SYM_PREFIX "pam_sm_setcred";
+#ifdef PAM_SHL
+ _sym = "_pam_sm_authenticate";
+ _sym2 = "_pam_sm_setcred";
+#endif
+ break;
+ case PAM_T_SESS:
+ handler_p = &the_handlers->open_session;
+ sym = SHLIB_SYM_PREFIX "pam_sm_open_session";
+ handler_p2 = &the_handlers->close_session;
+ sym2 = SHLIB_SYM_PREFIX "pam_sm_close_session";
+#ifdef PAM_SHL
+ _sym = "_pam_sm_open_session";
+ _sym2 = "_pam_sm_close_session";
+#endif
+ break;
+ case PAM_T_ACCT:
+ handler_p = &the_handlers->acct_mgmt;
+ sym = SHLIB_SYM_PREFIX "pam_sm_acct_mgmt";
+#ifdef PAM_SHL
+ _sym = "_pam_sm_acct_mgmt";
+#endif
+ break;
+ case PAM_T_PASS:
+ handler_p = &the_handlers->chauthtok;
+ sym = SHLIB_SYM_PREFIX "pam_sm_chauthtok";
+#ifdef PAM_SHL
+ _sym = "_pam_sm_chauthtok";
+#endif
+ break;
+ default:
+ /* Illegal module type */
+ D(("_pam_add_handler: illegal module type %d", type));
+ return PAM_ABORT;
+ }
+
+ /* are the modules reliable? */
+ if (
+#ifdef PAM_DYNAMIC
+ mod->type != PAM_MT_DYNAMIC_MOD
+ &&
+#endif /* PAM_DYNAMIC */
+#ifdef PAM_STATIC
+ mod->type != PAM_MT_STATIC_MOD
+ &&
+#endif /* PAM_STATIC */
+ mod->type != PAM_MT_FAULTY_MOD
+ ) {
+ D(("_pam_add_handlers: illegal module library type; %d", mod->type));
+ _pam_system_log(LOG_ERR,
+ "internal error: module library type not known: %s;%d",
+ sym, mod->type);
+ return PAM_ABORT;
+ }
+
+ /* now identify this module's functions - for non-faulty modules */
+
+#ifdef PAM_DYNAMIC
+ if ((mod->type == PAM_MT_DYNAMIC_MOD) &&
+# ifdef PAM_SHL
+ (shl_findsym(&mod->dl_handle, sym, (short) TYPE_PROCEDURE, &func) &&
+ shl_findsym(&mod->dl_handle, _sym, (short) TYPE_PROCEDURE, &func))
+# else /* PAM_SHL */
+ (func = (servicefn) dlsym(mod->dl_handle, sym)) == NULL
+# endif /* PAM_SHL */
+ ) {
+ _pam_system_log(LOG_ERR, "unable to resolve symbol: %s", sym);
+ }
+#endif
+#ifdef PAM_STATIC
+ if ((mod->type == PAM_MT_STATIC_MOD) &&
+ (func = (servicefn)_pam_get_static_sym(mod->dl_handle, sym)) == NULL) {
+ _pam_system_log(LOG_ERR, "unable to resolve static symbol: %s", sym);
+ }
+#endif
+ if (sym2) {
+#ifdef PAM_DYNAMIC
+ if ((mod->type == PAM_MT_DYNAMIC_MOD) &&
+# ifdef PAM_SHL
+ (shl_findsym(&mod->dl_handle,sym2,(short)TYPE_PROCEDURE, &func2)&&
+ shl_findsym(&mod->dl_handle,_sym2,(short)TYPE_PROCEDURE, &func2))
+# else /* PAM_SHL */
+ (func2 = (servicefn) dlsym(mod->dl_handle, sym2)) == NULL
+# endif /* PAM_SHL */
+ ) {
+ _pam_system_log(LOG_ERR, "unable to resolve symbol: %s", sym2);
+ }
+#endif
+#ifdef PAM_STATIC
+ if ((mod->type == PAM_MT_STATIC_MOD) &&
+ (func2 = (servicefn)_pam_get_static_sym(mod->dl_handle, sym2))
+ == NULL) {
+ _pam_system_log(LOG_ERR, "unable to resolve symbol: %s", sym2);
+ }
+#endif
+ }
+
+ /* here func (and perhaps func2) point to the appropriate functions */
+
+ /* add new handler to end of existing list */
+ while (*handler_p != NULL) {
+ handler_p = &((*handler_p)->next);
+ }
+
+ if ((*handler_p = malloc(sizeof(struct handler))) == NULL) {
+ _pam_system_log(LOG_CRIT, "cannot malloc struct handler #1");
+ return (PAM_ABORT);
+ }
+
+ (*handler_p)->must_fail = must_fail; /* failure forced? */
+ (*handler_p)->func = func;
+ memcpy((*handler_p)->actions,actions,sizeof((*handler_p)->actions));
+ (*handler_p)->argc = argc;
+ (*handler_p)->argv = argv; /* not a copy */
+ (*handler_p)->next = NULL;
+
+ /* some of the modules have a second calling function */
+ if (handler_p2) {
+ /* add new handler to end of existing list */
+ while (*handler_p2) {
+ handler_p2 = &((*handler_p2)->next);
+ }
+
+ if ((*handler_p2 = malloc(sizeof(struct handler))) == NULL) {
+ _pam_system_log(LOG_CRIT, "cannot malloc struct handler #2");
+ return (PAM_ABORT);
+ }
+
+ (*handler_p2)->must_fail = must_fail; /* failure forced? */
+ (*handler_p2)->func = func2;
+ memcpy((*handler_p2)->actions,actions,sizeof((*handler_p2)->actions));
+ (*handler_p2)->argc = argc;
+ if (argv) {
+ if (((*handler_p2)->argv = malloc(argvlen)) == NULL) {
+ _pam_system_log(LOG_CRIT, "cannot malloc argv for handler #2");
+ return (PAM_ABORT);
+ }
+ memcpy((*handler_p2)->argv, argv, argvlen);
+ } else {
+ (*handler_p2)->argv = NULL; /* no arguments */
+ }
+ (*handler_p2)->next = NULL;
+ }
+
+ D(("_pam_add_handler: returning successfully"));
+
+ return PAM_SUCCESS;
+}
+
+/* Free various allocated structures and dlclose() the libs */
+int _pam_free_handlers(pam_handle_t *pamh)
+{
+ struct loaded_module *mod;
+
+ D(("called."));
+ IF_NO_PAMH("_pam_free_handlers",pamh,PAM_SYSTEM_ERR);
+
+ mod = pamh->handlers.module;
+
+ /* Close all loaded modules */
+
+ while (pamh->handlers.modules_used) {
+ D(("_pam_free_handlers: dlclose(%s)", mod->name));
+ free(mod->name);
+#ifdef PAM_DYNAMIC
+ if (mod->type == PAM_MT_DYNAMIC_MOD) {
+# ifdef PAM_SHL
+ shl_unload(mod->dl_handle);
+# else
+ dlclose(mod->dl_handle);
+# endif
+ }
+#endif
+ mod++;
+ pamh->handlers.modules_used--;
+ }
+
+ /* Free all the handlers */
+
+ _pam_free_handlers_aux(&(pamh->handlers.conf.authenticate));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.setcred));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.acct_mgmt));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.open_session));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.close_session));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.chauthtok));
+
+ _pam_free_handlers_aux(&(pamh->handlers.other.authenticate));
+ _pam_free_handlers_aux(&(pamh->handlers.other.setcred));
+ _pam_free_handlers_aux(&(pamh->handlers.other.acct_mgmt));
+ _pam_free_handlers_aux(&(pamh->handlers.other.open_session));
+ _pam_free_handlers_aux(&(pamh->handlers.other.close_session));
+ _pam_free_handlers_aux(&(pamh->handlers.other.chauthtok));
+
+ /* no more loaded modules */
+
+ _pam_drop(pamh->handlers.module);
+
+ /* Indicate that handlers are not initialized for this pamh */
+
+ pamh->handlers.handlers_loaded = 0;
+
+ return PAM_SUCCESS;
+}
+
+void _pam_start_handlers(pam_handle_t *pamh)
+{
+ D(("called."));
+ /* NB. There is no check for a NULL pamh here, since no return
+ * value to communicate the fact! */
+
+ /* Indicate that handlers are not initialized for this pamh */
+ pamh->handlers.handlers_loaded = 0;
+
+ pamh->handlers.modules_allocated = 0;
+ pamh->handlers.modules_used = 0;
+ pamh->handlers.module = NULL;
+
+ /* initialize the .conf and .other entries */
+
+ pamh->handlers.conf.authenticate = NULL;
+ pamh->handlers.conf.setcred = NULL;
+ pamh->handlers.conf.acct_mgmt = NULL;
+ pamh->handlers.conf.open_session = NULL;
+ pamh->handlers.conf.close_session = NULL;
+ pamh->handlers.conf.chauthtok = NULL;
+
+ pamh->handlers.other.authenticate = NULL;
+ pamh->handlers.other.setcred = NULL;
+ pamh->handlers.other.acct_mgmt = NULL;
+ pamh->handlers.other.open_session = NULL;
+ pamh->handlers.other.close_session = NULL;
+ pamh->handlers.other.chauthtok = NULL;
+}
+
+void _pam_free_handlers_aux(struct handler **hp)
+{
+ struct handler *h = *hp;
+ struct handler *last;
+
+ D(("called."));
+ while (h) {
+ last = h;
+ _pam_drop(h->argv); /* This is all alocated in a single chunk */
+ h = h->next;
+ memset(last, 0, sizeof(*last));
+ free(last);
+ }
+
+ *hp = NULL;
+}
diff --git a/libpam/pam_item.c b/libpam/pam_item.c
new file mode 100644
index 00000000..f595a0b4
--- /dev/null
+++ b/libpam/pam_item.c
@@ -0,0 +1,304 @@
+/* pam_item.c */
+
+/*
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:17 agmorgan
+ * Initial revision
+ *
+ * Revision 1.3 1999/11/08 05:41:05 morgan
+ * pam_log - extra paranoia
+ * otherwise debugging changes
+ *
+ * Revision 1.2 1998/12/27 04:34:22 morgan
+ * reverting logging functions within libpam. Gone are the externally
+ * advertised API replaced by a more simple (libpam internal) one.
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "pam_private.h"
+
+#define RESET(X, Y) \
+{ \
+ char *_TMP_ = (X); \
+ if (_TMP_ != (Y)) { \
+ (X) = (Y) ? _pam_strdup(Y) : NULL; \
+ if (_TMP_) \
+ free(_TMP_); \
+ } \
+}
+
+/* functions */
+
+int pam_set_item (
+ pam_handle_t *pamh,
+ int item_type,
+ const void *item)
+{
+ int retval;
+
+ D(("called"));
+
+ IF_NO_PAMH("pam_set_item", pamh, PAM_SYSTEM_ERR);
+
+ retval = PAM_SUCCESS;
+
+ switch (item_type) {
+ case PAM_SERVICE:
+ /* Setting handlers_loaded to 0 will cause the handlers
+ * to be reloaded on the next call to a service module.
+ */
+ pamh->handlers.handlers_loaded = 0;
+ RESET(pamh->service_name, item);
+ {
+ char *tmp;
+ for (tmp=pamh->service_name; *tmp; ++tmp)
+ *tmp = tolower(*tmp); /* require lower case */
+ }
+ break;
+ case PAM_USER:
+ RESET(pamh->user, item);
+ break;
+ case PAM_USER_PROMPT:
+ RESET(pamh->prompt, item);
+ break;
+ case PAM_TTY:
+ D(("setting tty to %s", item));
+ RESET(pamh->tty, item);
+ break;
+ case PAM_RUSER:
+ RESET(pamh->ruser, item);
+ break;
+ case PAM_RHOST:
+ RESET(pamh->rhost, item);
+ break;
+ case PAM_AUTHTOK:
+ /*
+ * The man page says this is only supposed to be available to
+ * the module providers. In order to use this item the app
+ * has to #include <security/pam_modules.h>. This is something
+ * it is *not* supposed to do with "Linux-"PAM! - AGM.
+ */
+ {
+ char *_TMP_ = pamh->authtok;
+ if (_TMP_ == item) /* not changed so leave alone */
+ break;
+ pamh->authtok = (item) ? _pam_strdup(item) : NULL;
+ if (_TMP_) {
+ _pam_overwrite(_TMP_);
+ free(_TMP_);
+ }
+ break;
+ }
+ case PAM_OLDAUTHTOK:
+ /* See note above. */
+ {
+ char *_TMP_ = pamh->oldauthtok;
+ if (_TMP_ == item) /* not changed so leave alone */
+ break;
+ pamh->oldauthtok = (item) ? _pam_strdup(item) : NULL;
+ if (_TMP_) {
+ _pam_overwrite(_TMP_);
+ free(_TMP_);
+ }
+ break;
+ }
+ case PAM_CONV: /* want to change the conversation function */
+ if (item == NULL) {
+ _pam_system_log(LOG_ERR,
+ "pam_set_item: attempt to set conv() to NULL");
+ retval = PAM_PERM_DENIED;
+ } else {
+ struct pam_conv *tconv;
+
+ if ((tconv=
+ (struct pam_conv *) malloc(sizeof(struct pam_conv))
+ ) == NULL) {
+ _pam_system_log(LOG_CRIT,
+ "pam_set_item: malloc failed for pam_conv");
+ retval = PAM_BUF_ERR;
+ } else {
+ memcpy(tconv, item, sizeof(struct pam_conv));
+ _pam_drop(pamh->pam_conversation);
+ pamh->pam_conversation = tconv;
+ }
+ }
+ break;
+ case PAM_FAIL_DELAY:
+ pamh->fail_delay.delay_fn_ptr = item;
+ break;
+ default:
+ retval = PAM_BAD_ITEM;
+ }
+
+ return (retval);
+}
+
+int pam_get_item (
+ const pam_handle_t *pamh,
+ int item_type,
+ const void **item)
+{
+ D(("called."));
+ IF_NO_PAMH("pam_get_item",pamh,PAM_SYSTEM_ERR);
+
+ if (item == NULL) {
+ _pam_system_log(LOG_ERR,
+ "pam_get_item: nowhere to place requested item");
+ return PAM_PERM_DENIED;
+ }
+
+ switch (item_type) {
+ case PAM_SERVICE:
+ *item = pamh->service_name;
+ break;
+ case PAM_USER:
+ D(("returning user=%s", pamh->user));
+ *item = pamh->user;
+ break;
+ case PAM_USER_PROMPT:
+ D(("returning userprompt=%s", pamh->user));
+ *item = pamh->prompt;
+ break;
+ case PAM_TTY:
+ D(("returning tty=%s", pamh->tty));
+ *item = pamh->tty;
+ break;
+ case PAM_RUSER:
+ *item = pamh->ruser;
+ break;
+ case PAM_RHOST:
+ *item = pamh->rhost;
+ break;
+ case PAM_AUTHTOK:
+ *item = pamh->authtok;
+ break;
+ case PAM_OLDAUTHTOK:
+ *item = pamh->oldauthtok;
+ break;
+ case PAM_CONV:
+ *item = pamh->pam_conversation;
+ break;
+ case PAM_FAIL_DELAY:
+ *item = pamh->fail_delay.delay_fn_ptr;
+ break;
+ default:
+ /* XXX - I made this up */
+ return PAM_BAD_ITEM;
+ }
+
+ return PAM_SUCCESS;
+}
+
+/* added by AGM 1996/3/2 */
+
+int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt)
+{
+ const char *use_prompt;
+ int retval;
+ struct pam_message msg,*pmsg;
+ struct pam_response *resp;
+
+ D(("called."));
+ IF_NO_PAMH("pam_get_user", pamh, PAM_SYSTEM_ERR);
+
+ if (pamh->pam_conversation == NULL) {
+ _pam_system_log(LOG_ERR, "pam_get_user: no conv element in pamh");
+ return PAM_SERVICE_ERR;
+ }
+
+ if (user == NULL) { /* ensure the the module has suplied a destination */
+ _pam_system_log(LOG_ERR, "pam_get_user: nowhere to record username");
+ return PAM_PERM_DENIED;
+ } else
+ *user = NULL;
+
+ if (pamh->user) { /* have one so return it */
+ *user = pamh->user;
+ return PAM_SUCCESS;
+ }
+
+ /* will need a prompt */
+ use_prompt = prompt;
+ if (use_prompt == NULL) {
+ use_prompt = pamh->prompt;
+ if (use_prompt == NULL) {
+ use_prompt = PAM_DEFAULT_PROMPT;
+ }
+ }
+
+ /* If we are resuming an old conversation, we verify that the prompt
+ is the same. Anything else is an error. */
+ if (pamh->former.want_user) {
+ /* must have a prompt to resume with */
+ if (! pamh->former.prompt) {
+ _pam_system_log(LOG_ERR,
+ "pam_get_user: failed to resume with prompt"
+ );
+ return PAM_ABORT;
+ }
+
+ /* must be the same prompt as last time */
+ if (strcmp(pamh->former.prompt, use_prompt)) {
+ _pam_system_log(LOG_ERR,
+ "pam_get_user: resumed with different prompt");
+ return PAM_ABORT;
+ }
+
+ /* ok, we can resume where we left off last time */
+ pamh->former.want_user = PAM_FALSE;
+ _pam_overwrite(pamh->former.prompt);
+ _pam_drop(pamh->former.prompt);
+ }
+
+ /* converse with application -- prompt user for a username */
+ pmsg = &msg;
+ msg.msg_style = PAM_PROMPT_ECHO_ON;
+ msg.msg = use_prompt;
+ resp = NULL;
+
+ retval = pamh->pam_conversation->
+ conv(1, (const struct pam_message **) &pmsg, &resp,
+ pamh->pam_conversation->appdata_ptr);
+
+ if (retval == PAM_CONV_AGAIN) {
+ /* conversation function is waiting for an event - save state */
+ D(("conversation function is not ready yet"));
+ pamh->former.want_user = PAM_TRUE;
+ pamh->former.prompt = _pam_strdup(use_prompt);
+ } else if (resp == NULL) {
+ /*
+ * conversation should have given a response
+ */
+ D(("pam_get_user: no response provided"));
+ retval = PAM_CONV_ERR;
+ } else if (retval == PAM_SUCCESS) { /* copy the username */
+ /*
+ * now we set the PAM_USER item -- this was missing from pre.53
+ * releases. However, reading the Sun manual, it is part of
+ * the standard API.
+ */
+ RESET(pamh->user, resp->resp);
+ *user = pamh->user;
+ }
+
+ if (resp) {
+ /*
+ * note 'resp' is allocated by the application and is
+ * correctly free()'d here
+ */
+ _pam_drop_reply(resp, 1);
+ }
+
+ D(("completed"));
+ return retval; /* pass on any error from conversation */
+}
diff --git a/libpam/pam_log.c b/libpam/pam_log.c
new file mode 100644
index 00000000..d990c8c0
--- /dev/null
+++ b/libpam/pam_log.c
@@ -0,0 +1,382 @@
+/*
+ * pam_log.c -- PAM system logging
+ *
+ * $Id$
+ *
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#else
+# define _BSD_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "pam_private.h"
+
+#ifdef __hpux
+# include <stdio.h>
+# include <syslog.h>
+# ifdef __STDC__
+# ifndef __P
+# define __P(p) p
+# endif /* __P */
+# include <stdarg.h>
+# define VA_LOCAL_DECL va_list ap;
+# define VA_START(f) va_start(ap, f)
+# define VA_END va_end(ap)
+# else /* __STDC__ */
+# ifndef __P
+# define __P(p) ()
+# endif /* __P */
+# include <varargs.h>
+# define VA_LOCAL_DECL va_list ap;
+# define VA_START(f) va_start(ap)
+# define VA_END va_end(ap)
+# endif /* __STDC__ */
+/**************************************************************
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ **************************************************************/
+
+static void dopr();
+static char *end;
+# ifndef _SCO_DS
+/* VARARGS3 */
+int
+# ifdef __STDC__
+snprintf(char *str, size_t count, const char *fmt, ...)
+# else /* __STDC__ */
+snprintf(str, count, fmt, va_alist)
+ char *str;
+ size_t count;
+ const char *fmt;
+ va_dcl
+# endif /* __STDC__ */
+{
+ int len;
+ VA_LOCAL_DECL
+
+ VA_START(fmt);
+ len = vsnprintf(str, count, fmt, ap);
+ VA_END;
+ return len;
+}
+# endif /* _SCO_DS */
+
+int
+# ifdef __STDC__
+vsnprintf(char *str, size_t count, const char *fmt, va_list args)
+# else /* __STDC__ */
+vsnprintf(str, count, fmt, args)
+ char *str;
+ int count;
+ char *fmt;
+ va_list args;
+# endif /* __STDC__ */
+{
+ str[0] = 0;
+ end = str + count - 1;
+ dopr( str, fmt, args );
+ if (count > 0)
+ end[0] = 0;
+ return strlen(str);
+}
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+static void fmtstr __P((char *value, int ljust, int len, int zpad,
+ int maxwidth));
+static void fmtnum __P((long value, int base, int dosign, int ljust, int len,
+ int zpad));
+static void dostr __P(( char * , int ));
+static char *output;
+static void dopr_outch __P(( int c ));
+
+static void
+# ifdef __STDC__
+dopr(char * buffer, const char * format, va_list args )
+# else /* __STDC__ */
+dopr( buffer, format, args )
+ char *buffer;
+ char *format;
+ va_list args;
+# endif /* __STDC__ */
+{
+ int ch;
+ long value;
+ int longflag = 0;
+ int pointflag = 0;
+ int maxwidth = 0;
+ char *strvalue;
+ int ljust;
+ int len;
+ int zpad;
+
+ output = buffer;
+ while( (ch = *format++) ){
+ switch( ch ){
+ case '%':
+ ljust = len = zpad = maxwidth = 0;
+ longflag = pointflag = 0;
+ nextch:
+ ch = *format++;
+ switch( ch ){
+ case 0:
+ dostr( "**end of format**" , 0);
+ return;
+ case '-': ljust = 1; goto nextch;
+ case '0': /* set zero padding if len not set */
+ if(len==0 && !pointflag) zpad = '0';
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ if (pointflag)
+ maxwidth = maxwidth*10 + ch - '0';
+ else
+ len = len*10 + ch - '0';
+ goto nextch;
+ case '*':
+ if (pointflag)
+ maxwidth = va_arg( args, int );
+ else
+ len = va_arg( args, int );
+ goto nextch;
+ case '.': pointflag = 1; goto nextch;
+ case 'l': longflag = 1; goto nextch;
+ case 'u': case 'U':
+ /*fmtnum(value,base,dosign,ljust,len,zpad) */
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value, 10,0, ljust, len, zpad ); break;
+ case 'o': case 'O':
+ /*fmtnum(value,base,dosign,ljust,len,zpad) */
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value, 8,0, ljust, len, zpad ); break;
+ case 'd': case 'D':
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value, 10,1, ljust, len, zpad ); break;
+ case 'x':
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value, 16,0, ljust, len, zpad ); break;
+ case 'X':
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value,-16,0, ljust, len, zpad ); break;
+ case 's':
+ strvalue = va_arg( args, char *);
+ if (maxwidth > 0 || !pointflag) {
+ if (pointflag && len > maxwidth)
+ len = maxwidth; /* Adjust padding */
+ fmtstr( strvalue,ljust,len,zpad, maxwidth);
+ }
+ break;
+ case 'c':
+ ch = va_arg( args, int );
+ dopr_outch( ch ); break;
+ case '%': dopr_outch( ch ); continue;
+ default:
+ dostr( "???????" , 0);
+ }
+ break;
+ default:
+ dopr_outch( ch );
+ break;
+ }
+ }
+ *output = 0;
+}
+
+static void
+fmtstr( value, ljust, len, zpad, maxwidth )
+ char *value;
+ int ljust, len, zpad, maxwidth;
+{
+ int padlen, strlen; /* amount to pad */
+
+ if( value == 0 ){
+ value = "<NULL>";
+ }
+ for( strlen = 0; value[strlen]; ++ strlen ); /* strlen */
+ if (strlen > maxwidth && maxwidth)
+ strlen = maxwidth;
+ padlen = len - strlen;
+ if( padlen < 0 ) padlen = 0;
+ if( ljust ) padlen = -padlen;
+ while( padlen > 0 ) {
+ dopr_outch( ' ' );
+ --padlen;
+ }
+ dostr( value, maxwidth );
+ while( padlen < 0 ) {
+ dopr_outch( ' ' );
+ ++padlen;
+ }
+}
+
+static void
+fmtnum( value, base, dosign, ljust, len, zpad )
+ long value;
+ int base, dosign, ljust, len, zpad;
+{
+ int signvalue = 0;
+ unsigned long uvalue;
+ char convert[20];
+ int place = 0;
+ int padlen = 0; /* amount to pad */
+ int caps = 0;
+
+ /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n",
+ value, base, dosign, ljust, len, zpad )); */
+ uvalue = value;
+ if( dosign ){
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ }
+ }
+ if( base < 0 ){
+ caps = 1;
+ base = -base;
+ }
+ do{
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ }while(uvalue);
+ convert[place] = 0;
+ padlen = len - place;
+ if( padlen < 0 ) padlen = 0;
+ if( ljust ) padlen = -padlen;
+ /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n",
+ convert,place,signvalue,padlen)); */
+ if( zpad && padlen > 0 ){
+ if( signvalue ){
+ dopr_outch( signvalue );
+ --padlen;
+ signvalue = 0;
+ }
+ while( padlen > 0 ){
+ dopr_outch( zpad );
+ --padlen;
+ }
+ }
+ while( padlen > 0 ) {
+ dopr_outch( ' ' );
+ --padlen;
+ }
+ if( signvalue ) dopr_outch( signvalue );
+ while( place > 0 ) dopr_outch( convert[--place] );
+ while( padlen < 0 ){
+ dopr_outch( ' ' );
+ ++padlen;
+ }
+}
+
+static void
+dostr( str , cut)
+ char *str;
+ int cut;
+{
+ if (cut) {
+ while(*str && cut-- > 0) dopr_outch(*str++);
+ } else {
+ while(*str) dopr_outch(*str++);
+ }
+}
+
+static void
+dopr_outch( c )
+ int c;
+{
+ if( end == 0 || output < end )
+ *output++ = c;
+}
+
+int
+# ifdef __STDC__
+vsyslog(int priority, const char *fmt, ...)
+# else /* __STDC__ */
+vsyslog(priority, fmt, va_alist)
+ int priority;
+ const char *fmt;
+ va_dcl
+# endif /* __STDC__ */
+{
+ VA_LOCAL_DECL
+ char logbuf[BUFSIZ];
+
+ VA_START(fmt);
+
+ vsnprintf(logbuf, BUFSIZ, fmt, ap);
+ syslog(priority, "%s", logbuf);
+
+ VA_END;
+}
+#endif /* __hpux */
+
+/* internal logging function */
+
+void _pam_system_log(int priority, const char *format, ... )
+{
+ va_list args;
+ char *eformat;
+
+ D(("pam_system_log called"));
+
+ if (format == NULL) {
+ D(("NULL format to _pam_system_log() call"));
+ return;
+ }
+
+ va_start(args, format);
+
+ eformat = malloc(sizeof(_PAM_SYSTEM_LOG_PREFIX)+strlen(format));
+ if (eformat != NULL) {
+ strcpy(eformat, _PAM_SYSTEM_LOG_PREFIX);
+ strcpy(eformat + sizeof(_PAM_SYSTEM_LOG_PREFIX) - 1, format);
+ vsyslog(priority, eformat, args);
+ _pam_overwrite(eformat);
+ _pam_drop(eformat);
+ } else {
+ vsyslog(priority, format, args);
+ }
+
+ va_end(args);
+
+ D(("done."));
+}
+
diff --git a/libpam/pam_malloc.c b/libpam/pam_malloc.c
new file mode 100644
index 00000000..44d583e7
--- /dev/null
+++ b/libpam/pam_malloc.c
@@ -0,0 +1,404 @@
+/*
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:18 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1998/12/27 04:34:23 morgan
+ * reverting logging functions within libpam. Gone are the externally
+ * advertised API replaced by a more simple (libpam internal) one.
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.2 1996/12/01 03:14:13 morgan
+ * use _pam_macros.h
+ *
+ * Revision 1.1 1996/11/10 21:26:11 morgan
+ * Initial revision
+ *
+ */
+
+/*
+ * This pair of files helps to locate memory leaks. It is a wrapper for
+ * the malloc family of calls. (Actutally, it currently only deals
+ * with calloc, malloc, realloc, free and exit)
+ *
+ * To use these functions the header "pam_malloc.h" must be included
+ * in all parts of the code (that use the malloc functions) and this
+ * file must be linked with the result. The pam_malloc_flags can be
+ * set from another function and determine the level of logging.
+ *
+ * The output is via the macros defined in _pam_macros.h
+ *
+ * It is a debugging tool and should be turned off in released code.
+ *
+ * This suite was written by Andrew Morgan <morgan@parc.power.net> for
+ * Linux-PAM.
+ */
+
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+#include "pam_private.h"
+
+#include <security/pam_malloc.h>
+#include <security/_pam_macros.h>
+
+/* this must be done to stop infinite recursion! */
+#undef malloc
+#undef calloc
+#undef free
+#undef realloc
+#undef exit
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * default debugging level
+ */
+
+int pam_malloc_flags = PAM_MALLOC_ALL;
+int pam_malloc_delay_length = 4;
+
+#define on(x) ((pam_malloc_flags&(x))==(x))
+
+/*
+ * the implementation
+ */
+
+static const char *last_fn=NULL;
+static const char *last_file=NULL;
+static const char *last_call=NULL;
+static int last_line = 1;
+
+#define err(x) { _pam_output_xdebug_info(); _pam_output_debug x ; }
+
+static void set_last_(const char *x, const char *f
+ , const char *fn, const int l)
+{
+ last_fn = x ? x : "error-in-pam_malloc..";
+ last_file = f ? f : "*bad-file*";
+ last_call = fn ? fn: "*bad-fn*";
+ last_line = l;
+}
+
+static void _pam_output_xdebug_info(void)
+{
+ FILE *logfile;
+ int must_close = 1;
+
+ if (!(logfile = fopen(_PAM_LOGFILE,"a"))) {
+ logfile = stderr;
+ must_close = 0;
+ }
+ fprintf(logfile, "[%s:%s(%d)->%s()] ",
+ last_file, last_call, last_line, last_fn);
+ if (must_close) {
+ fflush(logfile);
+ fclose(logfile);
+ }
+}
+
+static void hinder(void)
+{
+ if (on(PAM_MALLOC_PAUSE)) {
+ if (on(0)) err(("pause requested"));
+ sleep(pam_malloc_delay_length);
+ }
+
+ if (on(PAM_MALLOC_STOP)) {
+ if (on(0)) err(("stop requested"));
+ exit(1);
+ }
+}
+
+/*
+ * here are the memory pointer registering functions.. these actually
+ * use malloc(!) but that's ok! ;^)
+ */
+
+struct reference {
+ void *ptr; /* pointer */
+ int nelements; /* number of elements */
+ int size; /* - each of this size */
+ char *file; /* where it was requested - filename */
+ char *function; /* - function */
+ int line; /* - line number */
+/*
+ * linking info
+ */
+ struct reference *next;
+};
+
+static void _dump(const char *say, const struct reference *ref)
+{
+ _pam_output_debug(" <%s: %p (#%d of %d) req. by %s(); %s line %d>\n"
+ , say
+ , ref->ptr,ref->nelements,ref->size
+ , ref->function,ref->file,ref->line);
+}
+
+static struct reference *root=NULL;
+
+static char *_strdup(const char *x)
+{
+ char *s;
+
+ s = (char *)malloc(strlen(x)+1);
+ if (s == NULL) {
+ if (on(0)) err(("_strdup failed"));
+ exit(1);
+ }
+
+ strcpy(s,x);
+ return s;
+}
+
+static void add_new_ref(void *new, int n, int size)
+{
+ struct reference *ref=NULL;
+
+ ref = (struct reference *) malloc( sizeof(struct reference) );
+ if (new == NULL || ref == NULL) {
+ if (on(0)) err(("internal error {add_new_ref}"));
+ exit(1);
+ }
+
+ ref->ptr = new;
+ ref->nelements = n;
+ ref->size = size;
+
+ ref->file = _strdup(last_file);
+ ref->function = _strdup(last_call);
+ ref->line = last_line;
+
+ ref->next = root;
+
+ if (on(PAM_MALLOC_REQUEST)) {
+ _dump("new_ptr", ref);
+ }
+
+ root = ref;
+}
+
+static void del_old_ref(void *old)
+{
+ struct reference *this,*last;
+
+ if (old == NULL) {
+ if (on(0)) err(("internal error {del_old_ref}"));
+ exit(1);
+ }
+
+ /* locate old pointer */
+
+ last = NULL;
+ this = root;
+ while (this) {
+ if (this->ptr == old)
+ break;
+ last = this;
+ this = this->next;
+ }
+
+ /* Did we find a reference ? */
+
+ if (this) {
+ if (on(PAM_MALLOC_FREE)) {
+ _dump("free old_ptr", this);
+ }
+ if (last == NULL) {
+ root = this->next;
+ } else {
+ last->next = this->next;
+ }
+ free(this->file);
+ free(this->function);
+ free(this);
+ } else {
+ if (on(0)) err(("ERROR!: bad memory"));
+ hinder();
+ }
+}
+
+static void verify_old_ref(void *old)
+{
+ struct reference *this;
+
+ if (old == NULL) {
+ if (on(0)) err(("internal error {verify_old_ref}"));
+ exit(1);
+ }
+
+ /* locate old pointer */
+
+ this = root;
+ while (this) {
+ if (this->ptr == old)
+ break;
+ this = this->next;
+ }
+
+ /* Did we find a reference ? */
+
+ if (this) {
+ if (on(PAM_MALLOC_VERIFY)) {
+ _dump("verify_ptr", this);
+ }
+ } else {
+ if (on(0)) err(("ERROR!: bad request"));
+ hinder();
+ }
+}
+
+static void dump_memory_list(const char *dump)
+{
+ struct reference *this;
+
+ this = root;
+ if (this) {
+ if (on(0)) err(("un-free()'d memory"));
+ while (this) {
+ _dump(dump, this);
+ this = this->next;
+ }
+ } else {
+ if (on(0)) err(("no memory allocated"));
+ }
+}
+
+/* now for the wrappers */
+
+#define _fn(x) set_last_(x,file,fn,line)
+
+void *pam_malloc(size_t size, const char *file, const char *fn, const int line)
+{
+ void *new;
+
+ _fn("malloc");
+
+ if (on(PAM_MALLOC_FUNC)) err(("request for %d", size));
+
+ new = malloc(size);
+ if (new == NULL) {
+ if (on(PAM_MALLOC_FAIL)) err(("returned NULL"));
+ } else {
+ if (on(PAM_MALLOC_REQUEST)) err(("request new"));
+ add_new_ref(new, 1, size);
+ }
+
+ return new;
+}
+
+void *pam_calloc(size_t nelm, size_t size
+ , const char *file, const char *fn, const int line)
+{
+ void *new;
+
+ _fn("calloc");
+
+ if (on(PAM_MALLOC_FUNC)) err(("request for %d of %d", nelm, size));
+
+ new = calloc(nelm,size);
+ if (new == NULL) {
+ if (on(PAM_MALLOC_FAIL)) err(("returned NULL"));
+ } else {
+ if (on(PAM_MALLOC_REQUEST)) err(("request new"));
+ add_new_ref(new, nelm, size);
+ }
+
+ return new;
+}
+
+void pam_free(void *ptr
+ , const char *file, const char *fn, const int line)
+{
+ _fn("free");
+
+ if (on(PAM_MALLOC_FUNC)) err(("request to free %p", ptr));
+
+ if (ptr == NULL) {
+ if (on(PAM_MALLOC_NULL)) err(("passed NULL pointer"));
+ } else {
+ if (on(PAM_MALLOC_FREE)) err(("deleted old"));
+ del_old_ref(ptr);
+ free(ptr);
+ }
+}
+
+void *pam_memalign(size_t ali, size_t size
+ , const char *file, const char *fn, const int line)
+{
+ _fn("memalign");
+ if (on(0)) err(("not implemented currently (Sorry)"));
+ exit(1);
+}
+
+void *pam_realloc(void *ptr, size_t size
+ , const char *file, const char *fn, const int line)
+{
+ void *new;
+
+ _fn("realloc");
+
+ if (on(PAM_MALLOC_FUNC)) err(("resize %p to %d", ptr, size));
+
+ if (ptr == NULL) {
+ if (on(PAM_MALLOC_NULL)) err(("passed NULL pointer"));
+ } else {
+ verify_old_ref(ptr);
+ }
+
+ new = realloc(ptr, size);
+ if (new == NULL) {
+ if (on(PAM_MALLOC_FAIL)) err(("returned NULL"));
+ } else {
+ if (ptr) {
+ if (on(PAM_MALLOC_FREE)) err(("deleted old"));
+ del_old_ref(ptr);
+ } else {
+ if (on(PAM_MALLOC_NULL)) err(("old is NULL"));
+ }
+ if (on(PAM_MALLOC_REQUEST)) err(("request new"));
+ add_new_ref(new, 1, size);
+ }
+
+ return new;
+}
+
+void *pam_valloc(size_t size
+ , const char *file, const char *fn, const int line)
+{
+ _fn("valloc");
+ if (on(0)) err(("not implemented currently (Sorry)"));
+ exit(1);
+}
+
+#include <alloca.h>
+
+void *pam_alloca(size_t size
+ , const char *file, const char *fn, const int line)
+{
+ _fn("alloca");
+ if (on(0)) err(("not implemented currently (Sorry)"));
+ exit(1);
+}
+
+void pam_exit(int i
+ , const char *file, const char *fn, const int line)
+{
+ _fn("exit");
+
+ if (on(0)) err(("passed (%d)", i));
+ if (on(PAM_MALLOC_LEAKED)) {
+ dump_memory_list("leaked");
+ }
+ exit(i);
+}
+
+/* end of file */
diff --git a/libpam/pam_map.c b/libpam/pam_map.c
new file mode 100644
index 00000000..8b969123
--- /dev/null
+++ b/libpam/pam_map.c
@@ -0,0 +1,85 @@
+/* pam_map.c - PAM mapping interface
+ *
+ * $Id$
+ *
+ * This is based on the X/Open XSSO specification of March 1997.
+ * It is not implemented as it is going to change... after 1997/9/25.
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:19 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ */
+
+#include <stdio.h>
+
+#include "pam_private.h"
+
+/* p 54 */
+
+int pam_get_mapped_authtok(pam_handle_t *pamh,
+ const char *target_module_username,
+ const char *target_module_type,
+ const char *target_authn_domain,
+ size_t *target_authtok_len
+ unsigned char **target_module_authtok);
+{
+ D(("called"));
+
+ IF_NO_PAMH("pam_get_mapped_authtok",pamh,PAM_SYSTEM_ERR);
+
+ return PAM_SYSTEM_ERROR;
+}
+
+/* p 68 */
+
+int pam_set_mapped_authtok(pam_handle_t *pamh,
+ char *target_module_username,
+ size_t *target_authtok_len,
+ unsigned char *target_module_authtok,
+ char *target_module_type,
+ char *target_authn_domain)
+{
+ D(("called"));
+
+ IF_NO_PAMH("pam_set_mapped_authtok",pamh,PAM_SYSTEM_ERR);
+
+ return PAM_SYSTEM_ERROR;
+}
+
+/* p 56 */
+
+int pam_get_mapped_username(pam_handle_t *pamh,
+ const char *src_username,
+ const char *src_module_type,
+ const char *src_authn_domain,
+ const char *target_module_type,
+ const char *target_authn_domain,
+ char **target_module_username)
+{
+ D(("called"));
+
+ IF_NO_PAMH("pam_get_mapped_username",pamh,PAM_SYSTEM_ERR);
+
+ return PAM_SYSTEM_ERROR;
+}
+
+/* p 70 */
+
+int pam_set_mapped_username(pam_handle_t *pamh,
+ char *src_username,
+ char *src_module_type,
+ char *src_authn_domain,
+ char *target_module_username,
+ char *target_module_type,
+ char *target_authn_domain)
+{
+ D(("called"));
+
+ IF_NO_PAMH("pam_set_mapped_username",pamh,PAM_SYSTEM_ERR);
+
+ return PAM_SYSTEM_ERROR;
+}
diff --git a/libpam/pam_misc.c b/libpam/pam_misc.c
new file mode 100644
index 00000000..9bd52bfa
--- /dev/null
+++ b/libpam/pam_misc.c
@@ -0,0 +1,305 @@
+/* pam_misc.c -- This is random stuff */
+
+/*
+ * $Id$
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <ctype.h>
+
+#include "pam_private.h"
+
+/* caseless string comparison: POSIX does not define this.. */
+int _pam_strCMP(const char *s, const char *t)
+{
+ int cf;
+
+ do {
+ cf = tolower(*s) - tolower(*t);
+ ++t;
+ } while (!cf && *s++);
+
+ return cf;
+}
+
+char *_pam_StrTok(char *from, const char *format, char **next)
+/*
+ * this function is a variant of the standard strtok, it differs in that
+ * it takes an additional argument and doesn't nul terminate tokens until
+ * they are actually reached.
+ */
+{
+ char table[256], *end;
+ int i;
+
+ if (from == NULL && (from = *next) == NULL)
+ return from;
+
+ /* initialize table */
+ for (i=1; i<256; table[i++] = '\0');
+ for (i=0; format[i] ; table[(int)format[i++]] = 'y');
+
+ /* look for first non-blank char */
+ while (*from && table[(int)*from]) {
+ ++from;
+ }
+
+ if (*from == '[') {
+ /*
+ * special case, "[...]" is considered to be a single
+ * object. Note, however, if one of the format[] chars is
+ * '[' this single string will not be read correctly.
+ */
+ for (end=++from; *end && *end != ']'; ++end) {
+ if (*end == '\\' && end[1] == ']')
+ ++end;
+ }
+ /* note, this string is stripped of its edges: "..." is what
+ remains */
+ } else if (*from) {
+ /* simply look for next blank char */
+ for (end=from; *end && !table[(int)*end]; ++end);
+ } else {
+ return (*next = NULL); /* no tokens left */
+ }
+
+ /* now terminate what we have */
+ if (*end)
+ *end++ = '\0';
+
+ /* indicate what it left */
+ if (*end) {
+ *next = end;
+ } else {
+ *next = NULL; /* have found last token */
+ }
+
+ /* return what we have */
+ return from;
+}
+
+/*
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ */
+
+char *_pam_strdup(const char *x)
+{
+ register char *new=NULL;
+
+ if (x != NULL) {
+ register int i;
+
+ for (i=0; x[i]; ++i); /* length of string */
+ if ((new = malloc(++i)) == NULL) {
+ i = 0;
+ _pam_system_log(LOG_CRIT, "_pam_strdup: failed to get memory");
+ } else {
+ while (i-- > 0) {
+ new[i] = x[i];
+ }
+ }
+ x = NULL;
+ }
+
+ return new; /* return the duplicate or NULL on error */
+}
+
+/* Generate argv, argc from s */
+/* caller must free(argv) */
+
+int _pam_mkargv(char *s, char ***argv, int *argc)
+{
+ int l;
+ int argvlen = 0;
+ char *sbuf, *sbuf_start;
+ char **our_argv = NULL;
+ char **argvbuf;
+ char *argvbufp;
+#ifdef DEBUG
+ int count=0;
+#endif
+
+ D(("_pam_mkargv called: %s",s));
+
+ *argc = 0;
+
+ l = strlen(s);
+ if (l) {
+ if ((sbuf = sbuf_start = _pam_strdup(s)) == NULL) {
+ _pam_system_log(LOG_CRIT,
+ "pam_mkargv: null returned by _pam_strdup");
+ D(("arg NULL"));
+ } else {
+ /* Overkill on the malloc, but not large */
+ argvlen = (l + 1) * ((sizeof(char)) + sizeof(char *));
+ if ((our_argv = argvbuf = malloc(argvlen)) == NULL) {
+ _pam_system_log(LOG_CRIT,
+ "pam_mkargv: null returned by malloc");
+ } else {
+ char *tmp=NULL;
+
+ argvbufp = (char *) argvbuf + (l * sizeof(char *));
+ D(("[%s]",sbuf));
+ while ((sbuf = _pam_StrTok(sbuf, " \n\t", &tmp))) {
+ D(("arg #%d",++count));
+ D(("->[%s]",sbuf));
+ strcpy(argvbufp, sbuf);
+ D(("copied token"));
+ *argvbuf = argvbufp;
+ argvbufp += strlen(argvbufp) + 1;
+ D(("stepped in argvbufp"));
+ (*argc)++;
+ argvbuf++;
+ sbuf = NULL;
+ D(("loop again?"));
+ }
+ _pam_drop(sbuf_start);
+ }
+ }
+ }
+
+ *argv = our_argv;
+
+ D(("_pam_mkargv returned"));
+
+ return(argvlen);
+}
+
+/*
+ * this function is used to protect the modules from accidental or
+ * semi-mallicious harm that an application may do to confuse the API.
+ */
+
+void _pam_sanitize(pam_handle_t *pamh)
+{
+ /*
+ * this is for security. We reset the auth-tokens here.
+ */
+ pam_set_item(pamh,PAM_AUTHTOK,NULL);
+ pam_set_item(pamh,PAM_OLDAUTHTOK,NULL);
+}
+
+/*
+ * This function scans the array and replaces the _PAM_ACTION_UNDEF
+ * entries with the default action.
+ */
+
+void _pam_set_default_control(int *control_array, int default_action)
+{
+ int i;
+
+ for (i=0; i<_PAM_RETURN_VALUES; ++i) {
+ if (control_array[i] == _PAM_ACTION_UNDEF) {
+ control_array[i] = default_action;
+ }
+ }
+}
+
+/*
+ * This function is used to parse a control string. This string is a
+ * series of tokens of the following form:
+ *
+ * "[ ]*return_code[ ]*=[ ]*action/[ ]".
+ */
+
+#include "pam_tokens.h"
+
+void _pam_parse_control(int *control_array, char *tok)
+{
+ const char *error;
+ int ret;
+
+ while (*tok) {
+ int act, len;
+
+ /* skip leading space */
+ while (isspace((int)*tok) && *++tok);
+ if (!*tok)
+ break;
+
+ /* identify return code */
+ for (ret=0; ret<=_PAM_RETURN_VALUES; ++ret) {
+ len = strlen(_pam_token_returns[ret]);
+ if (!strncmp(_pam_token_returns[ret], tok, len)) {
+ break;
+ }
+ }
+ if (ret > _PAM_RETURN_VALUES || !*(tok += len)) {
+ error = "expecting return value";
+ goto parse_error;
+ }
+
+ /* observe '=' */
+ while (isspace((int)*tok) && *++tok);
+ if (!*tok || *tok++ != '=') {
+ error = "expecting '='";
+ goto parse_error;
+ }
+
+ /* skip leading space */
+ while (isspace((int)*tok) && *++tok);
+ if (!*tok) {
+ error = "expecting action";
+ goto parse_error;
+ }
+
+ /* observe action type */
+ for (act=0; act < (-(_PAM_ACTION_UNDEF)); ++act) {
+ len = strlen(_pam_token_actions[act]);
+ if (!strncmp(_pam_token_actions[act], tok, len)) {
+ act *= -1;
+ tok += len;
+ break;
+ }
+ }
+ if (act > 0) {
+ /*
+ * Either we have a number or we have hit an error. In
+ * principle, there is nothing to stop us accepting
+ * negative offsets. (Although we would have to think of
+ * another way of encoding the tokens.) However, I really
+ * think this would be both hard to administer and easily
+ * cause looping problems. So, for now, we will just
+ * allow forward jumps. (AGM 1998/1/7)
+ */
+ if (!isdigit((int)*tok)) {
+ error = "expecting jump number";
+ goto parse_error;
+ }
+ /* parse a number */
+ act = 0;
+ do {
+ act *= 10;
+ act += *tok - '0'; /* XXX - this assumes ascii behavior */
+ } while (*++tok && isdigit((int)*tok));
+ if (! act) {
+ /* we do not allow 0 jumps. There is a token ('ignore')
+ for that */
+ error = "expecting non-zero";
+ goto parse_error;
+ }
+ }
+
+ /* set control_array element */
+ if (ret != _PAM_RETURN_VALUES) {
+ control_array[ret] = act;
+ } else {
+ /* set the default to 'act' */
+ _pam_set_default_control(control_array, act);
+ }
+ }
+
+ /* that was a success */
+ return;
+
+parse_error:
+ /* treat everything as bad */
+ _pam_system_log(LOG_ERR, "pam_parse: %s; [...%s]", error, tok);
+ for (ret=0; ret<_PAM_RETURN_VALUES; control_array[ret++]=_PAM_ACTION_BAD);
+
+}
diff --git a/libpam/pam_password.c b/libpam/pam_password.c
new file mode 100644
index 00000000..afbaa580
--- /dev/null
+++ b/libpam/pam_password.c
@@ -0,0 +1,52 @@
+/* pam_password.c - PAM Password Management */
+
+/*
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* #define DEBUG */
+
+#include "pam_private.h"
+
+int pam_chauthtok(pam_handle_t *pamh, int flags)
+{
+ int retval;
+
+ D(("called."));
+
+ IF_NO_PAMH("pam_chauthtok", pamh, PAM_SYSTEM_ERR);
+
+ if (pamh->former.choice == PAM_NOT_STACKED) {
+ _pam_start_timer(pamh); /* we try to make the time for a failure
+ independent of the time it takes to
+ fail */
+ _pam_sanitize(pamh);
+ pamh->former.update = PAM_FALSE;
+ }
+
+ /* first call to check if there will be a problem */
+ if (pamh->former.update ||
+ (retval = _pam_dispatch(pamh, flags|PAM_PRELIM_CHECK,
+ PAM_CHAUTHTOK)) == PAM_SUCCESS) {
+ D(("completed check ok: former=%d", pamh->former.update));
+ pamh->former.update = PAM_TRUE;
+ retval = _pam_dispatch(pamh, flags|PAM_UPDATE_AUTHTOK,
+ PAM_CHAUTHTOK);
+ }
+
+ /* if we completed we should clean up */
+ if (retval != PAM_INCOMPLETE) {
+ _pam_sanitize(pamh);
+ pamh->former.update = PAM_FALSE;
+ _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */
+ D(("pam_chauthtok exit %d - %d", retval, pamh->former.choice));
+ } else {
+ D(("will resume when ready", retval));
+ }
+
+ return retval;
+}
+
diff --git a/libpam/pam_private.h b/libpam/pam_private.h
new file mode 100644
index 00000000..51660fd1
--- /dev/null
+++ b/libpam/pam_private.h
@@ -0,0 +1,312 @@
+/*
+ * pam_private.h
+ *
+ * $Id$
+ *
+ * This is the Linux-PAM Library Private Header. It contains things
+ * internal to the Linux-PAM library. Things not needed by either an
+ * application or module.
+ *
+ * Please see end of file for copyright.
+ *
+ * Creator: Marc Ewing.
+ * Maintained: AGM
+ */
+
+#ifndef _PAM_PRIVATE_H
+#define _PAM_PRIVATE_H
+
+/* this is not used at the moment --- AGM */
+#define LIBPAM_VERSION 67
+
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+
+/* the Linux-PAM configuration file */
+
+#define PAM_CONFIG "/etc/pam.conf"
+#define PAM_CONFIG_D "/etc/pam.d"
+#define PAM_CONFIG_DF "/etc/pam.d/%s"
+
+#define PAM_DEFAULT_SERVICE "other" /* lower case */
+#define PAM_DEFAULT_SERVICE_FILE PAM_CONFIG_D "/" PAM_DEFAULT_SERVICE
+
+#ifdef PAM_LOCKING
+/*
+ * the Linux-PAM lock file. If it exists Linux-PAM will abort. Use it
+ * to block access to libpam
+ */
+#define PAM_LOCK_FILE "/var/lock/subsys/PAM"
+#endif
+
+/* components of the pam_handle structure */
+
+struct handler {
+ int must_fail;
+ int (*func)(pam_handle_t *pamh, int flags, int argc, char **argv);
+ int actions[_PAM_RETURN_VALUES];
+ int argc;
+ char **argv;
+ struct handler *next;
+};
+
+struct loaded_module {
+ char *name;
+ int type; /* PAM_STATIC_MOD or PAM_DYNAMIC_MOD */
+ void *dl_handle;
+};
+
+#define PAM_MT_DYNAMIC_MOD 0
+#define PAM_MT_STATIC_MOD 1
+#define PAM_MT_FAULTY_MOD 2
+
+struct handlers {
+ struct handler *authenticate;
+ struct handler *setcred;
+ struct handler *acct_mgmt;
+ struct handler *open_session;
+ struct handler *close_session;
+ struct handler *chauthtok;
+};
+
+struct service {
+ struct loaded_module *module; /* Only used for dynamic loading */
+ int modules_allocated;
+ int modules_used;
+ int handlers_loaded;
+
+ struct handlers conf; /* the configured handlers */
+ struct handlers other; /* the default handlers */
+};
+
+/*
+ * Environment helper functions
+ */
+
+#define PAM_ENV_CHUNK 10 /* chunks of memory calloc()'d *
+ * at once */
+
+struct pam_environ {
+ int entries; /* the number of pointers available */
+ int requested; /* the number of pointers used: *
+ * 1 <= requested <= entries */
+ char **list; /* the environment storage (a list *
+ * of pointers to malloc() memory) */
+};
+
+#include <sys/time.h>
+
+typedef enum { PAM_FALSE, PAM_TRUE } _pam_boolean;
+
+struct _pam_fail_delay {
+ _pam_boolean set;
+ unsigned int delay;
+ time_t begin;
+ const void *delay_fn_ptr;
+};
+
+struct _pam_former_state {
+/* this is known and set by _pam_dispatch() */
+ int choice; /* which flavor of module function did we call? */
+
+/* state info for the _pam_dispatch_aux() function */
+ int depth; /* how deep in the stack were we? */
+ int impression; /* the impression at that time */
+ int status; /* the status before returning incomplete */
+
+/* state info used by pam_get_user() function */
+ int want_user;
+ char *prompt; /* saved prompt information */
+
+/* state info for the pam_chauthtok() function */
+ _pam_boolean update;
+};
+
+struct pam_handle {
+ char *authtok;
+ struct pam_conv *pam_conversation;
+ char *oldauthtok;
+ char *prompt; /* for use by pam_get_user() */
+ char *service_name;
+ char *user;
+ char *rhost;
+ char *ruser;
+ char *tty;
+ struct pam_data *data;
+ struct pam_environ *env; /* structure to maintain environment list */
+ struct _pam_fail_delay fail_delay; /* helper function for easy delays */
+ struct service handlers;
+ struct _pam_former_state former; /* library state - support for
+ event driven applications */
+};
+
+/* Values for select arg to _pam_dispatch() */
+#define PAM_NOT_STACKED 0
+#define PAM_AUTHENTICATE 1
+#define PAM_SETCRED 2
+#define PAM_ACCOUNT 3
+#define PAM_OPEN_SESSION 4
+#define PAM_CLOSE_SESSION 5
+#define PAM_CHAUTHTOK 6
+
+#define _PAM_ACTION_IS_JUMP(x) ((x) > 0)
+#define _PAM_ACTION_IGNORE 0
+#define _PAM_ACTION_OK -1
+#define _PAM_ACTION_DONE -2
+#define _PAM_ACTION_BAD -3
+#define _PAM_ACTION_DIE -4
+#define _PAM_ACTION_RESET -5
+/* Add any new entries here. Will need to change ..._UNDEF and then
+ * need to change pam_tokens.h */
+#define _PAM_ACTION_UNDEF -6 /* this is treated as an error
+ ( = _PAM_ACTION_BAD) */
+
+/* character tables for parsing config files */
+extern const char * const _pam_token_actions[-_PAM_ACTION_UNDEF];
+extern const char * const _pam_token_returns[_PAM_RETURN_VALUES+1];
+
+/*
+ * internally defined functions --- these should not be directly
+ * called by applications or modules
+ */
+int _pam_dispatch(pam_handle_t *pamh, int flags, int choice);
+
+/* Free various allocated structures and dlclose() the libs */
+int _pam_free_handlers(pam_handle_t *pamh);
+
+/* Parse config file, allocate handler structures, dlopen() */
+int _pam_init_handlers(pam_handle_t *pamh);
+
+/* Set all hander stuff to 0/NULL - called once from pam_start() */
+void _pam_start_handlers(pam_handle_t *pamh);
+
+/* environment helper functions */
+
+/* create the environment structure */
+int _pam_make_env(pam_handle_t *pamh);
+
+/* delete the environment structure */
+void _pam_drop_env(pam_handle_t *pamh);
+
+#ifdef LINUX_PAM
+
+/* these functions deal with failure delays as required by the
+ authentication modules and application. Their *interface* is likely
+ to remain the same although their function is hopefully going to
+ improve */
+
+/* reset the timer to no-delay */
+void _pam_reset_timer(pam_handle_t *pamh);
+
+/* this sets the clock ticking */
+void _pam_start_timer(pam_handle_t *pamh);
+
+/* this waits for the clock to stop ticking if status != PAM_SUCCESS */
+void _pam_await_timer(pam_handle_t *pamh, int status);
+
+
+#endif /* LINUX_PAM */
+
+typedef void (*voidfunc(void))(void);
+#ifdef PAM_STATIC
+
+/* The next two in ../modules/_pam_static/pam_static.c */
+
+/* Return pointer to data structure used to define a static module */
+struct pam_module * _pam_open_static_handler(const char *path);
+
+/* Return pointer to function requested from static module */
+
+voidfunc *_pam_get_static_sym(struct pam_module *mod, const char *symname);
+
+#endif
+
+/* For now we just use a stack and linear search for module data. */
+/* If it becomes apparent that there is a lot of data, it should */
+/* changed to either a sorted list or a hash table. */
+
+struct pam_data {
+ char *name;
+ void *data;
+ void (*cleanup)(pam_handle_t *pamh, void *data, int error_status);
+ struct pam_data *next;
+};
+
+void _pam_free_data(pam_handle_t *pamh, int status);
+
+int _pam_strCMP(const char *s, const char *t);
+char *_pam_StrTok(char *from, const char *format, char **next);
+
+char *_pam_strdup(const char *s);
+
+int _pam_mkargv(char *s, char ***argv, int *argc);
+
+void _pam_sanitize(pam_handle_t *pamh);
+
+void _pam_set_default_control(int *control_array, int default_action);
+
+void _pam_parse_control(int *control_array, char *tok);
+
+void _pam_system_log(int priority, const char *format, ... );
+#define _PAM_SYSTEM_LOG_PREFIX "PAM "
+
+/*
+ * XXX - Take care with this. It could confuse the logic of a trailing
+ * else
+ */
+
+#define IF_NO_PAMH(X,pamh,ERR) \
+if ((pamh) == NULL) { \
+ _pam_system_log(LOG_ERR, X ": NULL pam handle passed"); \
+ return ERR; \
+}
+
+/* Definition for the default username prompt used by pam_get_user() */
+
+#define PAM_DEFAULT_PROMPT "Please enter username: "
+
+/*
+ * include some helpful macros
+ */
+
+#include <security/_pam_macros.h>
+
+/*
+ * Copyright (C) 1995 by Red Hat Software, Marc Ewing
+ * Copyright (c) 1996-8, Andrew G. Morgan <morgan@linux.kernel.org>
+ *
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#endif /* _PAM_PRIVATE_H_ */
diff --git a/libpam/pam_second.c b/libpam/pam_second.c
new file mode 100644
index 00000000..614275af
--- /dev/null
+++ b/libpam/pam_second.c
@@ -0,0 +1,46 @@
+/*
+ * pam_second.c -- PAM secondary authentication
+ * (based on XSSO draft spec of March 1997)
+ *
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:20 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pam_private.h"
+
+/* p 42 */
+
+int pam_authenticate_secondary(pam_handle_t *pamh,
+ char *target_username,
+ char *target_module_type,
+ char *target_authn_domain,
+ char *target_supp_data,
+ unsigned char *target_module_authtok,
+ int flags)
+{
+ int retval=PAM_SYSTEM_ERR;
+
+ D(("called"));
+
+ _pam_start_timer(pamh); /* we try to make the time for a failure
+ independent of the time it takes to
+ fail */
+
+ IF_NO_PAMH("pam_authenticate_secondary",pamh,PAM_SYSTEM_ERR);
+
+ _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */
+
+ D(("pam_authenticate_secondary exit"));
+
+ return retval;
+}
diff --git a/libpam/pam_session.c b/libpam/pam_session.c
new file mode 100644
index 00000000..eae464c8
--- /dev/null
+++ b/libpam/pam_session.c
@@ -0,0 +1,41 @@
+/* pam_session.c - PAM Session Management */
+
+/*
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:20 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.3 1996/12/01 03:14:13 morgan
+ * use _pam_macros.h
+ *
+ * Revision 1.2 1996/03/10 02:19:12 morgan
+ * some oversight meant that this wasn't being compiled. It needed a
+ * couple of changes.
+ *
+ *
+ */
+
+#include <stdio.h>
+
+#include "pam_private.h"
+
+int pam_open_session(pam_handle_t *pamh, int flags)
+{
+ D(("called"));
+
+ IF_NO_PAMH("pam_open_session",pamh,PAM_SYSTEM_ERR);
+ return _pam_dispatch(pamh, flags, PAM_OPEN_SESSION);
+}
+
+int pam_close_session(pam_handle_t *pamh, int flags)
+{
+ D(("called"));
+
+ IF_NO_PAMH("pam_close_session",pamh,PAM_SYSTEM_ERR);
+ return _pam_dispatch(pamh, flags, PAM_CLOSE_SESSION);
+}
diff --git a/libpam/pam_start.c b/libpam/pam_start.c
new file mode 100644
index 00000000..fb222d62
--- /dev/null
+++ b/libpam/pam_start.c
@@ -0,0 +1,112 @@
+/* pam_start.c */
+
+/* Creator Marc Ewing
+ * Maintained by AGM
+ *
+ * $Id$
+ *
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "pam_private.h"
+
+int pam_start (
+ const char *service_name,
+ const char *user,
+ const struct pam_conv *pam_conversation,
+ pam_handle_t **pamh)
+{
+ D(("called pam_start: [%s] [%s] [%p] [%p]"
+ ,service_name, user, pam_conversation, pamh));
+
+ if ((*pamh = calloc(1, sizeof(**pamh))) == NULL) {
+ _pam_system_log(LOG_CRIT, "pam_start: calloc failed for *pamh");
+ return (PAM_BUF_ERR);
+ }
+
+ if (service_name) {
+ char *tmp;
+
+ if (((*pamh)->service_name = _pam_strdup(service_name)) == NULL) {
+ _pam_system_log(LOG_CRIT,
+ "pam_start: _pam_strdup failed for service name");
+ _pam_drop(*pamh);
+ return (PAM_BUF_ERR);
+ }
+ for (tmp=(*pamh)->service_name; *tmp; ++tmp)
+ *tmp = tolower(*tmp); /* require lower case */
+ } else
+ (*pamh)->service_name = NULL;
+
+ if (user) {
+ if (((*pamh)->user = _pam_strdup(user)) == NULL) {
+ _pam_system_log(LOG_CRIT,
+ "pam_start: _pam_strdup failed for user");
+ _pam_drop((*pamh)->service_name);
+ _pam_drop(*pamh);
+ return (PAM_BUF_ERR);
+ }
+ } else
+ (*pamh)->user = NULL;
+
+ (*pamh)->tty = NULL;
+ (*pamh)->prompt = NULL; /* prompt for pam_get_user() */
+ (*pamh)->ruser = NULL;
+ (*pamh)->rhost = NULL;
+ (*pamh)->authtok = NULL;
+ (*pamh)->oldauthtok = NULL;
+ (*pamh)->fail_delay.delay_fn_ptr = NULL;
+ (*pamh)->former.choice = PAM_NOT_STACKED;
+
+ if (pam_conversation == NULL
+ || ((*pamh)->pam_conversation = (struct pam_conv *)
+ malloc(sizeof(struct pam_conv))) == NULL) {
+ _pam_system_log(LOG_CRIT, "pam_start: malloc failed for pam_conv");
+ _pam_drop((*pamh)->service_name);
+ _pam_drop((*pamh)->user);
+ _pam_drop(*pamh);
+ return (PAM_BUF_ERR);
+ } else {
+ memcpy((*pamh)->pam_conversation, pam_conversation,
+ sizeof(struct pam_conv));
+ }
+
+ (*pamh)->data = NULL;
+ if ( _pam_make_env(*pamh) != PAM_SUCCESS ) {
+ _pam_system_log(LOG_ERR,"pam_start: failed to initialize environment");
+ _pam_drop((*pamh)->service_name);
+ _pam_drop((*pamh)->user);
+ _pam_drop(*pamh);
+ return PAM_ABORT;
+ }
+
+ _pam_reset_timer(*pamh); /* initialize timer support */
+
+ _pam_start_handlers(*pamh); /* cannot fail */
+
+ /* According to the SunOS man pages, loading modules and resolving
+ * symbols happens on the first call from the application. */
+
+ /*
+ * XXX - should we call _pam_init_handlers() here ? The following
+ * is new as of Linux-PAM 0.55
+ */
+
+ if ( _pam_init_handlers(*pamh) != PAM_SUCCESS ) {
+ _pam_system_log(LOG_ERR, "pam_start: failed to initialize handlers");
+ _pam_drop_env(*pamh); /* purge the environment */
+ _pam_drop((*pamh)->service_name);
+ _pam_drop((*pamh)->user);
+ _pam_drop(*pamh);
+ return PAM_ABORT;
+ }
+
+ D(("exiting pam_start successfully"));
+
+ return PAM_SUCCESS;
+}
diff --git a/libpam/pam_static.c b/libpam/pam_static.c
new file mode 100644
index 00000000..64a3dd31
--- /dev/null
+++ b/libpam/pam_static.c
@@ -0,0 +1,141 @@
+/* pam_static.c -- static module loading helper functions */
+
+/* created by Michael K. Johnson, johnsonm@redhat.com
+ *
+ * $Id$
+ */
+
+/* This whole file is only used for PAM_STATIC */
+
+#ifdef PAM_STATIC
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pam_private.h"
+
+/*
+ * Need to include pointers to static modules; this was built by each
+ * of the modules that register...
+ */
+
+#include "../modules/_static_module_list"
+
+/*
+ * and here is a structure that connects libpam to the above static
+ * modules
+ */
+
+static struct pam_module *static_modules[] = {
+
+#include "../modules/_static_module_entry"
+
+ NULL
+};
+
+/*
+ * and now for the functions
+ */
+
+/* Return pointer to data structure used to define a static module */
+struct pam_module * _pam_open_static_handler(const char *path)
+{
+ int i;
+ const char *clpath = path;
+ char *lpath, *end;
+
+ if (strchr(clpath, '/')) {
+ /* ignore path and leading "/" */
+ clpath = strrchr(lpath, '/') + 1;
+ }
+ /* create copy to muck with (must free before return) */
+ lpath = _pam_strdup(clpath);
+ /* chop .so off copy if it exists (or other extension on other
+ platform...) */
+ end = strstr(lpath, ".so");
+ if (end) {
+ *end = '\0';
+ }
+
+ /* now go find the module */
+ for (i = 0; static_modules[i] != NULL; i++) {
+ D(("%s=?%s\n", lpath, static_modules[i]->name));
+ if (static_modules[i]->name &&
+ ! strcmp(static_modules[i]->name, lpath)) {
+ break;
+ }
+ }
+
+ if (static_modules[i] == NULL) {
+ _pam_system_log(NULL, NULL, LOG_ERR, "no static module named %s",
+ lpath);
+ }
+
+ free(lpath);
+ return (static_modules[i]);
+}
+
+/* Return pointer to function requested from static module
+ * Can't just return void *, because ANSI C disallows casting a
+ * pointer to a function to a void *...
+ * This definition means:
+ * _pam_get_static_sym is a function taking two arguments and
+ * returning a pointer to a function which takes no arguments
+ * and returns void... */
+voidfunc *_pam_get_static_sym(struct pam_module *mod, const char *symname) {
+
+ if (! strcmp(symname, "pam_sm_authenticate")) {
+ return ((voidfunc *)mod->pam_sm_authenticate);
+ } else if (! strcmp(symname, "pam_sm_setcred")) {
+ return ((voidfunc *)mod->pam_sm_setcred);
+ } else if (! strcmp(symname, "pam_sm_acct_mgmt")) {
+ return ((voidfunc *)mod->pam_sm_acct_mgmt);
+ } else if (! strcmp(symname, "pam_sm_open_session")) {
+ return ((voidfunc *)mod->pam_sm_open_session);
+ } else if (! strcmp(symname, "pam_sm_close_session")) {
+ return ((voidfunc *)mod->pam_sm_close_session);
+ } else if (! strcmp(symname, "pam_sm_chauthtok")) {
+ return ((voidfunc *)mod->pam_sm_chauthtok);
+ }
+ /* getting to this point is an error */
+ return ((voidfunc *)NULL);
+}
+
+#endif /* PAM_STATIC */
+
+/*
+ * Copyright (C) 1995 by Red Hat Software, Michael K. Johnson
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/libpam/pam_strerror.c b/libpam/pam_strerror.c
new file mode 100644
index 00000000..ecf8c0bb
--- /dev/null
+++ b/libpam/pam_strerror.c
@@ -0,0 +1,112 @@
+/* pam_strerror.c */
+
+/* $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:21 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.6 1997/01/04 20:12:02 morgan
+ * replaced conditional FAIL_NOW with ABORT
+ *
+ * Revision 1.5 1996/07/07 23:58:56 morgan
+ * corrected "... " to "..."
+ *
+ * Revision 1.4 1996/06/02 08:03:29 morgan
+ * spelling correction
+ *
+ * Revision 1.3 1996/03/16 23:08:54 morgan
+ * PAM --> Linux-PAM ;)
+ *
+ */
+
+#include "pam_private.h"
+
+const char *pam_strerror(pam_handle_t *pamh, int errnum)
+{
+#ifdef UGLY_HACK_FOR_PRIOR_BEHAVIOR_SUPPORT /* will be removed from v 1.0 */
+
+ int possible_error;
+
+ possible_error = (int) pamh;
+ if (!(possible_error >= 0 && possible_error <= PAM_BAD_ITEM)) {
+ possible_error = errnum;
+ }
+
+/* mask standard behavior to use possible_error variable. */
+#define errnum possible_error
+
+#endif /* UGLY_HACK_FOR_PRIOR_BEHAVIOR_SUPPORT */
+
+ switch (errnum) {
+ case PAM_SUCCESS:
+ return "Success";
+ case PAM_ABORT:
+ return "Critical error - immediate abort";
+ case PAM_OPEN_ERR:
+ return "dlopen() failure";
+ case PAM_SYMBOL_ERR:
+ return "Symbol not found";
+ case PAM_SERVICE_ERR:
+ return "Error in service module";
+ case PAM_SYSTEM_ERR:
+ return "System error";
+ case PAM_BUF_ERR:
+ return "Memory buffer error";
+ case PAM_PERM_DENIED:
+ return "Permission denied";
+ case PAM_AUTH_ERR:
+ return "Authentication failure";
+ case PAM_CRED_INSUFFICIENT:
+ return "Insufficient credentials to access authentication data";
+ case PAM_AUTHINFO_UNAVAIL:
+ return "Authentication service cannot retrieve authentication info.";
+ case PAM_USER_UNKNOWN:
+ return "User not known to the underlying authentication module";
+ case PAM_MAXTRIES:
+ return "Have exhasted maximum number of retries for service.";
+ case PAM_NEW_AUTHTOK_REQD:
+ return "Authentication token is no longer valid; new one required.";
+ case PAM_ACCT_EXPIRED:
+ return "User account has expired";
+ case PAM_SESSION_ERR:
+ return "Cannot make/remove an entry for the specified session";
+ case PAM_CRED_UNAVAIL:
+ return "Authentication service cannot retrieve user credentials";
+ case PAM_CRED_EXPIRED:
+ return "User credentials expired";
+ case PAM_CRED_ERR:
+ return "Failure setting user credentials";
+ case PAM_NO_MODULE_DATA:
+ return "No module specific data is present";
+ case PAM_BAD_ITEM:
+ return "Bad item passed to pam_*_item()";
+ case PAM_CONV_ERR:
+ return "Conversation error";
+ case PAM_AUTHTOK_ERR:
+ return "Authentication token manipulation error";
+ case PAM_AUTHTOK_RECOVER_ERR:
+ return "Authentication information cannot be recovered";
+ case PAM_AUTHTOK_LOCK_BUSY:
+ return "Authentication token lock busy";
+ case PAM_AUTHTOK_DISABLE_AGING:
+ return "Authentication token aging disabled";
+ case PAM_TRY_AGAIN:
+ return "Failed preliminary check by password service";
+ case PAM_IGNORE:
+ return "Please ignore underlying account module";
+ case PAM_MODULE_UNKNOWN:
+ return "Module is unknown";
+ case PAM_AUTHTOK_EXPIRED:
+ return "Authentication token expired";
+ case PAM_CONV_AGAIN:
+ return "Conversation is waiting for event";
+ case PAM_INCOMPLETE:
+ return "Application needs to call libpam again";
+ }
+
+ return "Unknown Linux-PAM error (need to upgrde libpam?)";
+}
diff --git a/libpam/pam_tokens.h b/libpam/pam_tokens.h
new file mode 100644
index 00000000..1fd32641
--- /dev/null
+++ b/libpam/pam_tokens.h
@@ -0,0 +1,111 @@
+/*
+ * pam_tokens.h
+ *
+ * $Id$
+ *
+ * This is a Linux-PAM Library Private Header file. It contains tokens
+ * that are used when we parse the configuration file(s).
+ *
+ * Please see end of file for copyright.
+ *
+ * Creator: Andrew Morgan.
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:21 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ */
+
+#ifndef _PAM_TOKENS_H
+#define _PAM_TOKENS_H
+
+/* an array of actions */
+
+const char * const _pam_token_actions[-_PAM_ACTION_UNDEF] = {
+ "ignore", /* 0 */
+ "ok", /* -1 */
+ "done", /* -2 */
+ "bad", /* -3 */
+ "die", /* -4 */
+ "reset", /* -5 */
+};
+
+/* an array of possible return values */
+
+const char * const _pam_token_returns[_PAM_RETURN_VALUES+1] = {
+ "success", /* 0 */
+ "open_err", /* 1 */
+ "symbol_err", /* 2 */
+ "service_err", /* 3 */
+ "system_err", /* 4 */
+ "buf_err", /* 5 */
+ "perm_denied", /* 6 */
+ "auth_err", /* 7 */
+ "cred_insufficient", /* 8 */
+ "authinfo_unavail", /* 9 */
+ "user_unknown", /* 10 */
+ "maxtries", /* 11 */
+ "new_authtok_reqd", /* 12 */
+ "acct_expired", /* 13 */
+ "session_err", /* 14 */
+ "cred_unavail", /* 15 */
+ "cred_expired", /* 16 */
+ "cred_err", /* 17 */
+ "no_module_data", /* 18 */
+ "conv_err", /* 19 */
+ "authtok_err", /* 20 */
+ "authtok_recover_err", /* 21 */
+ "authtok_lock_busy", /* 22 */
+ "authtok_disable_aging", /* 23 */
+ "try_again", /* 24 */
+ "ignore", /* 25 */
+ "abort", /* 26 */
+ "authtok_expired", /* 27 */
+ "module_unknown", /* 28 */
+ "bad_item", /* 29 */
+/* add new return codes here */
+ "default" /* this is _PAM_RETURN_VALUES and indicates
+ the default return action */
+};
+
+/*
+ * Copyright (C) 1998, Andrew G. Morgan <morgan@linux.kernel.org>
+ *
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#endif /* _PAM_PRIVATE_H_ */
diff --git a/libpam_misc/.cvsignore b/libpam_misc/.cvsignore
new file mode 100644
index 00000000..d04bebe5
--- /dev/null
+++ b/libpam_misc/.cvsignore
@@ -0,0 +1,7 @@
+libpam_misc.so
+libpam_misc.a
+libpamd_misc.so
+libpamd_misc.a
+help_env.o
+misc_conv.o
+xstrdup.o
diff --git a/libpam_misc/Makefile b/libpam_misc/Makefile
new file mode 100644
index 00000000..89a27536
--- /dev/null
+++ b/libpam_misc/Makefile
@@ -0,0 +1,96 @@
+# $Id$
+#
+
+dummy:
+ @echo "*** This is not a top-level Makefile!"
+
+# ///////////////////////////////////////////////////////////////////
+
+# uncomment if you wnat libpam_misc to be made as a dynamic library
+# AGM has had some segfaulting from libdl when I did this. I have not
+# investigated the cause...
+
+MAKE_DYNAMIC=yes
+
+ifeq ($(DEBUG_REL),yes)
+ LIBNAME=pamd_misc
+else
+ LIBNAME=pam_misc
+endif
+LIBMAJOR=$(MAJOR_REL)
+LIBMINOR=$(MINOR_REL)
+
+FILES=misc_conv help_env
+
+#
+# Probably no need to alter anything below here.
+#
+
+# build dynamic library names
+
+LIBDYNAMIC=lib$(LIBNAME).$(DYNTYPE)
+LIBDYNMAJ=$(LIBDYNAMIC).$(LIBMAJOR)
+LIBDYNMIN=$(LIBDYNMAJ).$(LIBMINOR)
+
+# static library name
+
+LIBSTATIC = lib$(LIBNAME).a
+
+# sources and object files
+
+LIBSRC = $(addsuffix .c,$(FILES))
+LIBOBJ = $(addsuffix .o,$(FILES))
+
+# rules
+
+all: $(LIBSTATIC) $(LIBDYNAMIC)
+
+$(LIBDYNAMIC): $(LIBOBJ)
+ifdef MAKE_DYNAMIC
+ ifeq ($(USESONAME),yes)
+ $(LD_L) $(SOSWITCH) $(LIBDYNMAJ) -o $@ $(LIBOBJ) $(LINKLIBS)
+ else
+ $(LD_L) -o $@ $(LIBOBJ)
+ endif
+ ifeq ($(NEEDSONAME),yes)
+ rm -f $(LIBDYNMIN)
+ ln -s $(LIBDYNAMIC) $(LIBDYNMAJ)
+ rm -f $(LIBDYNMAJ)
+ ln -s $(LIBDYNAMIC) $(LIBDYNMIN)
+ endif
+endif
+
+$(LIBSTATIC): $(LIBOBJ)
+ $(AR) $@ $(LIBOBJ)
+ $(RANLIB) $@
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(INCLUDED)
+ $(INSTALL) -m 644 ./pam_misc.h $(FAKEROOT)$(INCLUDED)
+ifdef MAKE_DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBDYNAMIC) $(FAKEROOT)$(LIBDIR)/$(LIBDYNMIN)
+ $(LDCONFIG)
+ ifneq ($(DYNTYPE),"sl")
+ ( cd $(FAKEROOT)$(LIBDIR) ; ln -sf $(LIBDYNMAJ) $(LIBDYNAMIC) )
+ endif
+endif
+ $(INSTALL) -m 644 $(LIBSTATIC) $(FAKEROOT)$(LIBDIR)
+
+clean:
+ rm -f *.so *.a core a.out *~
+
+remove:
+ rm -f $(FAKEROOT)$(INCLUDED)/pam_misc.h
+ rm -f $(FAKEROOT)$(LIBDIR)/$(LIBDYNAMIC).*
+ rm -f $(FAKEROOT)$(LIBDIR)/$(LIBDYNAMIC)
+ $(LDCONFIG)
+ rm -f $(FAKEROOT)$(LIBDIR)/$(LIBSTATIC)
+ rm -f $(FAKEROOT)$(INCLUDED)/chk_malloc.h
+
+.c.o:
+ $(CC) -c $(DEFS) $(CFLAGS) $<
+
+extraclean:
+ @$(MAKE) clean
+ rm -f *.o *.bak
+
diff --git a/libpam_misc/help_env.c b/libpam_misc/help_env.c
new file mode 100644
index 00000000..d52b3a02
--- /dev/null
+++ b/libpam_misc/help_env.c
@@ -0,0 +1,118 @@
+/*
+ * $Id$
+ *
+ * This file was written by Andrew G. Morgan <morgan@parc.power.net>
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:24 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:15 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.2 1997/01/04 20:19:20 morgan
+ * added a prototype (no warning) and fixed paste function
+ *
+ * Revision 1.1 1996/12/01 03:25:37 morgan
+ * Initial revision
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <security/pam_misc.h>
+
+/*
+ * This is a useful function for dumping the Linux-PAM environment
+ * into some local memory, prior to it all getting lost when pam_end()
+ * is called.
+ *
+ * Initially it was assumed that libpam did not do this part correctly
+ * (based on a loose email definition). The X/Open XSSO spec makes it
+ * clear that this function is a duplicate of the one already in
+ * libpam and therefore unnecessary. IT WILL BE COMPLETELY REMOVED
+ * IN libpam_misc 1.0 */
+
+char **pam_misc_copy_env(pam_handle_t *pamh);
+char **pam_misc_copy_env(pam_handle_t *pamh)
+{
+ return pam_getenvlist(pamh);
+}
+
+/*
+ * This function should be used to carefully dispose of the copied
+ * environment.
+ *
+ * usage: env = pam_misc_drop_env(env);
+ */
+
+char **pam_misc_drop_env(char **dump)
+{
+ int i;
+
+ for (i=0; dump[i] != NULL; ++i) {
+ D(("dump[%d]=`%s'", i, dump[i]));
+ _pam_overwrite(dump[i]);
+ _pam_drop(dump[i]);
+ }
+ _pam_drop(dump);
+
+ return NULL;
+}
+
+/*
+ * This function takes the supplied environment and uploads it to be
+ * the PAM one.
+ */
+
+int pam_misc_paste_env(pam_handle_t *pamh, const char * const * user_env)
+{
+ for (; user_env && *user_env; ++user_env) {
+ int retval;
+
+ D(("uploading: %s", *user_env));
+ retval = pam_putenv(pamh, *user_env);
+ if (retval != PAM_SUCCESS) {
+ D(("error setting %s: %s", *user_env, pam_strerror(pamh,retval)));
+ return retval;
+ }
+ }
+ D(("done."));
+ return PAM_SUCCESS;
+}
+
+/*
+ * This is a wrapper to make pam behave in the way that setenv() does.
+ */
+
+int pam_misc_setenv(pam_handle_t *pamh, const char *name
+ , const char *value, int readonly)
+{
+ char *tmp;
+ int retval;
+
+ if (readonly) {
+ const char *etmp;
+
+ /* we check if the variable is there already */
+ etmp = pam_getenv(pamh, name);
+ if (etmp != NULL) {
+ D(("failed to set readonly variable: %s", name));
+ return PAM_PERM_DENIED; /* not allowed to overwrite */
+ }
+ }
+ tmp = malloc(2+strlen(name)+strlen(value));
+ if (tmp != NULL) {
+ sprintf(tmp,"%s=%s",name,value);
+ D(("pam_putt()ing: %s", tmp));
+ retval = pam_putenv(pamh, tmp);
+ _pam_overwrite(tmp); /* purge */
+ _pam_drop(tmp); /* forget */
+ } else {
+ D(("malloc failure"));
+ retval = PAM_BUF_ERR;
+ }
+
+ return retval;
+}
diff --git a/libpam_misc/misc_conv.c b/libpam_misc/misc_conv.c
new file mode 100644
index 00000000..f2811a26
--- /dev/null
+++ b/libpam_misc/misc_conv.c
@@ -0,0 +1,344 @@
+/*
+ * $Id$
+ *
+ * A generic conversation function for text based applications
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org>
+ */
+
+#ifdef linux
+#define _GNU_SOURCE
+#include <features.h>
+#endif
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+
+#define INPUTSIZE PAM_MAX_MSG_SIZE /* maximum length of input+1 */
+#define CONV_ECHO_ON 1 /* types of echo state */
+#define CONV_ECHO_OFF 0
+
+/*
+ * external timeout definitions - these can be overriden by the
+ * application.
+ */
+
+time_t pam_misc_conv_warn_time = 0; /* time when we warn */
+time_t pam_misc_conv_die_time = 0; /* time when we timeout */
+
+const char *pam_misc_conv_warn_line = "..\a.Time is running out...\n";
+const char *pam_misc_conv_die_line = "..\a.Sorry, your time is up!\n";
+
+int pam_misc_conv_died=0; /* application can probe this for timeout */
+
+static void pam_misc_conv_delete_binary(void **delete_me)
+{
+ if (delete_me && *delete_me) {
+ unsigned char *packet = *(unsigned char **)delete_me;
+ int length;
+
+ length = (packet[0]<<24)+(packet[1]<<16)+(packet[2]<<8)+packet[3];
+ memset(packet, 0, length);
+ free(packet);
+ *delete_me = packet = NULL;
+ }
+}
+
+/* These function pointers are for application specific binary
+ conversations. One or both of the arguments to the first function
+ must be non-NULL. The first function must return PAM_SUCCESS or
+ PAM_CONV_ERR. If input is non-NULL, a response is expected, this
+ response should be malloc()'d and will eventually be free()'d by
+ the calling module. The structure of this malloc()'d response is as
+ follows:
+
+ { int length, char data[length] }
+
+ For convenience, the pointer used by the two function pointer
+ prototypes is 'void *'.
+
+ The ...free() fn pointer is used to discard a binary message that
+ is not of the default form. It should be explicitly overwritten
+ when using some other convention for the structure of a binary
+ prompt (not recommended). */
+
+int (*pam_binary_handler_fn)(const void *send, void **receive) = NULL;
+void (*pam_binary_handler_free)(void **packet_p) = pam_misc_conv_delete_binary;
+
+/* the following code is used to get text input */
+
+volatile static int expired=0;
+
+/* return to the previous signal handling */
+static void reset_alarm(struct sigaction *o_ptr)
+{
+ (void) alarm(0); /* stop alarm clock - if still ticking */
+ (void) sigaction(SIGALRM, o_ptr, NULL);
+}
+
+/* this is where we intercept the alarm signal */
+static void time_is_up(int ignore)
+{
+ expired = 1;
+}
+
+/* set the new alarm to hit the time_is_up() function */
+static int set_alarm(int delay, struct sigaction *o_ptr)
+{
+ struct sigaction new_sig;
+
+ sigemptyset(&new_sig.sa_mask);
+ new_sig.sa_flags = 0;
+ new_sig.sa_handler = time_is_up;
+ if ( sigaction(SIGALRM, &new_sig, o_ptr) ) {
+ return 1; /* setting signal failed */
+ }
+ if ( alarm(delay) ) {
+ (void) sigaction(SIGALRM, o_ptr, NULL);
+ return 1; /* failed to set alarm */
+ }
+ return 0; /* all seems to have worked */
+}
+
+/* return the number of seconds to next alarm. 0 = no delay, -1 = expired */
+static int get_delay(void)
+{
+ time_t now;
+
+ expired = 0; /* reset flag */
+ (void) time(&now);
+
+ /* has the quit time past? */
+ if (pam_misc_conv_die_time && now >= pam_misc_conv_die_time) {
+ fprintf(stderr,"%s",pam_misc_conv_die_line);
+
+ pam_misc_conv_died = 1; /* note we do not reset the die_time */
+ return -1; /* time is up */
+ }
+
+ /* has the warning time past? */
+ if (pam_misc_conv_warn_time && now >= pam_misc_conv_warn_time) {
+ fprintf(stderr, "%s", pam_misc_conv_warn_line);
+ pam_misc_conv_warn_time = 0; /* reset warn_time */
+
+ /* indicate remaining delay - if any */
+
+ return (pam_misc_conv_die_time ? pam_misc_conv_die_time - now:0 );
+ }
+
+ /* indicate possible warning delay */
+
+ if (pam_misc_conv_warn_time)
+ return (pam_misc_conv_warn_time - now);
+ else if (pam_misc_conv_die_time)
+ return (pam_misc_conv_die_time - now);
+ else
+ return 0;
+}
+
+/* read a line of input string, giving prompt when appropriate */
+static char *read_string(int echo, const char *prompt)
+{
+ struct termios term_before, term_tmp;
+ char line[INPUTSIZE];
+ struct sigaction old_sig;
+ int delay, nc, have_term=0;
+
+ D(("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt));
+
+ if (isatty(STDIN_FILENO)) { /* terminal state */
+
+ /* is a terminal so record settings and flush it */
+ if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) {
+ D(("<error: failed to get terminal settings>"));
+ return NULL;
+ }
+ memcpy(&term_tmp, &term_before, sizeof(term_tmp));
+ if (!echo) {
+ term_tmp.c_lflag &= ~(ECHO);
+ }
+ have_term = 1;
+
+ } else if (!echo) {
+ D(("<warning: cannot turn echo off>"));
+ }
+
+ /* set up the signal handling */
+ delay = get_delay();
+
+ /* reading the line */
+ while (delay >= 0) {
+
+ fprintf(stderr, "%s", prompt);
+ /* this may, or may not set echo off -- drop pending input */
+ if (have_term)
+ (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp);
+
+ if ( delay > 0 && set_alarm(delay, &old_sig) ) {
+ D(("<failed to set alarm>"));
+ break;
+ } else {
+ nc = read(STDIN_FILENO, line, INPUTSIZE-1);
+ if (have_term) {
+ (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
+ if (!echo || expired) /* do we need a newline? */
+ fprintf(stderr,"\n");
+ }
+ if ( delay > 0 ) {
+ reset_alarm(&old_sig);
+ }
+ if (expired) {
+ delay = get_delay();
+ } else if (nc > 0) { /* we got some user input */
+ char *input;
+
+ if (nc > 0 && line[nc-1] == '\n') { /* <NUL> terminate */
+ line[--nc] = '\0';
+ } else {
+ line[nc] = '\0';
+ }
+ input = x_strdup(line);
+ _pam_overwrite(line);
+
+ return input; /* return malloc()ed string */
+ } else if (nc == 0) { /* Ctrl-D */
+ D(("user did not want to type anything"));
+ fprintf(stderr, "\n");
+ break;
+ }
+ }
+ }
+
+ /* getting here implies that the timer expired */
+ if (have_term)
+ (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
+
+ memset(line, 0, INPUTSIZE); /* clean up */
+ return NULL;
+}
+
+/* end of read_string functions */
+
+int misc_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response, void *appdata_ptr)
+{
+ int count=0;
+ struct pam_response *reply;
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ D(("allocating empty response structure array."));
+
+ reply = (struct pam_response *) calloc(num_msg,
+ sizeof(struct pam_response));
+ if (reply == NULL) {
+ D(("no memory for responses"));
+ return PAM_CONV_ERR;
+ }
+
+ D(("entering conversation function."));
+
+ for (count=0; count < num_msg; ++count) {
+ char *string=NULL;
+
+ switch (msgm[count]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ string = read_string(CONV_ECHO_OFF,msgm[count]->msg);
+ if (string == NULL) {
+ goto failed_conversation;
+ }
+ break;
+ case PAM_PROMPT_ECHO_ON:
+ string = read_string(CONV_ECHO_ON,msgm[count]->msg);
+ if (string == NULL) {
+ goto failed_conversation;
+ }
+ break;
+ case PAM_ERROR_MSG:
+ if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) {
+ goto failed_conversation;
+ }
+ break;
+ case PAM_TEXT_INFO:
+ if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) {
+ goto failed_conversation;
+ }
+ break;
+ case PAM_BINARY_PROMPT:
+ {
+ void *pack_out=NULL;
+ const void *pack_in = msgm[count]->msg;
+
+ if (!pam_binary_handler_fn
+ || pam_binary_handler_fn(pack_in, &pack_out) != PAM_SUCCESS
+ || pack_out == NULL) {
+ goto failed_conversation;
+ }
+ string = (char *) pack_out;
+ pack_out = NULL;
+
+ break;
+ }
+ default:
+ fprintf(stderr, "erroneous conversation (%d)\n"
+ ,msgm[count]->msg_style);
+ goto failed_conversation;
+ }
+
+ if (string) { /* must add to reply array */
+ /* add string to list of responses */
+
+ reply[count].resp_retcode = 0;
+ reply[count].resp = string;
+ string = NULL;
+ }
+ }
+
+ /* New (0.59+) behavior is to always have a reply - this is
+ compatable with the X/Open (March 1997) spec. */
+ *response = reply;
+ reply = NULL;
+
+ return PAM_SUCCESS;
+
+failed_conversation:
+
+ if (reply) {
+ for (count=0; count<num_msg; ++count) {
+ if (reply[count].resp == NULL) {
+ continue;
+ }
+ switch (msgm[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ case PAM_PROMPT_ECHO_OFF:
+ _pam_overwrite(reply[count].resp);
+ free(reply[count].resp);
+ break;
+ case PAM_BINARY_PROMPT:
+ pam_binary_handler_free((void **) &reply[count].resp);
+ break;
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ /* should not actually be able to get here... */
+ free(reply[count].resp);
+ }
+ reply[count].resp = NULL;
+ }
+ /* forget reply too */
+ free(reply);
+ reply = NULL;
+ }
+
+ return PAM_CONV_ERR;
+}
+
diff --git a/libpam_misc/pam_misc.h b/libpam_misc/pam_misc.h
new file mode 100644
index 00000000..fbf7a9f1
--- /dev/null
+++ b/libpam_misc/pam_misc.h
@@ -0,0 +1,56 @@
+/* $Id$ */
+
+#ifndef __PAMMISC_H
+#define __PAMMISC_H
+
+#include <security/pam_appl.h>
+
+/* include some useful macros */
+
+#include <security/_pam_macros.h>
+
+/* functions defined in pam_misc.* libraries */
+
+extern int misc_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response, void *appdata_ptr);
+
+#include <time.h>
+
+extern time_t pam_misc_conv_warn_time; /* time that we should warn user */
+extern time_t pam_misc_conv_die_time; /* cut-off time for input */
+extern const char *pam_misc_conv_warn_line; /* warning notice */
+extern const char *pam_misc_conv_die_line; /* cut-off remark */
+extern int pam_misc_conv_died; /* 1 = cut-off time reached (0 not) */
+extern int (*pam_binary_handler_fn)(const void *send, void **receive);
+
+/*
+ * Environment helper functions
+ */
+
+/* transcribe given environment (to pam) */
+extern int pam_misc_paste_env(pam_handle_t *pamh
+ , const char * const * user_env);
+
+/* char **pam_misc_copy_env(pam_handle_t *pamh);
+
+ This is no longer defined as a prototype because the X/Open XSSO
+ spec makes it clear that PAM's pam_getenvlist() does exactly
+ what this was needed for.
+
+ A wrapper is still provided in the pam_misc library - so that
+ legacy applications will still work. But _BE_WARNED_ it will
+ disappear by the release of libpam 1.0 . */
+
+/* delete environment as obtained from (pam_getenvlist) */
+extern char **pam_misc_drop_env(char **env);
+
+/* provide something like the POSIX setenv function for the (Linux-)PAM
+ * environment. */
+
+extern int pam_misc_setenv(pam_handle_t *pamh, const char *name
+ , const char *value, int readonly);
+
+#endif
+
+
+
diff --git a/libpam_misc/xstrdup.c b/libpam_misc/xstrdup.c
new file mode 100644
index 00000000..6a4ca6f7
--- /dev/null
+++ b/libpam_misc/xstrdup.c
@@ -0,0 +1,31 @@
+/* $Id$ */
+
+#include <malloc.h>
+#include <string.h>
+#include <security/pam_misc.h>
+
+/*
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ */
+
+char *xstrdup(const char *x)
+{
+ register char *new=NULL;
+
+ if (x != NULL) {
+ register int i;
+
+ for (i=0; x[i]; ++i); /* length of string */
+ if ((new = malloc(++i)) == NULL) {
+ i = 0;
+ } else {
+ while (i-- > 0) {
+ new[i] = x[i];
+ }
+ }
+ x = NULL;
+ }
+
+ return new; /* return the duplicate or NULL on error */
+}
diff --git a/libpamc/.cvsignore b/libpamc/.cvsignore
new file mode 100644
index 00000000..ce01b11d
--- /dev/null
+++ b/libpamc/.cvsignore
@@ -0,0 +1,3 @@
+libpamc.so*
+static
+dynamic
diff --git a/libpamc/License b/libpamc/License
new file mode 100644
index 00000000..90106954
--- /dev/null
+++ b/libpamc/License
@@ -0,0 +1,42 @@
+Unless otherwise *explicitly* stated the following text describes the
+licensed conditions under which the contents of this libpamc release
+may be distributed:
+
+-------------------------------------------------------------------------
+Redistribution and use in source and binary forms of libpamc,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain any existing copyright
+ notice, and this entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+2. Redistributions in binary form must reproduce all prior and current
+ copyright notices, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+3. The name of any author may not be used to endorse or promote
+ products derived from this software without their specific prior
+ written permission.
+
+ALTERNATIVELY, this product may be distributed under the terms of the
+GNU Library General Public License (LGPL), in which case the
+provisions of the GNU LGPL are required INSTEAD OF the above
+restrictions. (This clause is necessary due to a potential conflict
+between the GNU LGPL and the restrictions contained in a BSD-style
+copyright.)
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+-------------------------------------------------------------------------
+
diff --git a/libpamc/Makefile b/libpamc/Makefile
new file mode 100644
index 00000000..6052e96e
--- /dev/null
+++ b/libpamc/Makefile
@@ -0,0 +1,110 @@
+#
+# $Id$
+#
+
+# lots of debugging information goes to /tmp/pam-debug.log
+#MOREFLAGS += -D"DEBUG"
+
+ifeq ($(DEBUG_REL),yes)
+ LIBNAME=libpamcd
+else
+ LIBNAME=libpamc
+endif
+VERSION=.$(MAJOR_REL)
+MODIFICATION=.$(MINOR_REL)
+
+# ---------------------------------------------
+
+dummy:
+ @echo "*** This is not a top-level Makefile!"
+
+# ---------------------------------------------
+
+CFLAGS += $(DYNAMIC) $(STATIC) $(MOREFLAGS)
+
+# dynamic library names
+
+LIBPAMC = $(LIBNAME).$(DYNTYPE)
+LIBPAMCNAME = $(LIBPAMC)$(VERSION)
+LIBPAMCFULL = $(LIBPAMCNAME)$(MODIFICATION)
+
+# static library name
+
+LIBPAMCSTATIC = $(LIBNAME).a
+
+LIBOBJECTS = pamc_client.o pamc_converse.o pamc_load.o
+
+ifdef DYNAMIC_LIBPAM
+DLIBOBJECTS = $(addprefix dynamic/,$(LIBOBJECTS))
+endif
+
+ifdef STATIC_LIBPAM
+SLIBOBJECTS = $(addprefix static/,$(LIBOBJECTS))
+endif
+
+# ---------------------------------------------
+## rules
+
+all: dirs $(LIBPAMC) $(LIBPAMCSTATIC)
+
+dirs:
+ifdef DYNAMIC_LIBPAM
+ mkdir -p dynamic
+endif
+ifdef STATIC_LIBPAM
+ mkdir -p static
+endif
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+$(LIBPAMC): $(DLIBOBJECTS)
+ifdef DYNAMIC_LIBPAM
+ ifeq ($(USESONAME),yes)
+ $(LD_L) $(SOSWITCH) $(LIBPAMCNAME) -o $@ $(DLIBOBJECTS) $(MODULES) $(LINKLIBS)
+ else
+ $(LD_L) -o $@ $(DLIBOBJECTS) $(MODULES)
+ endif
+ ifeq ($(NEEDSONAME),yes)
+ rm -f $(LIBPAMCFULL)
+ ln -s $(LIBPAMC) $(LIBPAMCFULL)
+ rm -f $(LIBPAMCNAME)
+ ln -s $(LIBPAMC) $(LIBPAMCNAME)
+ endif
+endif
+
+$(LIBPAMCSTATIC): $(SLIBOBJECTS)
+ifdef STATIC_LIBPAM
+ $(AR) $@ $(SLIBOBJECTS) $(MODULES)
+ $(RANLIB) $@
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(INCLUDED)
+ $(INSTALL) -m 644 include/security/pam_client.h $(FAKEROOT)$(INCLUDED)
+ifdef DYNAMIC_LIBPAM
+ $(INSTALL) -m $(SHLIBMODE) $(LIBPAMC) $(FAKEROOT)$(LIBDIR)/$(LIBPAMCFULL)
+ $(LDCONFIG)
+ ifneq ($(DYNTYPE),"sl")
+ ( cd $(FAKEROOT)$(LIBDIR) ; rm -f $(LIBPAMC) ; ln -s $(LIBPAMCNAME) $(LIBPAMC) )
+ endif
+endif
+ifdef STATIC_LIBPAM
+ $(INSTALL) -m 644 $(LIBPAMCSTATIC) $(FAKEROOT)$(LIBDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(INCLUDED)/pam_client.h
+ $(LDCONFIG)
+ rm -f $(FAKEROOT)$(LIBDIR)/$(LIBPAMCSTATIC)
+
+clean:
+ rm -f a.out core *~ static/*.o dynamic/*.o
+
+extraclean: clean
+ rm -f *.a *.out *.o *.so ./include/security/*~
+ if [ -d dynamic ]; then rmdir dynamic ; fi
+ if [ -d static ]; then rmdir static ; fi
diff --git a/libpamc/include/security/pam_client.h b/libpamc/include/security/pam_client.h
new file mode 100644
index 00000000..4995e667
--- /dev/null
+++ b/libpamc/include/security/pam_client.h
@@ -0,0 +1,186 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 1999 Andrew G. Morgan <morgan@linux.kernel.org>
+ *
+ * This header file provides the prototypes for the PAM client API
+ */
+
+#ifndef PAM_CLIENT_H
+#define PAM_CLIENT_H
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+/* opaque agent handling structure */
+
+typedef struct pamc_handle_s *pamc_handle_t;
+
+/* binary prompt structure pointer */
+#ifndef __u32
+# define __u32 unsigned int
+#endif
+#ifndef __u8
+# define __u8 unsigned char
+#endif
+typedef struct { __u32 length; __u8 control; } *pamc_bp_t;
+
+/*
+ * functions provided by libpamc
+ */
+
+/*
+ * Initialize the agent abstraction library
+ */
+
+pamc_handle_t pamc_start(void);
+
+/*
+ * Terminate the authentication process
+ */
+
+int pamc_end(pamc_handle_t *pch);
+
+/*
+ * force the loading of a specified agent
+ */
+
+int pamc_load(pamc_handle_t pch, const char *agent_id);
+
+/*
+ * Single conversation interface for binary prompts
+ */
+
+int pamc_converse(pamc_handle_t pch, pamc_bp_t *prompt_p);
+
+/*
+ * disable an agent
+ */
+
+int pamc_disable(pamc_handle_t pch, const char *agent_id);
+
+/*
+ * obtain a list of available agents
+ */
+
+char **pamc_list_agents(pamc_handle_t pch);
+
+/*
+ * PAM_BP_ MACROS for creating, destroying and manipulating binary prompts
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifndef PAM_BP_ASSERT
+# define PAM_BP_ASSERT(x) do { printf(__FILE__ "(%d): %s\n", \
+ __LINE__, x) ; exit(1); } while (0)
+#endif /* PAM_BP_ASSERT */
+
+#ifndef PAM_BP_CALLOC
+# define PAM_BP_CALLOC calloc
+#endif /* PAM_BP_CALLOC */
+
+#ifndef PAM_BP_FREE
+# define PAM_BP_FREE free
+#endif /* PAM_BP_FREE */
+
+#define __PAM_BP_OCTET(x,y) (*((y) + (__u8 *)(x)))
+
+#define PAM_BP_MIN_SIZE (sizeof(__u32) + sizeof(__u8))
+#define PAM_PB_MAX_LENGTH 0x8000 /* an advisory limit */
+#define PAM_BP_CONTROL(x) (__PAM_BP_OCTET(x,4))
+#define PAM_BP_SIZE(x) ((__PAM_BP_OCTET(x,0)<<24)+ \
+ (__PAM_BP_OCTET(x,1)<<16)+ \
+ (__PAM_BP_OCTET(x,2)<< 8)+ \
+ (__PAM_BP_OCTET(x,3) ))
+#define PAM_BP_LENGTH(x) (PAM_BP_SIZE(x) - PAM_BP_MIN_SIZE)
+#define PAM_BP_DATA(x) (PAM_BP_MIN_SIZE + (__u8 *) (x))
+
+/* Note, this macro always '\0' terminates renewed packets */
+
+#define PAM_BP_RENEW(old_p, cntrl, data_length) \
+do { \
+ if (old_p) { \
+ if (*(old_p)) { \
+ __u32 __size = PAM_BP_SIZE(*(old_p)); \
+ memset(*(old_p), 0, __size); \
+ PAM_BP_FREE(*(old_p)); \
+ } \
+ if (cntrl) { \
+ __u32 __size; \
+ \
+ __size = PAM_BP_MIN_SIZE + data_length; \
+ if ((*(old_p) = PAM_BP_CALLOC(1, 1+__size))) { \
+ __PAM_BP_OCTET(*(old_p), 3) = __size & 0xFF; \
+ __PAM_BP_OCTET(*(old_p), 2) = (__size>>=8) & 0xFF; \
+ __PAM_BP_OCTET(*(old_p), 1) = (__size>>=8) & 0xFF; \
+ __PAM_BP_OCTET(*(old_p), 0) = (__size>>=8) & 0xFF; \
+ (*(old_p))->control = cntrl; \
+ } else { \
+ PAM_BP_ASSERT("out of memory for binary prompt"); \
+ } \
+ } else { \
+ *old_p = NULL; \
+ } \
+ } else { \
+ PAM_BP_ASSERT("programming error, invalid binary prompt pointer"); \
+ } \
+} while (0)
+
+#define PAM_BP_FILL(prmpt, offset, length, data) \
+do { \
+ int bp_length; \
+ __u8 *prompt = (__u8 *) (prmpt); \
+ bp_length = PAM_BP_LENGTH(prompt); \
+ if (bp_length < ((length)+(offset))) { \
+ PAM_BP_ASSERT("attempt to write over end of prompt"); \
+ } \
+ memcpy((offset) + PAM_BP_DATA(prompt), (data), (length)); \
+} while (0)
+
+#define PAM_BP_EXTRACT(prmpt, offset, length, data) \
+do { \
+ int bp_length; \
+ __u8 *prompt = (__u8 *) (prmpt); \
+ bp_length = PAM_BP_LENGTH(prompt); \
+ if (((offset) < 0) || bp_length < ((length)+(offset)) \
+ || (length) < 0) { \
+ PAM_BP_ASSERT("invalid extraction from prompt"); \
+ } \
+ memcpy((data), (offset) + PAM_BP_DATA(prompt), (length)); \
+} while (0)
+
+
+/* Control types */
+
+#define PAM_BPC_FALSE 0
+#define PAM_BPC_TRUE 1
+
+#define PAM_BPC_OK 0x01 /* continuation packet */
+#define PAM_BPC_SELECT 0x02 /* initialization packet */
+#define PAM_BPC_DONE 0x03 /* termination packet */
+#define PAM_BPC_FAIL 0x04 /* unable to execute */
+
+/* The following control characters are only legal for echanges
+ between an agent and a client (it is the responsibility of the
+ client to enforce this rule in the face of a rogue server): */
+
+#define PAM_BPC_GETENV 0x41 /* obtain client env.var */
+#define PAM_BPC_PUTENV 0x42 /* set client env.var */
+#define PAM_BPC_TEXT 0x43 /* display message */
+#define PAM_BPC_ERROR 0x44 /* display error message */
+#define PAM_BPC_PROMPT 0x45 /* echo'd text prompt */
+#define PAM_BPC_PASS 0x46 /* non-echo'd text prompt*/
+
+/* quick check for prompts that are legal for the client (by
+ implication the server too) to send to libpamc */
+
+#define PAM_BPC_FOR_CLIENT(/* pamc_bp_t */ prompt) \
+ (((prompt)->control <= PAM_BPC_FAIL && (prompt)->control >= PAM_BPC_OK) \
+ ? PAM_BPC_TRUE:PAM_BPC_FALSE)
+
+
+#endif /* PAM_CLIENT_H */
diff --git a/libpamc/libpamc.h b/libpamc/libpamc.h
new file mode 100644
index 00000000..15662cc3
--- /dev/null
+++ b/libpamc/libpamc.h
@@ -0,0 +1,63 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@ftp.kernel.org>
+ *
+ */
+
+#ifndef LIBPAMC_H
+#define LIBPAMC_H
+
+#include <security/pam_client.h>
+#include <security/pam_misc.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#define _PAMC_DEFAULT_TOP_FD 10
+
+struct pamc_handle_s {
+ struct pamc_agent_s *current;
+ struct pamc_agent_s *chain;
+ struct pamc_blocked_s *blocked_agents;
+ int max_path;
+ char **agent_paths;
+ int combined_status;
+ int highest_fd_to_close;
+};
+
+typedef struct pamc_blocked_s {
+ char *id; /* <NUL> terminated */
+ struct pamc_blocked_s *next;
+} pamc_blocked_t;
+
+typedef struct pamc_agent_s {
+ char *id;
+ int id_length;
+ struct pamc_agent_s *next;
+ int writer; /* write to agent */
+ int reader; /* read from agent */
+ pid_t pid; /* agent process id */
+} pamc_agent_t;
+
+/* used to build a tree of unique, sorted agent ids */
+
+typedef struct pamc_id_node {
+ struct pamc_id_node *left, *right;
+ int child_count;
+ char *agent_id;
+} pamc_id_node_t;
+
+/* internal function */
+int __pamc_valid_agent_id(int id_length, const char *id);
+
+#define PAMC_SYSTEM_AGENT_PATH "/lib/pamc:/usr/lib/pamc"
+#define PAMC_SYSTEM_AGENT_SEPARATOR ':'
+
+#endif /* LIBPAMC_H */
diff --git a/libpamc/pamc_client.c b/libpamc/pamc_client.c
new file mode 100644
index 00000000..9d2bc671
--- /dev/null
+++ b/libpamc/pamc_client.c
@@ -0,0 +1,189 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@ftp.kernel.org>
+ *
+ * pamc_start and pamc_end
+ */
+
+#include "libpamc.h"
+
+/*
+ * liberate path list
+ */
+
+static void __pamc_delete_path_list(pamc_handle_t pch)
+{
+ int i;
+
+ for (i=0; pch->agent_paths[i]; ++i) {
+ free(pch->agent_paths[i]);
+ pch->agent_paths[i] = NULL;
+ }
+
+ free(pch->agent_paths);
+ pch->agent_paths = NULL;
+}
+
+/*
+ * open the pamc library
+ */
+
+pamc_handle_t pamc_start(void)
+{
+ int i, count, last, this;
+ const char *default_path;
+ pamc_handle_t pch;
+
+ pch = calloc(1, sizeof(struct pamc_handle_s));
+ if (pch == NULL) {
+ D(("no memory for *pch"));
+ return NULL;
+ }
+
+ pch->highest_fd_to_close = _PAMC_DEFAULT_TOP_FD;
+
+ default_path = getenv("PAMC_AGENT_PATH");
+ if (default_path == NULL) {
+ default_path = PAMC_SYSTEM_AGENT_PATH;
+ }
+
+ /* number of individual paths */
+ for (count=1, i=0; default_path[i]; ++i) {
+ if (default_path[i] == PAMC_SYSTEM_AGENT_SEPARATOR) {
+ ++count;
+ }
+ }
+
+ pch->agent_paths = calloc(count+1, sizeof(char *));
+ if (pch->agent_paths == NULL) {
+ D(("no memory for path list"));
+ goto drop_pch;
+ }
+
+ this = last = i = 0;
+ while ( default_path[i] || (i != last) ) {
+ if ( default_path[i] == PAMC_SYSTEM_AGENT_SEPARATOR
+ || !default_path[i] ) {
+ int length;
+
+ pch->agent_paths[this] = malloc(length = 1+i-last);
+
+ if (pch->agent_paths[this] == NULL) {
+ D(("no memory for next path"));
+ goto drop_list;
+ }
+
+ memcpy(pch->agent_paths[this], default_path + last, i-last);
+ pch->agent_paths[this][i-last] = '\0';
+ if (length > pch->max_path) {
+ pch->max_path = length;
+ }
+
+ if (++this == count) {
+ break;
+ }
+
+ last = ++i;
+ } else {
+ ++i;
+ }
+ }
+
+ return pch;
+
+drop_list:
+ __pamc_delete_path_list(pch);
+
+drop_pch:
+ free(pch);
+
+ return NULL;
+}
+
+/*
+ * shutdown each of the loaded agents and
+ */
+
+static int __pamc_shutdown_agents(pamc_handle_t pch)
+{
+ int retval = PAM_BPC_TRUE;
+
+ D(("called"));
+
+ while (pch->chain) {
+ pid_t pid;
+ int status;
+ pamc_agent_t *this;
+
+ this = pch->chain;
+ D(("cleaning up agent %p", this));
+ pch->chain = pch->chain->next;
+ this->next = NULL;
+ D(("cleaning up agent: %s", this->id));
+
+ /* close off contact with agent and wait for it to shutdown */
+
+ close(this->writer);
+ this->writer = -1;
+ close(this->reader);
+ this->reader = -1;
+
+ pid = waitpid(this->pid, &status, 0);
+ if (pid == this->pid) {
+
+ D(("is exit:%d, exit val:%d",
+ WIFEXITED(status), WEXITSTATUS(status)));
+
+ if (!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
+ retval = PAM_BPC_FALSE;
+ }
+ } else {
+ D(("problem shutting down agent (%s): pid(%d) != waitpid(%d)!?",
+ this->id, this->pid, pid));
+ retval = PAM_BPC_FALSE;
+ }
+ pid = this->pid = 0;
+
+ memset(this->id, 0, this->id_length);
+ free(this->id);
+ this->id = NULL;
+ this->id_length = 0;
+
+ free(this);
+ this = NULL;
+ }
+
+ return retval;
+}
+
+/*
+ * close the pamc library
+ */
+
+int pamc_end(pamc_handle_t *pch_p)
+{
+ int retval;
+
+ if (pch_p == NULL) {
+ D(("called with no pch_p"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (*pch_p == NULL) {
+ D(("called with no *pch_p"));
+ return PAM_BPC_FALSE;
+ }
+
+ D(("removing path_list"));
+ __pamc_delete_path_list(*pch_p);
+
+ D(("shutting down agents"));
+ retval = __pamc_shutdown_agents(*pch_p);
+
+ D(("freeing *pch_p"));
+ free(*pch_p);
+ *pch_p = NULL;
+
+ return retval;
+}
diff --git a/libpamc/pamc_converse.c b/libpamc/pamc_converse.c
new file mode 100644
index 00000000..92ef7525
--- /dev/null
+++ b/libpamc/pamc_converse.c
@@ -0,0 +1,211 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@ftp.kernel.org>
+ *
+ * pamc_converse
+ */
+
+#include "libpamc.h"
+
+/*
+ * select agent
+ */
+
+static int __pamc_select_agent(pamc_handle_t pch, char *agent_id)
+{
+ pamc_agent_t *agent;
+
+ for (agent = pch->chain; agent; agent = agent->next) {
+ if (!strcmp(agent->id, agent_id)) {
+ pch->current = agent;
+ return PAM_BPC_TRUE;
+ }
+ }
+
+ D(("failed to locate agent"));
+ pch->current = NULL;
+ return PAM_BPC_FALSE;
+}
+
+/*
+ * pass a binary prompt to the active agent and wait for a reply prompt
+ */
+
+int pamc_converse(pamc_handle_t pch, pamc_bp_t *prompt_p)
+{
+ __u32 size, offset=0;
+ __u8 control, raw[PAM_BP_MIN_SIZE];
+
+ D(("called"));
+
+ if (pch == NULL) {
+ D(("null pch"));
+ goto pamc_converse_failure;
+ }
+
+ if (prompt_p == NULL) {
+ D(("null prompt_p"));
+ goto pamc_converse_failure;
+ }
+
+ if (*prompt_p == NULL) {
+ D(("null *prompt_p"));
+ goto pamc_converse_failure;
+ }
+
+ /* from here on, failures are interoperability problems.. */
+
+ size = PAM_BP_SIZE(*prompt_p);
+ if (size < PAM_BP_MIN_SIZE) {
+ D(("problem with size being too short (%u)", size));
+ goto pamc_unknown_prompt;
+ }
+
+ if (PAM_BPC_FOR_CLIENT(*prompt_p) != PAM_BPC_TRUE) {
+ D(("*prompt_p is not legal for the client to use"));
+ goto pamc_unknown_prompt;
+ }
+
+ /* do we need to select the agent? */
+ if ((*prompt_p)->control == PAM_BPC_SELECT) {
+ char *rawh;
+ int i, retval;
+
+ D(("selecting a specified agent"));
+
+ rawh = (char *) *prompt_p;
+ for (i = PAM_BP_MIN_SIZE; i<size; ++i) {
+ if (rawh[i] == '/') {
+ break;
+ }
+ }
+
+ if ( (i >= size)
+ || !__pamc_valid_agent_id(i-PAM_BP_MIN_SIZE,
+ rawh + PAM_BP_MIN_SIZE) ) {
+ goto pamc_unknown_prompt;
+ }
+
+ rawh[i] = '\0';
+ retval = pamc_load(pch, PAM_BP_MIN_SIZE + rawh);
+ if (retval == PAM_BPC_TRUE) {
+ retval = __pamc_select_agent(pch, PAM_BP_MIN_SIZE + rawh);
+ }
+ rawh[i] = '/';
+
+ if (retval != PAM_BPC_TRUE) {
+ goto pamc_unknown_prompt;
+ }
+
+ D(("agent is loaded"));
+ }
+
+ if (pch->current == NULL) {
+ D(("unable to address agent"));
+ goto pamc_unknown_prompt;
+ }
+
+ /* pump all of the prompt into the agent */
+ do {
+ int rval = write(pch->current->writer,
+ offset + (const __u8 *) (*prompt_p),
+ size - offset);
+ if (rval == -1) {
+ switch (errno) {
+ case EINTR:
+ break;
+ default:
+ D(("problem writing to agent: %s", strerror(errno)));
+ goto pamc_unknown_prompt;
+ }
+ } else {
+ offset += rval;
+ }
+ } while (offset < size);
+
+ D(("whole prompt sent to agent"));
+
+ /* read size and control for response prompt */
+
+ offset = 0;
+ memset(raw, 0, sizeof(raw));
+ do {
+ int rval;
+
+ rval = read(pch->current->reader, raw + offset,
+ PAM_BP_MIN_SIZE - offset);
+
+ if (rval == -1) {
+ switch (errno) {
+ case EINTR:
+ break;
+ default:
+ D(("problem reading from agent: %s", strerror(errno)));
+ goto pamc_unknown_prompt;
+ }
+ } else if (rval) {
+ offset += rval;
+ } else {
+ D(("agent has closed its output pipe - nothing more to read"));
+ goto pamc_converse_failure;
+ }
+ } while (offset < PAM_BP_MIN_SIZE);
+
+ /* construct the whole reply prompt */
+
+ size = PAM_BP_SIZE(raw);
+ control = PAM_BP_CONTROL(raw);
+ memset(raw, 0, sizeof(raw));
+
+ D(("agent replied with prompt of size %d and control %u",
+ size, control));
+
+ PAM_BP_RENEW(prompt_p, control, size - PAM_BP_MIN_SIZE);
+ if (*prompt_p == NULL) {
+ D(("problem making a new prompt for reply"));
+ goto pamc_unknown_prompt;
+ }
+
+ /* read the rest of the reply prompt -- note offset has the correct
+ value from the previous loop */
+
+ while (offset < size) {
+ int rval = read(pch->current->reader, offset + (__u8 *) *prompt_p,
+ size-offset);
+
+ if (rval == -1) {
+ switch (errno) {
+ case EINTR:
+ break;
+ default:
+ D(("problem reading from agent: %s", strerror(errno)));
+ goto pamc_unknown_prompt;
+ }
+ } else if (rval) {
+ offset += rval;
+ } else {
+ D(("problem reading prompt (%d) with %d to go",
+ size, size-offset));
+ goto pamc_converse_failure;
+ }
+ }
+
+ D(("returning success"));
+
+ return PAM_BPC_TRUE;
+
+pamc_converse_failure:
+
+ D(("conversation failure"));
+ PAM_BP_RENEW(prompt_p, 0, 0);
+ return PAM_BPC_FALSE;
+
+pamc_unknown_prompt:
+
+ /* the server is trying something that the client does not support */
+ D(("unknown prompt"));
+ PAM_BP_RENEW(prompt_p, PAM_BPC_FAIL, 0);
+ return PAM_BPC_TRUE;
+}
+
diff --git a/libpamc/pamc_load.c b/libpamc/pamc_load.c
new file mode 100644
index 00000000..b3c0b5d5
--- /dev/null
+++ b/libpamc/pamc_load.c
@@ -0,0 +1,477 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 1999 Andrew G. Morgan <morgan@ftp.kernel.org>
+ *
+ * pamc_load
+ */
+
+#include "libpamc.h"
+
+static int __pamc_exec_agent(pamc_handle_t pch, pamc_agent_t *agent)
+{
+ char *full_path;
+ int found_agent, length, reset_length, to_agent[2], from_agent[2];
+ int return_code = PAM_BPC_FAIL;
+
+ if (agent->id[agent->id_length] != '\0') {
+ PAM_BP_ASSERT("libpamc: internal error agent_id not terminated");
+ }
+
+ for (length=0; (length < agent->id_length); ++length) {
+ switch (agent->id[length]) {
+ case '/':
+ D(("ill formed agent id"));
+ return PAM_BPC_FAIL;
+ }
+ }
+
+ /* enough memory for any path + this agent */
+ reset_length = 3 + pch->max_path + agent->id_length;
+ D(("reset_length = %d (3+%d+%d)",
+ reset_length, pch->max_path, agent->id_length));
+ full_path = malloc(reset_length);
+ if (full_path == NULL) {
+ D(("no memory for agent path"));
+ return PAM_BPC_FAIL;
+ }
+
+ found_agent = 0;
+ for (length=0; pch->agent_paths[length]; ++length) {
+ struct stat buf;
+
+ D(("path: [%s]", pch->agent_paths[length]));
+ D(("agent id: [%s]", agent->id));
+
+ sprintf(full_path, "%s/%s", pch->agent_paths[length], agent->id);
+
+ D(("looking for agent here: [%s]\n", full_path));
+ if (stat(full_path, &buf) == 0) {
+ D(("file existis"));
+ found_agent = 1;
+ break;
+ }
+ }
+
+ if (! found_agent) {
+ D(("no agent was found"));
+ goto free_and_return;
+ }
+
+ if (pipe(to_agent)) {
+ D(("failed to open pipe to agent"));
+ goto free_and_return;
+ }
+
+ if (pipe(from_agent)) {
+ D(("failed to open pipe from agent"));
+ goto close_the_agent;
+ }
+
+ agent->pid = fork();
+ if (agent->pid == -1) {
+
+ D(("failed to fork for agent"));
+ goto close_both_pipes;
+
+ } else if (agent->pid == 0) {
+
+ int i;
+
+ dup2(from_agent[1], STDOUT_FILENO);
+ dup2(to_agent[0], STDIN_FILENO);
+
+ /* we close all of the files that have filedescriptors lower
+ and equal to twice the highest we have seen, The idea is
+ that we don't want to leak filedescriptors to agents from a
+ privileged client application.
+
+ XXX - this is a heuristic at this point. There is a growing
+ need for an extra 'set param' libpamc function, that could
+ be used to supply info like the highest fd to close etc..
+ */
+
+ if (from_agent[1] > pch->highest_fd_to_close) {
+ pch->highest_fd_to_close = 2*from_agent[1];
+ }
+
+ for (i=0; i <= pch->highest_fd_to_close; ++i) {
+ switch (i) {
+ case STDOUT_FILENO:
+ case STDERR_FILENO:
+ case STDIN_FILENO:
+ /* only these three remain open */
+ break;
+ default:
+ (void) close(i); /* don't care if its not open */
+ }
+ }
+
+ /* we make no attempt to drop other privileges - this library
+ has no idea how that would be done in the general case. It
+ is up to the client application (when calling
+ pamc_converse) to make sure no privilege will leak into an
+ (untrusted) agent. */
+
+ /* we propogate no environment - future versions of this
+ library may have the ability to audit all agent
+ transactions. */
+
+ D(("exec'ing agent %s", full_path));
+ execle(full_path, "pam-agent", NULL, NULL);
+
+ D(("exec failed"));
+ exit(1);
+
+ }
+
+ close(to_agent[0]);
+ close(from_agent[1]);
+
+ agent->writer = to_agent[1];
+ agent->reader = from_agent[0];
+
+ return_code = PAM_BPC_TRUE;
+ goto free_and_return;
+
+close_both_pipes:
+ close(from_agent[0]);
+ close(from_agent[1]);
+
+close_the_agent:
+ close(to_agent[0]);
+ close(to_agent[1]);
+
+free_and_return:
+ memset(full_path, 0, reset_length);
+ free(full_path);
+
+ D(("returning %d", return_code));
+
+ return return_code;
+}
+
+/*
+ * has the named agent been loaded?
+ */
+
+static int __pamc_agent_is_enabled(pamc_handle_t pch, const char *agent_id)
+{
+ pamc_agent_t *agent;
+
+ for (agent = pch->chain; agent; agent = agent->next) {
+ if (!strcmp(agent->id, agent_id)) {
+ D(("agent already loaded"));
+ return PAM_BPC_TRUE;
+ }
+ }
+
+ D(("agent is not loaded"));
+ return PAM_BPC_FALSE;
+}
+
+/*
+ * has the named agent been disabled?
+ */
+
+static int __pamc_agent_is_disabled(pamc_handle_t pch, const char *agent_id)
+{
+ pamc_blocked_t *blocked;
+
+ for (blocked=pch->blocked_agents; blocked; blocked = blocked->next) {
+ if (!strcmp(agent_id, blocked->id)) {
+ D(("agent is disabled"));
+ return PAM_BPC_TRUE;
+ }
+ }
+
+ D(("agent is not disabled"));
+ return PAM_BPC_FALSE;
+}
+
+/*
+ * disable an agent
+ */
+
+int pamc_disable(pamc_handle_t pch, const char *agent_id)
+{
+ pamc_blocked_t *block;
+
+ if (pch == NULL) {
+ D(("pch is NULL"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (agent_id == NULL) {
+ D(("agent_id is NULL"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (__pamc_agent_is_enabled(pch, agent_id) != PAM_BPC_FALSE) {
+ D(("agent is already loaded"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) {
+ D(("agent is already disabled"));
+ return PAM_BPC_TRUE;
+ }
+
+ block = calloc(1, sizeof(pamc_blocked_t));
+ if (block == NULL) {
+ D(("no memory for new blocking structure"));
+ return PAM_BPC_FALSE;
+ }
+
+ block->id = malloc(1 + strlen(agent_id));
+ if (block->id == NULL) {
+ D(("no memory for agent id"));
+ free(block);
+ return PAM_BPC_FALSE;
+ }
+
+ strcpy(block->id, agent_id);
+ block->next = pch->blocked_agents;
+ pch->blocked_agents = block;
+
+ return PAM_BPC_TRUE;
+}
+
+/*
+ * force the loading of a particular agent
+ */
+
+int pamc_load(pamc_handle_t pch, const char *agent_id)
+{
+ pamc_agent_t *agent;
+ int length;
+
+ /* santity checking */
+
+ if (pch == NULL) {
+ D(("pch is NULL"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (agent_id == NULL) {
+ D(("agent_id is NULL"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) {
+ D(("sorry agent is disabled"));
+ return PAM_BPC_FALSE;
+ }
+
+ length = strlen(agent_id);
+
+ /* scan list to see if agent is loaded */
+
+ if (__pamc_agent_is_enabled(pch, agent_id) == PAM_BPC_TRUE) {
+ D(("no need to load an already loaded agent (%s)", agent_id));
+ return PAM_BPC_TRUE;
+ }
+
+ /* not in the list, so we need to load it and add it to the head
+ of the chain */
+
+ agent = calloc(1, sizeof(pamc_agent_t));
+ if (agent == NULL) {
+ D(("no memory for new agent"));
+ return PAM_BPC_FALSE;
+ }
+ agent->id = calloc(1, 1+length);
+ if (agent->id == NULL) {
+ D(("no memory for new agent's id"));
+ goto fail_free_agent;
+ }
+ memcpy(agent->id, agent_id, length);
+ agent->id[length] = '\0';
+ agent->id_length = length;
+
+ if (__pamc_exec_agent(pch, agent) != PAM_BPC_TRUE) {
+ D(("unable to exec agent"));
+ goto fail_free_agent_id;
+ }
+
+ agent->next = pch->chain;
+ pch->chain = agent;
+
+ return PAM_BPC_TRUE;
+
+fail_free_agent_id:
+
+ memset(agent->id, 0, agent->id_length);
+ free(agent->id);
+
+ memset(agent, 0, sizeof(*agent));
+
+fail_free_agent:
+
+ free(agent);
+ return PAM_BPC_FALSE;
+}
+
+/*
+ * what's a valid agent name?
+ */
+
+int __pamc_valid_agent_id(int id_length, const char *id)
+{
+ int post, i;
+
+ for (i=post=0 ; i < id_length; ++i) {
+ int ch = id[i++];
+
+ if (isalpha(ch) || isdigit(ch) || (ch == '_')) {
+ continue;
+ } else if (post && (ch == '.')) {
+ continue;
+ } else if ((i > 1) && (!post) && (ch == '@')) {
+ post = 1;
+ } else {
+ D(("id=%s contains '%c' which is illegal", id, ch));
+ return 0;
+ }
+ }
+
+ if (!i) {
+ D(("length of id is 0"));
+ return 0;
+ } else {
+ return 1; /* id is valid */
+ }
+}
+
+/*
+ * building a tree of available agent names
+ */
+
+static pamc_id_node_t *__pamc_add_node(pamc_id_node_t *root, const char *id,
+ int *counter)
+{
+ if (root) {
+
+ int cmp;
+
+ if ((cmp = strcmp(id, root->agent_id))) {
+ if (cmp > 0) {
+ root->right = __pamc_add_node(root->right, id,
+ &(root->child_count));
+ } else {
+ root->left = __pamc_add_node(root->left, id,
+ &(root->child_count));
+ }
+ }
+
+ return root;
+
+ } else {
+
+ pamc_id_node_t *node = calloc(1, sizeof(pamc_id_node_t));
+
+ if (node) {
+ node->agent_id = malloc(1+strlen(id));
+ if (node->agent_id) {
+ strcpy(node->agent_id, id);
+ } else {
+ free(node);
+ node = NULL;
+ }
+ }
+
+ (*counter)++;
+ return node;
+ }
+}
+
+/*
+ * drop all of the tree and any remaining ids
+ */
+
+static pamc_id_node_t *__pamc_liberate_nodes(pamc_id_node_t *tree)
+{
+ if (tree) {
+ if (tree->agent_id) {
+ free(tree->agent_id);
+ tree->agent_id = NULL;
+ }
+
+ tree->left = __pamc_liberate_nodes(tree->left);
+ tree->right = __pamc_liberate_nodes(tree->right);
+
+ tree->child_count = 0;
+ free(tree);
+ }
+
+ return NULL;
+}
+
+/*
+ * fill a list with the contents of the tree (in ascii order)
+ */
+
+static void __pamc_fill_list_from_tree(pamc_id_node_t *tree, char **agent_list,
+ int *counter)
+{
+ if (tree) {
+ __pamc_fill_list_from_tree(tree->left, agent_list, counter);
+ agent_list[(*counter)++] = tree->agent_id;
+ tree->agent_id = NULL;
+ __pamc_fill_list_from_tree(tree->right, agent_list, counter);
+ }
+}
+
+/*
+ * get a list of the available agents
+ */
+
+char **pamc_list_agents(pamc_handle_t pch)
+{
+ int i, total_agent_count=0;
+ pamc_id_node_t *tree = NULL;
+ char **agent_list;
+
+ /* loop over agent paths */
+
+ for (i=0; pch->agent_paths[i]; ++i) {
+ DIR *dir;
+
+ dir = opendir(pch->agent_paths[i]);
+ if (dir) {
+ struct dirent *item;
+
+ while ((item = readdir(dir))) {
+
+ /* this is a cheat on recognizing agent_ids */
+ if (!__pamc_valid_agent_id(strlen(item->d_name),
+ item->d_name)) {
+ continue;
+ }
+
+ tree = __pamc_add_node(tree, item->d_name, &total_agent_count);
+ }
+
+ closedir(dir);
+ }
+ }
+
+ /* now, we build a list of ids */
+ D(("total of %d available agents\n", total_agent_count));
+
+ agent_list = calloc(total_agent_count+1, sizeof(char *));
+ if (agent_list) {
+ int counter=0;
+
+ __pamc_fill_list_from_tree(tree, agent_list, &counter);
+ if (counter != total_agent_count) {
+ PAM_BP_ASSERT("libpamc: internal error transcribing tree");
+ }
+ } else {
+ D(("no memory for agent list"));
+ }
+
+ __pamc_liberate_nodes(tree);
+
+ return agent_list;
+}
diff --git a/libpamc/test/agents/secret@here b/libpamc/test/agents/secret@here
new file mode 100755
index 00000000..18d8a661
--- /dev/null
+++ b/libpamc/test/agents/secret@here
@@ -0,0 +1,305 @@
+#!/usr/bin/perl
+#
+# This is a simple example PAM authentication agent, it implements a
+# simple shared secret authentication scheme. The PAM module pam_secret.so
+# is its counter part. Both the agent and the remote server are able to
+# authenticate one another, but the server is given the opportunity to
+# ignore a failed authentication.
+#
+
+$^W = 1;
+use strict;
+use IPC::Open2;
+$| = 1;
+
+# display extra information to STDERR
+my $debug = 0;
+if (scalar @ARGV) {
+ $debug = 1;
+}
+
+# Globals
+
+my %state;
+my $default_key;
+
+my $next_key = $$;
+
+# loop over binary prompts
+for (;;) {
+ my ($control, $data) = ReadBinaryPrompt();
+ my ($reply_control, $reply_data);
+
+ if ($control == 0) {
+ if ($debug) {
+ print STDERR "agent: no packet to read\n";
+ }
+ last;
+ } elsif ($control == 0x02) {
+ ($reply_control, $reply_data) = HandleAgentSelection($data);
+ } elsif ($control == 0x01) {
+ ($reply_control, $reply_data) = HandleContinuation($data);
+ } else {
+ if ($debug) {
+ print STDERR
+ "agent: unrecognized packet $control {$data} to read\n";
+ }
+ ($reply_control, $reply_data) = (0x04, "");
+ }
+
+ WriteBinaryPrompt($reply_control, $reply_data);
+}
+
+# Only willing to exit well if we've completed our authentication exchange
+
+if (scalar keys %state) {
+ if ($debug) {
+ print STDERR "The following sessions are still active:\n ";
+ print STDERR join ', ', keys %state;
+ print STDERR "\n";
+ }
+ exit 1;
+} else {
+ exit 0;
+}
+
+sub HandleAgentSelection ($) {
+ my ($data) = @_;
+
+ unless ( $data =~ /^([a-zA-Z0-9_]+\@?[a-zA-Z0-9_.]*)\/(.*)$/ ) {
+ return (0x04, "");
+ }
+
+ my ($agent_name, $payload) = ($1, $2);
+ if ($debug) {
+ print STDERR "agent: ". "agent=$agent_name, payload=$payload\n";
+ }
+
+ # this agent has a defined name
+ if ($agent_name ne "secret\@here") {
+ if ($debug) {
+ print STDERR "bad agent name: [$agent_name]\n";
+ }
+ return (0x04, "");
+ }
+
+ # the selection request is acompanied with a hexadecimal cookie
+ my @tokens = split '\|', $payload;
+
+ unless ((scalar @tokens) == 2) {
+ if ($debug) {
+ print STDERR "bad payload\n";
+ }
+ return (0x04, "");
+ }
+
+ unless ($tokens[1] =~ /^[a-z0-9]+$/) {
+ if ($debug) {
+ print STDERR "bad server cookie\n";
+ }
+ return (0x04, "");
+ }
+
+ my $shared_secret = IdentifyLocalSecret($tokens[0]);
+
+ unless (defined $shared_secret) {
+ # make a secret up
+ if ($debug) {
+ print STDERR "agent: cannot authenticate user\n";
+ }
+ $shared_secret = GetRandom();
+ }
+
+ my $local_cookie = GetRandom();
+ $default_key = $next_key++;
+
+ $state{$default_key} = $local_cookie ."|". $tokens[1] ."|". $shared_secret;
+
+ if ($debug) {
+ print STDERR "agent: \$state{$default_key} = $state{$default_key}\n";
+ }
+
+ return (0x01, $default_key ."|". $local_cookie);
+}
+
+sub HandleContinuation ($) {
+ my ($data) = @_;
+
+ my ($key, $server_digest) = split '\|', $data;
+
+ unless (defined $state{$key}) {
+ # retries and out of sequence prompts are not permitted
+ return (0x04, "");
+ }
+
+ my $expected_digest = CreateDigest($state{$key});
+ my ($local_cookie, $remote_cookie, $shared_secret)
+ = split '\|', $state{$key};
+ delete $state{$key};
+
+ unless ($expected_digest eq $server_digest) {
+ if ($debug) {
+ print STDERR "agent: don't trust server - faking reply\n";
+ print STDERR "agent: got ($server_digest)\n";
+ print STDERR "agent: expected ($expected_digest)\n";
+ }
+
+ ## FIXME: Agent should exchange a prompt with the client warning
+ ## that the server is faking us out.
+
+ return (0x03, CreateDigest($expected_digest . $data . GetRandom()));
+ }
+
+ if ($debug) {
+ print STDERR "agent: server appears to know the secret\n";
+ }
+
+ my $session_authenticated_ticket =
+ CreateDigest($remote_cookie."|".$shared_secret."|".$local_cookie);
+
+ # FIXME: Agent should set a derived session key environment
+ # variable (available for the client (and its children) to sign
+ # future data exchanges.
+
+ if ($debug) {
+ print STDERR "agent: should putenv("
+ ."\"AUTH_SESSION_TICKET=$session_authenticated_ticket\")\n";
+ }
+
+ # return agent's authenticating digest
+ return (0x03, CreateDigest($shared_secret."|".$remote_cookie
+ ."|".$local_cookie));
+}
+
+sub ReadBinaryPrompt {
+ my $buffer = " ";
+ my $count = read(STDIN, $buffer, 5);
+ if ($count == 0) {
+ # no more packets to read
+ return (0, "");
+ }
+
+ if ($count != 5) {
+ # broken packet header
+ return (-1, "");
+ }
+
+ my ($length, $control) = unpack("N C", $buffer);
+ if ($length < 5) {
+ # broken packet length
+ return (-1, "");
+ }
+
+ my $data = "";
+ $length -= 5;
+ while ($count = read(STDIN, $buffer, $length)) {
+ $data .= $buffer;
+ if ($count != $length) {
+ $length -= $count;
+ next;
+ }
+
+ if ($debug) {
+ print STDERR "agent: ". "data is [$data]\n";
+ }
+
+ return ($control, $data);
+ }
+
+ # broken packet data
+ return (-1, "");
+}
+
+sub WriteBinaryPrompt ($$) {
+ my ($control, $data) = @_;
+
+ my $length = 5 + length($data);
+ if ($debug) {
+ printf STDERR "agent: ". "{%d|0x%.2x|%s}\n", $length, $control, $data;
+ }
+ my $bp = pack("N C a*", $length, $control, $data);
+ print STDOUT $bp;
+ if ($debug) {
+ printf STDERR "agent: ". "agent has replied\n";
+ }
+}
+
+##
+## Here is where we parse the simple secret file
+## The format of this file is a list of lines of the following form:
+##
+## user@client0.host.name secret_string1
+## user@client1.host.name secret_string2
+## user@client2.host.name secret_string3
+##
+
+sub IdentifyLocalSecret ($) {
+ my ($identifier) = @_;
+ my $secret;
+
+ if (open SECRETS, "< ". (getpwuid($<))[7] ."/.secret\@here") {
+ my $line;
+ while (defined ($line = <SECRETS>)) {
+ my ($id, $sec) = split /[\s]+/, $line;
+ if ((defined $id) && ($id eq $identifier)) {
+ $secret = $sec;
+ last;
+ }
+ }
+ close SECRETS;
+ }
+
+ return $secret;
+}
+
+## Here is where we generate a message digest
+
+sub CreateDigest ($) {
+ my ($data) = @_;
+
+ my $pid = open2(\*MD5out, \*MD5in, "/usr/bin/md5sum -")
+ or die "you'll need /usr/bin/md5sum installed";
+
+ my $oldfd = select MD5in; $|=1; select $oldfd;
+ print MD5in "$data";
+ close MD5in;
+ my $reply = <MD5out>;
+ ($reply) = split /\s/, $reply;
+ if ($debug) {
+ print STDERR "agent: ". "md5 said: <$reply>\n";
+ }
+ close MD5out;
+
+ return $reply;
+}
+
+## get a random number
+
+sub GetRandom {
+
+ if ( -r "/dev/urandom" ) {
+ open RANDOM, "< /dev/urandom" or die "crazy";
+
+ my $i;
+ my $reply = "";
+
+ for ($i=0; $i<4; ++$i) {
+ my $buffer = " ";
+ while (read(RANDOM, $buffer, 4) != 4) {
+ ;
+ }
+ $reply .= sprintf "%.8x", unpack("N", $buffer);
+ if ($debug) {
+ print STDERR "growing reply: [$reply]\n";
+ }
+ }
+ close RANDOM;
+
+ return $reply;
+ } else {
+ print STDERR "agent: ". "[got linux?]\n";
+ return "%.8x%.8x%.8x%.8x", time, time, time, time;
+ }
+
+}
+
diff --git a/libpamc/test/modules/Makefile b/libpamc/test/modules/Makefile
new file mode 100644
index 00000000..48065462
--- /dev/null
+++ b/libpamc/test/modules/Makefile
@@ -0,0 +1,9 @@
+CFLAGS = -g -fPIC -I"../../include"
+
+pam_secret.so: pam_secret.o
+ ld -x --shared -o pam_secret.so pam_secret.o -lc
+
+.o.c:
+
+clean:
+ rm -f *.so *.o
diff --git a/libpamc/test/modules/pam_secret.c b/libpamc/test/modules/pam_secret.c
new file mode 100644
index 00000000..04c7631b
--- /dev/null
+++ b/libpamc/test/modules/pam_secret.c
@@ -0,0 +1,670 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 1999 Andrew G. Morgan <morgan@linux.kernel.org>
+ */
+
+/*
+ * WARNING: AS WRITTEN THIS CODE IS NOT SECURE. THE MD5 IMPLEMENTATION
+ * NEEDS TO BE INTEGRATED MORE NATIVELY.
+ */
+
+/* #define DEBUG */
+
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <security/pam_modules.h>
+#include <security/pam_client.h>
+#include <security/_pam_macros.h>
+
+/*
+ * This is a sample module that demonstrates the use of binary prompts
+ * and how they can be used to implement sophisticated authentication
+ * schemes.
+ */
+
+struct ps_state_s {
+ int retval; /* last retval returned by the authentication fn */
+ int state; /* what state the module was in when it
+ returned incomplete */
+
+ char *username; /* the name of the local user */
+
+ char server_cookie[33]; /* storage for 32 bytes of server cookie */
+ char client_cookie[33]; /* storage for 32 bytes of client cookie */
+
+ char *secret_data; /* pointer to <NUL> terminated secret_data */
+ int invalid_secret; /* indication of whether the secret is valid */
+
+ pamc_bp_t current_prompt; /* place to store the current prompt */
+ pamc_bp_t current_reply; /* place to receive the reply prompt */
+};
+
+#define PS_STATE_ID "PAM_SECRET__STATE"
+#define PS_AGENT_ID "secret@here"
+#define PS_STATE_DEAD 0
+#define PS_STATE_INIT 1
+#define PS_STATE_PROMPT1 2
+#define PS_STATE_PROMPT2 3
+
+#define MAX_LEN_HOSTNAME 512
+#define MAX_FILE_LINE_LEN 1024
+
+/*
+ * Routine for generating 16*8 bits of random data represented in ASCII hex
+ */
+
+static int generate_cookie(unsigned char *buffer_33)
+{
+ static const char hexarray[] = "0123456789abcdef";
+ int i, fd;
+
+ /* fill buffer_33 with 32 hex characters (lower case) + '\0' */
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0) {
+ D(("failed to open /dev/urandom"));
+ return 0;
+ }
+ read(fd, buffer_33 + 16, 16);
+ close(fd);
+
+ /* expand top 16 bytes into 32 nibbles */
+ for (i=0; i<16; ++i) {
+ buffer_33[2*i ] = hexarray[(buffer_33[16+i] & 0xf0)>>4];
+ buffer_33[2*i+1] = hexarray[(buffer_33[16+i] & 0x0f)];
+ }
+
+ buffer_33[32] = '\0';
+
+ return 1;
+}
+
+/*
+ * XXX - This is a hack, and is fundamentally insecure. Its subject to
+ * all sorts of attacks not to mention the fact that all our secrets
+ * will be displayed on the command line for someone doing 'ps' to
+ * see. This is just for programming convenience in this instance, it
+ * needs to be replaced with the md5 code. Although I am loath to
+ * add yet another instance of md5 code to the Linux-PAM source code.
+ * [Need to think of a cleaner way to do this for the distribution as
+ * a whole...]
+ */
+
+#define COMMAND_FORMAT "/bin/echo -n '%s|%s|%s'|/usr/bin/md5sum -"
+
+int create_digest(const char *d1, const char *d2, const char *d3,
+ char *buffer_33)
+{
+ int length;
+ char *buffer;
+ FILE *pipe;
+
+ length = strlen(d1)+strlen(d2)+strlen(d3)+sizeof(COMMAND_FORMAT);
+ buffer = malloc(length);
+ if (buffer == NULL) {
+ D(("out of memory"));
+ return 0;
+ }
+
+ sprintf(buffer, COMMAND_FORMAT, d1,d2,d3);
+
+ D(("executing pipe [%s]", buffer));
+ pipe = popen(buffer, "r");
+ memset(buffer, 0, length);
+ free(buffer);
+
+ if (pipe == NULL) {
+ D(("failed to launch pipe"));
+ return 0;
+ }
+
+ if (fgets(buffer_33, 33, pipe) == NULL) {
+ D(("failed to read digest"));
+ return 0;
+ }
+
+ if (strlen(buffer_33) != 32) {
+ D(("digest was not 32 chars"));
+ return 0;
+ }
+
+ fclose(pipe);
+
+ D(("done [%s]", buffer_33));
+
+ return 1;
+}
+
+/*
+ * method to attempt to instruct the application's conversation function
+ */
+
+static int converse(pam_handle_t *pamh, struct ps_state_s *new)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("called"));
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (retval == PAM_SUCCESS) {
+ struct pam_message msg;
+ struct pam_response *single_reply;
+ const struct pam_message *msg_ptr;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_style = PAM_BINARY_PROMPT;
+ msg.msg = (const char *) new->current_prompt;
+ msg_ptr = &msg;
+
+ single_reply = NULL;
+ retval = conv->conv(1, &msg_ptr, &single_reply, conv->appdata_ptr);
+ if (retval == PAM_SUCCESS) {
+ if ((single_reply == NULL) || (single_reply->resp == NULL)) {
+ retval == PAM_CONV_ERR;
+ } else {
+ new->current_reply = (pamc_bp_t) single_reply->resp;
+ single_reply->resp = NULL;
+ }
+ }
+
+ if (single_reply) {
+ free(single_reply);
+ }
+ }
+
+#ifdef DEBUG
+ if (retval == PAM_SUCCESS) {
+ D(("reply has length=%d and control=%u",
+ PAM_BP_LENGTH(new->current_reply),
+ PAM_BP_CONTROL(new->current_reply)));
+ }
+ D(("returning %s", pam_strerror(pamh, retval)));
+#endif
+
+ return retval;
+}
+
+/*
+ * identify the secret in question
+ */
+
+#define SECRET_FILE_FORMAT "%s/.secret@here"
+
+char *identify_secret(char *identity, const char *user)
+{
+ struct passwd *pwd;
+ char *temp;
+ FILE *secrets;
+ int length_id;
+
+ pwd = getpwnam(user);
+ if ((pwd == NULL) || (pwd->pw_dir == NULL)) {
+ D(("user [%s] is not known", user));
+ }
+
+ length_id = strlen(pwd->pw_dir) + sizeof(SECRET_FILE_FORMAT);
+ temp = malloc(length_id);
+ if (temp == NULL) {
+ D(("out of memory"));
+ pwd = NULL;
+ return NULL;
+ }
+
+ sprintf(temp, SECRET_FILE_FORMAT, pwd->pw_dir);
+ pwd = NULL;
+
+ D(("opening key file [%s]", temp));
+ secrets = fopen(temp, "r");
+ memset(temp, 0, length_id);
+
+ if (secrets == NULL) {
+ D(("failed to open key file"));
+ return NULL;
+ }
+
+ length_id = strlen(identity);
+ temp = malloc(MAX_FILE_LINE_LEN);
+
+ for (;;) {
+ char *secret = NULL;
+
+ if (fgets(temp, MAX_FILE_LINE_LEN, secrets) == NULL) {
+ fclose(secrets);
+ return NULL;
+ }
+
+ D(("cf[%s][%s]", identity, temp));
+ if (memcmp(temp, identity, length_id)) {
+ continue;
+ }
+
+ D(("found entry"));
+ fclose(secrets);
+
+ for (secret=temp+length_id; *secret; ++secret) {
+ if (!(*secret == ' ' || *secret == '\n' || *secret == '\t')) {
+ break;
+ }
+ }
+
+ memmove(temp, secret, MAX_FILE_LINE_LEN-(secret-(temp+length_id)));
+ secret = temp;
+
+ for (; *secret; ++secret) {
+ if (*secret == ' ' || *secret == '\n' || *secret == '\t') {
+ break;
+ }
+ }
+
+ if (*secret) {
+ *secret = '\0';
+ }
+
+ D(("secret found [%s]", temp));
+
+ return temp;
+ }
+
+ /* NOT REACHED */
+}
+
+/*
+ * function to perform the two message authentication process
+ * (with support for event driven conversation functions)
+ */
+
+static int auth_sequence(pam_handle_t *pamh,
+ const struct ps_state_s *old, struct ps_state_s *new)
+{
+ const char *rhostname;
+ const char *rusername;
+ int retval;
+
+ retval = pam_get_item(pamh, PAM_RUSER, (const void **) &rusername);
+ if ((retval != PAM_SUCCESS) || (rusername == NULL)) {
+ D(("failed to obtain an rusername"));
+ new->state = PS_STATE_DEAD;
+ return PAM_AUTH_ERR;
+ }
+
+ retval = pam_get_item(pamh, PAM_RHOST, (const void **) &rhostname);
+ if ((retval != PAM_SUCCESS) || (rhostname == NULL)) {
+ D(("failed to identify local hostname: ", pam_strerror(pamh, retval)));
+ new->state = PS_STATE_DEAD;
+ return PAM_AUTH_ERR;
+ }
+
+ D(("switch on new->state=%d [%s@%s]", new->state, rusername, rhostname));
+ switch (new->state) {
+
+ case PS_STATE_INIT:
+ {
+ const char *user = NULL;
+
+ retval = pam_get_user(pamh, &user, NULL);
+
+ if ((retval == PAM_SUCCESS) && (user == NULL)) {
+ D(("success but no username?"));
+ new->state = PS_STATE_DEAD;
+ retval = PAM_USER_UNKNOWN;
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (retval == PAM_CONV_AGAIN) {
+ retval = PAM_INCOMPLETE;
+ } else {
+ new->state = PS_STATE_DEAD;
+ }
+ D(("state init failed: %s", pam_strerror(pamh, retval)));
+ return retval;
+ }
+
+ /* nothing else in this 'case' can be retried */
+
+ new->username = strdup(user);
+ if (new->username == NULL) {
+ D(("out of memory"));
+ new->state = PS_STATE_DEAD;
+ return PAM_BUF_ERR;
+ }
+
+ if (! generate_cookie(new->server_cookie)) {
+ D(("problem generating server cookie"));
+ new->state = PS_STATE_DEAD;
+ return PAM_ABORT;
+ }
+
+ new->current_prompt = NULL;
+ PAM_BP_RENEW(&new->current_prompt, PAM_BPC_SELECT,
+ sizeof(PS_AGENT_ID) + strlen(rusername) + 1
+ + strlen(rhostname) + 1 + 32);
+ sprintf(PAM_BP_DATA(new->current_prompt),
+ PS_AGENT_ID "/%s@%s|%.32s", rusername, rhostname,
+ new->server_cookie);
+
+ /* note, the BP is guaranteed by the spec to be <NUL> terminated */
+ D(("initialization packet [%s]", PAM_BP_DATA(new->current_prompt)));
+
+ /* fall through */
+ new->state = PS_STATE_PROMPT1;
+
+ D(("fall through to state_prompt1"));
+ }
+
+ case PS_STATE_PROMPT1:
+ {
+ int i, length;
+
+ /* send {secret@here/jdoe@client.host|<s_cookie>} */
+ retval = converse(pamh, new);
+ if (retval != PAM_SUCCESS) {
+ if (retval == PAM_CONV_AGAIN) {
+ D(("conversation failed to complete"));
+ return PAM_INCOMPLETE;
+ } else {
+ new->state = PS_STATE_DEAD;
+ return retval;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ D(("failed to read ruser@rhost"));
+ new->state = PS_STATE_DEAD;
+ return PAM_AUTH_ERR;
+ }
+
+ /* expect to receive the following {<seqid>|<a_cookie>} */
+ if (new->current_reply == NULL) {
+ D(("converstation returned [%s] but gave no reply",
+ pam_strerror(pamh, retval)));
+ new->state = PS_STATE_DEAD;
+ return PAM_CONV_ERR;
+ }
+
+ /* find | */
+ length = PAM_BP_LENGTH(new->current_reply);
+ for (i=0; i<length; ++i) {
+ if (PAM_BP_DATA(new->current_reply)[i] == '|') {
+ break;
+ }
+ }
+ if (i >= length) {
+ D(("malformed response (no |) of length %d", length));
+ new->state = PS_STATE_DEAD;
+ return PAM_CONV_ERR;
+ }
+ if ((length - ++i) != 32) {
+ D(("cookie is incorrect length (%d,%d) %d != 32",
+ length, i, length-i));
+ new->state = PS_STATE_DEAD;
+ return PAM_CONV_ERR;
+ }
+
+ /* copy client cookie */
+ memcpy(new->client_cookie, PAM_BP_DATA(new->current_reply)+i, 32);
+
+ /* generate a prompt that is length(seqid) + length(|) + 32 long */
+ PAM_BP_RENEW(&new->current_prompt, PAM_BPC_OK, i+32);
+ /* copy the head of the response prompt */
+ memcpy(PAM_BP_DATA(new->current_prompt),
+ PAM_BP_DATA(new->current_reply), i);
+ PAM_BP_RENEW(&new->current_reply, 0, 0);
+
+ /* look up the secret */
+ new->invalid_secret = 0;
+
+ if (new->secret_data == NULL) {
+ char *ruser_rhost;
+
+ ruser_rhost = malloc(strlen(rusername)+2+strlen(rhostname));
+ if (ruser_rhost == NULL) {
+ D(("out of memory"));
+ new->state = PS_STATE_DEAD;
+ return PAM_BUF_ERR;
+ }
+ sprintf(ruser_rhost, "%s@%s", rusername, rhostname);
+ new->secret_data = identify_secret(ruser_rhost, new->username);
+
+ memset(ruser_rhost, 0, strlen(ruser_rhost));
+ free(ruser_rhost);
+ }
+
+ if (new->secret_data == NULL) {
+ D(("secret not found for user"));
+ new->invalid_secret = 1;
+
+ /* need to make up a secret */
+ new->secret_data = malloc(32 + 1);
+ if (new->secret_data == NULL) {
+ D(("out of memory"));
+ new->state = PS_STATE_DEAD;
+ return PAM_BUF_ERR;
+ }
+ if (! generate_cookie(new->secret_data)) {
+ D(("what's up - no fake cookie generated?"));
+ new->state = PS_STATE_DEAD;
+ return PAM_ABORT;
+ }
+ }
+
+ /* construct md5[<client_cookie>|<server_cookie>|<secret_data>] */
+ if (! create_digest(new->client_cookie, new->server_cookie,
+ new->secret_data,
+ PAM_BP_DATA(new->current_prompt)+i)) {
+ D(("md5 digesting failed"));
+ new->state = PS_STATE_DEAD;
+ return PAM_ABORT;
+ }
+
+ /* prompt2 is now constructed - fall through to send it */
+ }
+
+ case PS_STATE_PROMPT2:
+ {
+ /* send {<seqid>|md5[<client_cookie>|<server_cookie>|<secret_data>]} */
+ retval = converse(pamh, new);
+ if (retval != PAM_SUCCESS) {
+ if (retval == PAM_CONV_AGAIN) {
+ D(("conversation failed to complete"));
+ return PAM_INCOMPLETE;
+ } else {
+ new->state = PS_STATE_DEAD;
+ return retval;
+ }
+ }
+
+ /* After we complete this section, we should not be able to
+ recall this authentication function. So, we force all
+ future calls into the weeds. */
+
+ new->state = PS_STATE_DEAD;
+
+ /* expect reply:{md5[<secret_data>|<server_cookie>|<client_cookie>]} */
+
+ {
+ int cf;
+ char expectation[33];
+
+ if (!create_digest(new->secret_data, new->server_cookie,
+ new->client_cookie, expectation)) {
+ new->state = PS_STATE_DEAD;
+ return PAM_ABORT;
+ }
+
+ cf = strcmp(expectation, PAM_BP_DATA(new->current_reply));
+ memset(expectation, 0, sizeof(expectation));
+ if (cf || new->invalid_secret) {
+ D(("failed to authenticate"));
+ return PAM_AUTH_ERR;
+ }
+ }
+
+ D(("correctly authenticated :)"));
+ return PAM_SUCCESS;
+ }
+
+ default:
+ new->state = PS_STATE_DEAD;
+
+ case PS_STATE_DEAD:
+
+ D(("state is currently dead/unknown"));
+ return PAM_AUTH_ERR;
+ }
+
+ fprintf(stderr, "pam_secret: this should not be reached\n");
+ return PAM_ABORT;
+}
+
+static void clean_data(pam_handle_t *pamh, void *datum, int error_status)
+{
+ struct ps_state_s *data = datum;
+
+ D(("liberating datum=%p", datum));
+
+ if (data) {
+ D(("renew prompt"));
+ PAM_BP_RENEW(&data->current_prompt, 0, 0);
+ D(("renew reply"));
+ PAM_BP_RENEW(&data->current_reply, 0, 0);
+ D(("overwrite datum"));
+ memset(data, 0, sizeof(struct ps_state_s));
+ D(("liberate datum"));
+ free(data);
+ }
+
+ D(("done."));
+}
+
+/*
+ * front end for the authentication function
+ */
+
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ struct ps_state_s *new_data;
+ const struct ps_state_s *old_data;
+
+ D(("called"));
+
+ new_data = calloc(1, sizeof(struct ps_state_s));
+ if (new_data == NULL) {
+ D(("out of memory"));
+ return PAM_BUF_ERR;
+ }
+ new_data->retval = PAM_SUCCESS;
+
+ retval = pam_get_data(pamh, PS_STATE_ID, (const void **) &old_data);
+ if (retval == PAM_SUCCESS) {
+ new_data->state = old_data->state;
+ memcpy(new_data->server_cookie, old_data->server_cookie, 32);
+ memcpy(new_data->client_cookie, old_data->client_cookie, 32);
+ if (old_data->username) {
+ new_data->username = strdup(old_data->username);
+ }
+ if (old_data->secret_data) {
+ new_data->secret_data = strdup(old_data->secret_data);
+ }
+ if (old_data->current_prompt) {
+ int length;
+
+ length = PAM_BP_LENGTH(old_data->current_prompt);
+ PAM_BP_RENEW(&new_data->current_prompt,
+ PAM_BP_CONTROL(old_data->current_prompt), length);
+ PAM_BP_FILL(new_data->current_prompt, 0, length,
+ PAM_BP_DATA(old_data->current_prompt));
+ }
+ /* don't need to duplicate current_reply */
+ } else {
+ old_data = NULL;
+ new_data->state = PS_STATE_INIT;
+ }
+
+ D(("call auth_sequence"));
+ new_data->retval = auth_sequence(pamh, old_data, new_data);
+ D(("returned from auth_sequence"));
+
+ retval = pam_set_data(pamh, PS_STATE_ID, new_data, clean_data);
+ if (retval != PAM_SUCCESS) {
+ D(("unable to store new_data"));
+ } else {
+ retval = new_data->retval;
+ }
+
+ old_data = new_data = NULL;
+
+ D(("done (%d)", retval));
+ return retval;
+}
+
+/*
+ * front end for the credential setting function
+ */
+
+#define AUTH_SESSION_TICKET_ENV_FORMAT "AUTH_SESSION_TICKET="
+
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ const struct ps_state_s *old_data;
+
+ D(("called"));
+
+ /* XXX - need to pay attention to the various flavors of call */
+
+ /* XXX - need provide an option to turn this feature on/off: if
+ other modules want to supply an AUTH_SESSION_TICKET, we should
+ leave it up to the admin which module dominiates. */
+
+ retval = pam_get_data(pamh, PS_STATE_ID, (const void **) &old_data);
+ if (retval != PAM_SUCCESS) {
+ D(("no data to base decision on"));
+ return PAM_AUTH_ERR;
+ }
+
+ /*
+ * If ok, export a derived shared secret session ticket to the
+ * client's PAM environment - the ticket has the form
+ *
+ * AUTH_SESSION_TICKET =
+ * md5[<server_cookie>|<secret_data>|<client_cookie>]
+ *
+ * This is a precursor to supporting a spoof resistant trusted
+ * path mechanism. This shared secret ticket can be used to add
+ * a hard-to-guess checksum to further authentication data.
+ */
+
+ retval = old_data->retval;
+ if (retval == PAM_SUCCESS) {
+ char envticket[sizeof(AUTH_SESSION_TICKET_ENV_FORMAT)+32];
+
+ memcpy(envticket, AUTH_SESSION_TICKET_ENV_FORMAT,
+ sizeof(AUTH_SESSION_TICKET_ENV_FORMAT));
+
+ if (! create_digest(old_data->server_cookie, old_data->secret_data,
+ old_data->client_cookie,
+ envticket+sizeof(AUTH_SESSION_TICKET_ENV_FORMAT)-1
+ )) {
+ D(("unable to generate a digest for session ticket"));
+ return PAM_ABORT;
+ }
+
+ D(("putenv[%s]", envticket));
+ retval = pam_putenv(pamh, envticket);
+ memset(envticket, 0, sizeof(envticket));
+ }
+
+ old_data = NULL;
+ D(("done (%d)", retval));
+
+ return retval;
+}
diff --git a/libpamc/test/regress/Makefile b/libpamc/test/regress/Makefile
new file mode 100644
index 00000000..ff63e5f0
--- /dev/null
+++ b/libpamc/test/regress/Makefile
@@ -0,0 +1,7 @@
+CFLAGS = -g -I ../../include
+
+test.libpamc: test.libpamc.o
+ $(CC) -o $@ $< -L ../.. -lpamc
+
+clean:
+ rm -f test.libpamc test.libpamc.o
diff --git a/libpamc/test/regress/run_test.sh b/libpamc/test/regress/run_test.sh
new file mode 100755
index 00000000..a1bf010b
--- /dev/null
+++ b/libpamc/test/regress/run_test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+export LD_LIBRARY_PATH=../..
+export PAMC_AGENT_PATH="../agents"
+
+./test.libpamc
diff --git a/libpamc/test/regress/test.libpamc.c b/libpamc/test/regress/test.libpamc.c
new file mode 100644
index 00000000..b5fb1b82
--- /dev/null
+++ b/libpamc/test/regress/test.libpamc.c
@@ -0,0 +1,334 @@
+/*
+ * This is a small test program for testing libpamc against the
+ * secret@here agent. It does the same as the test.secret@here perl
+ * script in this directory, but via the libpamc API.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <security/pam_client.h>
+#include <ctype.h>
+
+struct internal_packet {
+ int length;
+ int at;
+ char *buffer;
+};
+
+
+void append_data(struct internal_packet *packet, int extra, const char *data)
+{
+ if ((extra + packet->at) >= packet->length) {
+ if (packet->length == 0) {
+ packet->length = 1000;
+ }
+ /* make sure we have at least a char extra space available */
+ while (packet->length <= (extra + packet->at)) {
+ packet->length <<= 1;
+ }
+ packet->buffer = realloc(packet->buffer, packet->length);
+ if (packet->buffer == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ }
+
+ if (data != NULL) {
+ memcpy(packet->at + packet->buffer, data, extra);
+ }
+ packet->at += extra;
+
+ /* assisting string manipulation */
+ packet->buffer[packet->at] = '\0';
+}
+
+void append_string(struct internal_packet *packet, const char *string,
+ int with_nul)
+{
+ append_data(packet, strlen(string) + (with_nul ? 1:0), string);
+}
+
+char *identify_secret(char *identity)
+{
+ struct internal_packet temp_packet;
+ FILE *secrets;
+ int length_id;
+
+ temp_packet.length = temp_packet.at = 0;
+ temp_packet.buffer = NULL;
+
+ append_string(&temp_packet, "/home/", 0);
+ append_string(&temp_packet, getlogin(), 0);
+ append_string(&temp_packet, "/.secret@here", 1);
+
+ secrets = fopen(temp_packet.buffer, "r");
+ if (secrets == NULL) {
+ fprintf(stderr, "server: failed to open\n [%s]\n",
+ temp_packet.buffer);
+ exit(1);
+ }
+
+ length_id = strlen(identity);
+ for (;;) {
+ char *secret = NULL;
+ temp_packet.at = 0;
+
+ if (fgets(temp_packet.buffer, temp_packet.length, secrets) == NULL) {
+ fclose(secrets);
+ return NULL;
+ }
+
+ if (memcmp(temp_packet.buffer, identity, length_id)) {
+ continue;
+ }
+
+ fclose(secrets);
+ for (secret=temp_packet.buffer; *secret; ++secret) {
+ if (*secret == ' ' || *secret == '\n' || *secret == '\t') {
+ break;
+ }
+ }
+ for (; *secret; ++secret) {
+ if (!(*secret == ' ' || *secret == '\n' || *secret == '\t')) {
+ break;
+ }
+ }
+
+ for (temp_packet.buffer=secret; *temp_packet.buffer;
+ ++temp_packet.buffer) {
+ if (*temp_packet.buffer == ' ' || *temp_packet.buffer == '\n'
+ || *temp_packet.buffer == '\t') {
+ break;
+ }
+ }
+ if (*temp_packet.buffer) {
+ *temp_packet.buffer = '\0';
+ }
+
+ return secret;
+ }
+
+ /* NOT REACHED */
+}
+
+/*
+ * This is a hack, and is fundamentally insecure. All our secrets will be
+ * displayed on the command line for someone doing 'ps' to see. This
+ * is just for programming convenience in this instance, since this
+ * program is simply a regression test. The pam_secret module should
+ * not do this, but make use of md5 routines directly.
+ */
+
+char *create_digest(int length, const char *raw)
+{
+ struct internal_packet temp_packet;
+ FILE *pipe;
+
+ temp_packet.length = temp_packet.at = 0;
+ temp_packet.buffer = NULL;
+
+ append_string(&temp_packet, "/bin/echo -n '", 0);
+ append_string(&temp_packet, raw, 0);
+ append_string(&temp_packet, "'|/usr/bin/md5sum -", 1);
+
+ pipe = popen(temp_packet.buffer, "r");
+ if (pipe == NULL) {
+ fprintf(stderr, "server: failed to run\n [%s]\n", temp_packet.buffer);
+ exit(1);
+ }
+
+ temp_packet.at = 0;
+ append_data(&temp_packet, 32, NULL);
+
+ if (fgets(temp_packet.buffer, 33, pipe) == NULL) {
+ fprintf(stderr, "server: failed to read digest\n");
+ exit(1);
+ }
+ if (strlen(temp_packet.buffer) != 32) {
+ fprintf(stderr, "server: digest was not 32 chars?? [%s]\n",
+ temp_packet.buffer);
+ exit(1);
+ }
+
+ fclose(pipe);
+
+ return temp_packet.buffer;
+}
+
+void packet_to_prompt(pamc_bp_t *prompt_p, __u8 control,
+ struct internal_packet *packet)
+{
+ PAM_BP_RENEW(prompt_p, control, packet->at);
+ PAM_BP_FILL(*prompt_p, 0, packet->at, packet->buffer);
+ packet->at = 0;
+}
+
+void prompt_to_packet(pamc_bp_t prompt, struct internal_packet *packet)
+{
+ int data_length;
+
+ data_length = PAM_BP_LENGTH(prompt);
+ packet->at = 0;
+ append_data(packet, data_length, NULL);
+ PAM_BP_EXTRACT(prompt, 0, data_length, packet->buffer);
+}
+
+int main(int argc, char **argv)
+{
+ pamc_handle_t pch;
+ pamc_bp_t prompt = NULL;
+ struct internal_packet packet_data, *packet;
+ char *temp_string, *secret, *user, *a_cookie, *seqid, *digest;
+ const char *cookie = "123451234512345";
+ int retval;
+
+ packet = &packet_data;
+ packet->length = 0;
+ packet->at = 0;
+ packet->buffer = NULL;
+
+ pch = pamc_start();
+ if (pch == NULL) {
+ fprintf(stderr, "server: unable to get a handle from libpamc\n");
+ exit(1);
+ }
+
+ temp_string = getlogin();
+ if (temp_string == NULL) {
+ fprintf(stderr, "server: who are you?\n");
+ exit(1);
+ }
+#define DOMAIN "@local.host"
+ user = malloc(1+strlen(temp_string)+strlen(DOMAIN));
+ if (user == NULL) {
+ fprintf(stderr, "server: out of memory for user id\n");
+ exit(1);
+ }
+ sprintf(user, "%s%s", temp_string, DOMAIN);
+
+ append_string(packet, "secret@here/", 0);
+ append_string(packet, user, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, cookie, 0);
+ packet_to_prompt(&prompt, PAM_BPC_SELECT, packet);
+
+ /* get the library to accept the first packet (which should load
+ the secret@here agent) */
+
+ retval = pamc_converse(pch, &prompt);
+ fprintf(stderr, "server: after conversation\n");
+ if (PAM_BP_CONTROL(prompt) != PAM_BPC_OK) {
+ fprintf(stderr, "server: prompt had unexpected control type: %u\n",
+ PAM_BP_CONTROL(prompt));
+ exit(1);
+ }
+
+ fprintf(stderr, "server: got a prompt back\n");
+
+ prompt_to_packet(prompt, packet);
+
+ temp_string = strtok(packet->buffer, "|");
+ if (temp_string == NULL) {
+ fprintf(stderr, "server: prompt does not contain anything");
+ exit(1);
+ }
+ seqid = strdup(temp_string);
+ if (seqid == NULL) {
+ fprintf(stderr, "server: unable to store sequence id\n");
+ }
+
+ temp_string = strtok(NULL, "|");
+ if (temp_string == NULL) {
+ fprintf(stderr, "server: no cookie from agent\n");
+ exit(1);
+ }
+ a_cookie = strdup(temp_string);
+ if (a_cookie == NULL) {
+ fprintf(stderr, "server: no memory to store agent cookie\n");
+ exit(1);
+ }
+
+ fprintf(stderr, "server: agent responded with {%s|%s}\n", seqid, a_cookie);
+ secret = identify_secret(user);
+ fprintf(stderr, "server: secret=%s\n", secret);
+
+ /* now, we construct the response */
+ packet->at = 0;
+ append_string(packet, a_cookie, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, cookie, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, secret, 0);
+
+ fprintf(stderr, "server: get digest of %s\n", packet->buffer);
+
+ digest = create_digest(packet->at, packet->buffer);
+
+ fprintf(stderr, "server: secret=%s, digest=%s\n", secret, digest);
+
+ packet->at = 0;
+ append_string(packet, seqid, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, digest, 0);
+ packet_to_prompt(&prompt, PAM_BPC_OK, packet);
+
+ retval = pamc_converse(pch, &prompt);
+ fprintf(stderr, "server: after 2nd conversation\n");
+ if (PAM_BP_CONTROL(prompt) != PAM_BPC_DONE) {
+ fprintf(stderr, "server: 2nd prompt had unexpected control type: %u\n",
+ PAM_BP_CONTROL(prompt));
+ exit(1);
+ }
+
+ prompt_to_packet(prompt, packet);
+ PAM_BP_RENEW(&prompt, 0, 0);
+
+ temp_string = strtok(packet->buffer, "|");
+ if (temp_string == NULL) {
+ fprintf(stderr, "no digest from agent\n");
+ exit(1);
+ }
+ temp_string = strdup(temp_string);
+
+ packet->at = 0;
+ append_string(packet, secret, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, cookie, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, a_cookie, 0);
+
+ fprintf(stderr, "server: get digest of %s\n", packet->buffer);
+
+ digest = create_digest(packet->at, packet->buffer);
+
+ fprintf(stderr, "server: digest=%s\n", digest);
+
+ if (strcmp(digest, temp_string)) {
+ fprintf(stderr, "server: agent doesn't know the secret\n");
+ fprintf(stderr, "server: agent says: [%s]\n"
+ "server: server says: [%s]\n", temp_string, digest);
+ exit(1);
+ } else {
+ fprintf(stderr, "server: agent seems to know the secret\n");
+
+ packet->at = 0;
+ append_string(packet, cookie, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, secret, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, a_cookie, 0);
+
+ digest = create_digest(packet->at, packet->buffer);
+
+ fprintf(stderr, "server: putenv(\"AUTH_SESSION_TICKET=%s\")\n",
+ digest);
+ }
+
+
+ retval = pamc_end(&pch);
+
+ fprintf(stderr, "server: agent(s) were %shappy to terminate\n",
+ retval == PAM_BPC_TRUE ? "":"un");
+
+ exit(!retval);
+}
diff --git a/libpamc/test/regress/test.secret@here b/libpamc/test/regress/test.secret@here
new file mode 100755
index 00000000..2e0b9b94
--- /dev/null
+++ b/libpamc/test/regress/test.secret@here
@@ -0,0 +1,152 @@
+#!/usr/bin/perl
+
+##
+## this is a test script for regressing changes to the secret@here PAM
+## agent
+##
+
+$^W = 1;
+use strict;
+use IPC::Open2;
+
+$| = 1;
+
+my $whoami = `/usr/bin/whoami`; chomp $whoami;
+my $cookie = "12345";
+my $user_domain = "$whoami\@local.host";
+
+my $pid = open2(\*Reader, \*Writer, "../agents/secret\@here blah")
+ or die "failed to load secret\@here agent";
+
+unless (-f (getpwuid($<))[7]."/.secret\@here") {
+ print STDERR "server: ". "no " .(getpwuid($<))[7]. "/.secret\@here file\n";
+ die "no config file";
+}
+
+WriteBinaryPrompt(\*Writer, 0x02, "secret\@here/$user_domain|$cookie");
+
+my ($control, $data) = ReadBinaryPrompt(\*Reader);
+
+print STDERR "server: ". "reply: control=$control, data=$data\n";
+if ($control != 1) {
+ die "expected 1 (OK) for the first agent reply; got $control";
+}
+my ($seqid, $a_cookie) = split '\|', $data;
+
+# server needs to convince agent that it knows the secret before
+# agent will give a valid response
+my $secret = IdentifyLocalSecret($user_domain);
+my $digest = CreateDigest($a_cookie."|".$cookie."|".$secret);
+
+print STDERR "server: ". "digest = $digest\n";
+WriteBinaryPrompt(\*Writer, 0x01, "$seqid|$digest");
+
+# The agent will authenticate us and then reply with its
+# authenticating digest. we check that before we're done.
+
+($control, $data) = ReadBinaryPrompt(\*Reader);
+if ($control != 0x03) {
+ die "server: agent did not reply with a 'done' prompt ($control)\n";
+}
+
+unless ($data eq CreateDigest($secret."|".$cookie."|".$a_cookie)) {
+ die "server: agent is not authenticated\n";
+}
+
+print STDERR "server: agent appears to know secret\n";
+
+my $session_authenticated_ticket
+ = CreateDigest($cookie."|".$secret."|".$a_cookie);
+
+print STDERR "server: should putenv("
+ ."\"AUTH_SESSION_TICKET=$session_authenticated_ticket\")\n";
+
+exit 0;
+
+sub CreateDigest ($) {
+ my ($data) = @_;
+
+ my $pid = open2(\*MD5out, \*MD5in, "/usr/bin/md5sum -")
+ or die "you'll need /usr/bin/md5sum installed";
+
+ my $oldfd = select MD5in; $|=1; select $oldfd;
+ print MD5in "$data";
+ close MD5in;
+ my $reply = <MD5out>;
+ ($reply) = split /\s/, $reply;
+ print STDERR "server: ". "md5 said: <$reply>\n";
+ close MD5out;
+
+ return $reply;
+}
+
+sub ReadBinaryPrompt ($) {
+ my ($fd) = @_;
+
+ my $buffer = " ";
+ my $count = read($fd, $buffer, 5);
+ if ($count == 0) {
+ # no more packets to read
+ return (0, "");
+ }
+
+ if ($count != 5) {
+ # broken packet header
+ return (-1, "");
+ }
+
+ my ($length, $control) = unpack("N C", $buffer);
+ if ($length < 5) {
+ # broken packet length
+ return (-1, "");
+ }
+
+ my $data = "";
+ $length -= 5;
+ while ($count = read($fd, $buffer, $length)) {
+ $data .= $buffer;
+ if ($count != $length) {
+ $length -= $count;
+ next;
+ }
+
+ print STDERR "server: ". "data is [$data]\n";
+
+ return ($control, $data);
+ }
+
+ # broken packet data
+ return (-1, "");
+}
+
+sub WriteBinaryPrompt ($$$) {
+ my ($fd, $control, $data) = @_;
+
+ my $length = 5 + length($data);
+ printf STDERR "server: ". "{%d|0x%.2x|%s}\n", $length, $control, $data;
+ my $bp = pack("N C a*", $length, $control, $data);
+ print $fd $bp;
+
+ print STDERR "server: ". "control passed to agent\@here\n";
+}
+
+sub IdentifyLocalSecret ($) {
+ my ($identifier) = @_;
+ my $secret;
+
+ my $whoami = `/usr/bin/whoami` ; chomp $whoami;
+ if (open SECRETS, "< " .(getpwuid($<))[7]. "/.secret\@here") {
+ my $line;
+ while (defined ($line = <SECRETS>)) {
+ my ($id, $sec) = split /[\s]/, $line;
+ if ((defined $id) && ($id eq $identifier)) {
+ $secret = $sec;
+ last;
+ }
+ }
+ close SECRETS;
+ }
+
+ return $secret;
+}
+
diff --git a/modules/Makefile b/modules/Makefile
new file mode 100644
index 00000000..9b89ccfd
--- /dev/null
+++ b/modules/Makefile
@@ -0,0 +1,70 @@
+# $Id$
+#
+# Makefile
+#
+# This makefile controls the build process of shared and static PAM modules.
+#
+#
+
+MODDIRS=$(shell /bin/ls -d pam_*)
+
+# ////////////////////////////////////////////////////
+# // You should not modify anything below this line //
+# ////////////////////////////////////////////////////
+
+dummy:
+ @echo "*** This is not a top-level Makefile! ***"
+
+# -----------------------------------------------------------
+
+all:
+ @echo modules for $(OS) are:
+ @ls -d $(MODDIRS) 2>/dev/null ; echo :--------
+ @echo
+ifdef STATIC
+ rm -f ./_static_module_*
+endif
+ @for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then { \
+ $(MAKE) -C $$i all ; \
+ if [ $$? -ne 0 ]; then exit 1 ; fi ; \
+ } elif [ -f ./.$$i ]; then { \
+ cat ./.$$i ; \
+ } fi ; \
+ done
+
+download:
+ @./download-all
+
+install:
+ for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then { \
+ $(MAKE) -C $$i install ; \
+ if [ $$? -ne 0 ]; then exit 1 ; fi ; \
+ } fi ; \
+ done
+
+remove:
+ for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then { \
+ $(MAKE) -C $$i remove ; \
+ } fi ; \
+ done
+
+lclean:
+ rm -f _static_module_*
+
+clean: lclean
+ for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then { \
+ $(MAKE) -C $$i clean ; \
+ } fi ; \
+ done
+
+extraclean: lclean
+ for i in $(MODDIRS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i extraclean ; \
+ fi ; \
+ done
+
diff --git a/modules/README b/modules/README
new file mode 100644
index 00000000..86415947
--- /dev/null
+++ b/modules/README
@@ -0,0 +1,55 @@
+This directory contains the modules.
+
+If you want to reserve a module name please email <pam-list@redhat.com>
+and announce its name. Andrew Morgan, <morgan@parc.power.net>, will
+add it to the Makefile in the next release of Linux-PAM.
+
+As of Linux-PAM-0.40 modules can optionally conform to the static
+modules conventions.
+
+This file was updated for Linux-PAM-0.53.
+
+The conventions are as follows:
+
+There are only 6 functions that a module may declare as "public" they
+fall into 4 managment groups as follows:
+
+ functions Management group
+ ------------------------------------------ ----------------
+ pam_sm_authenticate, pam_sm_setcred, PAM_SM_AUTH
+ pam_sm_acct_mgmt, PAM_SM_ACCOUNT
+ pam_sm_open_session, pam_sm_close_session, PAM_SM_SESSION
+ pam_sm_chauthtok PAM_SM_PASSWORD
+
+If a module contains definitions for any of the above functions, it
+must supply definitions for all of the functions in the corresponding
+management group.
+
+The header file that defines the ANSI prototypes for these functions
+is <security/pam_modules.h> . In the case that the module wishes to
+offer the functions of a given managment group, it must #define
+PAM_SM_XXX, where XXX is one of the above four tokens. These
+definitions must occur *prior* to the
+#include <security/pam_modules.h> line.
+
+The pam_sm_... functions should be defined to be of type 'PAM_EXTERN int'.
+
+In the case that a module is being compiled with PAM_STATIC #define'd
+it should also define a globally accessible structure
+_"NAME"_modstruct containing references to each of the functions
+defined by the module. (this structure is defined in
+<security/pam_modules.h>. "NAME" is the title of the module
+(eg. "pam_deny")
+
+If a module wants to be included in the static libpam.a its Makefile
+should execute "register_static" with appropriate arguments (in this
+directory).
+
+[
+For SIMPLE working examples, see
+
+ ./modules/pam_deny/* and ./modules/pam_rootok/*
+.]
+
+Andrew Morgan
+96/11/10
diff --git a/modules/dont_makefile b/modules/dont_makefile
new file mode 100644
index 00000000..f256ce1b
--- /dev/null
+++ b/modules/dont_makefile
@@ -0,0 +1,19 @@
+#########################################################################
+# This is a makefile that does nothing. It is designed to be included
+# by module Makefile-s when they are not compatable with the local
+# system
+#########################################################################
+
+all:
+ @echo "This module will not be compiled on this system"
+
+extraclean: clean
+
+install: clean
+
+clean:
+ @echo "Nothing to do"
+
+#########################################################################
+# all over..
+#########################################################################
diff --git a/modules/download-all b/modules/download-all
new file mode 100755
index 00000000..427d0bba
--- /dev/null
+++ b/modules/download-all
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# $Id$
+#
+cat <<EOT
+For a number of reasons it is not practical for Linux-PAM to be
+distributed with every module out there. However, this shell script
+is intended as a convenient way for users to download modules from the
+'net in some semiautomated fashion.
+
+Please feel free to send (pam-list@redhat.com) snippets of code that
+will help others to download and unpack your favorite module into the
+Linux-PAM source tree. Especially welcome are snippets of the
+following form:
+
+ncftp ftp://my.ftpsite.org/pub/fluff/pam_fluff.tar.gz
+rm -fr pam_fluff
+tar zvfx pam_fluff.tar.gz
+
+Cheers
+
+Andrew
+morgan@linux.kernel.org
+EOT
+
+# --- insert your snippets below ---
+
+# --- insert your snippets above ---
+
+exit 0
diff --git a/modules/pam_access/.cvsignore b/modules/pam_access/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_access/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_access/Makefile b/modules/pam_access/Makefile
new file mode 100644
index 00000000..3d3611c4
--- /dev/null
+++ b/modules/pam_access/Makefile
@@ -0,0 +1,106 @@
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+
+TITLE=pam_access
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/access.conf
+export CONFILE
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+endif
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+DEFS=-DCONFILE=\"$(CONFILE)\"
+
+CFLAGS += $(DEFS)
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(CONFILE)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+ rm -f ./.ignore_age
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_access/README b/modules/pam_access/README
new file mode 100644
index 00000000..df10c269
--- /dev/null
+++ b/modules/pam_access/README
@@ -0,0 +1,40 @@
+# Description of its configuration file (/etc/security/access.conf):
+#
+# 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) 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
+############################################################################
diff --git a/modules/pam_access/access.conf b/modules/pam_access/access.conf
new file mode 100644
index 00000000..abfefa5e
--- /dev/null
+++ b/modules/pam_access/access.conf
@@ -0,0 +1,52 @@
+# 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) 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.
+#
+##############################################################################
+#
+# Disallow console logins to all but a few accounts.
+#
+#-:ALL EXCEPT wheel shutdown sync:console
+#
+# Disallow non-local logins to privileged accounts (group wheel).
+#
+#-:wheel:ALL EXCEPT LOCAL .win.tue.nl
+#
+# Some accounts are not allowed to login from anywhere:
+#
+#-:wsbscaro wsbsecr wsbspac wsbsym wscosor wstaiwde:ALL
+#
+# All other accounts are allowed to login from anywhere.
+#
diff --git a/modules/pam_access/install_conf b/modules/pam_access/install_conf
new file mode 100755
index 00000000..0667b5ec
--- /dev/null
+++ b/modules/pam_access/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+CONF=./access.conf
+QUIET_INSTALL=../../.quiet_install
+MODULE=pam_access
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
new file mode 100644
index 00000000..12133392
--- /dev/null
+++ b/modules/pam_access/pam_access.c
@@ -0,0 +1,424 @@
+/* pam_access module */
+
+/*
+ * Written by Alexei Nogin <alexei@nogin.dnttm.ru> 1997/06/15
+ * (I took login_access from logdaemon-5.6 and converted it to PAM
+ * using parts of pam_time code.)
+ *
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+/* man page says above file includes this... */
+extern int gethostname(char *name, size_t len);
+
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/utsname.h>
+
+#ifndef BROKEN_NETWORK_MATCH
+# include <netdb.h>
+# include <sys/socket.h>
+#endif
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_ACCOUNT
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+
+/* --- static functions for checking whether the user should be let in --- */
+
+static void _log_err(const char *format, ... )
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_access", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(LOG_ERR, format, args);
+ va_end(args);
+ closelog();
+}
+
+#define PAM_ACCESS_CONFIG CONFILE
+
+int strcasecmp(const char *s1, const char *s2);
+
+/* login_access.c from logdaemon-5.6 with several changes by A.Nogin: */
+
+ /*
+ * This module implements a simple but effective form of login access
+ * control based on login names and on host (or domain) names, internet
+ * addresses (or network numbers), or on terminal line names in case of
+ * non-networked logins. Diagnostics are reported through syslog(3).
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+#if !defined(MAXHOSTNAMELEN) || (MAXHOSTNAMELEN < 64)
+#undef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+ /* Delimiters for fields and for lists of users, ttys or hosts. */
+
+static char fs[] = ":"; /* field separator */
+static char sep[] = ", \t"; /* list-element separator */
+
+ /* Constants to be used in assignments only, not in comparisons... */
+
+#define YES 1
+#define NO 0
+
+ /*
+ * A structure to bundle up all login-related information to keep the
+ * functional interfaces as generic as possible.
+ */
+struct login_info {
+ struct passwd *user;
+ char *from;
+};
+
+typedef int match_func (char *, struct login_info *);
+
+static int list_match (char *, struct login_info *,
+ match_func *);
+static int user_match (char *, struct login_info *);
+static int from_match (char *, struct login_info *);
+static int string_match (char *, char *);
+
+/* login_access - match username/group and host/tty with access control file */
+
+static int login_access(struct passwd *user, char *from)
+{
+ struct login_info item;
+ FILE *fp;
+ char line[BUFSIZ];
+ char *perm; /* becomes permission field */
+ char *users; /* becomes list of login names */
+ char *froms; /* becomes list of terminals or hosts */
+ int match = NO;
+ int end;
+ int lineno = 0; /* for diagnostics */
+
+ /*
+ * Bundle up the arguments to avoid unnecessary clumsiness lateron.
+ */
+ item.user = user;
+ item.from = from;
+
+ /*
+ * 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.
+ * Non-comment lines are broken at the ':' character. All fields are
+ * mandatory. The first field should be a "+" or "-" character. A
+ * non-existing table means no access control.
+ */
+
+ if ((fp = fopen(PAM_ACCESS_CONFIG, "r"))!=NULL) {
+ while (!match && fgets(line, sizeof(line), fp)) {
+ lineno++;
+ if (line[end = strlen(line) - 1] != '\n') {
+ _log_err("%s: line %d: missing newline or line too long",
+ PAM_ACCESS_CONFIG, lineno);
+ continue;
+ }
+ if (line[0] == '#')
+ continue; /* comment line */
+ while (end > 0 && isspace(line[end - 1]))
+ end--;
+ line[end] = 0; /* strip trailing whitespace */
+ if (line[0] == 0) /* skip blank lines */
+ continue;
+ if (!(perm = strtok(line, fs))
+ || !(users = strtok((char *) 0, fs))
+ || !(froms = strtok((char *) 0, fs))
+ || strtok((char *) 0, fs)) {
+ _log_err("%s: line %d: bad field count", PAM_ACCESS_CONFIG, lineno);
+ continue;
+ }
+ if (perm[0] != '+' && perm[0] != '-') {
+ _log_err("%s: line %d: bad first field", PAM_ACCESS_CONFIG, lineno);
+ continue;
+ }
+ match = (list_match(froms, &item, from_match)
+ && list_match(users, &item, user_match));
+ }
+ (void) fclose(fp);
+ } else if (errno != ENOENT) {
+ _log_err("cannot open %s: %m", PAM_ACCESS_CONFIG);
+ }
+ return (match == 0 || (line[0] == '+'));
+}
+
+/* list_match - match an item against a list of tokens with exceptions */
+
+static int list_match(char *list, struct login_info *item, match_func *match_fn)
+{
+ char *tok;
+ int match = NO;
+
+ /*
+ * Process tokens one at a time. We have exhausted all possible matches
+ * when we reach an "EXCEPT" token or the end of the list. If we do find
+ * a match, look for an "EXCEPT" list and recurse to determine whether
+ * the match is affected by any exceptions.
+ */
+
+ for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) {
+ if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */
+ break;
+ if ((match = (*match_fn) (tok, item))) /* YES */
+ break;
+ }
+ /* Process exceptions to matches. */
+
+ if (match != NO) {
+ while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT"))
+ /* VOID */ ;
+ if (tok == 0 || list_match((char *) 0, item, match_fn) == NO)
+ return (match);
+ }
+ return (NO);
+}
+
+/* myhostname - figure out local machine name */
+
+static char * myhostname(void)
+{
+ static char name[MAXHOSTNAMELEN + 1];
+
+ gethostname(name, MAXHOSTNAMELEN);
+ name[MAXHOSTNAMELEN] = 0;
+ return (name);
+}
+
+/* netgroup_match - match group against machine or user */
+
+static int netgroup_match(char *group, char *machine, char *user)
+{
+#ifdef NIS
+ static char *mydomain = 0;
+
+ if (mydomain == 0)
+ yp_get_default_domain(&mydomain);
+ return (innetgr(group, machine, user, mydomain));
+#else
+ _log_err("NIS netgroup support not configured");
+ return (NO);
+#endif
+}
+
+/* user_match - match a username against one token */
+
+static int user_match(char *tok, struct login_info *item)
+{
+ char *string = item->user->pw_name;
+ struct login_info fake_item;
+ struct group *group;
+ int i;
+ char *at;
+
+ /*
+ * If a token has the magic value "ALL" the match always succeeds.
+ * Otherwise, return YES if the token fully matches the username, if the
+ * token is a group that contains the username, or if the token is the
+ * name of the user's primary group.
+ */
+
+ if ((at = strchr(tok + 1, '@')) != 0) { /* split user@host pattern */
+ *at = 0;
+ fake_item.from = myhostname();
+ return (user_match(tok, item) && from_match(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 (YES);
+ } else if ((group = getgrnam(tok))) { /* try group membership */
+ if (item->user->pw_gid == group->gr_gid)
+ return (YES);
+ for (i = 0; group->gr_mem[i]; i++)
+ if (strcasecmp(string, group->gr_mem[i]) == 0)
+ return (YES);
+ }
+ return (NO);
+}
+
+/* from_match - match a host or tty against a list of tokens */
+
+static int from_match(char *tok, struct login_info *item)
+{
+ char *string = item->from;
+ int tok_len;
+ int str_len;
+
+ /*
+ * 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
+ * name, return YES if it matches the last fields of the string. If the
+ * token has the magic value "LOCAL", return YES if the string does not
+ * contain a "." character. If the token is a network number, return YES
+ * if it matches the head of the string.
+ */
+
+ if (tok[0] == '@') { /* netgroup */
+ return (netgroup_match(tok + 1, string, (char *) 0));
+ } else if (string_match(tok, string)) { /* ALL or exact match */
+ return (YES);
+ } 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)
+ return (YES);
+ } 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];
+ 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 >= sizeof(hn))
+ return (NO);
+ if (!strncmp(tok, hn, tok_len))
+ return (YES);
+#endif /* BROKEN_NETWORK_MATCH */
+ }
+ return (NO);
+}
+
+/* string_match - match a string against one token */
+
+static int string_match(char *tok, char *string)
+{
+
+ /*
+ * If the token has the magic value "ALL" the match always succeeds.
+ * Otherwise, return YES if the token fully matches the string.
+ */
+
+ if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */
+ return (YES);
+ } else if (strcasecmp(tok, string) == 0) { /* try exact match */
+ return (YES);
+ }
+ return (NO);
+}
+
+/* end of login_access.c */
+
+int strcasecmp(const char *s1, const char *s2)
+{
+ while ((toupper(*s1)==toupper(*s2)) && (*s1) && (*s2)) {s1++; s2++;}
+ return(toupper(*s1)-toupper(*s2));
+}
+
+/* --- public account management functions --- */
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ const char *user=NULL;
+ char *from=NULL;
+ struct passwd *user_pw;
+
+ /* set username */
+
+ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
+ || *user == '\0') {
+ _log_err("cannot determine the user's name");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* remote host name */
+
+ if (pam_get_item(pamh, PAM_RHOST, (const void **)&from)
+ != PAM_SUCCESS) {
+ _log_err("cannot find the remote host name");
+ return PAM_ABORT;
+ }
+
+ if (from==NULL) {
+
+ /* local login, set tty name */
+
+ if (pam_get_item(pamh, PAM_TTY, (const void **)&from) != PAM_SUCCESS
+ || from == NULL) {
+ D(("PAM_TTY not set, probing stdin"));
+ from = ttyname(STDIN_FILENO);
+ if (from == NULL) {
+ _log_err("couldn't get the tty name");
+ return PAM_ABORT;
+ }
+ if (pam_set_item(pamh, PAM_TTY, from) != PAM_SUCCESS) {
+ _log_err("couldn't set tty name");
+ return PAM_ABORT;
+ }
+ }
+ if (strncmp("/dev/",from,5) == 0) { /* strip leading /dev/ */
+ from += 5;
+ }
+
+ }
+ if ((user_pw=getpwnam(user))==NULL) return (PAM_USER_UNKNOWN);
+ if (login_access(user_pw,from)) return (PAM_SUCCESS); else {
+ _log_err("access denied for user `%s' from `%s'",user,from);
+ return (PAM_PERM_DENIED);
+ }
+}
+
+/* end of module definition */
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_access_modstruct = {
+ "pam_access",
+ NULL,
+ NULL,
+ pam_sm_acct_mgmt,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
diff --git a/modules/pam_cracklib/.cvsignore b/modules/pam_cracklib/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_cracklib/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_cracklib/Makefile b/modules/pam_cracklib/Makefile
new file mode 100644
index 00000000..554362d4
--- /dev/null
+++ b/modules/pam_cracklib/Makefile
@@ -0,0 +1,118 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Cristian Gafton <gafton@redhat.com> 1996/09/10
+#
+
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+#
+# here you should make default variable defines...
+#
+MKDIR=mkdir -p
+LD_D=gcc -shared -Xlinker -x
+INSTALL=install
+SECUREDIR=/usr/lib/security
+#
+HAVE_CRACKLIB=yes
+EXTRALS=-lcrypt
+endif
+
+ifeq ($(HAVE_CRACKLIB),yes)
+
+TITLE=pam_cracklib
+CRACKLIB=-lcrack
+ifeq ($(shell if [ -f /usr/lib/cracklib_dict.hwm ]; then echo yes ; fi),yes)
+ CRACKLIB_DICTPATH=/usr/lib/cracklib_dict
+else
+ CRACKLIB_DICTPATH=/usr/share/dict/cracklib_dict
+endif
+#
+ifeq ($(shell if [ -f /usr/include/crypt.h ]; then echo yes ; fi),yes)
+ NEED_CRYPT_HEADER=-DNEED_CRYPT_HEADER
+endif
+#
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+ifdef CRACKLIB_DICTPATH
+CFLAGS+=-DCRACKLIB_DICTPATH=\"$(CRACKLIB_DICTPATH)\"
+endif
+
+dynamic/%.o : %.c
+ $(CC) $(NEED_CRYPT_HEADER) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(NEED_CRYPT_HEADER) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC) Makefile
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(CRACKLIB) $(EXTRALS) $(LINKLIBS)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~ *.so
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+else
+
+include ../dont_makefile
+
+endif
diff --git a/modules/pam_cracklib/README b/modules/pam_cracklib/README
new file mode 100644
index 00000000..e4b02731
--- /dev/null
+++ b/modules/pam_cracklib/README
@@ -0,0 +1,21 @@
+
+pam_cracklib:
+ check the passwd against dictionary words.
+
+RECOGNIZED ARGUMENTS:
+ debug verbose log
+
+ type=XXX alter the message printed as a prompt to the user.
+ the message printed is in the form
+ "New XXX password: ".
+ Default XXX=UNIX
+
+ retry=N Prompt user at most N times before returning with
+ error. Default N=1.
+
+MODULE SERVICES PROVIDED:
+ passwd chauthtok
+
+AUTHOR:
+ Cristian Gafton <gafton@sorosis.ro>
+
diff --git a/modules/pam_cracklib/pam_cracklib.c b/modules/pam_cracklib/pam_cracklib.c
new file mode 100644
index 00000000..d3d74947
--- /dev/null
+++ b/modules/pam_cracklib/pam_cracklib.c
@@ -0,0 +1,776 @@
+/* pam_cracklib module */
+
+/*
+ * 0.85. added six new options to use this with long passwords.
+ * 0.8. tidied output and improved D(()) usage for debugging.
+ * 0.7. added support for more obscure checks for new passwd.
+ * 0.6. root can reset user passwd to any values (it's only warned)
+ * 0.5. supports retries - 'retry=N' argument
+ * 0.4. added argument 'type=XXX' for 'New XXX password' prompt
+ * 0.3. Added argument 'debug'
+ * 0.2. new password is feeded to cracklib for verify after typed once
+ * 0.1. First release
+ */
+
+/*
+ * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10
+ * Long password support by Philip W. Dalrymple <pwd@mdtsoft.com> 1997/07/18
+ * See the end of the file for Copyright Information
+ *
+ * Modification for long password systems (>8 chars). The original
+ * module had problems when used in a md5 password system in that it
+ * allowed too short passwords but required that at least half of the
+ * bytes in the new password did not appear in the old one. this
+ * action is still the default and the changes should not break any
+ * current user. This modification adds 6 new options, one to set the
+ * number of bytes in the new password that are not in the old one,
+ * the other five to control the length checking, these are all
+ * documented (or will be before anyone else sees this code) in the PAM
+ * S.A.G. in the section on the cracklib module.
+ */
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <stdio.h>
+#ifdef NEED_CRYPT_HEADER
+# include <crypt.h>
+#endif
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+extern char *FascistCheck(char *pw, const char *dictpath);
+
+#ifndef CRACKLIB_DICTPATH
+#define CRACKLIB_DICTPATH "/usr/share/dict/cracklib_dict"
+#endif
+
+#define PROMPT1 "New %s password: "
+#define PROMPT2 "Retype new %s password: "
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+#ifndef LINUX_PAM
+#include <security/pam_appl.h>
+#endif /* LINUX_PAM */
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-Cracklib", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+#define PAM_DEBUG_ARG 0x0001
+
+/* module data - AGM: */
+
+struct cracklib_options {
+ int retry_times;
+ int diff_ok;
+ int min_length;
+ int dig_credit;
+ int up_credit;
+ int low_credit;
+ int oth_credit;
+ int use_authtok;
+ char prompt_type[BUFSIZ];
+};
+
+static int _pam_parse(struct cracklib_options *opt, int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+ char *ep = NULL;
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv,"type=",5))
+ strcpy(opt->prompt_type, *argv+5);
+ else if (!strncmp(*argv,"retry=",6)) {
+ opt->retry_times = strtol(*argv+6,&ep,10);
+ if (!ep || (opt->retry_times < 1))
+ opt->retry_times = 1;
+ } else if (!strncmp(*argv,"difok=",6)) {
+ opt->diff_ok = strtol(*argv+6,&ep,10);
+ if (!ep || (opt->diff_ok < 0))
+ opt->diff_ok = 10;
+ } else if (!strncmp(*argv,"minlen=",7)) {
+ opt->min_length = strtol(*argv+7,&ep,10);
+ if (!ep || (opt->min_length < 5))
+ opt->min_length = 5;
+ } else if (!strncmp(*argv,"dcredit=",8)) {
+ opt->dig_credit = strtol(*argv+8,&ep,10);
+ if (!ep || (opt->dig_credit < 0))
+ opt->dig_credit = 0;
+ } else if (!strncmp(*argv,"ucredit=",8)) {
+ opt->up_credit = strtol(*argv+8,&ep,10);
+ if (!ep || (opt->up_credit < 0))
+ opt->up_credit = 0;
+ } else if (!strncmp(*argv,"lcredit=",8)) {
+ opt->low_credit = strtol(*argv+8,&ep,10);
+ if (!ep || (opt->low_credit < 0))
+ opt->low_credit = 0;
+ } else if (!strncmp(*argv,"ocredit=",8)) {
+ opt->oth_credit = strtol(*argv+8,&ep,10);
+ if (!ep || (opt->oth_credit < 0))
+ opt->oth_credit = 0;
+ } else if (!strncmp(*argv,"use_authtok",11)) {
+ opt->use_authtok = 1;
+ } else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* Helper functions */
+
+/* this is a front-end for module-application conversations */
+static int converse(pam_handle_t *pamh, int ctrl, int nargs,
+ struct pam_message **message,
+ struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+
+ if ( retval == PAM_SUCCESS ) {
+ retval = conv->conv(nargs, (const struct pam_message **)message,
+ response, conv->appdata_ptr);
+ if (retval != PAM_SUCCESS && (ctrl && PAM_DEBUG_ARG)) {
+ _pam_log(LOG_DEBUG, "conversation failure [%s]",
+ pam_strerror(pamh, retval));
+ }
+ } else {
+ _pam_log(LOG_ERR, "couldn't obtain coversation function [%s]",
+ pam_strerror(pamh, retval));
+ }
+
+ return retval; /* propagate error status */
+}
+
+static int make_remark(pam_handle_t *pamh, unsigned int ctrl,
+ int type, const char *text)
+{
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+ int retval;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+ resp = NULL;
+
+ retval = converse(pamh, ctrl, 1, pmsg, &resp);
+ if (retval == PAM_SUCCESS)
+ _pam_drop_reply(resp, 1);
+
+ return retval;
+}
+
+/* use this to free strings. ESPECIALLY password strings */
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ free(xx);
+ return NULL;
+}
+
+/*
+ * can't be a palindrome - like `R A D A R' or `M A D A M'
+ */
+static int palindrome(const char *old, const char *new)
+{
+ int i, j;
+
+ i = strlen (new);
+
+ for (j = 0;j < i;j++)
+ if (new[i - j - 1] != new[j])
+ return 0;
+
+ return 1;
+}
+
+/*
+ * more than half of the characters are different ones.
+ * or at least diff_ok are different
+ * NOTE that the defaults are NOT the same as befor this
+ * change. as long as there are at least 10 different bytes
+ * in a new password it will now pass even if the password
+ * is longer than 20 bytes (MD5)
+ */
+
+static int similiar(struct cracklib_options *opt, const char *old, const char *new)
+{
+ int i, j;
+
+ for (i = j = 0;new[i] && old[i];i++)
+ if (strchr (new, old[i]))
+ j++;
+
+ if (j >= opt->diff_ok || i >= j * 2)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * a nice mix of characters.
+ */
+static int simple(struct cracklib_options *opt, const char *old, const char *new)
+{
+ int digits = 0;
+ int uppers = 0;
+ int lowers = 0;
+ int others = 0;
+ int size;
+ int i;
+
+ for (i = 0;new[i];i++) {
+ if (isdigit (new[i]))
+ digits++;
+ else if (isupper (new[i]))
+ uppers++;
+ else if (islower (new[i]))
+ lowers++;
+ else
+ others++;
+ }
+
+ /*
+ * The scam was this - a password of only one character type
+ * must be 8 letters long. Two types, 7, and so on.
+ * This is now changed, the base size and the credits or defaults
+ * see the docs on the module for info on these parameters, the
+ * defaults cause the effect to be the same as before the change
+ */
+
+ if (digits > opt->dig_credit)
+ digits = opt->dig_credit;
+
+ if (uppers > opt->up_credit)
+ uppers = opt->up_credit;
+
+ if (lowers > opt->low_credit)
+ lowers = opt->low_credit;
+
+ if (others > opt->oth_credit)
+ others = opt->oth_credit;
+
+ size = opt->min_length;
+ size -= digits;
+ size -= uppers;
+ size -= lowers;
+ size -= others;
+
+ if (size <= i)
+ return 0;
+
+ return 1;
+}
+
+static char * str_lower(char *string)
+{
+ char *cp;
+
+ for (cp = string; *cp; cp++)
+ *cp = tolower(*cp);
+ return string;
+}
+
+static const char * password_check(struct cracklib_options *opt, const char *old, const char *new)
+{
+ const char *msg = NULL;
+ char *oldmono, *newmono, *wrapped;
+
+ if (strcmp(new, old) == 0) {
+ msg = "is the same as the old one";
+ return msg;
+ }
+
+ newmono = str_lower(x_strdup(new));
+ oldmono = str_lower(x_strdup(old));
+ wrapped = malloc(strlen(oldmono) * 2 + 1);
+ strcpy (wrapped, oldmono);
+ strcat (wrapped, oldmono);
+
+ if (palindrome(oldmono, newmono))
+ msg = "is a palindrome";
+
+ if (!msg && strcmp(oldmono, newmono) == 0)
+ msg = "case changes only";
+
+ if (!msg && similiar(opt, oldmono, newmono))
+ msg = "is too similiar to the old one";
+
+ if (!msg && simple(opt, old, new))
+ msg = "is too simple";
+
+ if (!msg && strstr(wrapped, newmono))
+ msg = "is rotated";
+
+ memset(newmono, 0, strlen(newmono));
+ memset(oldmono, 0, strlen(oldmono));
+ memset(wrapped, 0, strlen(wrapped));
+ free(newmono);
+ free(oldmono);
+ free(wrapped);
+
+ return msg;
+}
+
+
+#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+
+static const char * check_old_password(const char *forwho, const char *newpass)
+{
+ static char buf[16384];
+ char *s_luser, *s_uid, *s_npas, *s_pas;
+ const char *msg = NULL;
+ FILE *opwfile;
+
+ opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+ if (opwfile == NULL)
+ return NULL;
+
+ while (fgets(buf, 16380, opwfile)) {
+ if (!strncmp(buf, forwho, strlen(forwho))) {
+ buf[strlen(buf)-1] = '\0';
+ s_luser = strtok(buf, ":,");
+ s_uid = strtok(NULL, ":,");
+ s_npas = strtok(NULL, ":,");
+ s_pas = strtok(NULL, ":,");
+ while (s_pas != NULL) {
+ if (!strcmp(crypt(newpass, s_pas), s_pas)) {
+ msg = "has been already used";
+ break;
+ }
+ s_pas = strtok(NULL, ":,");
+ }
+ break;
+ }
+ }
+ fclose(opwfile);
+
+ return msg;
+}
+
+
+static int _pam_unix_approve_pass(pam_handle_t *pamh,
+ unsigned int ctrl,
+ struct cracklib_options *opt,
+ const char *pass_old,
+ const char *pass_new)
+{
+ const char *msg = NULL;
+ const char *user;
+ int retval;
+
+ if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) {
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "bad authentication token");
+ make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ pass_new == NULL ?
+ "No password supplied":"Password unchanged" );
+ return PAM_AUTHTOK_ERR;
+ }
+
+ /*
+ * if one wanted to hardwire authentication token strength
+ * checking this would be the place
+ */
+ msg = password_check(opt, pass_old,pass_new);
+ if (!msg) {
+ retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
+ if (retval != PAM_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_ERR,"Can not get username");
+ return PAM_AUTHTOK_ERR;
+ }
+ }
+ msg = check_old_password(user, pass_new);
+ }
+
+ if (msg) {
+ char remark[BUFSIZ];
+
+ memset(remark,0,sizeof(remark));
+ sprintf(remark,"BAD PASSWORD: %s",msg);
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "new passwd fails strength check: %s",
+ msg);
+ make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
+ return PAM_AUTHTOK_ERR;
+ };
+ return PAM_SUCCESS;
+
+}
+
+/* The Main Thing (by Cristian Gafton, CEO at this module :-)
+ * (stolen from http://home.netscape.com)
+ */
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ struct cracklib_options options;
+
+ options.retry_times = 1;
+ options.diff_ok = 10;
+ options.min_length = 9;
+ options.dig_credit = 1;
+ options.up_credit = 1;
+ options.low_credit = 1;
+ options.oth_credit = 1;
+ options.use_authtok = 0;
+ memset(options.prompt_type, 0, BUFSIZ);
+
+ ctrl = _pam_parse(&options, argc, argv);
+
+ D(("called."));
+ if (!options.prompt_type[0])
+ strcpy(options.prompt_type,"UNIX");
+
+ if (flags & PAM_PRELIM_CHECK) {
+ /* Check for passwd dictionary */
+ struct stat st;
+ char buf[sizeof(CRACKLIB_DICTPATH)+10];
+
+ D(("prelim check"));
+
+ memset(buf,0,sizeof(buf)); /* zero the buffer */
+ sprintf(buf,"%s.pwd",CRACKLIB_DICTPATH);
+
+ if (!stat(buf,&st) && st.st_size)
+ return PAM_SUCCESS;
+ else {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE,"dict path '%s'[.pwd] is invalid",
+ CRACKLIB_DICTPATH);
+ return PAM_ABORT;
+ }
+
+ /* Not reached */
+ return PAM_SERVICE_ERR;
+
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+ int retval;
+ char *token1, *token2, *oldtoken;
+ struct pam_message msg[1],*pmsg[1];
+ struct pam_response *resp;
+ const char *cracklib_dictpath = CRACKLIB_DICTPATH;
+ char prompt[BUFSIZ];
+
+ D(("do update"));
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK,
+ (const void **)&oldtoken);
+ if (retval != PAM_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_ERR,"Can not get old passwd");
+ oldtoken=NULL;
+ retval = PAM_SUCCESS;
+ }
+
+ do {
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+ token1 = token2 = NULL;
+
+ if (!options.retry_times) {
+ D(("returning %s because maxtries reached",
+ pam_strerror(pamh, retval)));
+ return retval;
+ }
+
+ /* Planned modus operandi:
+ * Get a passwd.
+ * Verify it against cracklib.
+ * If okay get it a second time.
+ * Check to be the same with the first one.
+ * set PAM_AUTHTOK and return
+ */
+
+ if (options.use_authtok == 1) {
+ const char *item = NULL;
+
+ retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &item);
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _pam_log(LOG_ALERT
+ ,"pam_get_item returned error to pam_cracklib"
+ );
+ } else if (item != NULL) { /* we have a password! */
+ token1 = x_strdup(item);
+ item = NULL;
+ } else {
+ retval = PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ }
+
+ } else {
+ /* Prepare to ask the user for the first time */
+ memset(prompt,0,sizeof(prompt));
+ sprintf(prompt,PROMPT1,options.prompt_type);
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[0].msg = prompt;
+
+ resp = NULL;
+ retval = converse(pamh, ctrl, 1, pmsg, &resp);
+ if (resp != NULL) {
+ /* interpret the response */
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+ token1 = x_strdup(resp[0].resp);
+ if (token1 == NULL) {
+ _pam_log(LOG_NOTICE,
+ "could not recover authentication token 1");
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ */
+ _pam_drop_reply(resp, 1);
+ } else {
+ retval = (retval == PAM_SUCCESS) ?
+ PAM_AUTHTOK_RECOVER_ERR:retval ;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"unable to obtain a password");
+ continue;
+ }
+
+ D(("testing password, retval = %s", pam_strerror(pamh, retval)));
+ /* now test this passwd against cracklib */
+ {
+ char *crack_msg;
+ char remark[BUFSIZ];
+
+ bzero(remark,sizeof(remark));
+ D(("against cracklib"));
+ if ((crack_msg = FascistCheck(token1, cracklib_dictpath))) {
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"bad password: %s",crack_msg);
+ sprintf(remark,"BAD PASSWORD: %s", crack_msg);
+ make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
+ if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
+ retval = PAM_AUTHTOK_ERR;
+ else
+ retval = PAM_SUCCESS;
+ } else {
+ /* check it for strength too... */
+ D(("for strength"));
+ if (oldtoken) {
+ retval = _pam_unix_approve_pass(pamh,ctrl,&options,
+ oldtoken,token1);
+ if (retval != PAM_SUCCESS) {
+ if (getuid() || (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
+ retval = PAM_AUTHTOK_ERR;
+ else
+ retval = PAM_SUCCESS;
+ }
+ }
+ }
+ }
+
+ D(("after testing: retval = %s", pam_strerror(pamh, retval)));
+ /* if cracklib/strength check said it is a bad passwd... */
+ if ((retval != PAM_SUCCESS) && (retval != PAM_IGNORE)) {
+ int temp_unused;
+
+ temp_unused = pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ token1 = _pam_delete(token1);
+ continue;
+ }
+
+ /* Now we have a good passwd. Ask for it once again */
+
+ if (options.use_authtok == 0) {
+ bzero(prompt,sizeof(prompt));
+ sprintf(prompt,PROMPT2,options.prompt_type);
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[0].msg = prompt;
+
+ resp = NULL;
+ retval = converse(pamh, ctrl, 1, pmsg, &resp);
+ if (resp != NULL) {
+ /* interpret the response */
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+ token2 = x_strdup(resp[0].resp);
+ if (token2 == NULL) {
+ _pam_log(LOG_NOTICE,
+ "could not recover authentication token 2");
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ */
+ _pam_drop_reply(resp, 1);
+ } else {
+ retval = (retval == PAM_SUCCESS) ?
+ PAM_AUTHTOK_RECOVER_ERR:retval ;
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (ctrl && PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG
+ ,"unable to obtain the password a second time");
+ continue;
+ }
+
+ /* Hopefully now token1 and token2 the same password ... */
+ if (strcmp(token1,token2) != 0) {
+ /* tell the user */
+ make_remark(pamh, ctrl, PAM_ERROR_MSG, MISTYPED_PASS);
+ token1 = _pam_delete(token1);
+ token2 = _pam_delete(token2);
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE,"Password mistyped");
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ continue;
+ }
+
+ /* Yes, the password was typed correct twice
+ * we store this password as an item
+ */
+
+ {
+ const char *item = NULL;
+
+ retval = pam_set_item(pamh, PAM_AUTHTOK, token1);
+
+ /* clean up */
+ token1 = _pam_delete(token1);
+ token2 = _pam_delete(token2);
+
+ if ( (retval != PAM_SUCCESS) ||
+ ((retval = pam_get_item(pamh, PAM_AUTHTOK,
+ (const void **)&item)
+ ) != PAM_SUCCESS) ) {
+ _pam_log(LOG_CRIT, "error manipulating password");
+ continue;
+ }
+ item = NULL; /* break link to password */
+ return PAM_SUCCESS;
+ }
+ }
+
+ } while (options.retry_times--);
+
+ } else {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "UNKNOWN flags setting %02X",flags);
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Not reached */
+ return PAM_SERVICE_ERR;
+}
+
+
+
+#ifdef PAM_STATIC
+/* static module data */
+struct pam_module _pam_cracklib_modstruct = {
+ "pam_cracklib",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok
+};
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1996.
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The following copyright was appended for the long password support
+ * added with the libpam 0.58 release:
+ *
+ * Modificaton Copyright (c) Philip W. Dalrymple III <pwd@mdtsoft.com>
+ * 1997. All rights reserved
+ *
+ * THE MODIFICATION THAT PROVIDES SUPPORT FOR LONG PASSWORD TYPE CHECKING TO
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_deny/.cvsignore b/modules/pam_deny/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_deny/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_deny/Makefile b/modules/pam_deny/Makefile
new file mode 100644
index 00000000..e6fa2e60
--- /dev/null
+++ b/modules/pam_deny/Makefile
@@ -0,0 +1,131 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:33 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.7 1997/04/05 06:43:41 morgan
+# full-source-tree and fakeroot
+#
+# Revision 1.6 1997/02/15 19:04:27 morgan
+# fixed email
+#
+# Revision 1.5 1996/11/10 20:11:48 morgan
+# crossplatform support
+#
+# Revision 1.4 1996/09/05 06:50:12 morgan
+# ld --> gcc
+#
+# Revision 1.3 1996/05/26 15:48:38 morgan
+# make dynamic and static dirs
+#
+# Revision 1.2 1996/05/26 04:00:16 morgan
+# changes for automated static/dynamic modules
+#
+# Revision 1.1 1996/03/16 17:47:36 morgan
+# Initial revision
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+endif
+
+#
+
+TITLE=pam_deny
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_deny/README b/modules/pam_deny/README
new file mode 100644
index 00000000..6683bdcc
--- /dev/null
+++ b/modules/pam_deny/README
@@ -0,0 +1,4 @@
+# $Id$
+#
+
+this module always fails, it ignores all options.
diff --git a/modules/pam_deny/pam_deny.c b/modules/pam_deny/pam_deny.c
new file mode 100644
index 00000000..123cb21b
--- /dev/null
+++ b/modules/pam_deny/pam_deny.c
@@ -0,0 +1,100 @@
+/* pam_permit module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:33 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.4 1997/02/15 19:05:15 morgan
+ * fixed email
+ *
+ * Revision 1.3 1996/06/02 08:06:19 morgan
+ * changes for new static protocol
+ *
+ * Revision 1.2 1996/05/26 04:01:12 morgan
+ * added static support
+ *
+ * Revision 1.1 1996/03/16 17:47:36 morgan
+ * Initial revision
+ *
+ */
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+
+/* --- authentication management functions --- */
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_AUTH_ERR;
+}
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_CRED_UNAVAIL;
+}
+
+/* --- account management functions --- */
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_ACCT_EXPIRED;
+}
+
+/* --- password management --- */
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_AUTHTOK_ERR;
+}
+
+/* --- session management --- */
+
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SYSTEM_ERR;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SYSTEM_ERR;
+}
+
+/* end of module definition */
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_deny_modstruct = {
+ "pam_deny",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+#endif
diff --git a/modules/pam_env/.cvsignore b/modules/pam_env/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_env/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_env/Makefile b/modules/pam_env/Makefile
new file mode 100644
index 00000000..e872d1ee
--- /dev/null
+++ b/modules/pam_env/Makefile
@@ -0,0 +1,111 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:33 agmorgan
+# Initial revision
+#
+# Revision 1.2 1999/10/09 05:08:28 morgan
+# removed libpwdb dependencies (libpwdb support is depreciated)
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.1 1997/04/05 06:42:35 morgan
+# Initial revision
+#
+# Revision 1.1 1997/01/04 20:32:52 morgan
+# Initial revision
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/12/8
+# Adaptations by Dave Kinclea and Cristian Gafton
+#
+
+TITLE=pam_env
+
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/pam_env.conf
+export CONFILE
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(EXTRALIB)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS) $(EXTRALIB)
+endif
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_env/README b/modules/pam_env/README
new file mode 100644
index 00000000..04df323b
--- /dev/null
+++ b/modules/pam_env/README
@@ -0,0 +1,72 @@
+# $Date$
+# $Author$
+# $Id$
+#
+# This is the configuration file for pam_env, a PAM module to load in
+# a configurable list of environment variables for a
+#
+# The original idea for this came from Andrew G. Morgan ...
+#<quote>
+# Mmm. Perhaps you might like to write a pam_env module that reads a
+# default environment from a file? I can see that as REALLY
+# useful... Note it would be an "auth" module that returns PAM_IGNORE
+# for the auth part and sets the environment returning PAM_SUCCESS in
+# the setcred function...
+#</quote>
+#
+# What I wanted was the REMOTEHOST variable set, purely for selfish
+# reasons, and AGM didn't want it added to the SimpleApps login
+# program (which is where I added the patch). So, my first concern is
+# that variable, from there there are numerous others that might/would
+# be useful to be set: NNTPSERVER, LESS, PATH, PAGER, MANPAGER .....
+#
+# Of course, these are a different kind of variable than REMOTEHOST in
+# that they are things that are likely to be configured by
+# administrators rather than set by logging in, how to treat them both
+# in the same config file?
+#
+# Here is my idea:
+#
+# Each line starts with the variable name, there are then two possible
+# options for each variable DEFAULT and OVERRIDE.
+# DEFAULT allows and administrator to set the value of the
+# variable to some default value, if none is supplied then the empty
+# string is assumed. The OVERRIDE option tells pam_env that it should
+# enter in its value (overriding the default value) if there is one
+# to use. OVERRIDE is not used, "" is assumed and no override will be
+# done.
+#
+# VARIABLE [DEFAULT=[value]] [OVERRIDE=[value]]
+#
+# (Possibly non-existent) environment variables may be used in values
+# using the ${string} syntax and (possibly non-existent) PAM_ITEMs may
+# be used in values using the @{string} syntax. Both the $ and @
+# characters can be backslash escaped to be used as literal values
+# values can be delimited with "", escaped " not supported.
+#
+#
+# First, some special variables
+#
+# Set the REMOTEHOST variable for any hosts that are remote, default
+# to "localhost" rather than not being set at all
+REMOTEHOST DEFAULT=localhost OVERRIDE=@{PAM_RHOST}
+#
+# Set the DISPLAY variable if it seems reasonable
+DISPLAY DEFAULT=${REMOTEHOST}:0.0 OVERRIDE=${DISPLAY}
+#
+#
+# Now some simple variables
+#
+PAGER DEFAULT=less
+MANPAGER DEFAULT=less
+LESS DEFAULT="M q e h15 z23 b80"
+NNTPSERVER DEFAULT=localhost
+PATH DEFAULT=${HOME}/bin:/usr/local/bin:/bin\
+:/usr/bin:/usr/local/bin/X11:/usr/bin/X11
+#
+# silly examples of escaped variables, just to show how they work.
+#
+DOLLAR DEFAULT=\$
+DOLLARDOLLAR DEFAULT= OVERRIDE=\$${DOLLAR}
+DOLLARPLUS DEFAULT=\${REMOTEHOST}${REMOTEHOST}
+ATSIGN DEFAULT="" OVERRIDE=\@
diff --git a/modules/pam_env/install_conf b/modules/pam_env/install_conf
new file mode 100755
index 00000000..4c608400
--- /dev/null
+++ b/modules/pam_env/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+QUIET_INSTALL=../../.quiet_install
+CONF=./pam_env.conf-example
+MODULE=pam_env
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_env/pam_env.c b/modules/pam_env/pam_env.c
new file mode 100644
index 00000000..5ba89268
--- /dev/null
+++ b/modules/pam_env/pam_env.c
@@ -0,0 +1,839 @@
+/* pam_mail module */
+
+/*
+ * $Id$
+ *
+ * Written by Dave Kinchlea <kinch@kinch.ark.com> 1997/01/31
+ * Inspired by Andrew Morgan <morgan@parc.power.net, who also supplied the
+ * template for this file (via pam_mail)
+ */
+
+#ifndef DEFAULT_CONF_FILE
+#define DEFAULT_CONF_FILE "/etc/security/pam_env.conf"
+#endif
+
+#define DEFAULT_ETC_ENVFILE "/etc/environment"
+#define DEFAULT_READ_ENVFILE 1
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <features.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH /* This is primarily a AUTH_SETCRED module */
+#define PAM_SM_SESSION /* But I like to be friendly */
+#define PAM_SM_PASSWORD /* "" */
+#define PAM_SM_ACCOUNT /* "" */
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* This little structure makes it easier to keep variables together */
+
+typedef struct var {
+ char *name;
+ char *value;
+ char *defval;
+ char *override;
+} VAR;
+
+#define BUF_SIZE 1024
+#define MAX_ENV 8192
+
+#define GOOD_LINE 0
+#define BAD_LINE 100 /* This must be > the largest PAM_* error code */
+
+#define DEFINE_VAR 101
+#define UNDEFINE_VAR 102
+#define ILLEGAL_VAR 103
+
+static int _assemble_line(FILE *, char *, int);
+static int _parse_line(char *, VAR *);
+static int _check_var(pam_handle_t *, VAR *); /* This is the real meat */
+static void _clean_var(VAR *);
+static int _expand_arg(pam_handle_t *, char **);
+static const char * _pam_get_item_byname(pam_handle_t *, const char *);
+static int _define_var(pam_handle_t *, VAR *);
+static int _undefine_var(pam_handle_t *, VAR *);
+
+/* This is a flag used to designate an empty string */
+static char quote='Z';
+
+/* some syslogging */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-env", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x01
+#define PAM_NEW_CONF_FILE 0x02
+#define PAM_ENV_SILENT 0x04
+#define PAM_NEW_ENV_FILE 0x10
+
+static int _pam_parse(int flags, int argc, const char **argv, char **conffile,
+ char **envfile, int *readenv)
+{
+ int ctrl=0;
+
+
+ /* step through arguments */
+ for (; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv,"conffile=",9)) {
+ *conffile = x_strdup(9+*argv);
+ if (*conffile != NULL) {
+ D(("new Configuration File: %s", *conffile));
+ ctrl |= PAM_NEW_CONF_FILE;
+ } else {
+ _log_err(LOG_CRIT,
+ "Configuration file specification missing argument - ignored");
+ }
+ } else if (!strncmp(*argv,"envfile=",8)) {
+ *envfile = x_strdup(8+*argv);
+ if (*envfile != NULL) {
+ D(("new Env File: %s", *envfile));
+ ctrl |= PAM_NEW_ENV_FILE;
+ } else {
+ _log_err(LOG_CRIT,
+ "Env file specification missing argument - ignored");
+ }
+ } else if (!strncmp(*argv,"readenv=",8))
+ *readenv = atoi(8+*argv);
+ else
+ _log_err(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+
+ return ctrl;
+}
+
+static int _parse_config_file(pam_handle_t *pamh, int ctrl, char **conffile)
+{
+ int retval;
+ const char *file;
+ char buffer[BUF_SIZE];
+ FILE *conf;
+ VAR Var, *var=&Var;
+
+ var->name=NULL; var->defval=NULL; var->override=NULL;
+ D(("Called."));
+
+ if (ctrl & PAM_NEW_CONF_FILE) {
+ file = *conffile;
+ } else {
+ file = DEFAULT_CONF_FILE;
+ }
+
+ D(("Config file name is: %s", file));
+
+ /*
+ * Lets try to open the config file, parse it and process
+ * any variables found.
+ */
+
+ if ((conf = fopen(file,"r")) == NULL) {
+ _log_err(LOG_ERR, "Unable to open config file: %s",
+ strerror(errno));
+ return PAM_IGNORE;
+ }
+
+ /* _pam_assemble_line will provide a complete line from the config file, with all
+ * comments removed and any escaped newlines fixed up
+ */
+
+ while (( retval = _assemble_line(conf, buffer, BUF_SIZE)) > 0) {
+ D(("Read line: %s", buffer));
+
+ if ((retval = _parse_line(buffer, var)) == GOOD_LINE) {
+ retval = _check_var(pamh, var);
+
+ if (DEFINE_VAR == retval) {
+ retval = _define_var(pamh, var);
+
+ } else if (UNDEFINE_VAR == retval) {
+ retval = _undefine_var(pamh, var);
+ }
+ }
+ if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval
+ && BAD_LINE != retval && PAM_BAD_ITEM != retval) break;
+
+ _clean_var(var);
+
+ } /* while */
+
+ (void) fclose(conf);
+
+ /* tidy up */
+ _clean_var(var); /* We could have got here prematurely, this is safe though */
+ _pam_overwrite(*conffile);
+ _pam_drop(*conffile);
+ file = NULL;
+ D(("Exit."));
+ return (retval<0?PAM_ABORT:PAM_SUCCESS);
+}
+
+static int _parse_env_file(pam_handle_t *pamh, int ctrl, char **env_file)
+{
+ int retval=PAM_SUCCESS, i, t;
+ const char *file;
+ char buffer[BUF_SIZE], *key, *mark;
+ FILE *conf;
+
+ if (ctrl & PAM_NEW_ENV_FILE)
+ file = *env_file;
+ else
+ file = DEFAULT_ETC_ENVFILE;
+
+ D(("Env file name is: %s", file));
+
+ if ((conf = fopen(file,"r")) == NULL) {
+ D(("Unable to open env file: %s", strerror(errno)));
+ return PAM_ABORT;
+ }
+
+ while (_assemble_line(conf, buffer, BUF_SIZE) > 0) {
+ D(("Read line: %s", buffer));
+ key = buffer;
+
+ /* skip leading white space */
+ key += strspn(key, " \n\t");
+
+ /* skip blanks lines and comments */
+ if (!key || key[0] == '#')
+ continue;
+
+ /* skip over "export " if present so we can be compat with
+ bash type declerations */
+ if (strncmp(key, "export ", (size_t) 7) == 0)
+ key += 7;
+
+ /* now find the end of value */
+ mark = key;
+ while(mark[0] != '\n' && mark[0] != '#' && mark[0] != '\0')
+ mark++;
+ if (mark[0] != '\0')
+ mark[0] = '\0';
+
+ /*
+ * sanity check, the key must be alpha-numeric
+ */
+
+ for ( i = 0 ; key[i] != '=' && key[i] != '\0' ; i++ )
+ if (!isalnum(key[i]) && key[i] != '_') {
+ D(("key is not alpha numeric - '%s', ignoring", key));
+ continue;
+ }
+
+ /* now we try to be smart about quotes around the value,
+ but not too smart, we can't get all fancy with escaped
+ values like bash */
+ if (key[i] == '=' && (key[++i] == '\"' || key[i] == '\'')) {
+ for ( t = i+1 ; key[t] != '\0' ; t++)
+ if (key[t] != '\"' && key[t] != '\'')
+ key[i++] = key[t];
+ else if (key[t+1] != '\0')
+ key[i++] = key[t];
+ key[i] = '\0';
+ }
+
+ /* set the env var, if it fails, we break out of the loop */
+ retval = pam_putenv(pamh, key);
+ if (retval != PAM_SUCCESS) {
+ D(("error setting env \"%s\"", key));
+ break;
+ }
+ }
+
+ (void) fclose(conf);
+
+ /* tidy up */
+ _pam_overwrite(*env_file);
+ _pam_drop(*env_file);
+ file = NULL;
+ D(("Exit."));
+ return (retval<0?PAM_IGNORE:PAM_SUCCESS);
+}
+
+/*
+ * This is where we read a line of the PAM config file. The line may be
+ * preceeded by lines of comments and also extended with "\\\n"
+ */
+
+static int _assemble_line(FILE *f, char *buffer, int buf_len)
+{
+ char *p = buffer;
+ char *s, *os;
+ int used = 0;
+
+ /* loop broken with a 'break' when a non-'\\n' ended line is read */
+
+ D(("called."));
+ for (;;) {
+ if (used >= buf_len) {
+ /* Overflow */
+ D(("_assemble_line: overflow"));
+ return -1;
+ }
+ if (fgets(p, buf_len - used, f) == NULL) {
+ if (used) {
+ /* Incomplete read */
+ return -1;
+ } else {
+ /* EOF */
+ return 0;
+ }
+ }
+
+ /* skip leading spaces --- line may be blank */
+
+ s = p + strspn(p, " \n\t");
+ if (*s && (*s != '#')) {
+ os = s;
+
+ /*
+ * we are only interested in characters before the first '#'
+ * character
+ */
+
+ while (*s && *s != '#')
+ ++s;
+ if (*s == '#') {
+ *s = '\0';
+ used += strlen(os);
+ break; /* the line has been read */
+ }
+
+ s = os;
+
+ /*
+ * Check for backslash by scanning back from the end of
+ * the entered line, the '\n' has been included since
+ * normally a line is terminated with this
+ * character. fgets() should only return one though!
+ */
+
+ s += strlen(s);
+ while (s > os && ((*--s == ' ') || (*s == '\t')
+ || (*s == '\n')));
+
+ /* check if it ends with a backslash */
+ if (*s == '\\') {
+ *s = '\0'; /* truncate the line here */
+ used += strlen(os);
+ p = s; /* there is more ... */
+ } else {
+ /* End of the line! */
+ used += strlen(os);
+ break; /* this is the complete line */
+ }
+
+ } else {
+ /* Nothing in this line */
+ /* Don't move p */
+ }
+ }
+
+ return used;
+}
+
+static int _parse_line(char *buffer, VAR *var)
+{
+ /*
+ * parse buffer into var, legal syntax is
+ * VARIABLE [DEFAULT=[[string]] [OVERRIDE=[value]]
+ *
+ * Any other options defined make this a bad line,
+ * error logged and no var set
+ */
+
+ int length, quoteflg=0;
+ char *ptr, **valptr, *tmpptr;
+
+ D(("Called buffer = <%s>", buffer));
+
+ length = strcspn(buffer," \t\n");
+
+ if ((var->name = malloc(length + 1)) == NULL) {
+ _log_err(LOG_ERR, "Couldn't malloc %d bytes", length+1);
+ return PAM_BUF_ERR;
+ }
+
+ /*
+ * The first thing on the line HAS to be the variable name,
+ * it may be the only thing though.
+ */
+ strncpy(var->name, buffer, length);
+ var->name[length] = '\0';
+ D(("var->name = <%s>, length = %d", var->name, length));
+
+ /*
+ * Now we check for arguments, we only support two kinds and ('cause I am lazy)
+ * each one can actually be listed any number of times
+ */
+
+ ptr = buffer+length;
+ while ((length = strspn(ptr, " \t")) > 0) {
+ ptr += length; /* remove leading whitespace */
+ D((ptr));
+ if (strncmp(ptr,"DEFAULT=",8) == 0) {
+ ptr+=8;
+ D(("Default arg found: <%s>", ptr));
+ valptr=&(var->defval);
+ } else if (strncmp(ptr, "OVERRIDE=", 9) == 0) {
+ ptr+=9;
+ D(("Override arg found: <%s>", ptr));
+ valptr=&(var->override);
+ } else {
+ D(("Unrecognized options: <%s> - ignoring line", ptr));
+ _log_err(LOG_ERR, "Unrecognized Option: %s - ignoring line", ptr);
+ return BAD_LINE;
+ }
+
+ if ('"' != *ptr) { /* Escaped quotes not supported */
+ length = strcspn(ptr, " \t\n");
+ tmpptr = ptr+length;
+ } else {
+ tmpptr = strchr(++ptr, '"');
+ if (!tmpptr) {
+ D(("Unterminated quoted string: %s", ptr-1));
+ _log_err(LOG_ERR, "Unterminated quoted string: %s", ptr-1);
+ return BAD_LINE;
+ }
+ length = tmpptr - ptr;
+ if (*++tmpptr && ' ' != *tmpptr && '\t' != *tmpptr && '\n' != *tmpptr) {
+ D(("Quotes must cover the entire string: <%s>", ptr));
+ _log_err(LOG_ERR, "Quotes must cover the entire string: <%s>", ptr);
+ return BAD_LINE;
+ }
+ quoteflg++;
+ }
+ if (length) {
+ if ((*valptr = malloc(length + 1)) == NULL) {
+ D(("Couldn't malloc %d bytes", length+1));
+ _log_err(LOG_ERR, "Couldn't malloc %d bytes", length+1);
+ return PAM_BUF_ERR;
+ }
+ (void)strncpy(*valptr,ptr,length);
+ (*valptr)[length]='\0';
+ } else if (quoteflg--) {
+ *valptr = &quote; /* a quick hack to handle the empty string */
+ }
+ ptr = tmpptr; /* Start the search where we stopped */
+ } /* while */
+
+ /*
+ * The line is parsed, all is well.
+ */
+
+ D(("Exit."));
+ ptr = NULL; tmpptr = NULL; valptr = NULL;
+ return GOOD_LINE;
+}
+
+static int _check_var(pam_handle_t *pamh, VAR *var)
+{
+ /*
+ * Examine the variable and determine what action to take.
+ * Returns DEFINE_VAR, UNDEFINE_VAR depending on action to take
+ * or a PAM_* error code if passed back from other routines
+ *
+ * if no DEFAULT provided, the empty string is assumed
+ * if no OVERRIDE provided, the empty string is assumed
+ * if DEFAULT= and OVERRIDE evaluates to the empty string,
+ * this variable should be undefined
+ * if DEFAULT="" and OVERRIDE evaluates to the empty string,
+ * this variable should be defined with no value
+ * if OVERRIDE=value and value turns into the empty string, DEFAULT is used
+ *
+ * If DEFINE_VAR is to be returned, the correct value to define will
+ * be pointed to by var->value
+ */
+
+ int retval;
+
+ D(("Called."));
+
+ /*
+ * First thing to do is to expand any arguments, but only
+ * if they are not the special quote values (cause expand_arg
+ * changes memory).
+ */
+
+ if (var->defval && (&quote != var->defval) &&
+ ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) {
+ return retval;
+ }
+ if (var->override && (&quote != var->override) &&
+ ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) {
+ return retval;
+ }
+
+ /* Now its easy */
+
+ if (var->override && *(var->override) && &quote != var->override) {
+ /* if there is a non-empty string in var->override, we use it */
+ D(("OVERRIDE variable <%s> being used: <%s>", var->name, var->override));
+ var->value = var->override;
+ retval = DEFINE_VAR;
+ } else {
+
+ var->value = var->defval;
+ if (&quote == var->defval) {
+ /*
+ * This means that the empty string was given for defval value
+ * which indicates that a variable should be defined with no value
+ */
+ *var->defval = '\0';
+ D(("An empty variable: <%s>", var->name));
+ retval = DEFINE_VAR;
+ } else if (var->defval) {
+ D(("DEFAULT variable <%s> being used: <%s>", var->name, var->defval));
+ retval = DEFINE_VAR;
+ } else {
+ D(("UNDEFINE variable <%s>", var->name));
+ retval = UNDEFINE_VAR;
+ }
+ }
+
+ D(("Exit."));
+ return retval;
+}
+
+static int _expand_arg(pam_handle_t *pamh, char **value)
+{
+ const char *orig=*value, *tmpptr=NULL;
+ char *ptr; /*
+ * Sure would be nice to use tmpptr but it needs to be
+ * a constant so that the compiler will shut up when I
+ * call pam_getenv and _pam_get_item_byname -- sigh
+ */
+
+ char type, tmpval[BUF_SIZE]; /* No unexpanded variable can be bigger than BUF_SIZE */
+ char tmp[MAX_ENV]; /* I know this shouldn't be hard-coded but it's so
+ * much easier this way */
+
+ D(("Remember to initialize tmp!"));
+ tmp[0] = '\0';
+
+ /*
+ * (possibly non-existent) environment variables can be used as values
+ * by prepending a "$" and wrapping in {} (ie: ${HOST}), can escape with "\"
+ * (possibly non-existent) PAM items can be used as values
+ * by prepending a "@" and wrapping in {} (ie: @{PAM_RHOST}, can escape
+ *
+ */
+ D(("Expanding <%s>",orig));
+ while (*orig) { /* while there is some input to deal with */
+ if ('\\' == *orig) {
+ ++orig;
+ if ('$' != *orig && '@' != *orig) {
+ D(("Unrecognized escaped character: <%c> - ignoring", *orig));
+ _log_err(LOG_ERR, "Unrecognized escaped character: <%c> - ignoring",
+ *orig);
+ } else if ((strlen(tmp) + 1) < MAX_ENV) {
+ tmp[strlen(tmp)] = *orig++; /* Note the increment */
+ } else {
+ /* is it really a good idea to try to log this? */
+ D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
+ _log_err(LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
+ }
+ continue;
+ }
+ if ('$' == *orig || '@' == *orig) {
+ if ('{' != *(orig+1)) {
+ D(("Expandable variables must be wrapped in {} <%s> - ignoring", orig));
+ _log_err(LOG_ERR, "Expandable variables must be wrapped in {} <%s> - ignoring",
+ orig);
+ if ((strlen(tmp) + 1) < MAX_ENV) {
+ tmp[strlen(tmp)] = *orig++; /* Note the increment */
+ }
+ continue;
+ } else {
+ D(("Expandable argument: <%s>", orig));
+ type = *orig;
+ orig+=2; /* skip the ${ or @{ characters */
+ ptr = strchr(orig, '}');
+ if (ptr) {
+ *ptr++ = '\0';
+ } else {
+ D(("Unterminated expandable variable: <%s>", orig-2));
+ _log_err(LOG_ERR, "Unterminated expandable variable: <%s>", orig-2);
+ return PAM_ABORT;
+ }
+ strncpy(tmpval, orig, (size_t) BUF_SIZE);
+ orig=ptr;
+ /*
+ * so, we know we need to expand tmpval, it is either
+ * an environment variable or a PAM_ITEM. type will tell us which
+ */
+ switch (type) {
+
+ case '$':
+ D(("Expanding env var: <%s>",tmpval));
+ tmpptr = pam_getenv(pamh, tmpval);
+ D(("Expanded to <%s>", tmpptr));
+ break;
+
+ case '@':
+ D(("Expanding pam item: <%s>",tmpval));
+ tmpptr = _pam_get_item_byname(pamh, tmpval);
+ D(("Expanded to <%s>", tmpptr));
+ break;
+
+ default:
+ D(("Impossible error, type == <%c>", type));
+ _log_err(LOG_CRIT, "Impossible error, type == <%c>", type);
+ return PAM_ABORT;
+ } /* switch */
+
+ if (tmpptr) {
+ if ((strlen(tmp) + strlen(tmpptr)) < MAX_ENV) {
+ strcat(tmp, tmpptr);
+ } else {
+ /* is it really a good idea to try to log this? */
+ D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
+ _log_err(LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
+ }
+ }
+ } /* if ('{' != *orig++) */
+ } else { /* if ( '$' == *orig || '@' == *orig) */
+ if ((strlen(tmp) + 1) < MAX_ENV) {
+ tmp[strlen(tmp)] = *orig++; /* Note the increment */
+ } else {
+ /* is it really a good idea to try to log this? */
+ D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
+ _log_err(LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
+ }
+ }
+ } /* for (;*orig;) */
+
+ if (strlen(tmp) > strlen(*value)) {
+ free(*value);
+ if ((*value = malloc(strlen(tmp) +1)) == NULL) {
+ D(("Couldn't malloc %d bytes for expanded var", strlen(tmp)+1));
+ _log_err(LOG_ERR,"Couldn't malloc %d bytes for expanded var",
+ strlen(tmp)+1);
+ return PAM_BUF_ERR;
+ }
+ }
+ strcpy(*value, tmp);
+ memset(tmp,'\0',sizeof(tmp));
+ D(("Exit."));
+
+ return PAM_SUCCESS;
+}
+
+static const char * _pam_get_item_byname(pam_handle_t *pamh, const char *name)
+{
+ /*
+ * This function just allows me to use names as given in the config
+ * file and translate them into the appropriate PAM_ITEM macro
+ */
+
+ int item;
+ const char *itemval;
+
+ D(("Called."));
+ if (strcmp(name, "PAM_USER") == 0) {
+ item = PAM_USER;
+ } else if (strcmp(name, "PAM_USER_PROMPT") == 0) {
+ item = PAM_USER_PROMPT;
+ } else if (strcmp(name, "PAM_TTY") == 0) {
+ item = PAM_TTY;
+ } else if (strcmp(name, "PAM_RUSER") == 0) {
+ item = PAM_RUSER;
+ } else if (strcmp(name, "PAM_RHOST") == 0) {
+ item = PAM_RHOST;
+ } else {
+ D(("Unknown PAM_ITEM: <%s>", name));
+ _log_err(LOG_ERR, "Unknown PAM_ITEM: <%s>", name);
+ return NULL;
+ }
+
+ if (pam_get_item(pamh, item, (const void **)&itemval) != PAM_SUCCESS) {
+ D(("pam_get_item failed"));
+ return NULL; /* let pam_get_item() log the error */
+ }
+ D(("Exit."));
+ return itemval;
+}
+
+static int _define_var(pam_handle_t *pamh, VAR *var)
+{
+ /* We have a variable to define, this is a simple function */
+
+ char *envvar;
+ int size, retval=PAM_SUCCESS;
+
+ D(("Called."));
+ size = strlen(var->name)+strlen(var->value)+2;
+ if ((envvar = malloc(size)) == NULL) {
+ D(("Malloc fail, size = %d", size));
+ _log_err(LOG_ERR, "Malloc fail, size = %d", size);
+ return PAM_BUF_ERR;
+ }
+ (void) sprintf(envvar,"%s=%s",var->name,var->value);
+ retval = pam_putenv(pamh, envvar);
+ free(envvar); envvar=NULL;
+ D(("Exit."));
+ return retval;
+}
+
+static int _undefine_var(pam_handle_t *pamh, VAR *var)
+{
+ /* We have a variable to undefine, this is a simple function */
+
+ D(("Called and exit."));
+ return pam_putenv(pamh, var->name);
+}
+
+static void _clean_var(VAR *var)
+{
+ if (var->name) {
+ free(var->name);
+ }
+ if (var->defval && (&quote != var->defval)) {
+ free(var->defval);
+ }
+ if (var->override && (&quote != var->override)) {
+ free(var->override);
+ }
+ var->name = NULL;
+ var->value = NULL; /* never has memory specific to it */
+ var->defval = NULL;
+ var->override = NULL;
+ return;
+}
+
+
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ int retval, ctrl, readenv=DEFAULT_READ_ENVFILE;
+ char *conf_file=NULL, *env_file=NULL;
+
+ /*
+ * this module sets environment variables read in from a file
+ */
+
+ D(("Called."));
+ ctrl = _pam_parse(flags, argc, argv, &conf_file, &env_file, &readenv);
+
+ retval = _parse_config_file(pamh, ctrl, &conf_file);
+
+ if(readenv)
+ _parse_env_file(pamh, ctrl, &env_file);
+
+ /* indicate success or failure */
+
+ D(("Exit."));
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ _log_err(LOG_NOTICE, "pam_sm_acct_mgmt called inappropriatly");
+ return PAM_SERVICE_ERR;
+}
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval, ctrl, readenv=DEFAULT_READ_ENVFILE;
+ char *conf_file=NULL, *env_file=NULL;
+
+ /*
+ * this module sets environment variables read in from a file
+ */
+
+ D(("Called."));
+ ctrl = _pam_parse(flags, argc, argv, &conf_file, &env_file, &readenv);
+
+ retval = _parse_config_file(pamh, ctrl, &conf_file);
+
+ if(readenv)
+ _parse_env_file(pamh, ctrl, &env_file);
+
+ /* indicate success or failure */
+
+ D(("Exit."));
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc,
+ const char **argv)
+{
+ D(("Called and Exit"));
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ _log_err(LOG_NOTICE, "pam_sm_chauthtok called inappropriatly");
+ return PAM_SERVICE_ERR;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_env_modstruct = {
+ "pam_env",
+ 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/modules/pam_env/pam_env.conf-example b/modules/pam_env/pam_env.conf-example
new file mode 100644
index 00000000..c2c4333f
--- /dev/null
+++ b/modules/pam_env/pam_env.conf-example
@@ -0,0 +1,72 @@
+# $Date$
+# $Author$
+# $Id$
+#
+# This is the configuration file for pam_env, a PAM module to load in
+# a configurable list of environment variables for a
+#
+# The original idea for this came from Andrew G. Morgan ...
+#<quote>
+# Mmm. Perhaps you might like to write a pam_env module that reads a
+# default environment from a file? I can see that as REALLY
+# useful... Note it would be an "auth" module that returns PAM_IGNORE
+# for the auth part and sets the environment returning PAM_SUCCESS in
+# the setcred function...
+#</quote>
+#
+# What I wanted was the REMOTEHOST variable set, purely for selfish
+# reasons, and AGM didn't want it added to the SimpleApps login
+# program (which is where I added the patch). So, my first concern is
+# that variable, from there there are numerous others that might/would
+# be useful to be set: NNTPSERVER, LESS, PATH, PAGER, MANPAGER .....
+#
+# Of course, these are a different kind of variable than REMOTEHOST in
+# that they are things that are likely to be configured by
+# administrators rather than set by logging in, how to treat them both
+# in the same config file?
+#
+# Here is my idea:
+#
+# Each line starts with the variable name, there are then two possible
+# options for each variable DEFAULT and OVERRIDE.
+# DEFAULT allows and administrator to set the value of the
+# variable to some default value, if none is supplied then the empty
+# string is assumed. The OVERRIDE option tells pam_env that it should
+# enter in its value (overriding the default value) if there is one
+# to use. OVERRIDE is not used, "" is assumed and no override will be
+# done.
+#
+# VARIABLE [DEFAULT=[value]] [OVERRIDE=[value]]
+#
+# (Possibly non-existent) environment variables may be used in values
+# using the ${string} syntax and (possibly non-existent) PAM_ITEMs may
+# be used in values using the @{string} syntax. Both the $ and @
+# characters can be backslash escaped to be used as literal values
+# values can be delimited with "", escaped " not supported.
+#
+#
+# First, some special variables
+#
+# Set the REMOTEHOST variable for any hosts that are remote, default
+# to "localhost" rather than not being set at all
+#REMOTEHOST DEFAULT=localhost OVERRIDE=@{PAM_RHOST}
+#
+# Set the DISPLAY variable if it seems reasonable
+#DISPLAY DEFAULT=${REMOTEHOST}:0.0 OVERRIDE=${DISPLAY}
+#
+#
+# Now some simple variables
+#
+#PAGER DEFAULT=less
+#MANPAGER DEFAULT=less
+#LESS DEFAULT="M q e h15 z23 b80"
+#NNTPSERVER DEFAULT=localhost
+#PATH DEFAULT=${HOME}/bin:/usr/local/bin:/bin\
+#:/usr/bin:/usr/local/bin/X11:/usr/bin/X11
+#
+# silly examples of escaped variables, just to show how they work.
+#
+#DOLLAR DEFAULT=\$
+#DOLLARDOLLAR DEFAULT= OVERRIDE=\$${DOLLAR}
+#DOLLARPLUS DEFAULT=\${REMOTEHOST}${REMOTEHOST}
+#ATSIGN DEFAULT="" OVERRIDE=\@
diff --git a/modules/pam_filter/.cvsignore b/modules/pam_filter/.cvsignore
new file mode 100644
index 00000000..877dafe0
--- /dev/null
+++ b/modules/pam_filter/.cvsignore
@@ -0,0 +1,2 @@
+dynamic
+security
diff --git a/modules/pam_filter/.upperLOWER b/modules/pam_filter/.upperLOWER
new file mode 100644
index 00000000..2531b468
--- /dev/null
+++ b/modules/pam_filter/.upperLOWER
@@ -0,0 +1 @@
+a test filter that transposes upper and lower case characters
diff --git a/modules/pam_filter/Makefile b/modules/pam_filter/Makefile
new file mode 100644
index 00000000..f4db56a9
--- /dev/null
+++ b/modules/pam_filter/Makefile
@@ -0,0 +1,140 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+#
+
+ifeq ($(OS),solaris)
+
+include ../dont_makefile
+
+else
+
+TITLE=pam_filter
+FILTERS=upperLOWER
+FILTERSDIR=$(SUPLEMENTED)/pam_filter
+export FILTERSDIR
+
+CFLAGS += -I. -I..
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+#
+# this is where we compile this module
+#
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register filters
+
+dirs:
+ if [ ! -f security ]; then ln -sf include security ; fi
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+filters:
+ @for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i all ; \
+ fi ; \
+ done
+
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+endif
+
+ifdef DYNAMIC
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+endif
+
+ifdef STATIC
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ @for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i install ; \
+ fi ; \
+ done
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(INCLUDED)
+ $(INSTALL) -m 644 include/pam_filter.h $(FAKEROOT)$(INCLUDED)
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(INCLUDED)/pam_filter.h
+ @for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i remove ; \
+ fi ; \
+ done
+
+lclean:
+ rm -f $(LIBSHARED) $(LIBOBJD) $(LIBOBJS) core *~
+
+clean: lclean
+ @for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i clean ; \
+ fi ; \
+ done
+
+extraclean: lclean
+ @rm -f security
+ @rm -f *.a *.o *.so *.bak
+ for i in $(FILTERS) ; do \
+ if [ -d $$i ]; then \
+ $(MAKE) -C $$i extraclean ; \
+ fi ; \
+ done
+
+security:
+ ln -s include security
+
+endif
diff --git a/modules/pam_filter/README b/modules/pam_filter/README
new file mode 100644
index 00000000..850f1145
--- /dev/null
+++ b/modules/pam_filter/README
@@ -0,0 +1,94 @@
+#
+# $Id$
+#
+# This describes the behavior of this module with respect to the
+# /etc/pam.conf file.
+#
+# writen by Andrew Morgan <morgan@parc.power.net>
+#
+
+This module is intended to be a platform for providing access to all
+of the input/output that passes between the user and the application.
+It is only suitable for tty-based and (stdin/stdout) applications. And
+is only known to work on Linux based systems.
+
+The action of the module is dictated by the arguments it is given in
+the pam.conf file.
+
+recognized flags are:
+
+ debug print some information to syslog(3)
+
+ new_term set the PAM_TTY item to the new filtered
+ terminal (the default is to set it
+ to be that of the users terminal)
+
+ non_term don't try to set the PAM_TTY item
+
+ run1/run2 these arguments indicate that the
+ module should separate the application
+ from the user and insert a filter
+ program between them. The pathname of
+ the filter program follows the 'runN'
+ argument. Arguments that follow this
+ pathname are passed as arguments to
+ the filter program.
+
+ The distinction between run1 and run2
+ is which of the two functions of
+ the given management-type triggers the
+ execution of the indicated filter.
+
+ type: run1 run2
+ ----- ---- ----
+
+ auth pam_sm_authenticate pam_sm_setcred
+
+ account [ pam_sm_acct_mgmt (either is good) ]
+
+ session pam_sm_open_session pam_sm_close_session
+
+ password pam_sm_chauthtok/PRELIM pam_sm_chauthtok/UPDATE
+
+Note, in the case of 'password' PRELIM/UPDATE indicates which of the
+two calls to pam_sm_chauthtok from libpam (not the application) will
+trigger the filter.
+
+What a filter program should expect:
+------------------------------------
+
+Definitions for filter programs (which may be locally designed) are
+contained in the <security/pam_filter.h> file.
+
+Arguments are not passed to the filter on the command line, since this
+is plainly visible when a user types 'ps -a'. Instead they are passed
+as the filter's environment. Other information is passed in this way
+too.
+
+Here is a list of the environment variables that a filter should
+expect:
+
+ ARGS="filter_path_name argument list"
+ SERVICE="service_name" (as it appears in /etc/pam.conf)
+ USER="username"
+ TYPE="module_fn" (the name of the function in pam_filter.so
+ that invoked the filter)
+
+[This list is likely to grow. If you want something added, email me!]
+
+Among other things this module is intended to provide a useful means
+of logging the activity of users in as discrete a manner as possible.
+
+Existing filters:
+-----------------
+
+Currently, there is a single supplied filter (upperLOWER). The effect
+of using this filter is to transpose upper and lower case letters
+between the user and the application. This is really annoying when you
+try the 'xsh' example application! ;)
+
+TODO: provide more filters...
+ Decide if providing stderr interception is really overkill.
+
+Andrew G. Morgan <morgan@parc.power.net> 1996/5/27
+
diff --git a/modules/pam_filter/include/pam_filter.h b/modules/pam_filter/include/pam_filter.h
new file mode 100644
index 00000000..630198ee
--- /dev/null
+++ b/modules/pam_filter/include/pam_filter.h
@@ -0,0 +1,32 @@
+/*
+ * $Id$
+ *
+ * this file is associated with the Linux-PAM filter module.
+ * it was written by Andrew G. Morgan <morgan@linux.kernel.org>
+ *
+ */
+
+#ifndef PAM_FILTER_H
+#define PAM_FILTER_H
+
+#include <sys/file.h>
+
+/*
+ * this will fail if there is some problem with these file descriptors
+ * being allocated by the pam_filter Linux-PAM module. The numbers
+ * here are thought safe, but the filter developer should use the
+ * macros, as these numbers are subject to change.
+ *
+ * The APPXXX_FILENO file descriptors are the STDIN/OUT/ERR_FILENO of the
+ * application. The filter uses the STDIN/OUT/ERR_FILENO's to converse
+ * with the user, passes (modified) user input to the application via
+ * APPIN_FILENO, and receives application output from APPOUT_FILENO/ERR.
+ */
+
+#define APPIN_FILENO 3 /* write here to give application input */
+#define APPOUT_FILENO 4 /* read here to get application output */
+#define APPERR_FILENO 5 /* read here to get application errors */
+
+#define APPTOP_FILE 6 /* used by select */
+
+#endif
diff --git a/modules/pam_filter/pam_filter.c b/modules/pam_filter/pam_filter.c
new file mode 100644
index 00000000..a1ba10b0
--- /dev/null
+++ b/modules/pam_filter/pam_filter.c
@@ -0,0 +1,738 @@
+/*
+ * $Id$
+ *
+ * written by Andrew Morgan <morgan@transmeta.com> with much help from
+ * Richard Stevens' UNIX Network Programming book.
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <termio.h>
+
+#include <signal.h>
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/pam_filter.h>
+
+/* ------ some tokens used for convenience throughout this file ------- */
+
+#define FILTER_DEBUG 01
+#define FILTER_RUN1 02
+#define FILTER_RUN2 04
+#define NEW_TERM 010
+#define NON_TERM 020
+
+/* -------------------------------------------------------------------- */
+
+/* log errors */
+
+#include <stdarg.h>
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_filter", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+#define TERMINAL_LEN 12
+
+static int master(char *terminal)
+/*
+ * try to open all of the terminals in sequence return first free one,
+ * or -1
+ */
+{
+ const char ptys[] = "pqrs", *pty = ptys;
+ const char hexs[] = "0123456789abcdef", *hex;
+ struct stat tstat;
+ int fd;
+
+ strcpy(terminal, "/dev/pty??");
+
+ while (*pty) { /* step through four types */
+ terminal[8] = *pty++;
+ terminal[9] = '0';
+ if (stat(terminal,&tstat) < 0) {
+ _pam_log(LOG_WARNING, "unknown pseudo terminal; %s", terminal);
+ break;
+ }
+ for (hex = hexs; *hex; ) { /* step through 16 of these */
+ terminal[9] = *hex++;
+ if ((fd = open(terminal, O_RDWR)) >= 0) {
+ return fd;
+ }
+ }
+ }
+
+ /* no terminal found */
+
+ return -1;
+}
+
+static int process_args(pam_handle_t *pamh
+ , int argc, const char **argv, const char *type
+ , char ***evp, const char **filtername)
+{
+ int ctrl=0;
+
+ while (argc-- > 0) {
+ if (strcmp("debug",*argv) == 0) {
+ ctrl |= FILTER_DEBUG;
+ } else if (strcmp("new_term",*argv) == 0) {
+ ctrl |= NEW_TERM;
+ } else if (strcmp("non_term",*argv) == 0) {
+ ctrl |= NON_TERM;
+ } else if (strcmp("run1",*argv) == 0) {
+ ctrl |= FILTER_RUN1;
+ if (argc <= 0) {
+ _pam_log(LOG_ALERT,"no run filter supplied");
+ } else
+ break;
+ } else if (strcmp("run2",*argv) == 0) {
+ ctrl |= FILTER_RUN2;
+ if (argc <= 0) {
+ _pam_log(LOG_ALERT,"no run filter supplied");
+ } else
+ break;
+ } else {
+ _pam_log(LOG_ERR, "unrecognized option: %s (ignored)", *argv);
+ }
+ ++argv; /* step along list */
+ }
+
+ if (argc < 0) {
+ /* there was no reference to a filter */
+ *filtername = NULL;
+ *evp = NULL;
+ } else {
+ char **levp;
+ const char *tmp;
+ int i,size;
+
+ *filtername = *++argv;
+ if (ctrl & FILTER_DEBUG) {
+ _pam_log(LOG_DEBUG,"will run filter %s\n", *filtername);
+ }
+
+ levp = (char **) malloc(5*sizeof(char *));
+ if (levp == NULL) {
+ _pam_log(LOG_CRIT,"no memory for environment of filter");
+ return -1;
+ }
+
+ for (size=i=0; i<argc; ++i) {
+ size += strlen(argv[i])+1;
+ }
+
+ /* the "ARGS" variable */
+
+#define ARGS_OFFSET 5 /* sizeof("ARGS="); */
+#define ARGS_NAME "ARGS="
+
+ size += ARGS_OFFSET;
+
+ levp[0] = (char *) malloc(size);
+ if (levp[0] == NULL) {
+ _pam_log(LOG_CRIT,"no memory for filter arguments");
+ if (levp) {
+ free(levp);
+ }
+ return -1;
+ }
+
+ strncpy(levp[0],ARGS_NAME,ARGS_OFFSET);
+ for (i=0,size=ARGS_OFFSET; i<argc; ++i) {
+ strcpy(levp[0]+size, argv[i]);
+ size += strlen(argv[i]);
+ levp[0][size++] = ' ';
+ }
+ levp[0][--size] = '\0'; /* <NUL> terminate */
+
+ /* the "SERVICE" variable */
+
+#define SERVICE_OFFSET 8 /* sizeof("SERVICE="); */
+#define SERVICE_NAME "SERVICE="
+
+ pam_get_item(pamh, PAM_SERVICE, (const void **)&tmp);
+ size = SERVICE_OFFSET+strlen(tmp);
+
+ levp[1] = (char *) malloc(size+1);
+ if (levp[1] == NULL) {
+ _pam_log(LOG_CRIT,"no memory for service name");
+ if (levp) {
+ free(levp[0]);
+ free(levp);
+ }
+ return -1;
+ }
+
+ strncpy(levp[1],SERVICE_NAME,SERVICE_OFFSET);
+ strcpy(levp[1]+SERVICE_OFFSET, tmp);
+ levp[1][size] = '\0'; /* <NUL> terminate */
+
+ /* the "USER" variable */
+
+#define USER_OFFSET 5 /* sizeof("USER="); */
+#define USER_NAME "USER="
+
+ pam_get_user(pamh, &tmp, NULL);
+ if (tmp == NULL) {
+ tmp = "<unknown>";
+ }
+ size = USER_OFFSET+strlen(tmp);
+
+ levp[2] = (char *) malloc(size+1);
+ if (levp[2] == NULL) {
+ _pam_log(LOG_CRIT,"no memory for user's name");
+ if (levp) {
+ free(levp[1]);
+ free(levp[0]);
+ free(levp);
+ }
+ return -1;
+ }
+
+ strncpy(levp[2],USER_NAME,USER_OFFSET);
+ strcpy(levp[2]+USER_OFFSET, tmp);
+ levp[2][size] = '\0'; /* <NUL> terminate */
+
+ /* the "USER" variable */
+
+#define TYPE_OFFSET 5 /* sizeof("TYPE="); */
+#define TYPE_NAME "TYPE="
+
+ size = TYPE_OFFSET+strlen(type);
+
+ levp[3] = (char *) malloc(size+1);
+ if (levp[3] == NULL) {
+ _pam_log(LOG_CRIT,"no memory for type");
+ if (levp) {
+ free(levp[2]);
+ free(levp[1]);
+ free(levp[0]);
+ free(levp);
+ }
+ return -1;
+ }
+
+ strncpy(levp[3],TYPE_NAME,TYPE_OFFSET);
+ strcpy(levp[3]+TYPE_OFFSET, type);
+ levp[3][size] = '\0'; /* <NUL> terminate */
+
+ levp[4] = NULL; /* end list */
+
+ *evp = levp;
+ }
+
+ if ((ctrl & FILTER_DEBUG) && *filtername) {
+ char **e;
+
+ _pam_log(LOG_DEBUG,"filter[%s]: %s",type,*filtername);
+ _pam_log(LOG_DEBUG,"environment:");
+ for (e=*evp; e && *e; ++e) {
+ _pam_log(LOG_DEBUG," %s",*e);
+ }
+ }
+
+ return ctrl;
+}
+
+static void free_evp(char *evp[])
+{
+ int i;
+
+ if (evp)
+ for (i=0; i<4; ++i) {
+ if (evp[i])
+ free(evp[i]);
+ }
+ free(evp);
+}
+
+static int set_filter(pam_handle_t *pamh, int flags, int ctrl
+ , const char **evp, const char *filtername)
+{
+ int status=-1;
+ char terminal[TERMINAL_LEN];
+ struct termio stored_mode; /* initial terminal mode settings */
+ int fd[2], child=0, child2=0, aterminal;
+
+ if (filtername == NULL || *filtername != '/') {
+ _pam_log(LOG_ALERT, "filtername not permitted; require full path");
+ return PAM_ABORT;
+ }
+
+ if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
+ aterminal = 0;
+ } else {
+ aterminal = 1;
+ }
+
+ if (aterminal) {
+
+ /* open the master pseudo terminal */
+
+ fd[0] = master(terminal);
+ if (fd[0] < 0) {
+ _pam_log(LOG_CRIT,"no master terminal");
+ return PAM_AUTH_ERR;
+ }
+
+ /* set terminal into raw mode.. remember old mode so that we can
+ revert to it after the child has quit. */
+
+ /* this is termio terminal handling... */
+
+ if (ioctl(STDIN_FILENO, TCGETA, (char *) &stored_mode ) < 0) {
+ /* in trouble, so close down */
+ close(fd[0]);
+ _pam_log(LOG_CRIT, "couldn't copy terminal mode");
+ return PAM_ABORT;
+ } else {
+ struct termio t_mode = stored_mode;
+
+ t_mode.c_iflag = 0; /* no input control */
+ t_mode.c_oflag &= ~OPOST; /* no ouput post processing */
+
+ /* no signals, canonical input, echoing, upper/lower output */
+ t_mode.c_lflag &= ~(ISIG|ICANON|ECHO|XCASE);
+ t_mode.c_cflag &= ~(CSIZE|PARENB); /* no parity */
+ t_mode.c_cflag |= CS8; /* 8 bit chars */
+
+ t_mode.c_cc[VMIN] = 1; /* number of chars to satisfy a read */
+ t_mode.c_cc[VTIME] = 0; /* 0/10th second for chars */
+
+ if (ioctl(STDIN_FILENO, TCSETA, (char *) &t_mode) < 0) {
+ close(fd[0]);
+ _pam_log(LOG_WARNING, "couldn't put terminal in RAW mode");
+ return PAM_ABORT;
+ }
+
+ /*
+ * NOTE: Unlike the stream socket case here the child
+ * opens the slave terminal as fd[1] *after* the fork...
+ */
+ }
+ } else {
+
+ /*
+ * not a terminal line so just open a stream socket fd[0-1]
+ * both set...
+ */
+
+ if ( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0 ) {
+ _pam_log(LOG_CRIT,"couldn't open a stream pipe");
+ return PAM_ABORT;
+ }
+ }
+
+ /* start child process */
+
+ if ( (child = fork()) < 0 ) {
+
+ _pam_log(LOG_WARNING,"first fork failed");
+ if (aterminal) {
+ (void) ioctl(STDIN_FILENO, TCSETA, (char *) &stored_mode);
+ }
+
+ return PAM_AUTH_ERR;
+ }
+
+ if ( child == 0 ) { /* child process *is* application */
+
+ if (aterminal) {
+
+ /* close the controlling tty */
+
+#if defined(__hpux) && defined(O_NOCTTY)
+ int t = open("/dev/tty", O_RDWR|O_NOCTTY);
+#else
+ int t = open("/dev/tty",O_RDWR);
+ if (t > 0) {
+ (void) ioctl(t, TIOCNOTTY, NULL);
+ close(t);
+ }
+#endif /* defined(__hpux) && defined(O_NOCTTY) */
+
+ /* make this process it's own process leader */
+ if (setsid() == -1) {
+ _pam_log(LOG_WARNING,"child cannot become new session");
+ return PAM_ABORT;
+ }
+
+ /* find slave's name */
+ terminal[5] = 't'; /* want to open slave terminal */
+ fd[1] = open(terminal, O_RDWR);
+ close(fd[0]); /* process is the child -- uses line fd[1] */
+
+ if (fd[1] < 0) {
+ _pam_log(LOG_WARNING,"cannot open slave terminal; %s"
+ ,terminal);
+ return PAM_ABORT;
+ }
+
+ /* initialize the child's terminal to be the way the
+ parent's was before we set it into RAW mode */
+
+ if (ioctl(fd[1], TCSETA, (char *) &stored_mode) < 0) {
+ _pam_log(LOG_WARNING,"cannot set slave terminal mode; %s"
+ ,terminal);
+ close(fd[1]);
+ return PAM_ABORT;
+ }
+
+ } else {
+
+ /* nothing to do for a simple stream socket */
+
+ }
+
+ /* re-assign the stdin/out to fd[1] <- (talks to filter). */
+
+ if ( dup2(fd[1],STDIN_FILENO) != STDIN_FILENO ||
+ dup2(fd[1],STDOUT_FILENO) != STDOUT_FILENO ||
+ dup2(fd[1],STDERR_FILENO) != STDERR_FILENO ) {
+ _pam_log(LOG_WARNING
+ ,"unable to re-assign STDIN/OUT/ERR...'s");
+ close(fd[1]);
+ return PAM_ABORT;
+ }
+
+ /* make sure that file descriptors survive 'exec's */
+
+ if ( fcntl(STDIN_FILENO, F_SETFD, 0) ||
+ fcntl(STDOUT_FILENO,F_SETFD, 0) ||
+ fcntl(STDERR_FILENO,F_SETFD, 0) ) {
+ _pam_log(LOG_WARNING
+ ,"unable to re-assign STDIN/OUT/ERR...'s");
+ return PAM_ABORT;
+ }
+
+ /* now the user input is read from the parent/filter: forget fd */
+
+ close(fd[1]);
+
+ /* the current process is now aparently working with filtered
+ stdio/stdout/stderr --- success! */
+
+ return PAM_SUCCESS;
+ }
+
+ /*
+ * process is the parent here. So we can close the application's
+ * input/output
+ */
+
+ close(fd[1]);
+
+ /* Clear out passwords... there is a security problem here in
+ * that this process never executes pam_end. Consequently, any
+ * other sensitive data in this process is *not* explicitly
+ * overwritten, before the process terminates */
+
+ (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ (void) pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
+
+ /* fork a copy of process to run the actual filter executable */
+
+ if ( (child2 = fork()) < 0 ) {
+
+ _pam_log(LOG_WARNING,"filter fork failed");
+ child2 = 0;
+
+ } else if ( child2 == 0 ) { /* exec the child filter */
+
+ if ( dup2(fd[0],APPIN_FILENO) != APPIN_FILENO ||
+ dup2(fd[0],APPOUT_FILENO) != APPOUT_FILENO ||
+ dup2(fd[0],APPERR_FILENO) != APPERR_FILENO ) {
+ _pam_log(LOG_WARNING
+ ,"unable to re-assign APPIN/OUT/ERR...'s");
+ close(fd[0]);
+ exit(1);
+ }
+
+ /* make sure that file descriptors survive 'exec's */
+
+ if ( fcntl(APPIN_FILENO, F_SETFD, 0) == -1 ||
+ fcntl(APPOUT_FILENO,F_SETFD, 0) == -1 ||
+ fcntl(APPERR_FILENO,F_SETFD, 0) == -1 ) {
+ _pam_log(LOG_WARNING
+ ,"unable to retain APPIN/OUT/ERR...'s");
+ close(APPIN_FILENO);
+ close(APPOUT_FILENO);
+ close(APPERR_FILENO);
+ exit(1);
+ }
+
+ /* now the user input is read from the parent through filter */
+
+ execle(filtername, "<pam_filter>", NULL, evp);
+
+ /* getting to here is an error */
+
+ _pam_log(LOG_ALERT, "filter: %s, not executable", filtername);
+
+ } else { /* wait for either of the two children to exit */
+
+ while (child && child2) { /* loop if there are two children */
+ int lstatus=0;
+ int chid;
+
+ chid = wait(&lstatus);
+ if (chid == child) {
+
+ if (WIFEXITED(lstatus)) { /* exited ? */
+ status = WEXITSTATUS(lstatus);
+ } else if (WIFSIGNALED(lstatus)) { /* killed ? */
+ status = -1;
+ } else
+ continue; /* just stopped etc.. */
+ child = 0; /* the child has exited */
+
+ } else if (chid == child2) {
+ /*
+ * if the filter has exited. Let the child die
+ * naturally below
+ */
+ if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus))
+ child2 = 0;
+ } else {
+
+ _pam_log(LOG_ALERT
+ ,"programming error <chid=%d,lstatus=%x>: "
+ __FILE__ " line %d"
+ , lstatus, __LINE__ );
+ child = child2 = 0;
+ status = -1;
+
+ }
+ }
+ }
+
+ close(fd[0]);
+
+ /* if there is something running, wait for it to exit */
+
+ while (child || child2) {
+ int lstatus=0;
+ int chid;
+
+ chid = wait(&lstatus);
+
+ if (child && chid == child) {
+
+ if (WIFEXITED(lstatus)) { /* exited ? */
+ status = WEXITSTATUS(lstatus);
+ } else if (WIFSIGNALED(lstatus)) { /* killed ? */
+ status = -1;
+ } else
+ continue; /* just stopped etc.. */
+ child = 0; /* the child has exited */
+
+ } else if (child2 && chid == child2) {
+
+ if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus))
+ child2 = 0;
+
+ } else {
+
+ _pam_log(LOG_ALERT
+ ,"programming error <chid=%d,lstatus=%x>: "
+ __FILE__ " line %d"
+ , lstatus, __LINE__ );
+ child = child2 = 0;
+ status = -1;
+
+ }
+ }
+
+ if (aterminal) {
+ /* reset to initial terminal mode */
+ (void) ioctl(STDIN_FILENO, TCSETA, (char *) &stored_mode);
+ }
+
+ if (ctrl & FILTER_DEBUG) {
+ _pam_log(LOG_DEBUG,"parent process exited"); /* clock off */
+ }
+
+ /* quit the parent process, returning the child's exit status */
+
+ exit(status);
+}
+
+static int set_the_terminal(pam_handle_t *pamh)
+{
+ const char *tty;
+
+ if (pam_get_item(pamh, PAM_TTY, (const void **)&tty) != PAM_SUCCESS
+ || tty == NULL) {
+ tty = ttyname(STDIN_FILENO);
+ if (tty == NULL) {
+ _pam_log(LOG_ERR, "couldn't get the tty name");
+ return PAM_ABORT;
+ }
+ if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "couldn't set tty name");
+ return PAM_ABORT;
+ }
+ }
+ return PAM_SUCCESS;
+}
+
+static int need_a_filter(pam_handle_t *pamh
+ , int flags, int argc, const char **argv
+ , const char *name, int which_run)
+{
+ int ctrl;
+ char **evp;
+ const char *filterfile;
+ int retval;
+
+ ctrl = process_args(pamh, argc, argv, name, &evp, &filterfile);
+ if (ctrl == -1) {
+ return PAM_AUTHINFO_UNAVAIL;
+ }
+
+ /* set the tty to the old or the new one? */
+
+ if (!(ctrl & NON_TERM) && !(ctrl & NEW_TERM)) {
+ retval = set_the_terminal(pamh);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "tried and failed to set PAM_TTY");
+ }
+ } else {
+ retval = PAM_SUCCESS; /* nothing to do which is always a success */
+ }
+
+ if (retval == PAM_SUCCESS && (ctrl & which_run)) {
+ retval = set_filter(pamh, flags, ctrl
+ , (const char **)evp, filterfile);
+ }
+
+ if (retval == PAM_SUCCESS
+ && !(ctrl & NON_TERM) && (ctrl & NEW_TERM)) {
+ retval = set_the_terminal(pamh);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR
+ , "tried and failed to set new terminal as PAM_TTY");
+ }
+ }
+
+ free_evp(evp);
+
+ if (ctrl & FILTER_DEBUG) {
+ _pam_log(LOG_DEBUG, "filter/%s, returning %d", name, retval);
+ _pam_log(LOG_DEBUG, "[%s]", pam_strerror(pamh, retval));
+ }
+
+ return retval;
+}
+
+/* ----------------- public functions ---------------- */
+
+/*
+ * here are the advertised access points ...
+ */
+
+/* ------------------ authentication ----------------- */
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh
+ , int flags, int argc, const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv
+ , "authenticate", FILTER_RUN1);
+}
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv, "setcred", FILTER_RUN2);
+}
+
+/* --------------- account management ---------------- */
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv
+ , "setcred", FILTER_RUN1|FILTER_RUN2 );
+}
+
+/* --------------- session management ---------------- */
+
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv
+ , "open_session", FILTER_RUN1);
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ return need_a_filter(pamh, flags, argc, argv
+ , "close_session", FILTER_RUN2);
+}
+
+/* --------- updating authentication tokens --------- */
+
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ int runN;
+
+ if (flags & PAM_PRELIM_CHECK)
+ runN = FILTER_RUN1;
+ else if (flags & PAM_UPDATE_AUTHTOK)
+ runN = FILTER_RUN2;
+ else {
+ _pam_log(LOG_ERR, "unknown flags for chauthtok (0x%X)", flags);
+ return PAM_TRY_AGAIN;
+ }
+
+ return need_a_filter(pamh, flags, argc, argv, "chauthtok", runN);
+}
+
+#ifdef PAM_STATIC
+
+/* ------------ stuff for static modules ------------ */
+
+struct pam_module _pam_filter_modstruct = {
+ "pam_filter",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok,
+};
+
+#endif
diff --git a/modules/pam_filter/upperLOWER/.cvsignore b/modules/pam_filter/upperLOWER/.cvsignore
new file mode 100644
index 00000000..bcd63650
--- /dev/null
+++ b/modules/pam_filter/upperLOWER/.cvsignore
@@ -0,0 +1 @@
+upperLOWER
diff --git a/modules/pam_filter/upperLOWER/Makefile b/modules/pam_filter/upperLOWER/Makefile
new file mode 100644
index 00000000..a75b06bb
--- /dev/null
+++ b/modules/pam_filter/upperLOWER/Makefile
@@ -0,0 +1,64 @@
+#
+# $Id$
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:37 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.5 1997/04/05 06:41:35 morgan
+# fakeroot
+#
+# Revision 1.4 1997/01/04 20:25:04 morgan
+# removed need for make
+#
+# Revision 1.3 1996/11/10 20:13:08 morgan
+# email address
+#
+# Revision 1.2 1996/11/10 20:12:24 morgan
+# cross platform support
+#
+# Revision 1.1 1996/06/02 08:17:02 morgan
+# Initial revision
+#
+#
+# This directory contains a pam_filter filter executable
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+TITLE=upperLOWER
+
+#
+
+OBJS = $(TITLE).o
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+
+all: $(TITLE)
+
+$(TITLE): $(OBJS)
+ $(CC) -o $(TITLE) $(OBJS)
+ strip $(TITLE)
+
+install:
+ $(MKDIR) $(FAKEROOT)$(FILTERSDIR)
+ $(INSTALL) -m 511 $(TITLE) $(FAKEROOT)$(FILTERSDIR)
+
+remove:
+ cd $(FAKEROOT)$(FILTERSDIR) && rm -f $(TITLE)
+
+clean:
+ rm -f $(TITLE) $(OBJS) core *~
+
+extraclean: clean
+ rm -f *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_filter/upperLOWER/upperLOWER.c b/modules/pam_filter/upperLOWER/upperLOWER.c
new file mode 100644
index 00000000..3f7d26cd
--- /dev/null
+++ b/modules/pam_filter/upperLOWER/upperLOWER.c
@@ -0,0 +1,174 @@
+/*
+ * $Id$
+ *
+ * This is a sample filter program, for use with pam_filter (a module
+ * provided with Linux-PAM). This filter simply transposes upper and
+ * lower case letters, it is intended for demonstration purposes and
+ * it serves no purpose other than to annoy the user...
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:37 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/08 05:01:48 morgan
+ * glibc fixes (Thorsten Kukuk, Adam J. Richter)
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.1 1996/06/02 08:17:02 morgan
+ * Initial revision
+ *
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <security/pam_filter.h>
+
+/* ---------------------------------------------------------------- */
+
+#include <stdarg.h>
+#ifdef hpux
+# define log_this syslog
+#else
+static void log_this(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("upperLOWER", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+#endif
+
+#include <ctype.h>
+
+static void do_transpose(char *buffer,int len)
+{
+ int i;
+ for (i=0; i<len; ++i) {
+ if (islower(buffer[i])) {
+ buffer[i] = toupper(buffer[i]);
+ } else {
+ buffer[i] = tolower(buffer[i]);
+ }
+ }
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ char buffer[BUFSIZ];
+ fd_set readers;
+ void (*before_user)(char *,int);
+ void (*before_app)(char *,int);
+
+#ifdef DEBUG
+ {
+ int i;
+
+ fprintf(stderr,"environment :[\r\n");
+ for (i=0; envp[i]; ++i) {
+ fprintf(stderr,"-> %s\r\n",envp[i]);
+ }
+ fprintf(stderr,"]: end\r\n");
+ }
+#endif
+
+ if (argc != 1) {
+#ifdef DEBUG
+ fprintf(stderr,"filter invoked as conventional executable\n");
+#else
+ log_this(LOG_ERR, "filter invoked as conventional executable");
+#endif
+ exit(1);
+ }
+
+ before_user = before_app = do_transpose; /* assign filter functions */
+
+ /* enter a loop that deals with the input and output of the
+ user.. passing it to and from the application */
+
+ FD_ZERO(&readers); /* initialize reading mask */
+
+ for (;;) {
+
+ FD_SET(APPOUT_FILENO, &readers); /* wake for output */
+ FD_SET(APPERR_FILENO, &readers); /* wake for error */
+ FD_SET(STDIN_FILENO, &readers); /* wake for input */
+
+ if ( select(APPTOP_FILE,&readers,NULL,NULL,NULL) < 0 ) {
+#ifdef DEBUG
+ fprintf(stderr,"select failed\n");
+#else
+ log_this(LOG_WARNING,"select failed");
+#endif
+ break;
+ }
+
+ /* application errors */
+
+ if ( FD_ISSET(APPERR_FILENO,&readers) ) {
+ int got = read(APPERR_FILENO, buffer, BUFSIZ);
+ if (got <= 0) {
+ break;
+ } else {
+ /* translate to give to real terminal */
+ if (before_user != NULL)
+ before_user(buffer, got);
+ if ( write(STDERR_FILENO, buffer, got) != got ) {
+ log_this(LOG_WARNING,"couldn't write %d bytes?!",got);
+ break;
+ }
+ }
+ } else if ( FD_ISSET(APPOUT_FILENO,&readers) ) { /* app output */
+ int got = read(APPOUT_FILENO, buffer, BUFSIZ);
+ if (got <= 0) {
+ break;
+ } else {
+ /* translate to give to real terminal */
+ if (before_user != NULL)
+ before_user(buffer, got);
+ if ( write(STDOUT_FILENO, buffer, got) != got ) {
+ log_this(LOG_WARNING,"couldn't write %d bytes!?",got);
+ break;
+ }
+ }
+ }
+
+ if ( FD_ISSET(STDIN_FILENO, &readers) ) { /* user input */
+ int got = read(STDIN_FILENO, buffer, BUFSIZ);
+ if (got < 0) {
+ log_this(LOG_WARNING,"user input junked");
+ break;
+ } else if (got) {
+ /* translate to give to application */
+ if (before_app != NULL)
+ before_app(buffer, got);
+ if ( write(APPIN_FILENO, buffer, got) != got ) {
+ log_this(LOG_WARNING,"couldn't pass %d bytes!?",got);
+ break;
+ }
+ } else {
+ /* nothing received -- an error? */
+ log_this(LOG_WARNING,"user input null?");
+ break;
+ }
+ }
+ }
+
+ exit(0);
+}
+
+
+
diff --git a/modules/pam_ftp/.cvsignore b/modules/pam_ftp/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_ftp/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_ftp/Makefile b/modules/pam_ftp/Makefile
new file mode 100644
index 00000000..0ce77a7a
--- /dev/null
+++ b/modules/pam_ftp/Makefile
@@ -0,0 +1,88 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@linux.kernel.org> 1996/11/14
+#
+
+TITLE=pam_ftp
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_ftp/README b/modules/pam_ftp/README
new file mode 100644
index 00000000..6d03330c
--- /dev/null
+++ b/modules/pam_ftp/README
@@ -0,0 +1,18 @@
+This is the README for pam_ftp
+------------------------------
+
+This module is an authentication module that does simple ftp
+authentication.
+
+Recognized arguments:
+
+ "debug" print debug messages
+ "users=" comma separated list of users which
+ could login only with email adress
+ "ignore" allow invalid email adresses
+
+Options for:
+auth: for authentication it provides pam_authenticate() and
+ pam_setcred() hooks.
+
+Thorsten Kukuk <kukuk@suse.de>, 17. June 1999
diff --git a/modules/pam_ftp/pam_ftp.c b/modules/pam_ftp/pam_ftp.c
new file mode 100644
index 00000000..eb2c3fd9
--- /dev/null
+++ b/modules/pam_ftp/pam_ftp.c
@@ -0,0 +1,299 @@
+/* pam_ftp module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+ *
+ */
+
+#define PLEASE_ENTER_PASSWORD "Password required for %s."
+#define GUEST_LOGIN_PROMPT "Guest login ok, " \
+"send your complete e-mail address as password."
+
+/* the following is a password that "can't be correct" */
+#define BLOCK_PASSWORD "\177BAD PASSWPRD\177"
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <features.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-ftp", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static int converse(pam_handle_t *pamh, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse\n"));
+
+ retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
+ if ( retval == PAM_SUCCESS ) {
+
+ retval = conv->conv(nargs, ( const struct pam_message ** ) message
+ , response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function\n"));
+
+ if ((retval != PAM_SUCCESS) && (retval != PAM_CONV_AGAIN)) {
+ _pam_log(LOG_DEBUG, "conversation failure [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ } else {
+ _pam_log(LOG_ERR, "couldn't obtain coversation function [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation\n"));
+
+ return retval; /* propagate error status */
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 01
+#define PAM_IGNORE_EMAIL 02
+#define PAM_NO_ANON 04
+
+static int _pam_parse(int argc, const char **argv, char **users)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv,"users=",6)) {
+ *users = x_strdup(6+*argv);
+ if (*users == NULL) {
+ ctrl |= PAM_NO_ANON;
+ _pam_log(LOG_CRIT, "failed to duplicate user list - anon off");
+ }
+ } else if (!strcmp(*argv,"ignore")) {
+ ctrl |= PAM_IGNORE_EMAIL;
+ } else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/*
+ * check if name is in list or default list. place users name in *_user
+ * return 1 if listed 0 if not.
+ */
+
+static int lookup(const char *name, char *list, const char **_user)
+{
+ int anon = 0;
+
+ *_user = name; /* this is the default */
+ if (list) {
+ const char *l;
+ char *x;
+
+ x = list;
+ while ((l = strtok(x, ","))) {
+ x = NULL;
+ if (!strcmp(name, l)) {
+ *_user = list;
+ anon = 1;
+ }
+ }
+ } else {
+#define MAX_L 2
+ static const char *l[MAX_L] = { "ftp", "anonymous" };
+ int i;
+
+ for (i=0; i<MAX_L; ++i) {
+ if (!strcmp(l[i], name)) {
+ *_user = l[0];
+ anon = 1;
+ break;
+ }
+ }
+ }
+
+ return anon;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval, anon=0, ctrl;
+ const char *user;
+ char *users=NULL;
+
+ /*
+ * this module checks if the user name is ftp or annonymous. If
+ * this is the case, it can set the PAM_RUSER to the entered email
+ * address and SUCCEEDS, otherwise it FAILS.
+ */
+
+ ctrl = _pam_parse(argc, argv, &users);
+
+ retval = pam_get_user(pamh, &user, NULL);
+ if (retval != PAM_SUCCESS || user == NULL) {
+ _pam_log(LOG_ERR, "no user specified");
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (!(ctrl & PAM_NO_ANON)) {
+ anon = lookup(user, users, &user);
+ }
+
+ if (anon) {
+ retval = pam_set_item(pamh, PAM_USER, (const void *)user);
+ if (retval != PAM_SUCCESS || user == NULL) {
+ _pam_log(LOG_ERR, "user resetting failed");
+ return PAM_USER_UNKNOWN;
+ }
+ }
+
+ /*
+ * OK. we require an email address for user or the user's password.
+ * - build conversation and get their input.
+ */
+
+ {
+ struct pam_message msg[1], *mesg[1];
+ struct pam_response *resp=NULL;
+ const char *token;
+ char *prompt=NULL;
+ int i=0;
+
+ if (!anon) {
+ prompt = malloc(strlen(PLEASE_ENTER_PASSWORD) + strlen(user));
+ if (prompt == NULL) {
+ D(("out of memory!?"));
+ return PAM_BUF_ERR;
+ } else {
+ sprintf(prompt, PLEASE_ENTER_PASSWORD, user);
+ msg[i].msg = prompt;
+ }
+ } else {
+ msg[i].msg = GUEST_LOGIN_PROMPT;
+ }
+
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ mesg[i] = &msg[i];
+
+ retval = converse(pamh, ++i, mesg, &resp);
+ if (prompt) {
+ _pam_overwrite(prompt);
+ _pam_drop(prompt);
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (resp != NULL)
+ _pam_drop_reply(resp,i);
+ return ((retval == PAM_CONV_AGAIN)
+ ? PAM_INCOMPLETE:PAM_AUTHINFO_UNAVAIL);
+ }
+
+ if (anon) {
+ /* XXX: Some effort should be made to verify this email address! */
+
+ if (!(ctrl & PAM_IGNORE_EMAIL)) {
+ token = strtok(resp->resp, "@");
+ retval = pam_set_item(pamh, PAM_RUSER, token);
+
+ if ((token) && (retval == PAM_SUCCESS)) {
+ token = strtok(NULL, "@");
+ retval = pam_set_item(pamh, PAM_RHOST, token);
+ }
+ }
+
+ /* we are happy to grant annonymous access to the user */
+ retval = PAM_SUCCESS;
+
+ } else {
+ /*
+ * we have a password so set AUTHTOK
+ */
+
+ (void) pam_set_item(pamh, PAM_AUTHTOK, resp->resp);
+
+ /*
+ * this module failed, but the next one might succeed with
+ * this password.
+ */
+
+ retval = PAM_AUTH_ERR;
+ }
+
+ if (resp) { /* clean up */
+ _pam_drop_reply(resp, i);
+ }
+
+ /* success or failure */
+
+ return retval;
+ }
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_ftp_modstruct = {
+ "pam_ftp",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_group/.cvsignore b/modules/pam_group/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_group/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_group/Makefile b/modules/pam_group/Makefile
new file mode 100644
index 00000000..b61a57de
--- /dev/null
+++ b/modules/pam_group/Makefile
@@ -0,0 +1,98 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@linux.kernel.org> 1996/6/11
+#
+
+TITLE=pam_group
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/group.conf
+export CONFILE
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+DEFS=-DCONFILE=\"$(CONFILE)\"
+
+CFLAGS += $(DEFS)
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(ELIBS)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS) $(ELIBS)
+endif
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(CONFILE)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+ rm -f ./.ignore_age
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_group/group.conf b/modules/pam_group/group.conf
new file mode 100644
index 00000000..bdd76adb
--- /dev/null
+++ b/modules/pam_group/group.conf
@@ -0,0 +1,60 @@
+##
+## Note, to get this to work as it is currently typed you need
+##
+## 1. to run an application as root
+## 2. add the following groups to the /etc/group file:
+## floppy, games, sound
+##
+#
+# *** Please note that giving group membership on a session basis is
+# *** NOT inherently secure. If a user can create an executable that
+# *** is setgid a group that they are infrequently given membership
+# *** of, they can basically obtain group membership any time they
+# *** like. Example: games are alowed between the hours of 6pm and 6am
+# *** user joe logs in at 7pm writes a small C-program toplay.c that
+# *** invokes their favorite shell, compiles it and does
+# *** "chgrp games toplay; chmod g+s toplay". They are basically able
+# *** to play games any time... You have been warned. AGM
+#
+# this is an example configuration file for the pam_group module. Its
+# syntax is based on that of the pam_time module and (at some point in
+# the distant past was inspired by the 'shadow' package)
+#
+# the syntax of the lines is as follows:
+#
+# services;ttys;users;times;groups
+#
+# white space is ignored and lines maybe extended with '\\n' (escaped
+# newlines). From reading these comments, it is clear that
+# text following a '#' is ignored to the end of the line.
+#
+# the first four fields are described in the pam_time directory.
+# The only difference for these is how the time field is interpretted:
+# it is used to indicate "when" these groups are to be given to the user.
+#
+# groups
+# The (comma or space separated) list of groups that the user
+# inherits membership of. These groups are added if the previous
+# fields are satisfied by the user's request
+#
+
+#
+# Here is a simple example: running 'xsh' on tty* (any ttyXXX device),
+# the user 'us' is given access to the floppy (through membership of
+# the floppy group)
+#
+
+#xsh;tty*&!ttyp*;us;Al0000-2400;floppy
+
+#
+# another example: running 'xsh' on tty* (any ttyXXX device),
+# the user 'sword' is given access to games (through membership of
+# the floppy group) after work hours
+#
+
+#xsh; tty* ;sword;!Wk0900-1800;games, sound
+#xsh; tty* ;*;Al0900-1800;floppy
+
+#
+# End of group.conf file
+#
diff --git a/modules/pam_group/install_conf b/modules/pam_group/install_conf
new file mode 100755
index 00000000..03bb7edb
--- /dev/null
+++ b/modules/pam_group/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+QUIET_INSTALL=../../.quiet_install
+CONF=./group.conf
+MODULE=pam_group
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_group/pam_group.c b/modules/pam_group/pam_group.c
new file mode 100644
index 00000000..81953f76
--- /dev/null
+++ b/modules/pam_group/pam_group.c
@@ -0,0 +1,843 @@
+/* pam_group module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/7/6
+ */
+
+const static char rcsid[] =
+"$Id$;\n"
+"Version 0.5 for Linux-PAM\n"
+"Copyright (c) Andrew G. Morgan 1996 <morgan@linux.kernel.org>\n";
+
+#define _BSD_SOURCE
+
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+#include <syslog.h>
+#include <string.h>
+
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define PAM_GROUP_CONF CONFILE /* from external define */
+#define PAM_GROUP_BUFLEN 1000
+#define FIELD_SEPARATOR ';' /* this is new as of .02 */
+
+typedef enum { FALSE, TRUE } boolean;
+typedef enum { AND, OR } operator;
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* --- static functions for checking whether the user should be let in --- */
+
+static void _log_err(const char *format, ... )
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_group", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(LOG_CRIT, format, args);
+ va_end(args);
+ closelog();
+}
+
+static void shift_bytes(char *mem, int from, int by)
+{
+ while (by-- > 0) {
+ *mem = mem[from];
+ ++mem;
+ }
+}
+
+static int read_field(int fd, char **buf, int *from, int *to)
+{
+ /* is buf set ? */
+
+ if (! *buf) {
+ *buf = (char *) malloc(PAM_GROUP_BUFLEN);
+ if (! *buf) {
+ _log_err("out of memory");
+ return -1;
+ }
+ *from = *to = 0;
+ fd = open(PAM_GROUP_CONF, O_RDONLY);
+ }
+
+ /* do we have a file open ? return error */
+
+ if (fd < 0 && *to <= 0) {
+ _log_err( PAM_GROUP_CONF " not opened");
+ memset(*buf, 0, PAM_GROUP_BUFLEN);
+ _pam_drop(*buf);
+ return -1;
+ }
+
+ /* check if there was a newline last time */
+
+ if ((*to > *from) && (*to > 0)
+ && ((*buf)[*from] == '\0')) { /* previous line ended */
+ (*from)++;
+ (*buf)[0] = '\0';
+ return fd;
+ }
+
+ /* ready for more data: first shift the buffer's remaining data */
+
+ *to -= *from;
+ shift_bytes(*buf, *from, *to);
+ *from = 0;
+ (*buf)[*to] = '\0';
+
+ while (fd >= 0 && *to < PAM_GROUP_BUFLEN) {
+ int i;
+
+ /* now try to fill the remainder of the buffer */
+
+ i = read(fd, *to + *buf, PAM_GROUP_BUFLEN - *to);
+ if (i < 0) {
+ _log_err("error reading " PAM_GROUP_CONF);
+ return -1;
+ } else if (!i) {
+ close(fd);
+ fd = -1; /* end of file reached */
+ } else
+ *to += i;
+
+ /*
+ * contract the buffer. Delete any comments, and replace all
+ * multiple spaces with single commas
+ */
+
+ i = 0;
+#ifdef DEBUG_DUMP
+ D(("buffer=<%s>",*buf));
+#endif
+ while (i < *to) {
+ if ((*buf)[i] == ',') {
+ int j;
+
+ for (j=++i; j<*to && (*buf)[j] == ','; ++j);
+ if (j!=i) {
+ shift_bytes(i + (*buf), j-i, (*to) - j);
+ *to -= j-i;
+ }
+ }
+ switch ((*buf)[i]) {
+ int j,c;
+ case '#':
+ for (j=i; j < *to && (c = (*buf)[j]) != '\n'; ++j);
+ if (j >= *to) {
+ (*buf)[*to = ++i] = '\0';
+ } else if (c == '\n') {
+ shift_bytes(i + (*buf), j-i, (*to) - j);
+ *to -= j-i;
+ ++i;
+ } else {
+ _log_err("internal error in " __FILE__
+ " at line %d", __LINE__ );
+ return -1;
+ }
+ break;
+ case '\\':
+ if ((*buf)[i+1] == '\n') {
+ shift_bytes(i + *buf, 2, *to - (i+2));
+ *to -= 2;
+ }
+ break;
+ case '!':
+ case ' ':
+ case '\t':
+ if ((*buf)[i] != '!')
+ (*buf)[i] = ',';
+ /* delete any trailing spaces */
+ for (j=++i; j < *to && ( (c = (*buf)[j]) == ' '
+ || c == '\t' ); ++j);
+ shift_bytes(i + *buf, j-i, (*to)-j );
+ *to -= j-i;
+ break;
+ default:
+ ++i;
+ }
+ }
+ }
+
+ (*buf)[*to] = '\0';
+
+ /* now return the next field (set the from/to markers) */
+ {
+ int i;
+
+ for (i=0; i<*to; ++i) {
+ switch ((*buf)[i]) {
+ case '#':
+ case '\n': /* end of the line/file */
+ (*buf)[i] = '\0';
+ *from = i;
+ return fd;
+ case FIELD_SEPARATOR: /* end of the field */
+ (*buf)[i] = '\0';
+ *from = ++i;
+ return fd;
+ }
+ }
+ *from = i;
+ (*buf)[*from] = '\0';
+ }
+
+ if (*to <= 0) {
+ D(("[end of text]"));
+ *buf = NULL;
+ }
+ return fd;
+}
+
+/* read a member from a field */
+
+static int logic_member(const char *string, int *at)
+{
+ int len,c,to;
+ int done=0;
+ int token=0;
+
+ len=0;
+ to=*at;
+ do {
+ c = string[to++];
+
+ switch (c) {
+
+ case '\0':
+ --to;
+ done = 1;
+ break;
+
+ case '&':
+ case '|':
+ case '!':
+ if (token) {
+ --to;
+ }
+ done = 1;
+ break;
+
+ default:
+ if (isalpha(c) || c == '*' || isdigit(c) || c == '_'
+ || c == '-' || c == '.') {
+ token = 1;
+ } else if (token) {
+ --to;
+ done = 1;
+ } else {
+ ++*at;
+ }
+ }
+ } while (!done);
+
+ return to - *at;
+}
+
+typedef enum { VAL, OP } expect;
+
+static boolean logic_field(const void *me, const char *x, int rule,
+ boolean (*agrees)(const void *, const char *
+ , int, int))
+{
+ boolean left=FALSE, right, not=FALSE;
+ operator oper=OR;
+ int at=0, l;
+ expect next=VAL;
+
+ while ((l = logic_member(x,&at))) {
+ int c = x[at];
+
+ if (next == VAL) {
+ if (c == '!')
+ not = !not;
+ else if (isalpha(c) || c == '*') {
+ right = not ^ agrees(me, x+at, l, rule);
+ if (oper == AND)
+ left &= right;
+ else
+ left |= right;
+ next = OP;
+ } else {
+ _log_err("garbled syntax; expected name (rule #%d)", rule);
+ return FALSE;
+ }
+ } else { /* OP */
+ switch (c) {
+ case '&':
+ oper = AND;
+ break;
+ case '|':
+ oper = OR;
+ break;
+ default:
+ _log_err("garbled syntax; expected & or | (rule #%d)"
+ , rule);
+ D(("%c at %d",c,at));
+ return FALSE;
+ }
+ next = VAL;
+ }
+ at += l;
+ }
+
+ return left;
+}
+
+static boolean is_same(const void *A, const char *b, int len, int rule)
+{
+ int i;
+ const char *a;
+
+ a = A;
+ for (i=0; len > 0; ++i, --len) {
+ if (b[i] != a[i]) {
+ if (b[i++] == '*') {
+ return (!--len || !strncmp(b+i,a+strlen(a)-len,len));
+ } else
+ return FALSE;
+ }
+ }
+ return ( !len );
+}
+
+typedef struct {
+ int day; /* array of 7 bits, one set for today */
+ int minute; /* integer, hour*100+minute for now */
+} TIME;
+
+struct day {
+ const char *d;
+ int bit;
+} static const days[11] = {
+ { "su", 01 },
+ { "mo", 02 },
+ { "tu", 04 },
+ { "we", 010 },
+ { "th", 020 },
+ { "fr", 040 },
+ { "sa", 0100 },
+ { "wk", 076 },
+ { "wd", 0101 },
+ { "al", 0177 },
+ { NULL, 0 }
+};
+
+static TIME time_now(void)
+{
+ struct tm *local;
+ time_t the_time;
+ TIME this;
+
+ the_time = time((time_t *)0); /* get the current time */
+ local = localtime(&the_time);
+ this.day = days[local->tm_wday].bit;
+ this.minute = local->tm_hour*100 + local->tm_min;
+
+ D(("day: 0%o, time: %.4d", this.day, this.minute));
+ return this;
+}
+
+/* take the current date and see if the range "date" passes it */
+static boolean check_time(const void *AT, const char *times, int len, int rule)
+{
+ boolean not,pass;
+ int marked_day, time_start, time_end;
+ const TIME *at;
+ int i,j=0;
+
+ at = AT;
+ D(("checking: 0%o/%.4d vs. %s", at->day, at->minute, times));
+
+ if (times == NULL) {
+ /* this should not happen */
+ _log_err("internal error: " __FILE__ " line %d", __LINE__);
+ return FALSE;
+ }
+
+ if (times[j] == '!') {
+ ++j;
+ not = TRUE;
+ } else {
+ not = FALSE;
+ }
+
+ for (marked_day = 0; len > 0 && isalpha(times[j]); --len) {
+ int this_day=-1;
+
+ D(("%c%c ?", times[j], times[j+1]));
+ for (i=0; days[i].d != NULL; ++i) {
+ if (tolower(times[j]) == days[i].d[0]
+ && tolower(times[j+1]) == days[i].d[1] ) {
+ this_day = days[i].bit;
+ break;
+ }
+ }
+ j += 2;
+ if (this_day == -1) {
+ _log_err("bad day specified (rule #%d)", rule);
+ return FALSE;
+ }
+ marked_day ^= this_day;
+ }
+ if (marked_day == 0) {
+ _log_err("no day specified");
+ return FALSE;
+ }
+ D(("day range = 0%o", marked_day));
+
+ time_start = 0;
+ for (i=0; len > 0 && i < 4 && isdigit(times[i+j]); ++i, --len) {
+ time_start *= 10;
+ time_start += times[i+j]-'0'; /* is this portable? */
+ }
+ j += i;
+
+ if (times[j] == '-') {
+ time_end = 0;
+ for (i=1; len > 0 && i < 5 && isdigit(times[i+j]); ++i, --len) {
+ time_end *= 10;
+ time_end += times[i+j]-'0'; /* is this portable? */
+ }
+ j += i;
+ } else
+ time_end = -1;
+
+ D(("i=%d, time_end=%d, times[j]='%c'", i, time_end, times[j]));
+ if (i != 5 || time_end == -1) {
+ _log_err("no/bad times specified (rule #%d)", rule);
+ return TRUE;
+ }
+ D(("times(%d to %d)", time_start,time_end));
+ D(("marked_day = 0%o", marked_day));
+
+ /* compare with the actual time now */
+
+ pass = FALSE;
+ if (time_start < time_end) { /* start < end ? --> same day */
+ if ((at->day & marked_day) && (at->minute >= time_start)
+ && (at->minute < time_end)) {
+ D(("time is listed"));
+ pass = TRUE;
+ }
+ } else { /* spans two days */
+ if ((at->day & marked_day) && (at->minute >= time_start)) {
+ D(("caught on first day"));
+ pass = TRUE;
+ } else {
+ marked_day <<= 1;
+ marked_day |= (marked_day & 0200) ? 1:0;
+ D(("next day = 0%o", marked_day));
+ if ((at->day & marked_day) && (at->minute <= time_end)) {
+ D(("caught on second day"));
+ pass = TRUE;
+ }
+ }
+ }
+
+ return (not ^ pass);
+}
+
+static int find_member(const char *string, int *at)
+{
+ int len,c,to;
+ int done=0;
+ int token=0;
+
+ len=0;
+ to=*at;
+ do {
+ c = string[to++];
+
+ switch (c) {
+
+ case '\0':
+ --to;
+ done = 1;
+ break;
+
+ case '&':
+ case '|':
+ case '!':
+ if (token) {
+ --to;
+ }
+ done = 1;
+ break;
+
+ default:
+ if (isalpha(c) || isdigit(c) || c == '_' || c == '*'
+ || c == '-') {
+ token = 1;
+ } else if (token) {
+ --to;
+ done = 1;
+ } else {
+ ++*at;
+ }
+ }
+ } while (!done);
+
+ return to - *at;
+}
+
+#define GROUP_BLK 10
+#define blk_size(len) (((len-1 + GROUP_BLK)/GROUP_BLK)*GROUP_BLK)
+
+static int mkgrplist(char *buf, gid_t **list, int len)
+{
+ int l,at=0;
+ int blks;
+
+ blks = blk_size(len);
+ D(("cf. blks=%d and len=%d", blks,len));
+
+ while ((l = find_member(buf,&at))) {
+ int edge;
+
+ if (len >= blks) {
+ gid_t *tmp;
+
+ D(("allocating new block"));
+ tmp = (gid_t *) realloc((*list)
+ , sizeof(gid_t) * (blks += GROUP_BLK));
+ if (tmp != NULL) {
+ (*list) = tmp;
+ } else {
+ _log_err("out of memory for group list");
+ free(*list);
+ (*list) = NULL;
+ return -1;
+ }
+ }
+
+ /* '\0' terminate the entry */
+
+ edge = (buf[at+l]) ? 1:0;
+ buf[at+l] = '\0';
+ D(("found group: %s",buf+at));
+
+ /* this is where we convert a group name to a gid_t */
+#ifdef WANT_PWDB
+ {
+ int retval;
+ const struct pwdb *pw=NULL;
+
+ retval = pwdb_locate("group", PWDB_DEFAULT, buf+at
+ , PWDB_ID_UNKNOWN, &pw);
+ if (retval != PWDB_SUCCESS) {
+ _log_err("bad group: %s; %s", buf+at, pwdb_strerror(retval));
+ } else {
+ const struct pwdb_entry *pwe=NULL;
+
+ D(("group %s exists", buf+at));
+ retval = pwdb_get_entry(pw, "gid", &pwe);
+ if (retval == PWDB_SUCCESS) {
+ D(("gid = %d [%p]",* (const gid_t *) pwe->value,list));
+ (*list)[len++] = * (const gid_t *) pwe->value;
+ pwdb_entry_delete(&pwe); /* tidy up */
+ } else {
+ _log_err("%s group entry is bad; %s"
+ , pwdb_strerror(retval));
+ }
+ pw = NULL; /* break link - cached for later use */
+ }
+ }
+#else
+ {
+ const struct group *grp;
+
+ grp = getgrnam(buf+at);
+ if (grp == NULL) {
+ _log_err("bad group: %s", buf+at);
+ } else {
+ D(("group %s exists", buf+at));
+ (*list)[len++] = grp->gr_gid;
+ }
+ }
+#endif
+
+ /* next entry along */
+
+ at += l + edge;
+ }
+ D(("returning with [%p/len=%d]->%p",list,len,*list));
+ return len;
+}
+
+
+static int check_account(const char *service, const char *tty
+ , const char *user)
+{
+ int from=0,to=0,fd=-1;
+ char *buffer=NULL;
+ int count=0;
+ TIME here_and_now;
+ int retval=PAM_SUCCESS;
+ gid_t *grps;
+ int no_grps;
+
+ /*
+ * first we get the current list of groups - the application
+ * will have previously done an initgroups(), or equivalent.
+ */
+
+ D(("counting supplementary groups"));
+ no_grps = getgroups(0, NULL); /* find the current number of groups */
+ if (no_grps > 0) {
+ grps = calloc( blk_size(no_grps) , sizeof(gid_t) );
+ D(("copying current list into grps [%d big]",blk_size(no_grps)));
+ (void) getgroups(no_grps, grps);
+#ifdef DEBUG
+ {
+ int z;
+ for (z=0; z<no_grps; ++z) {
+ D(("gid[%d]=%d", z, grps[z]));
+ }
+ }
+#endif
+ } else {
+ D(("no supplementary groups known"));
+ no_grps = 0;
+ grps = NULL;
+ }
+
+ here_and_now = time_now(); /* find current time */
+
+ /* parse the rules in the configuration file */
+ do {
+ int good=TRUE;
+
+ /* here we get the service name field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ /* empty line .. ? */
+ continue;
+ }
+ ++count;
+ D(("working on rule #%d",count));
+
+ good = logic_field(service, buffer, count, is_same);
+ D(("with service: %s", good ? "passes":"fails" ));
+
+ /* here we get the terminal name field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; no tty entry #%d", count);
+ continue;
+ }
+ good &= logic_field(tty, buffer, count, is_same);
+ D(("with tty: %s", good ? "passes":"fails" ));
+
+ /* here we get the username field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; no user entry #%d", count);
+ continue;
+ }
+ good &= logic_field(user, buffer, count, is_same);
+ D(("with user: %s", good ? "passes":"fails" ));
+
+ /* here we get the time field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; no time entry #%d", count);
+ continue;
+ }
+
+ good &= logic_field(&here_and_now, buffer, count, check_time);
+ D(("with time: %s", good ? "passes":"fails" ));
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; no listed groups for rule #%d"
+ , count);
+ continue;
+ }
+
+ /*
+ * so we have a list of groups, we need to turn it into
+ * something to send to setgroups(2)
+ */
+
+ if (good) {
+ D(("adding %s to gid list", buffer));
+ good = mkgrplist(buffer, &grps, no_grps);
+ if (good < 0) {
+ no_grps = 0;
+ } else {
+ no_grps = good;
+ }
+ }
+
+ /* check the line is terminated correctly */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (buffer && buffer[0]) {
+ _log_err(PAM_GROUP_CONF "; poorly terminated rule #%d", count);
+ }
+
+ if (good > 0) {
+ D(("rule #%d passed, added %d groups", count, good));
+ } else if (good < 0) {
+ retval = PAM_BUF_ERR;
+ } else {
+ D(("rule #%d failed", count));
+ }
+
+ } while (buffer);
+
+ /* now set the groups for the user */
+
+ if (no_grps > 0) {
+ int err;
+ D(("trying to set %d groups", no_grps));
+#ifdef DEBUG
+ for (err=0; err<no_grps; ++err) {
+ D(("gid[%d]=%d", err, grps[err]));
+ }
+#endif
+ if ((err = setgroups(no_grps, grps))) {
+ D(("but couldn't set groups %d", err));
+ _log_err("unable to set the group membership for user (err=%d)"
+ , err);
+ retval = PAM_CRED_ERR;
+ }
+ }
+
+ if (grps) { /* tidy up */
+ memset(grps, 0, sizeof(gid_t) * blk_size(no_grps));
+ _pam_drop(grps);
+ no_grps = 0;
+ }
+
+ return retval;
+}
+
+/* --- public authentication management functions --- */
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ const char *service=NULL, *tty=NULL;
+ const char *user=NULL;
+ int retval;
+ unsigned setting;
+
+ /* only interested in establishing credentials */
+
+ setting = flags;
+ if (!(setting & PAM_ESTABLISH_CRED)) {
+ D(("ignoring call - not for establishing credentials"));
+ return PAM_SUCCESS; /* don't fail because of this */
+ }
+
+ /* set service name */
+
+ if (pam_get_item(pamh, PAM_SERVICE, (const void **)&service)
+ != PAM_SUCCESS || service == NULL) {
+ _log_err("cannot find the current service name");
+ return PAM_ABORT;
+ }
+
+ /* set username */
+
+ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
+ || *user == '\0') {
+ _log_err("cannot determine the user's name");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* set tty name */
+
+ if (pam_get_item(pamh, PAM_TTY, (const void **)&tty) != PAM_SUCCESS
+ || tty == NULL) {
+ D(("PAM_TTY not set, probing stdin"));
+ tty = ttyname(STDIN_FILENO);
+ if (tty == NULL) {
+ _log_err("couldn't get the tty name");
+ return PAM_ABORT;
+ }
+ if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
+ _log_err("couldn't set tty name");
+ return PAM_ABORT;
+ }
+ }
+
+ if (strncmp("/dev/",tty,5) == 0) { /* strip leading /dev/ */
+ tty += 5;
+ }
+
+ /* good, now we have the service name, the user and the terminal name */
+
+ D(("service=%s", service));
+ D(("user=%s", user));
+ D(("tty=%s", tty));
+
+#ifdef WANT_PWDB
+
+ /* We initialize the pwdb library and check the account */
+ retval = pwdb_start(); /* initialize */
+ if (retval == PWDB_SUCCESS) {
+ retval = check_account(service,tty,user); /* get groups */
+ (void) pwdb_end(); /* tidy up */
+ } else {
+ D(("failed to initialize pwdb; %s", pwdb_strerror(retval)));
+ _log_err("unable to initialize libpwdb");
+ retval = PAM_ABORT;
+ }
+
+#else /* WANT_PWDB */
+ retval = check_account(service,tty,user); /* get groups */
+#endif /* WANT_PWDB */
+
+ return retval;
+}
+
+/* end of module definition */
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_group_modstruct = {
+ "pam_group",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
diff --git a/modules/pam_issue/.cvsignore b/modules/pam_issue/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_issue/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_issue/Makefile b/modules/pam_issue/Makefile
new file mode 100644
index 00000000..6770ef36
--- /dev/null
+++ b/modules/pam_issue/Makefile
@@ -0,0 +1,86 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Michael K. Johnson <johnsonm@redhat.com> 1996/10/24
+#
+
+TITLE=pam_issue
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ mkdir -p ./dynamic
+endif
+ifdef STATIC
+ mkdir -p ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(LINKLIBS)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ mkdir -p $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ install -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_issue/pam_issue.c b/modules/pam_issue/pam_issue.c
new file mode 100644
index 00000000..9fbf2551
--- /dev/null
+++ b/modules/pam_issue/pam_issue.c
@@ -0,0 +1,265 @@
+/* pam_issue module - a simple /etc/issue parser to set PAM_USER_PROMPT
+ *
+ * Copyright 1999 by Ben Collins <bcollins@debian.org>
+ *
+ * Needs to be called before any other auth modules so we can setup the
+ * user prompt before it's first used. Allows one argument option, which
+ * is the full path to a file to be used for issue (uses /etc/issue as a
+ * default) such as "issue=/etc/issue.telnet".
+ *
+ * We can also parse escapes within the the issue file (enabled by
+ * default, but can be disabled with the "noesc" option). It's the exact
+ * same parsing as util-linux's agetty program performs.
+ *
+ * Released under the GNU LGPL version 2 or later
+ */
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <utmp.h>
+#include <malloc.h>
+
+#include <security/_pam_macros.h>
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+static int _user_prompt_set = 0;
+
+char *do_prompt (FILE *);
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ int retval = PAM_SUCCESS;
+ FILE *fd;
+ int parse_esc = 1;
+ char *prompt_tmp = NULL, *cur_prompt = NULL;
+ struct stat st;
+ char *issue_file = NULL;
+
+ /* If we've already set the prompt, don't set it again */
+ if(_user_prompt_set)
+ return PAM_IGNORE;
+ else
+ /* we set this here so if we fail below, we wont get further
+ than this next time around (only one real failure) */
+ _user_prompt_set = 1;
+
+ for ( ; argc-- > 0 ; ++argv ) {
+ if (!strncmp(*argv,"issue=",6)) {
+ issue_file = (char *) strdup(6+*argv);
+ if (issue_file != NULL) {
+ D(("set issue_file to: %s", issue_file));
+ } else {
+ D(("failed to strdup issue_file - ignored"));
+ return PAM_IGNORE;
+ }
+ } else if (!strcmp(*argv,"noesc")) {
+ parse_esc = 0;
+ D(("turning off escape parsing by request"));
+ } else
+ D(("unknown option passed: %s", *argv));
+ }
+
+ if (issue_file == NULL)
+ issue_file = strdup("/etc/issue");
+
+ if ((fd = fopen(issue_file, "r")) != NULL) {
+ int tot_size = 0;
+
+ if (stat(issue_file, &st) < 0)
+ return PAM_IGNORE;
+
+ retval = pam_get_item(pamh, PAM_USER_PROMPT, (const void **) &cur_prompt);
+ if (retval != PAM_SUCCESS)
+ return PAM_IGNORE;
+
+ /* first read in the issue file */
+
+ if (parse_esc)
+ prompt_tmp = do_prompt(fd);
+ else {
+ int count = 0;
+ prompt_tmp = malloc(st.st_size + 1);
+ if (prompt_tmp == NULL) return PAM_IGNORE;
+ memset (prompt_tmp, '\0', st.st_size + 1);
+ count = fread(prompt_tmp, sizeof(char *), st.st_size, fd);
+ prompt_tmp[st.st_size] = '\0';
+ }
+
+ fclose(fd);
+
+ tot_size = strlen(prompt_tmp) + strlen(cur_prompt) + 1;
+
+ /*
+ * alloc some extra space for the original prompt
+ * and postpend it to the buffer
+ */
+ prompt_tmp = realloc(prompt_tmp, tot_size);
+ strcpy(prompt_tmp+strlen(prompt_tmp), cur_prompt);
+
+ prompt_tmp[tot_size] = '\0';
+
+ retval = pam_set_item(pamh, PAM_USER_PROMPT, (const char *) prompt_tmp);
+
+ free(issue_file);
+ free(prompt_tmp);
+ } else {
+ D(("could not open issue_file: %s", issue_file));
+ return PAM_IGNORE;
+ }
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+char *do_prompt(FILE *fd)
+{
+ int c, size = 1024;
+ char *issue = (char *)malloc(size);
+ char buf[1024];
+ struct utsname uts;
+
+ if (issue == NULL || fd == NULL)
+ return NULL;
+
+ issue[0] = '\0'; /* zero this, for strcat to work on first buf */
+ (void) uname(&uts);
+
+ while ((c = getc(fd)) != EOF) {
+ if (c == '\\') {
+ c = getc(fd);
+ switch (c) {
+ case 's':
+ snprintf (buf, 1024, "%s", uts.sysname);
+ break;
+ case 'n':
+ snprintf (buf, 1024, "%s", uts.nodename);
+ break;
+ case 'r':
+ snprintf (buf, 1024, "%s", uts.release);
+ break;
+ case 'v':
+ snprintf (buf, 1024, "%s", uts.version);
+ break;
+ case 'm':
+ snprintf (buf, 1024, "%s", uts.machine);
+ break;
+ case 'o':
+ {
+ char domainname[256];
+
+ getdomainname(domainname, sizeof(domainname));
+ domainname[sizeof(domainname)-1] = '\0';
+ snprintf (buf, 1024, "%s", domainname);
+ }
+ break;
+
+ case 'd':
+ case 't':
+ {
+ const char *weekday[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu",
+ "Fri", "Sat" };
+ const char *month[] = {
+ "Jan", "Feb", "Mar", "Apr", "May",
+ "Jun", "Jul", "Aug", "Sep", "Oct",
+ "Nov", "Dec" };
+ time_t now;
+ struct tm *tm;
+
+ (void) time (&now);
+ tm = localtime(&now);
+
+ if (c == 'd')
+ snprintf (buf, 1024, "%s %s %d %d",
+ weekday[tm->tm_wday], month[tm->tm_mon],
+ tm->tm_mday,
+ tm->tm_year + 1900);
+ else
+ snprintf (buf, 1024, "%02d:%02d:%02d",
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ }
+ break;
+ case 'l':
+ {
+ char *ttyn = ttyname(1);
+ if (!strncmp(ttyn, "/dev/", 5))
+ ttyn += 5;
+ snprintf (buf, 1024, "%s", ttyn);
+ }
+ break;
+ case 'u':
+ case 'U':
+ {
+ int users = 0;
+ struct utmp *ut;
+ setutent();
+ while ((ut = getutent()))
+ if (ut->ut_type == USER_PROCESS)
+ users++;
+ endutent();
+ printf ("%d ", users);
+ if (c == 'U')
+ snprintf (buf, 1024, "%s", (users == 1) ?
+ " user" : " users");
+ break;
+ }
+ default:
+ buf[0] = c; buf[1] = '\0';
+ }
+ if ((strlen(issue) + strlen(buf)) < size + 1) {
+ size += strlen(buf) + 1;
+ issue = (char *) realloc (issue, size);
+ }
+ strcat(issue, buf);
+ } else {
+ buf[0] = c; buf[1] = '\0';
+ if ((strlen(issue) + strlen(buf)) < size + 1) {
+ size += strlen(buf) + 1;
+ issue = (char *) realloc (issue, size);
+ }
+ strcat(issue, buf);
+ }
+ }
+ return issue;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_issue_modstruct = {
+ "pam_issue",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_lastlog/.cvsignore b/modules/pam_lastlog/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_lastlog/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_lastlog/Makefile b/modules/pam_lastlog/Makefile
new file mode 100644
index 00000000..b5a6f212
--- /dev/null
+++ b/modules/pam_lastlog/Makefile
@@ -0,0 +1,112 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:40 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.2 1997/04/05 06:17:14 morgan
+# fakeroot fixed
+#
+# Revision 1.1 1997/01/04 20:29:28 morgan
+# Initial revision
+#
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/12/8
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+endif
+
+TITLE=pam_lastlog
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+
+####################### don't edit below #######################
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c
new file mode 100644
index 00000000..7207976e
--- /dev/null
+++ b/modules/pam_lastlog/pam_lastlog.c
@@ -0,0 +1,463 @@
+/* pam_lastlog module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+ *
+ * This module does the necessary work to display the last login
+ * time+date for this user, it then updates this entry for the
+ * present (login) service.
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_UTMP_H
+# include <utmp.h>
+#else
+# include <lastlog.h>
+#endif
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#ifdef WANT_PWDB
+#include <pwdb/pwdb_public.h> /* use POSIX front end */
+#endif
+
+#if defined(hpux) || defined(sunos) || defined(solaris)
+# ifndef _PATH_LASTLOG
+# define _PATH_LASTLOG "/usr/adm/lastlog"
+# endif /* _PATH_LASTLOG */
+# ifndef UT_HOSTSIZE
+# define UT_HOSTSIZE 16
+# endif /* UT_HOSTSIZE */
+# ifndef UT_LINESIZE
+# define UT_LINESIZE 12
+# endif /* UT_LINESIZE */
+#endif
+#if defined(hpux)
+struct lastlog {
+ time_t ll_time;
+ char ll_line[UT_LINESIZE];
+ char ll_host[UT_HOSTSIZE]; /* same as in utmp */
+};
+#endif /* hpux */
+
+/* XXX - time before ignoring lock. Is 1 sec enough? */
+#define LASTLOG_IGNORE_LOCK_TIME 1
+
+#define DEFAULT_HOST "" /* "[no.where]" */
+#define DEFAULT_TERM "" /* "tt???" */
+#define LASTLOG_NEVER_WELCOME "Welcome to your new account!"
+#define LASTLOG_INTRO "Last login:"
+#define LASTLOG_TIME " %s"
+#define _LASTLOG_HOST_FORMAT " from %%.%ds"
+#define _LASTLOG_LINE_FORMAT " on %%.%ds"
+#define LASTLOG_TAIL ""
+#define LASTLOG_MAXSIZE (sizeof(LASTLOG_INTRO)+0 \
+ +sizeof(LASTLOG_TIME)+strlen(the_time) \
+ +sizeof(_LASTLOG_HOST_FORMAT)+UT_HOSTSIZE \
+ +sizeof(_LASTLOG_LINE_FORMAT)+UT_LINESIZE \
+ +sizeof(LASTLOG_TAIL))
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* some syslogging */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-lastlog", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define LASTLOG_DATE 01 /* display the date of the last login */
+#define LASTLOG_HOST 02 /* display the last host used (if set) */
+#define LASTLOG_LINE 04 /* display the last terminal used */
+#define LASTLOG_NEVER 010 /* display a welcome message for first login */
+#define LASTLOG_DEBUG 020 /* send info to syslog(3) */
+#define LASTLOG_QUIET 040 /* keep quiet about things */
+
+static int _pam_parse(int flags, int argc, const char **argv)
+{
+ int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE);
+
+ /* does the appliction require quiet? */
+ if (flags & PAM_SILENT) {
+ ctrl |= LASTLOG_QUIET;
+ }
+
+ /* step through arguments */
+ for (; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug")) {
+ ctrl |= LASTLOG_DEBUG;
+ } else if (!strcmp(*argv,"nodate")) {
+ ctrl |= ~LASTLOG_DATE;
+ } else if (!strcmp(*argv,"noterm")) {
+ ctrl |= ~LASTLOG_LINE;
+ } else if (!strcmp(*argv,"nohost")) {
+ ctrl |= ~LASTLOG_HOST;
+ } else if (!strcmp(*argv,"silent")) {
+ ctrl |= LASTLOG_QUIET;
+ } else if (!strcmp(*argv,"never")) {
+ ctrl |= LASTLOG_NEVER;
+ } else {
+ _log_err(LOG_ERR,"unknown option; %s",*argv);
+ }
+ }
+
+ D(("ctrl = %o", ctrl));
+ return ctrl;
+}
+
+/* a front end for conversations */
+
+static int converse(pam_handle_t *pamh, int ctrl, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
+ if ( retval == PAM_SUCCESS ) {
+
+ retval = conv->conv(nargs, ( const struct pam_message ** ) message
+ , response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && (ctrl & LASTLOG_DEBUG) ) {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ } else {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation"));
+
+ return retval; /* propagate error status */
+}
+
+static int make_remark(pam_handle_t *pamh, int ctrl, const char *remark)
+{
+ int retval;
+
+ if (!(ctrl & LASTLOG_QUIET)) {
+ struct pam_message msg[1], *mesg[1];
+ struct pam_response *resp=NULL;
+
+ mesg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = remark;
+
+ retval = converse(pamh, ctrl, 1, mesg, &resp);
+
+ msg[0].msg = NULL;
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ } else {
+ D(("keeping quiet"));
+ retval = PAM_SUCCESS;
+ }
+
+ D(("returning %s", pam_strerror(pamh, retval)));
+ return retval;
+}
+
+/*
+ * Values for the announce flags..
+ */
+
+static int last_login_date(pam_handle_t *pamh, int announce, uid_t uid)
+{
+ struct flock last_lock;
+ struct lastlog last_login;
+ int retval = PAM_SESSION_ERR;
+ int last_fd;
+
+ /* obtain the last login date and all the relevant info */
+ last_fd = open(_PATH_LASTLOG, O_RDWR);
+ if (last_fd < 0) {
+ D(("unable to open the %s file", _PATH_LASTLOG));
+ if (announce & LASTLOG_DEBUG) {
+ _log_err(LOG_DEBUG, "unable to open %s file", _PATH_LASTLOG);
+ }
+ retval = PAM_PERM_DENIED;
+ } else {
+ int win;
+
+ /* read the lastlogin file - for this uid */
+ (void) lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET);
+
+ memset(&last_lock, 0, sizeof(last_lock));
+ last_lock.l_type = F_RDLCK;
+ last_lock.l_whence = SEEK_SET;
+ last_lock.l_start = sizeof(last_login) * (off_t) uid;
+ last_lock.l_len = sizeof(last_login);
+
+ if ( fcntl(last_fd, F_SETLK, &last_lock) < 0 ) {
+ D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
+ _log_err(LOG_ALERT, "%s file is locked/read", _PATH_LASTLOG);
+ sleep(LASTLOG_IGNORE_LOCK_TIME);
+ }
+
+ win = ( read(last_fd, &last_login, sizeof(last_login))
+ == sizeof(last_login) );
+
+ last_lock.l_type = F_UNLCK;
+ (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
+
+ if (!win) {
+ D(("First login for user uid=%d", _PATH_LASTLOG, uid));
+ if (announce & LASTLOG_DEBUG) {
+ _log_err(LOG_DEBUG, "creating lastlog for uid %d", uid);
+ }
+ memset(&last_login, 0, sizeof(last_login));
+ }
+
+ /* rewind */
+ (void) lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET);
+
+ if (!(announce & LASTLOG_QUIET)) {
+ if (last_login.ll_time) {
+ char *the_time;
+ char *remark;
+
+ the_time = ctime(&last_login.ll_time);
+ the_time[-1+strlen(the_time)] = '\0'; /* delete '\n' */
+
+ remark = malloc(LASTLOG_MAXSIZE);
+ if (remark == NULL) {
+ D(("no memory for last login remark"));
+ retval = PAM_BUF_ERR;
+ } else {
+ int at;
+
+ /* printing prefix */
+ at = sprintf(remark, "%s", LASTLOG_INTRO);
+
+ /* we want the date? */
+ if (announce & LASTLOG_DATE) {
+ at += sprintf(remark+at, LASTLOG_TIME, the_time);
+ }
+
+ /* we want & have the host? */
+ if ((announce & LASTLOG_HOST)
+ && (last_login.ll_host[0] != '\0')) {
+ char format[2*sizeof(_LASTLOG_HOST_FORMAT)];
+
+ (void) sprintf(format, _LASTLOG_HOST_FORMAT
+ , UT_HOSTSIZE);
+ D(("format: %s", format));
+ at += sprintf(remark+at, format, last_login.ll_host);
+ _pam_overwrite(format);
+ }
+
+ /* we want and have the terminal? */
+ if ((announce & LASTLOG_LINE)
+ && (last_login.ll_line[0] != '\0')) {
+ char format[2*sizeof(_LASTLOG_LINE_FORMAT)];
+
+ (void) sprintf(format, _LASTLOG_LINE_FORMAT
+ , UT_LINESIZE);
+ D(("format: %s", format));
+ at += sprintf(remark+at, format, last_login.ll_line);
+ _pam_overwrite(format);
+ }
+
+ /* display requested combo */
+ sprintf(remark+at, "%s", LASTLOG_TAIL);
+
+ retval = make_remark(pamh, announce, remark);
+
+ /* free all the stuff malloced */
+ _pam_overwrite(remark);
+ _pam_drop(remark);
+ }
+ } else if ((!last_login.ll_time) && (announce & LASTLOG_NEVER)) {
+ D(("this is the first time this user has logged in"));
+ retval = make_remark(pamh, announce, LASTLOG_NEVER_WELCOME);
+ }
+ } else {
+ D(("no text was requested"));
+ retval = PAM_SUCCESS;
+ }
+
+ /* write latest value */
+ {
+ const char *remote_host=NULL
+ , *terminal_line=DEFAULT_TERM;
+
+ /* set this login date */
+ D(("set the most recent login time"));
+
+ (void) time(&last_login.ll_time); /* set the time */
+
+ /* set the remote host */
+ (void) pam_get_item(pamh, PAM_RHOST, (const void **)&remote_host);
+ if (remote_host == NULL) {
+ remote_host = DEFAULT_HOST;
+ }
+
+ /* copy to last_login */
+ strncpy(last_login.ll_host, remote_host
+ , sizeof(last_login.ll_host));
+ remote_host = NULL;
+
+ /* set the terminal line */
+ (void) pam_get_item(pamh, PAM_TTY, (const void **)&terminal_line);
+ D(("terminal = %s", terminal_line));
+ if (terminal_line == NULL) {
+ terminal_line = DEFAULT_TERM;
+ } else if ( !strncmp("/dev/", terminal_line, 5) ) {
+ /* strip leading "/dev/" from tty.. */
+ terminal_line += 5;
+ }
+ D(("terminal = %s", terminal_line));
+
+ /* copy to last_login */
+ strncpy(last_login.ll_line, terminal_line
+ , sizeof(last_login.ll_line));
+ terminal_line = NULL;
+
+ D(("locking last_log file"));
+
+ /* now we try to lock this file-record exclusively; non-blocking */
+ memset(&last_lock, 0, sizeof(last_lock));
+ last_lock.l_type = F_WRLCK;
+ last_lock.l_whence = SEEK_SET;
+ last_lock.l_start = sizeof(last_login) * (off_t) uid;
+ last_lock.l_len = sizeof(last_login);
+
+ if ( fcntl(last_fd, F_SETLK, &last_lock) < 0 ) {
+ D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
+ _log_err(LOG_ALERT, "%s file is locked/write", _PATH_LASTLOG);
+ sleep(LASTLOG_IGNORE_LOCK_TIME);
+ }
+
+ D(("writing to the last_log file"));
+ (void) write(last_fd, &last_login, sizeof(last_login));
+
+ last_lock.l_type = F_UNLCK;
+ (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */
+ D(("unlocked"));
+
+ close(last_fd); /* all done */
+ }
+ D(("all done with last login"));
+ }
+
+ /* reset the last login structure */
+ memset(&last_login, 0, sizeof(last_login));
+
+ return retval;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc
+ , const char **argv)
+{
+ int retval, ctrl;
+ const char *user;
+ const struct passwd *pwd;
+ uid_t uid;
+
+ /*
+ * this module gets the uid of the PAM_USER. Uses it to display
+ * last login info and then updates the lastlog for that user.
+ */
+
+ ctrl = _pam_parse(flags, argc, argv);
+
+ /* which user? */
+
+ retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
+ if (retval != PAM_SUCCESS || user == NULL || *user == '\0') {
+ _log_err(LOG_NOTICE, "user unknown");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* what uid? */
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ D(("couldn't identify user %s", user));
+ return PAM_CRED_INSUFFICIENT;
+ }
+ uid = pwd->pw_uid;
+ pwd = NULL; /* tidy up */
+
+ /* process the current login attempt (indicate last) */
+
+ retval = last_login_date(pamh, ctrl, uid);
+
+ /* indicate success or failure */
+
+ uid = -1; /* forget this */
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_lastlog_modstruct = {
+ "pam_lastlog",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_limits/.cvsignore b/modules/pam_limits/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_limits/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_limits/Makefile b/modules/pam_limits/Makefile
new file mode 100644
index 00000000..fe684863
--- /dev/null
+++ b/modules/pam_limits/Makefile
@@ -0,0 +1,98 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Cristian Gafton <gafton@redhat.com> 1996/09/10
+#
+
+ifeq ($(OS),linux)
+TITLE=pam_limits
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/limits.conf
+export CONFILE
+
+CFLAGS+=-DLIMITS_FILE=\"$(CONFILE)\"
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~ *.so
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+else
+include ../dont_makefile
+endif
diff --git a/modules/pam_limits/README b/modules/pam_limits/README
new file mode 100644
index 00000000..06a6857a
--- /dev/null
+++ b/modules/pam_limits/README
@@ -0,0 +1,87 @@
+
+pam_limits module:
+ Imposing user limits on login.
+
+THEORY OF OPERATION:
+
+First, make a root-only-readable file (/etc/limits by default or LIMITS_FILE
+defined Makefile) that describes the resource limits you wish to impose. No
+limits are imposed on UID 0 accounts.
+
+Each line describes a limit for a user in the form:
+
+<domain> <type> <item> <value>
+
+Where:
+<domain> can be:
+ - an user name
+ - a group name, with @group syntax
+ - the wildcard *, for default entry
+
+<type> can have the two values:
+ - "soft" for enforcinf the soft limits
+ - "hard" for enforcing hard limits
+
+<item> can be one of the following:
+ - core - limits the core file size (KB)
+ - data - max data size (KB)
+ - fsize - maximum filesize (KB)
+ - memlock - max locked-in-memory address space (KB)
+ - nofile - max number of open files
+ - rss - max resident set size (KB)
+ - stack - max stack size (KB)
+ - cpu - max CPU time (MIN)
+ - nproc - max number of processes
+ - as - address space limit
+ - maxlogins - max number of logins for this user
+ - maxsyslogins - max number of logins on the system
+
+To completely disable limits for a user (or a group), a single dash (-)
+will do (Example: 'bin -', '@admin -'). Please remember that individual
+limits have priority over group limits, so if you impose no limits for admin
+group, but one of the members in this group have a limits line, the user
+will have its limits set according to this line.
+
+Also, please note that all limit settings are set PER LOGIN. They are
+not global, nor are they permanent (the session only)
+
+In the LIMITS_FILE, the # character introduces a comment - the rest of the
+line is ignored.
+
+The pam_limits module does its best to report configuration problems found
+in LIMITS_FILE via syslog.
+
+EXAMPLE configuration file:
+===========================
+* soft core 0
+* hard rss 10000
+@student hard nproc 20
+@faculty soft nproc 20
+@faculty hard nproc 50
+ftp hard nproc 0
+@student - maxlogins 4
+
+
+ARGUMENTS RECOGNIZED:
+ debug verbose logging
+
+ conf=/path/to/file the limits configuration file if different from the
+ one set at compile time.
+
+MODULE SERVICES PROVIDED:
+ session _open_session and _close_session (blank)
+
+USAGE:
+ For the services you need resources limits (login for example) put a
+ the following line in /etc/pam.conf as the last line for that
+ service (usually after the pam_unix session line:
+
+ login session required /lib/security/pam_limits.so
+
+ Replace "login" for each service you are using this module, replace
+ "/lib/security" path with your real modules path.
+
+AUTHOR:
+ Cristian Gafton <gafton@redhat.com>
+ Thanks to Elliot Lee <sopwith@redhat.com> for his comments on
+ improving this module.
diff --git a/modules/pam_limits/install_conf b/modules/pam_limits/install_conf
new file mode 100755
index 00000000..d92c1f95
--- /dev/null
+++ b/modules/pam_limits/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+QUIET_INSTALL=../../.quiet_install
+CONF=./limits.skel
+MODULE=pam_limits
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_limits/limits.skel b/modules/pam_limits/limits.skel
new file mode 100644
index 00000000..5ddd9b4c
--- /dev/null
+++ b/modules/pam_limits/limits.skel
@@ -0,0 +1,42 @@
+# /etc/security/limits.conf
+#
+#Each line describes a limit for a user in the form:
+#
+#<domain> <type> <item> <value>
+#
+#Where:
+#<domain> can be:
+# - an user name
+# - a group name, with @group syntax
+# - the wildcard *, for default entry
+#
+#<type> can have the two values:
+# - "soft" for enforcing the soft limits
+# - "hard" for enforcing hard limits
+#
+#<item> can be one of the following:
+# - core - limits the core file size (KB)
+# - data - max data size (KB)
+# - fsize - maximum filesize (KB)
+# - memlock - max locked-in-memory address space (KB)
+# - nofile - max number of open files
+# - rss - max resident set size (KB)
+# - stack - max stack size (KB)
+# - cpu - max CPU time (MIN)
+# - nproc - max number of processes
+# - as - address space limit
+# - maxlogins - max number of logins for this user
+# - priority - the priority to run user process with
+#
+#<domain> <type> <item> <value>
+#
+
+#* soft core 0
+#* hard rss 10000
+#@student hard nproc 20
+#@faculty soft nproc 20
+#@faculty hard nproc 50
+#ftp hard nproc 0
+#@student - maxlogins 4
+
+# End of file
diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c
new file mode 100644
index 00000000..e832dc63
--- /dev/null
+++ b/modules/pam_limits/pam_limits.c
@@ -0,0 +1,635 @@
+/*
+ * pam_limits - impose resource limits when opening a user session
+ *
+ * 1.6 - modified for PLD (added process priority settings)
+ * by Marcin Korzonek <mkorz@shadow.eu.org
+ * 1.5 - Elliot Lee's "max system logins patch"
+ * 1.4 - addressed bug in configuration file parser
+ * 1.3 - modified the configuration file format
+ * 1.2 - added 'debug' and 'conf=' arguments
+ * 1.1 - added @group support
+ * 1.0 - initial release - Linux ONLY
+ *
+ * See end for Copyright information
+ */
+
+#if !(defined(linux))
+#error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
+#endif
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <features.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#ifndef __USE_POSIX2
+#define __USE_POSIX2
+#endif /* __USE_POSIX2 */
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <utmp.h>
+#ifndef UT_USER /* some systems have ut_name instead of ut_user */
+#define UT_USER ut_user
+#endif
+
+#include <grp.h>
+#include <pwd.h>
+
+/* Module defines */
+#define LINE_LENGTH 1024
+
+#define LIMITS_DEF_USER 0 /* limit was set by an user entry */
+#define LIMITS_DEF_GROUP 1 /* limit was set by a group entry */
+#define LIMITS_DEF_DEFAULT 2 /* limit was set by an default entry */
+#define LIMITS_DEF_NONE 3 /* this limit was not set yet */
+
+/* internal data */
+static char conf_file[BUFSIZ];
+
+struct user_limits_struct {
+ int src_soft;
+ int src_hard;
+ struct rlimit limit;
+};
+
+static struct user_limits_struct limits[RLIM_NLIMITS];
+static int login_limit; /* the max logins limit */
+static int login_limit_def; /* which entry set the login limit */
+static int flag_numsyslogins; /* whether to limit logins only for a
+ specific user or to count all logins */
+static int priority; /* the priority to run user process with */
+
+#define LIMIT_LOGIN RLIM_NLIMITS+1
+#define LIMIT_NUMSYSLOGINS RLIM_NLIMITS+2
+
+#define LIMIT_PRI RLIM_NLIMITS+3
+
+#define LIMIT_SOFT 1
+#define LIMIT_HARD 2
+
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* logging */
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_limits", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x0001
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strncmp(*argv,"conf=",5))
+ strcpy(conf_file,*argv+5);
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+
+/* limits stuff */
+#ifndef LIMITS_FILE
+#define LIMITS_FILE "/etc/security/limits.conf"
+#endif
+
+#define LIMIT_ERR 1 /* error setting a limit */
+#define LOGIN_ERR 2 /* too many logins err */
+
+/* Counts the number of user logins and check against the limit*/
+static int check_logins(const char *name, int limit, int ctrl)
+{
+ struct utmp *ut;
+ unsigned int count;
+
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_DEBUG, "checking logins for '%s' / %d\n", name,limit);
+ }
+
+ if (limit < 0)
+ return 0; /* no limits imposed */
+ if (limit == 0) /* maximum 0 logins ? */ {
+ _pam_log(LOG_WARNING, "No logins allowed for '%s'\n", name);
+ return LOGIN_ERR;
+ }
+
+ setutent();
+ count = 0;
+ while((ut = getutent())) {
+#ifdef USER_PROCESS
+ if (ut->ut_type != USER_PROCESS)
+ continue;
+#endif
+ if (ut->UT_USER[0] == '\0')
+ continue;
+ if (!flag_numsyslogins
+ && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0)
+ continue;
+ if (++count >= limit)
+ break;
+ }
+ endutent();
+ if (count >= limit) {
+ if (name) {
+ _pam_log(LOG_WARNING, "Too many logins (max %d) for %s",
+ limit, name);
+ } else {
+ _pam_log(LOG_WARNING, "Too many system logins (max %d)", limit);
+ }
+ return LOGIN_ERR;
+ }
+ return 0;
+}
+
+/* checks if a user is on a list of members of the GID 0 group */
+static int is_on_list(char * const *list, const char *member)
+{
+ while (*list) {
+ if (strcmp(*list, member) == 0)
+ return 1;
+ list++;
+ }
+ return 0;
+}
+
+/* Checks if a user is a member of a group */
+static int is_on_group(const char *user_name, const char *group_name)
+{
+ struct passwd *pwd;
+ struct group *grp, *pgrp;
+ char uname[LINE_LENGTH], gname[LINE_LENGTH];
+
+ if (!strlen(user_name))
+ return 0;
+ if (!strlen(group_name))
+ return 0;
+ memset(uname, 0, sizeof(uname));
+ strncpy(uname, user_name, LINE_LENGTH);
+ memset(gname, 0, sizeof(gname));
+ strncpy(gname, group_name, LINE_LENGTH);
+
+ setpwent();
+ pwd = getpwnam(uname);
+ endpwent();
+ if (!pwd)
+ return 0;
+
+ /* the info about this group */
+ setgrent();
+ grp = getgrnam(gname);
+ endgrent();
+ if (!grp)
+ return 0;
+
+ /* first check: is a member of the group_name group ? */
+ if (is_on_list(grp->gr_mem, uname))
+ return 1;
+
+ /* next check: user primary group is group_name ? */
+ setgrent();
+ pgrp = getgrgid(pwd->pw_gid);
+ endgrent();
+ if (!pgrp)
+ return 0;
+ if (!strcmp(pgrp->gr_name, gname))
+ return 1;
+
+ return 0;
+}
+
+static int init_limits(void)
+{
+ int retval = PAM_SUCCESS;
+
+ D(("called."));
+
+ retval |= getrlimit(RLIMIT_CPU, &limits[RLIMIT_CPU].limit);
+ limits[RLIMIT_CPU].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_CPU].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_FSIZE, &limits[RLIMIT_FSIZE].limit);
+ limits[RLIMIT_FSIZE].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_FSIZE].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_DATA, &limits[RLIMIT_DATA].limit);
+ limits[RLIMIT_DATA].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_DATA].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_STACK, &limits[RLIMIT_STACK].limit);
+ limits[RLIMIT_STACK].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_STACK].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_CORE, &limits[RLIMIT_CORE].limit);
+ limits[RLIMIT_CORE].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_CORE].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_RSS, &limits[RLIMIT_RSS].limit);
+ limits[RLIMIT_RSS].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_RSS].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_NPROC, &limits[RLIMIT_NPROC].limit);
+ limits[RLIMIT_NPROC].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_NPROC].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_NOFILE, &limits[RLIMIT_NOFILE].limit);
+ limits[RLIMIT_NOFILE].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_NOFILE].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_MEMLOCK, &limits[RLIMIT_MEMLOCK].limit);
+ limits[RLIMIT_MEMLOCK].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_MEMLOCK].src_hard = LIMITS_DEF_NONE;
+
+ retval |= getrlimit(RLIMIT_AS, &limits[RLIMIT_AS].limit);
+ limits[RLIMIT_AS].src_soft = LIMITS_DEF_NONE;
+ limits[RLIMIT_AS].src_hard = LIMITS_DEF_NONE;
+ priority = 0;
+ login_limit = -2;
+ login_limit_def = LIMITS_DEF_NONE;
+ return retval;
+}
+
+static void process_limit(int source, const char *lim_type,
+ const char *lim_item, const char *lim_value,
+ int ctrl)
+{
+ int limit_item;
+ int limit_type = 0;
+ long limit_value;
+ const char **endptr = &lim_value;
+ const char *value_orig = lim_value;
+
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "%s: processing(%d) %s %s %s\n",
+ __FUNCTION__,source,lim_type,lim_item,lim_value);
+
+ if (strcmp(lim_item, "cpu") == 0)
+ limit_item = RLIMIT_CPU;
+ else if (strcmp(lim_item, "fsize") == 0)
+ limit_item = RLIMIT_FSIZE;
+ else if (strcmp(lim_item, "data") == 0)
+ limit_item = RLIMIT_DATA;
+ else if (strcmp(lim_item, "stack") == 0)
+ limit_item = RLIMIT_STACK;
+ else if (strcmp(lim_item, "core") == 0)
+ limit_item = RLIMIT_CORE;
+ else if (strcmp(lim_item, "rss") == 0)
+ limit_item = RLIMIT_RSS;
+ else if (strcmp(lim_item, "nproc") == 0)
+ limit_item = RLIMIT_NPROC;
+ else if (strcmp(lim_item, "nofile") == 0)
+ limit_item = RLIMIT_NOFILE;
+ else if (strcmp(lim_item, "memlock") == 0)
+ limit_item = RLIMIT_MEMLOCK;
+ else if (strcmp(lim_item, "as") == 0)
+ limit_item = RLIMIT_AS;
+ else if (strcmp(lim_item, "maxlogins") == 0) {
+ limit_item = LIMIT_LOGIN;
+ flag_numsyslogins = 0;
+ } else if (strcmp(lim_item, "maxsyslogins") == 0) {
+ limit_item = LIMIT_NUMSYSLOGINS;
+ flag_numsyslogins = 1;
+ } else if (strcmp(lim_item, "priority") == 0) {
+ limit_item = LIMIT_PRI;
+ } else {
+ _pam_log(LOG_DEBUG,"unknown limit item '%s'", lim_item);
+ return;
+ }
+
+ if (strcmp(lim_type,"soft")==0)
+ limit_type=LIMIT_SOFT;
+ else if (strcmp(lim_type, "hard")==0)
+ limit_type=LIMIT_HARD;
+ else if (strcmp(lim_type,"-")==0)
+ limit_type=LIMIT_SOFT | LIMIT_HARD;
+ else if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS) {
+ _pam_log(LOG_DEBUG,"unknown limit type '%s'", lim_type);
+ return;
+ }
+
+ limit_value = strtol(lim_value, endptr, 10);
+ if (limit_value == 0 && value_orig == *endptr) { /* no chars read */
+ if (strcmp(lim_value,"-") != 0) {
+ _pam_log(LOG_DEBUG,"wrong limit value '%s'", lim_value);
+ return;
+ } else
+ if (limit_item != LIMIT_LOGIN) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,
+ "'-' limit value valid for maxlogins type only");
+ return;
+ } else
+ limit_value = -1;
+ }
+
+ switch(limit_item) {
+ case RLIMIT_CPU:
+ limit_value *= 60;
+ break;
+ case RLIMIT_FSIZE:
+ case RLIMIT_DATA:
+ case RLIMIT_STACK:
+ case RLIMIT_CORE:
+ case RLIMIT_RSS:
+ case RLIMIT_MEMLOCK:
+ case RLIMIT_AS:
+ limit_value *= 1024;
+ break;
+ }
+
+ if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS
+ && limit_item != LIMIT_PRI
+ ) {
+ if (limit_type & LIMIT_SOFT) {
+ if (limits[limit_item].src_soft < source) {
+ return;
+ } else {
+ limits[limit_item].limit.rlim_cur = limit_value;
+ limits[limit_item].src_soft = source;
+ }
+ }
+ if (limit_type & LIMIT_HARD) {
+ if (limits[limit_item].src_hard < source) {
+ return;
+ } else {
+ limits[limit_item].limit.rlim_max = limit_value;
+ limits[limit_item].src_hard = source;
+ }
+ }
+ } else
+ if (limit_item == LIMIT_PRI) {
+ /* additional check */
+ priority = ((limit_value>0)?limit_value:0);
+ } else {
+ if (login_limit_def < source) {
+ return;
+ } else {
+ login_limit = limit_value;
+ login_limit_def = source;
+ }
+ }
+ return;
+}
+
+static int parse_config_file(const char *uname, int ctrl)
+{
+ FILE *fil;
+ char buf[LINE_LENGTH];
+
+#define CONF_FILE (conf_file[0])?conf_file:LIMITS_FILE
+ /* check for the LIMITS_FILE */
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"reading settings from '%s'", CONF_FILE);
+ fil = fopen(CONF_FILE, "r");
+ if (fil == NULL) {
+ _pam_log (LOG_WARNING, "can not read settings from %s", CONF_FILE);
+ return PAM_SERVICE_ERR;
+ }
+#undef CONF_FILE
+
+ /* init things */
+ memset(buf, 0, sizeof(buf));
+ /* start the show */
+ while (fgets(buf, LINE_LENGTH, fil) != NULL) {
+ char domain[LINE_LENGTH];
+ char ltype[LINE_LENGTH];
+ char item[LINE_LENGTH];
+ char value[LINE_LENGTH];
+ int i,j;
+ char *tptr;
+
+ tptr = buf;
+ /* skip the leading white space */
+ while (*tptr && isspace(*tptr))
+ tptr++;
+ strcpy(buf, (const char *)tptr);
+
+ /* Rip off the comments */
+ tptr = strchr(buf,'#');
+ if (tptr)
+ *tptr = '\0';
+ /* Rip off the newline char */
+ tptr = strchr(buf,'\n');
+ if (tptr)
+ *tptr = '\0';
+ /* Anything left ? */
+ if (!strlen(buf)) {
+ memset(buf, 0, sizeof(buf));
+ continue;
+ }
+
+ memset(domain, 0, sizeof(domain));
+ memset(ltype, 0, sizeof(ltype));
+ memset(item, 0, sizeof(item));
+ memset(value, 0, sizeof(value));
+
+ i = sscanf(buf,"%s%s%s%s", domain, ltype, item, value);
+ for(j=0; j < strlen(domain); j++)
+ domain[j]=tolower(domain[j]);
+ for(j=0; j < strlen(ltype); j++)
+ ltype[j]=tolower(ltype[j]);
+ for(j=0; j < strlen(item); j++)
+ item[j]=tolower(item[j]);
+ for(j=0; j < strlen(value); j++)
+ value[j]=tolower(value[j]);
+
+ if (i == 4) { /* a complete line */
+ if (strcmp(uname, domain) == 0) /* this user have a limit */
+ process_limit(LIMITS_DEF_USER, ltype, item, value, ctrl);
+ else if (domain[0]=='@') {
+ if (is_on_group(uname, domain+1))
+ process_limit(LIMITS_DEF_GROUP, ltype, item, value, ctrl);
+ } else if (strcmp(domain, "*") == 0)
+ process_limit(LIMITS_DEF_DEFAULT, ltype, item, value, ctrl);
+ } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */
+ if (strcmp(uname, domain) == 0) {
+ _pam_log(LOG_DEBUG, "no limits for '%s'", uname);
+ fclose(fil);
+ return PAM_IGNORE;
+ } else if (domain[0] == '@' && is_on_group(uname, domain+1)) {
+ _pam_log(LOG_DEBUG, "no limits for '%s' in group '%s'",
+ uname, domain+1);
+ fclose(fil);
+ return PAM_IGNORE;
+ }
+ } else {
+ _pam_log(LOG_DEBUG,"invalid line '%s'", buf);
+ }
+ }
+ fclose(fil);
+ return PAM_SUCCESS;
+}
+
+static int setup_limits(const char * uname, int ctrl)
+{
+ int i;
+ int retval = PAM_SUCCESS;
+
+ for (i=0; i<RLIM_NLIMITS; i++) {
+ if (limits[i].limit.rlim_cur > limits[i].limit.rlim_max)
+ limits[i].limit.rlim_cur = limits[i].limit.rlim_max;
+ retval |= setrlimit(i, &limits[i].limit);
+ }
+
+ if (retval != PAM_SUCCESS)
+ retval = LIMIT_ERR;
+
+ retval=setpriority(PRIO_PROCESS, 0, priority);
+
+ if (retval != PAM_SUCCESS)
+ retval = LIMIT_ERR;
+
+ if (login_limit > 0) {
+ if (check_logins(uname, login_limit, ctrl) == LOGIN_ERR)
+ retval |= LOGIN_ERR;
+ } else if (login_limit == 0)
+ retval |= LOGIN_ERR;
+ return retval;
+}
+
+/* now the session stuff */
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ char *user_name;
+ struct passwd *pwd;
+ int ctrl;
+
+ D(("called."));
+
+ memset(conf_file, 0, sizeof(conf_file));
+
+ ctrl = _pam_parse(argc, argv);
+ retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _pam_log(LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ setpwent();
+ pwd = getpwnam(user_name);
+ endpwent();
+ if (!pwd) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_WARNING, "open_session username '%s' does not exist",
+ user_name);
+ return PAM_SESSION_ERR;
+ }
+
+ /* do not impose limits on UID 0 accounts */
+ if (!pwd->pw_uid) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "user '%s' have UID 0 - no limits imposed",
+ user_name);
+ return PAM_SUCCESS;
+ }
+
+ retval = init_limits();
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "can not initialize");
+ return PAM_IGNORE;
+ }
+
+ retval = parse_config_file(pwd->pw_name,ctrl);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "error parsing the configuration file");
+ return PAM_IGNORE;
+ }
+
+ retval = setup_limits(pwd->pw_name, ctrl);
+ if (retval & LOGIN_ERR) {
+ printf("\nToo many logins for '%s'\n",pwd->pw_name);
+ sleep(2);
+ return PAM_PERM_DENIED;
+ }
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ /* nothing to do */
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_limits_modstruct = {
+ "pam_limits",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL
+};
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton, 1996-1997, <gafton@redhat.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_listfile/.cvsignore b/modules/pam_listfile/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_listfile/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_listfile/Makefile b/modules/pam_listfile/Makefile
new file mode 100644
index 00000000..02940390
--- /dev/null
+++ b/modules/pam_listfile/Makefile
@@ -0,0 +1,84 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+
+TITLE=pam_listfile
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_listfile/README b/modules/pam_listfile/README
new file mode 100644
index 00000000..b65e7dbb
--- /dev/null
+++ b/modules/pam_listfile/README
@@ -0,0 +1,25 @@
+SUMMARY:
+ pam_listfile:
+ Checks a specified item against a list in a file.
+ Options:
+ * item=[tty|user|rhost|ruser|group|shell]
+ * sense=[allow|deny] (action to take if found in file,
+ if the item is NOT found in the file, then
+ the opposite action is requested)
+ * file=/the/file/to/get/the/list/from
+ * onerr=[succeed|fail] (if something weird happens
+ such as unable to open the file, what to do?)
+ * apply=[user|@group]
+ restrict the user class for which the restriction
+ apply. Note that with item=[user|ruser|group] this
+ does not make sense, but for item=[tty|rhost|shell]
+ it have a meaning. (Cristian Gafton)
+
+ Also checks to make sure that the list file is a plain
+ file and not world writable.
+
+ - Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ v0.9 August 16, 1996.
+
+BUGS:
+ Bugs?
diff --git a/modules/pam_listfile/pam_listfile.c b/modules/pam_listfile/pam_listfile.c
new file mode 100644
index 00000000..8b1b45f0
--- /dev/null
+++ b/modules/pam_listfile/pam_listfile.c
@@ -0,0 +1,437 @@
+/*
+ * $Id$
+ *
+ */
+
+/*
+ * by Elliot Lee <sopwith@redhat.com>, Red Hat Software. July 25, 1996.
+ * log refused access error christopher mccrory <chrismcc@netus.com> 1998/7/11
+ *
+ * This code began life as the pam_rootok module.
+ */
+
+#ifdef linux
+# define _SVID_SOURCE
+# define _BSD_SOURCE
+# define __USE_BSD
+# define __USE_SVID
+# define __USE_MISC
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* some syslogging */
+
+#define LOCAL_LOG_PREFIX "PAM-listfile: "
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vsyslog(LOG_AUTH | err, format, args);
+ va_end(args);
+}
+
+/* checks if a user is on a list of members */
+static int is_on_list(char * const *list, const char *member)
+{
+ while (*list) {
+ if (strcmp(*list, member) == 0)
+ return 1;
+ list++;
+ }
+ return 0;
+}
+
+/* Checks if a user is a member of a group */
+static int is_on_group(const char *user_name, const char *group_name)
+{
+ struct passwd *pwd;
+ struct group *grp, *pgrp;
+ char uname[BUFSIZ], gname[BUFSIZ];
+
+ if (!strlen(user_name))
+ return 0;
+ if (!strlen(group_name))
+ return 0;
+ bzero(uname, sizeof(uname));
+ strncpy(uname, user_name, BUFSIZ-1);
+ bzero(gname, sizeof(gname));
+ strncpy(gname, group_name, BUFSIZ-1);
+
+ setpwent();
+ pwd = getpwnam(uname);
+ endpwent();
+ if (!pwd)
+ return 0;
+
+ /* the info about this group */
+ setgrent();
+ grp = getgrnam(gname);
+ endgrent();
+ if (!grp)
+ return 0;
+
+ /* first check: is a member of the group_name group ? */
+ if (is_on_list(grp->gr_mem, uname))
+ return 1;
+
+ /* next check: user primary group is group_name ? */
+ setgrent();
+ pgrp = getgrgid(pwd->pw_gid);
+ endgrent();
+ if (!pgrp)
+ return 0;
+ if (!strcmp(pgrp->gr_name, gname))
+ return 1;
+
+ return 0;
+}
+
+/* --- authentication management functions (only) --- */
+
+/* Extended Items that are not directly available via pam_get_item() */
+#define EI_GROUP (1 << 0)
+#define EI_SHELL (1 << 1)
+
+/* Constants for apply= parameter */
+#define APPLY_TYPE_NULL 0
+#define APPLY_TYPE_NONE 1
+#define APPLY_TYPE_USER 2
+#define APPLY_TYPE_GROUP 3
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ int retval, i, citem=0, extitem=0, onerr=PAM_SERVICE_ERR, sense=2;
+ const char *citemp;
+ char *ifname=NULL;
+ char aline[256];
+ char mybuf[256],myval[256];
+ struct stat fileinfo;
+ FILE *inf;
+ char apply_val[256];
+ int apply_type;
+
+ /* Stuff for "extended" items */
+ struct passwd *userinfo;
+ struct group *grpinfo;
+ char *itemlist[256]; /* Maximum of 256 items */
+
+ D(("called."));
+
+ apply_type=APPLY_TYPE_NULL;
+ memset(apply_val,0,sizeof(apply_val));
+
+ for(i=0; i < argc; i++) {
+ {
+ char *junk;
+ junk = (char *) malloc(strlen(argv[i])+1);
+ if (junk == NULL) {
+ return PAM_BUF_ERR;
+ }
+ strcpy(junk,argv[i]);
+ strncpy(mybuf,strtok(junk,"="),255);
+ strncpy(myval,strtok(NULL,"="),255);
+ free(junk);
+ }
+ if(!strcmp(mybuf,"onerr"))
+ if(!strcmp(myval,"succeed"))
+ onerr = PAM_SUCCESS;
+ else if(!strcmp(myval,"fail"))
+ onerr = PAM_SERVICE_ERR;
+ else
+ return PAM_SERVICE_ERR;
+ else if(!strcmp(mybuf,"sense"))
+ if(!strcmp(myval,"allow"))
+ sense=0;
+ else if(!strcmp(myval,"deny"))
+ sense=1;
+ else
+ return onerr;
+ else if(!strcmp(mybuf,"file")) {
+ ifname = (char *)malloc(strlen(myval)+1);
+ strcpy(ifname,myval);
+ } else if(!strcmp(mybuf,"item"))
+ if(!strcmp(myval,"user"))
+ citem = PAM_USER;
+ else if(!strcmp(myval,"tty"))
+ citem = PAM_TTY;
+ else if(!strcmp(myval,"rhost"))
+ citem = PAM_RHOST;
+ else if(!strcmp(myval,"ruser"))
+ citem = PAM_RUSER;
+ else { /* These items are related to the user, but are not
+ directly gettable with pam_get_item */
+ citem = PAM_USER;
+ if(!strcmp(myval,"group"))
+ extitem = EI_GROUP;
+ else if(!strcmp(myval,"shell"))
+ extitem = EI_SHELL;
+ else
+ citem = 0;
+ } else if(!strcmp(mybuf,"apply")) {
+ apply_type=APPLY_TYPE_NONE;
+ if (myval[0]=='@') {
+ apply_type=APPLY_TYPE_GROUP;
+ strncpy(apply_val,myval+1,sizeof(apply_val)-1);
+ } else {
+ apply_type=APPLY_TYPE_USER;
+ strncpy(apply_val,myval,sizeof(apply_val)-1);
+ }
+ } else {
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "Unknown option: %s",mybuf);
+ return onerr;
+ }
+ }
+
+ if(!citem) {
+ _pam_log(LOG_ERR,
+ LOCAL_LOG_PREFIX "Unknown item or item not specified");
+ return onerr;
+ } else if(!ifname) {
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "List filename not specified");
+ return onerr;
+ } else if(sense == 2) {
+ _pam_log(LOG_ERR,
+ LOCAL_LOG_PREFIX "Unknown sense or sense not specified");
+ return onerr;
+ } else if(
+ (apply_type==APPLY_TYPE_NONE) ||
+ ((apply_type!=APPLY_TYPE_NULL) && (*apply_val=='\0'))
+ ) {
+ _pam_log(LOG_ERR,
+ LOCAL_LOG_PREFIX "Invalid usage for apply= parameter");
+ return onerr;
+ }
+
+ /* Check if it makes sense to use the apply= parameter */
+ if (apply_type != APPLY_TYPE_NULL) {
+ if((citem==PAM_USER) || (citem==PAM_RUSER)) {
+ _pam_log(LOG_WARNING,
+ LOCAL_LOG_PREFIX "Non-sense use for apply= parameter");
+ apply_type=APPLY_TYPE_NULL;
+ }
+ if(extitem && (extitem==EI_GROUP)) {
+ _pam_log(LOG_WARNING,
+ LOCAL_LOG_PREFIX "Non-sense use for apply= parameter");
+ apply_type=APPLY_TYPE_NULL;
+ }
+ }
+
+ /* Short-circuit - test if this session apply for this user */
+ {
+ const char *user_name;
+ int rval;
+
+ rval=pam_get_user(pamh,&user_name,NULL);
+ if((rval==PAM_SUCCESS) && user_name[0]) {
+ /* Got it ? Valid ? */
+ if(apply_type==APPLY_TYPE_USER) {
+ if(strcmp(user_name, apply_val)) {
+ /* Does not apply to this user */
+#ifdef DEBUG
+ _pam_log(LOG_DEBUG,
+ LOCAL_LOG_PREFIX "don't apply: apply=%s, user=%s",
+ apply_val,user_name);
+#endif /* DEBUG */
+ return PAM_IGNORE;
+ }
+ } else if(apply_type==APPLY_TYPE_GROUP) {
+ if(!is_on_group(user_name,apply_val)) {
+ /* Not a member of apply= group */
+#ifdef DEBUG
+ _pam_log(LOG_DEBUG,
+ LOCAL_LOG_PREFIX
+ "don't apply: %s not a member of group %s",
+ user_name,apply_val);
+#endif /* DEBUG */
+ return PAM_IGNORE;
+ }
+ }
+ }
+ }
+
+ retval = pam_get_item(pamh,citem,(const void **)&citemp);
+ if(retval != PAM_SUCCESS) {
+ return onerr;
+ }
+ if((citem == PAM_USER) && !citemp) {
+ pam_get_user(pamh,&citemp,NULL);
+ if (retval != PAM_SUCCESS)
+ return PAM_SERVICE_ERR;
+ }
+
+ if(!citemp || (strlen(citemp) <= 0)) {
+ /* The item was NULL - we are sure not to match */
+ return sense?PAM_SUCCESS:PAM_AUTH_ERR;
+ }
+
+ if(extitem) {
+ switch(extitem) {
+ case EI_GROUP:
+ setpwent();
+ userinfo = getpwnam(citemp);
+ setgrent();
+ grpinfo = getgrgid(userinfo->pw_gid);
+ itemlist[0] = x_strdup(grpinfo->gr_name);
+ setgrent();
+ for (i=1; (i < sizeof(itemlist)/sizeof(itemlist[0])-1) &&
+ (grpinfo = getgrent()); ) {
+ if (is_on_list(grpinfo->gr_mem,citemp)) {
+ itemlist[i++] = x_strdup(grpinfo->gr_name);
+ }
+ }
+ itemlist[i] = NULL;
+ endgrent();
+ endpwent();
+ break;
+ case EI_SHELL:
+ setpwent();
+ userinfo = getpwnam(citemp); /* Assume that we have already gotten
+ PAM_USER in pam_get_item() - a valid
+ assumption since citem gets set to
+ PAM_USER in the extitem switch */
+ citemp = userinfo->pw_shell;
+ endpwent();
+ break;
+ default:
+ _pam_log(LOG_ERR,
+ LOCAL_LOG_PREFIX
+ "Internal weirdness, unknown extended item %d",
+ extitem);
+ return onerr;
+ }
+ }
+#ifdef DEBUG
+ _pam_log(LOG_INFO,
+ LOCAL_LOG_PREFIX
+ "Got file = %s, item = %d, value = %s, sense = %d",
+ ifname, citem, citemp, sense);
+#endif
+ if(lstat(ifname,&fileinfo)) {
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "Couldn't open %s",ifname);
+ return onerr;
+ }
+
+ if((fileinfo.st_mode & S_IWOTH)
+ || !S_ISREG(fileinfo.st_mode)) {
+ /* If the file is world writable or is not a
+ normal file, return error */
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX
+ "%s is either world writable or not a normal file",
+ ifname);
+ return PAM_AUTH_ERR;
+ }
+
+ inf = fopen(ifname,"r");
+ if(inf == NULL) { /* Check that we opened it successfully */
+ if (onerr == PAM_SERVICE_ERR) {
+ /* Only report if it's an error... */
+ _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "Error opening %s", ifname);
+ }
+ return onerr;
+ }
+ /* There should be no more errors from here on */
+ retval=PAM_AUTH_ERR;
+ /* This loop assumes that PAM_SUCCESS == 0
+ and PAM_AUTH_ERR != 0 */
+#ifdef DEBUG
+ assert(PAM_SUCCESS == 0);
+ assert(PAM_AUTH_ERR != 0);
+#endif
+ if(extitem == EI_GROUP) {
+ while((fgets(aline,255,inf) != NULL)
+ && retval) {
+ if(aline[strlen(aline) - 1] == '\n')
+ aline[strlen(aline) - 1] = '\0';
+ for(i=0;itemlist[i];)
+ /* If any of the items match, strcmp() == 0, and we get out
+ of this loop */
+ retval = (strcmp(aline,itemlist[i++]) && retval);
+ }
+ for(i=0;itemlist[i];)
+ free(itemlist[i++]);
+ } else {
+ while((fgets(aline,255,inf) != NULL)
+ && retval) {
+ if(aline[strlen(aline) - 1] == '\n')
+ aline[strlen(aline) - 1] = '\0';
+ retval = strcmp(aline,citemp);
+ }
+ }
+ fclose(inf);
+ free(ifname);
+ if ((sense && retval) || (!sense && !retval)) {
+#ifdef DEBUG
+ _pam_log(LOG_INFO, LOCAL_LOG_PREFIX
+ "Returning PAM_SUCCESS, retval = %d", retval);
+#endif
+ return PAM_SUCCESS;
+ }
+ else {
+ const char *service, *user_name;
+#ifdef DEBUG
+ _pam_log(LOG_INFO,LOCAL_LOG_PREFIX
+ "Returning PAM_AUTH_ERR, retval = %d", retval);
+#endif
+ (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
+ (void) pam_get_user(pamh, &user_name, NULL);
+ _pam_log(LOG_ALERT,LOCAL_LOG_PREFIX "Refused user %s for service %s",
+ user_name, service);
+ return PAM_AUTH_ERR;
+ }
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_listfile_modstruct = {
+ "pam_listfile",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
+
diff --git a/modules/pam_mail/.cvsignore b/modules/pam_mail/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_mail/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_mail/Makefile b/modules/pam_mail/Makefile
new file mode 100644
index 00000000..64f73b0b
--- /dev/null
+++ b/modules/pam_mail/Makefile
@@ -0,0 +1,113 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:45 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.3 1997/04/05 06:37:45 morgan
+# fakeroot
+#
+# Revision 1.2 1997/02/15 16:07:22 morgan
+# optional libpwdb compilation
+#
+# Revision 1.1 1997/01/04 20:32:52 morgan
+# Initial revision
+#
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/12/8
+#
+
+TITLE=pam_mail
+
+ifndef STATIC
+ifeq ($(HAVE_PWDBLIB),yes)
+CFLAGS += -DWANT_PWDB
+EXTRALIB = -lpwdb
+endif
+endif
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(EXTRALIB)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS) $(EXTRALIB)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_mail/README b/modules/pam_mail/README
new file mode 100644
index 00000000..155bd1db
--- /dev/null
+++ b/modules/pam_mail/README
@@ -0,0 +1,17 @@
+This is the README for pam_mail
+-------------------------------
+
+This PAM module tells the User that he has new/unread email.
+
+Options for:
+auth: for authentication it provides pam_authenticate() and
+ pam_setcred() hooks.
+
+ "debug" write more information to syslog
+ "dir=maildir" users mailbox is maildir/<login>
+ "hash=count" mail directory hash depth
+ "close" print message also on logout
+ "nopen" print message not on login
+ "noenv" don't set the MAIL environment variable
+ "empty" also print message if user has no mail
+
diff --git a/modules/pam_mail/pam_mail.c b/modules/pam_mail/pam_mail.c
new file mode 100644
index 00000000..f67b6f02
--- /dev/null
+++ b/modules/pam_mail/pam_mail.c
@@ -0,0 +1,504 @@
+/* pam_mail module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+ * $HOME additions by David Kinchlea <kinch@kinch.ark.com> 1997/1/7
+ * mailhash additions by Chris Adams <cadams@ro.com> 1998/7/11
+ */
+
+#define DEFAULT_MAIL_DIRECTORY "/var/spool/mail"
+#define MAIL_FILE_FORMAT "%s%s/%s"
+#define MAIL_ENV_NAME "MAIL"
+#define MAIL_ENV_FORMAT MAIL_ENV_NAME "=%s"
+#define YOUR_MAIL_VERBOSE_FORMAT "You have %s mail in %s."
+#define YOUR_MAIL_STANDARD_FORMAT "You have %smail."
+#define NO_MAIL_STANDARD_FORMAT "No mail."
+
+#define _BSD_SOURCE
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <ctype.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#ifdef WANT_PWDB
+#include <pwdb/pwdb_public.h>
+#endif
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_SESSION
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* some syslogging */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-mail", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x0001
+#define PAM_NO_LOGIN 0x0002
+#define PAM_LOGOUT_TOO 0x0004
+#define PAM_NEW_MAIL_DIR 0x0010
+#define PAM_MAIL_SILENT 0x0020
+#define PAM_NO_ENV 0x0040
+#define PAM_HOME_MAIL 0x0100
+#define PAM_EMPTY_TOO 0x0200
+#define PAM_STANDARD_MAIL 0x0400
+#define PAM_QUIET_MAIL 0x1000
+
+static int _pam_parse(int flags, int argc, const char **argv, char **maildir,
+ int *hashcount)
+{
+ int ctrl=0;
+
+ if (flags & PAM_SILENT) {
+ ctrl |= PAM_MAIL_SILENT;
+ }
+
+ *hashcount = 0;
+
+ /* step through arguments */
+ for (; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strcmp(*argv,"quiet"))
+ ctrl |= PAM_QUIET_MAIL;
+ else if (!strcmp(*argv,"standard"))
+ ctrl |= PAM_STANDARD_MAIL | PAM_EMPTY_TOO;
+ else if (!strncmp(*argv,"dir=",4)) {
+ *maildir = x_strdup(4+*argv);
+ if (*maildir != NULL) {
+ D(("new mail directory: %s", *maildir));
+ ctrl |= PAM_NEW_MAIL_DIR;
+ } else {
+ _log_err(LOG_CRIT,
+ "failed to duplicate mail directory - ignored");
+ }
+ } else if (!strncmp(*argv,"hash=",5)) {
+ char *ep = NULL;
+ *hashcount = strtol(*argv+5,&ep,10);
+ if (!ep || (*hashcount < 0)) {
+ *hashcount = 0;
+ }
+ } else if (!strcmp(*argv,"close")) {
+ ctrl |= PAM_LOGOUT_TOO;
+ } else if (!strcmp(*argv,"nopen")) {
+ ctrl |= PAM_NO_LOGIN;
+ } else if (!strcmp(*argv,"noenv")) {
+ ctrl |= PAM_NO_ENV;
+ } else if (!strcmp(*argv,"empty")) {
+ ctrl |= PAM_EMPTY_TOO;
+ } else {
+ _log_err(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ if ((*hashcount != 0) && !(ctrl & PAM_NEW_MAIL_DIR)) {
+ *maildir = x_strdup(DEFAULT_MAIL_DIRECTORY);
+ ctrl |= PAM_NEW_MAIL_DIR;
+ }
+
+ return ctrl;
+}
+
+/* a front end for conversations */
+
+static int converse(pam_handle_t *pamh, int ctrl, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
+ if ( retval == PAM_SUCCESS ) {
+
+ retval = conv->conv(nargs, ( const struct pam_message ** ) message
+ , response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && (PAM_DEBUG_ARG & ctrl) ) {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ } else {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation"));
+
+ return retval; /* propagate error status */
+}
+
+static int get_folder(pam_handle_t *pamh, int ctrl,
+ char **path_mail, char **folder_p, int hashcount)
+{
+ int retval;
+ const char *user, *path;
+ char *folder;
+ const struct passwd *pwd=NULL;
+
+ retval = pam_get_user(pamh, &user, NULL);
+ if (retval != PAM_SUCCESS || user == NULL) {
+ _log_err(LOG_ERR, "no user specified");
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (ctrl & PAM_NEW_MAIL_DIR) {
+ path = *path_mail;
+ if (*path == '~') { /* support for $HOME delivery */
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ _log_err(LOG_ERR, "user [%s] unknown", user);
+ _pam_overwrite(*path_mail);
+ _pam_drop(*path_mail);
+ return PAM_USER_UNKNOWN;
+ }
+ /*
+ * "~/xxx" and "~xxx" are treated as same
+ */
+ if (!*++path || (*path == '/' && !*++path)) {
+ _log_err(LOG_ALERT, "badly formed mail path [%s]", *path_mail);
+ _pam_overwrite(*path_mail);
+ _pam_drop(*path_mail);
+ return PAM_ABORT;
+ }
+ ctrl |= PAM_HOME_MAIL;
+ if (hashcount != 0) {
+ _log_err(LOG_ALERT, "can't do hash= and home directory mail");
+ }
+ }
+ } else {
+ path = DEFAULT_MAIL_DIRECTORY;
+ }
+
+ /* put folder together */
+
+ if (ctrl & PAM_HOME_MAIL) {
+ folder = malloc(sizeof(MAIL_FILE_FORMAT)
+ +strlen(pwd->pw_dir)+strlen(path));
+ } else {
+ folder = malloc(sizeof(MAIL_FILE_FORMAT)+strlen(path)+strlen(user)
+ +2*hashcount);
+ }
+
+ if (folder != NULL) {
+ if (ctrl & PAM_HOME_MAIL) {
+ sprintf(folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path);
+ } else {
+ int i;
+ char *hash = malloc(2*hashcount+1);
+
+ if (hash) {
+ for (i = 0; i < hashcount; i++) {
+ hash[2*i] = '/';
+ hash[2*i+1] = user[i];
+ }
+ hash[2*i] = '\0';
+ sprintf(folder, MAIL_FILE_FORMAT, path, hash, user);
+ _pam_overwrite(hash);
+ _pam_drop(hash);
+ } else {
+ sprintf(folder, "error");
+ }
+ }
+ D(("folder =[%s]", folder));
+ }
+
+ /* tidy up */
+
+ _pam_overwrite(*path_mail);
+ _pam_drop(*path_mail);
+ user = NULL;
+
+ if (folder == NULL) {
+ _log_err(LOG_CRIT, "out of memory for mail folder");
+ return PAM_BUF_ERR;
+ }
+
+ *folder_p = folder;
+ folder = NULL;
+
+ return PAM_SUCCESS;
+}
+
+static const char *get_mail_status(int ctrl, const char *folder)
+{
+ const char *type = NULL;
+ static char dir[256];
+ struct stat mail_st;
+ struct dirent **namelist;
+ int i;
+
+ if (stat(folder, &mail_st) == 0) {
+ if (S_ISDIR(mail_st.st_mode)) { /* Assume Maildir format */
+ sprintf(dir, "%.250s/new", folder);
+ i = scandir(dir, &namelist, 0, alphasort);
+ if (i > 2) {
+ type = "new";
+ while (--i)
+ free(namelist[i]);
+ } else {
+ while (--i >= 0)
+ free(namelist[i]);
+ sprintf(dir, "%.250s/cur", folder);
+ i = scandir(dir, &namelist, 0, alphasort);
+ if (i > 2) {
+ type = "old";
+ while (--i)
+ free(namelist[i]);
+ } else if (ctrl & PAM_EMPTY_TOO) {
+ while (--i >= 0)
+ free(namelist[i]);
+ type = "no";
+ } else {
+ type = NULL;
+ }
+ }
+ } else {
+ if (mail_st.st_size > 0) {
+ if (mail_st.st_atime < mail_st.st_mtime) /* new */
+ type = (ctrl & PAM_STANDARD_MAIL) ? "new " : "new";
+ else /* old */
+ type = (ctrl & PAM_STANDARD_MAIL) ? "" : "old";
+ } else if (ctrl & PAM_EMPTY_TOO) {
+ type = "no";
+ } else {
+ type = NULL;
+ }
+ }
+ }
+
+ memset(dir, 0, 256);
+ memset(&mail_st, 0, sizeof(mail_st));
+ D(("user has %s mail in %s folder", type, folder));
+ return type;
+}
+
+static int report_mail(pam_handle_t *pamh, int ctrl
+ , const char *type, const char *folder)
+{
+ int retval;
+
+ if (!(ctrl & PAM_MAIL_SILENT) || ((ctrl & PAM_QUIET_MAIL) && strcmp(type, "new"))) {
+ char *remark;
+
+ if (ctrl & PAM_STANDARD_MAIL)
+ if (!strcmp(type, "no"))
+ remark = malloc(strlen(NO_MAIL_STANDARD_FORMAT)+1);
+ else
+ remark = malloc(strlen(YOUR_MAIL_STANDARD_FORMAT)+strlen(type)+1);
+ else
+ remark = malloc(strlen(YOUR_MAIL_VERBOSE_FORMAT)+strlen(type)+strlen(folder)+1);
+ if (remark == NULL) {
+ retval = PAM_BUF_ERR;
+ } else {
+ struct pam_message msg[1], *mesg[1];
+ struct pam_response *resp=NULL;
+
+ if (ctrl & PAM_STANDARD_MAIL)
+ if (!strcmp(type, "no"))
+ sprintf(remark, NO_MAIL_STANDARD_FORMAT);
+ else
+ sprintf(remark, YOUR_MAIL_STANDARD_FORMAT, type);
+ else
+ sprintf(remark, YOUR_MAIL_VERBOSE_FORMAT, type, folder);
+
+ mesg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = remark;
+
+ retval = converse(pamh, ctrl, 1, mesg, &resp);
+
+ _pam_overwrite(remark);
+ _pam_drop(remark);
+ if (resp)
+ _pam_drop_reply(resp, 1);
+ }
+ } else {
+ D(("keeping quiet"));
+ retval = PAM_SUCCESS;
+ }
+
+ D(("returning %s", pam_strerror(pamh, retval)));
+ return retval;
+}
+
+static int _do_mail(pam_handle_t *, int, int, const char **, int);
+
+/* --- authentication functions --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,
+ const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+/* Checking mail as part of authentication */
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ if (!(flags & (PAM_ESTABLISH_CRED|PAM_DELETE_CRED)))
+ return PAM_IGNORE;
+ return _do_mail(pamh,flags,argc,argv,(flags & PAM_ESTABLISH_CRED));
+}
+
+/* --- session management functions --- */
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return _do_mail(pamh,flags,argc,argv,0);;
+}
+
+/* Checking mail as part of the session management */
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return _do_mail(pamh,flags,argc,argv,1);
+}
+
+
+/* --- The Beaf (Tm) --- */
+
+static int _do_mail(pam_handle_t *pamh, int flags, int argc,
+ const char **argv, int est)
+{
+ int retval, ctrl, hashcount;
+ char *path_mail=NULL, *folder;
+ const char *type;
+
+ /*
+ * this module (un)sets the MAIL environment variable, and checks if
+ * the user has any new mail.
+ */
+
+ ctrl = _pam_parse(flags, argc, argv, &path_mail, &hashcount);
+
+ /* Do we have anything to do? */
+
+ if (flags & PAM_SILENT)
+ return PAM_SUCCESS;
+
+ /* which folder? */
+
+ retval = get_folder(pamh, ctrl, &path_mail, &folder, hashcount);
+ if (retval != PAM_SUCCESS) {
+ D(("failed to find folder"));
+ return retval;
+ }
+
+ /* set the MAIL variable? */
+
+ if (!(ctrl & PAM_NO_ENV) && est) {
+ char *tmp;
+
+ tmp = malloc(strlen(folder)+sizeof(MAIL_ENV_FORMAT));
+ if (tmp != NULL) {
+ sprintf(tmp, MAIL_ENV_FORMAT, folder);
+ D(("setting env: %s", tmp));
+ retval = pam_putenv(pamh, tmp);
+ _pam_overwrite(tmp);
+ _pam_drop(tmp);
+ if (retval != PAM_SUCCESS) {
+ _pam_overwrite(folder);
+ _pam_drop(folder);
+ _log_err(LOG_CRIT, "unable to set " MAIL_ENV_NAME " variable");
+ return retval;
+ }
+ } else {
+ _log_err(LOG_CRIT, "no memory for " MAIL_ENV_NAME " variable");
+ _pam_overwrite(folder);
+ _pam_drop(folder);
+ return retval;
+ }
+ } else {
+ D(("not setting " MAIL_ENV_NAME " variable"));
+ }
+
+ /*
+ * OK. we've got the mail folder... what about its status?
+ */
+
+ if ((est && !(ctrl & PAM_NO_LOGIN))
+ || (!est && (ctrl & PAM_LOGOUT_TOO))) {
+ type = get_mail_status(ctrl, folder);
+ if (type != NULL) {
+ retval = report_mail(pamh, ctrl, type, folder);
+ type = NULL;
+ }
+ }
+
+ /* Delete environment variable? */
+ if (!est)
+ (void) pam_putenv(pamh, MAIL_ENV_NAME);
+
+ _pam_overwrite(folder); /* clean up */
+ _pam_drop(folder);
+
+ /* indicate success or failure */
+
+ return retval;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_mail_modstruct = {
+ "pam_mail",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_mkhomedir/.cvsignore b/modules/pam_mkhomedir/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_mkhomedir/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_mkhomedir/Makefile b/modules/pam_mkhomedir/Makefile
new file mode 100644
index 00000000..7c9f5d3a
--- /dev/null
+++ b/modules/pam_mkhomedir/Makefile
@@ -0,0 +1,90 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+
+#
+
+TITLE=pam_mkhomedir
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(LINKLIBS) -lpam
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c
new file mode 100644
index 00000000..ec05993d
--- /dev/null
+++ b/modules/pam_mkhomedir/pam_mkhomedir.c
@@ -0,0 +1,370 @@
+/* PAM Make Home Dir module
+
+ This module will create a users home directory if it does not exist
+ when the session begins. This allows users to be present in central
+ database (such as nis, kerb or ldap) without using a distributed
+ file system or pre-creating a large number of directories.
+
+ Here is a sample /etc/pam.d/login file for Debian GNU/Linux
+ 2.1:
+
+ auth requisite pam_securetty.so
+ auth sufficient pam_ldap.so
+ auth required pam_pwdb.so
+ auth optional pam_group.so
+ auth optional pam_mail.so
+ account requisite pam_time.so
+ account sufficient pam_ldap.so
+ account required pam_pwdb.so
+ session required pam_mkhomedir.so skel=/etc/skel/ umask=0022
+ session required pam_pwdb.so
+ session optional pam_lastlog.so
+ password required pam_pwdb.so
+
+ Released under the GNU LGPL version 2 or later
+ Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999
+ Structure taken from pam_lastlogin by Andrew Morgan
+ <morgan@parc.power.net> 1996
+ */
+
+/* I want snprintf dammit */
+#define _GNU_SOURCE 1
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_SESSION
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* argument parsing */
+#define MKHOMEDIR_DEBUG 020 /* keep quiet about things */
+#define MKHOMEDIR_QUIET 040 /* keep quiet about things */
+
+static unsigned int UMask = 0022;
+static char SkelDir[BUFSIZ] = "/etc/skel";
+
+/* some syslogging */
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-mkhomedir", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static int _pam_parse(int flags, int argc, const char **argv)
+{
+ int ctrl = 0;
+
+ /* does the appliction require quiet? */
+ if ((flags & PAM_SILENT) == PAM_SILENT)
+ ctrl |= MKHOMEDIR_QUIET;
+
+ /* step through arguments */
+ for (; argc-- > 0; ++argv)
+ {
+ if (!strcmp(*argv, "silent"))
+ {
+ ctrl |= MKHOMEDIR_QUIET;
+ }
+ else if (!strncmp(*argv,"umask=",6))
+ UMask = strtol(*argv+6,0,0);
+ else if (!strncmp(*argv,"skel=",5))
+ strcpy(SkelDir,*argv+5);
+ else
+ {
+ _log_err(LOG_ERR, "unknown option; %s", *argv);
+ }
+ }
+
+ D(("ctrl = %o", ctrl));
+ return ctrl;
+}
+
+/* This common function is used to send a message to the applications
+ conversion function. Our only use is to ask the application to print
+ an informative message that we are creating a home directory */
+static int converse(pam_handle_t * pamh, int ctrl, int nargs
+ ,struct pam_message **message
+ ,struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (retval == PAM_SUCCESS)
+ {
+
+ retval = conv->conv(nargs, (const struct pam_message **) message
+ ,response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && (ctrl & MKHOMEDIR_DEBUG))
+ {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+
+ }
+ else
+ {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation"));
+
+ return retval; /* propagate error status */
+}
+
+/* Ask the application to display a short text string for us. */
+static int make_remark(pam_handle_t * pamh, int ctrl, const char *remark)
+{
+ int retval;
+
+ if ((ctrl & MKHOMEDIR_QUIET) != MKHOMEDIR_QUIET)
+ {
+ struct pam_message msg[1], *mesg[1];
+ struct pam_response *resp = NULL;
+
+ mesg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = remark;
+
+ retval = converse(pamh, ctrl, 1, mesg, &resp);
+
+ msg[0].msg = NULL;
+ if (resp)
+ {
+ _pam_drop_reply(resp, 1);
+ }
+ }
+ else
+ {
+ D(("keeping quiet"));
+ retval = PAM_SUCCESS;
+ }
+
+ D(("returning %s", pam_strerror(pamh, retval)));
+ return retval;
+}
+
+/* Do the actual work of creating a home dir */
+static int create_homedir(pam_handle_t * pamh, int ctrl,
+ const struct passwd *pwd)
+{
+ char *remark;
+ DIR *D;
+ struct dirent *Dir;
+
+ /* Some scratch space */
+ remark = malloc(BUFSIZ);
+ if (remark == NULL)
+ {
+ D(("no memory for last login remark"));
+ return PAM_BUF_ERR;
+ }
+
+ /* Mention what is happening, if the notification fails that is OK */
+ if (snprintf(remark,BUFSIZ,"Creating home directory '%s'.",
+ pwd->pw_dir) == -1)
+ return PAM_PERM_DENIED;
+
+ make_remark(pamh, ctrl, remark);
+
+ /* Crete the home directory */
+ if (mkdir(pwd->pw_dir,0700) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to create home directory %s",pwd->pw_dir);
+ return PAM_PERM_DENIED;
+ }
+ if (chmod(pwd->pw_dir,0777 & (~UMask)) != 0 ||
+ chown(pwd->pw_dir,pwd->pw_uid,pwd->pw_gid) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to chance perms on home directory %s",pwd->pw_dir);
+ return PAM_PERM_DENIED;
+ }
+
+ /* See if we need to copy the skel dir over. */
+ if (SkelDir[0] == 0)
+ {
+ free(remark);
+ return PAM_SUCCESS;
+ }
+
+ /* Scan the directory */
+ D = opendir(SkelDir);
+ if (D == 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to read directory %s",SkelDir);
+ return PAM_PERM_DENIED;
+ }
+
+ for (Dir = readdir(D); Dir != 0; Dir = readdir(D))
+ {
+ int SrcFd;
+ int DestFd;
+ int Res;
+ struct stat St;
+
+ /* Skip some files.. */
+ if (strcmp(Dir->d_name,".") == 0 ||
+ strcmp(Dir->d_name,"..") == 0)
+ continue;
+
+ /* Check if it is a directory */
+ snprintf(remark,BUFSIZ,"%s/%s",SkelDir,Dir->d_name);
+ if (stat(remark,&St) != 0)
+ continue;
+ if (S_ISDIR(St.st_mode))
+ {
+ snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name);
+ if (mkdir(remark,(St.st_mode | 0222) & (~UMask)) != 0 ||
+ chmod(remark,(St.st_mode | 0222) & (~UMask)) != 0 ||
+ chown(remark,pwd->pw_uid,pwd->pw_gid) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to change perms on copy %s",remark);
+ return PAM_PERM_DENIED;
+ }
+ continue;
+ }
+
+ /* Open the source file */
+ if ((SrcFd = open(remark,O_RDONLY)) < 0 || fstat(SrcFd,&St) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to open src file %s",remark);
+ return PAM_PERM_DENIED;
+ }
+ stat(remark,&St);
+
+ /* Open the dest file */
+ snprintf(remark,BUFSIZ,"%s/%s",pwd->pw_dir,Dir->d_name);
+ if ((DestFd = open(remark,O_WRONLY | O_TRUNC | O_CREAT,0600)) < 0)
+ {
+ close(SrcFd);
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to open dest file %s",remark);
+ return PAM_PERM_DENIED;
+ }
+
+ /* Set the proper ownership and permissions for the module. We make
+ the file a+w and then mask it with the set mask. This preseves
+ execute bits */
+ if (fchmod(DestFd,(St.st_mode | 0222) & (~UMask)) != 0 ||
+ fchown(DestFd,pwd->pw_uid,pwd->pw_gid) != 0)
+ {
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to chang perms on copy %s",remark);
+ return PAM_PERM_DENIED;
+ }
+
+ /* Copy the file */
+ do
+ {
+ Res = read(SrcFd,remark,BUFSIZ);
+ if (Res < 0 || write(DestFd,remark,Res) != Res)
+ {
+ close(SrcFd);
+ close(DestFd);
+ free(remark);
+ _log_err(LOG_DEBUG, "unable to perform IO");
+ return PAM_PERM_DENIED;
+ }
+ }
+ while (Res != 0);
+ close(SrcFd);
+ close(DestFd);
+ }
+
+ free(remark);
+ return PAM_SUCCESS;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc
+ ,const char **argv)
+{
+ int retval, ctrl;
+ const char *user;
+ const struct passwd *pwd;
+ struct stat St;
+
+ /* Parse the flag values */
+ ctrl = _pam_parse(flags, argc, argv);
+
+ /* Determine the user name so we can get the home directory */
+ retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
+ if (retval != PAM_SUCCESS || user == NULL || *user == '\0')
+ {
+ _log_err(LOG_NOTICE, "user unknown");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* Get the password entry */
+ pwd = getpwnam(user);
+ if (pwd == NULL)
+ {
+ D(("couldn't identify user %s", user));
+ return PAM_CRED_INSUFFICIENT;
+ }
+
+ /* Stat the home directory, if something exists then we assume it is
+ correct and return a success*/
+ if (stat(pwd->pw_dir,&St) == 0)
+ return PAM_SUCCESS;
+
+ return create_homedir(pamh,ctrl,pwd);
+}
+
+/* Ignore */
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+struct pam_module _pam_mkhomedir_modstruct =
+{
+ "pam_mkhomedir",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+
+#endif
diff --git a/modules/pam_motd/.cvsignore b/modules/pam_motd/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_motd/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_motd/Makefile b/modules/pam_motd/Makefile
new file mode 100644
index 00000000..ef9f3113
--- /dev/null
+++ b/modules/pam_motd/Makefile
@@ -0,0 +1,90 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+
+#
+
+TITLE=pam_motd
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(LINKLIBS)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_motd/pam_motd.c b/modules/pam_motd/pam_motd.c
new file mode 100644
index 00000000..98976b66
--- /dev/null
+++ b/modules/pam_motd/pam_motd.c
@@ -0,0 +1,119 @@
+/* pam_motd module */
+
+/*
+ * Modified for pam_motd by Ben Collins <bcollins@debian.org>
+ *
+ * Based off of:
+ * $Id$
+ *
+ * Written by Michael K. Johnson <johnsonm@redhat.com> 1996/10/24
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+
+#include <security/_pam_macros.h>
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_SESSION
+#define DEFAULT_MOTD "/etc/motd"
+
+#include <security/pam_modules.h>
+
+/* --- session management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ int retval = PAM_IGNORE;
+ int fd;
+ char *mtmp=NULL, *motd_path=NULL;
+ struct pam_conv *conversation;
+ struct pam_message message;
+ struct pam_message *pmessage = &message;
+ struct pam_response *resp = NULL;
+ struct stat st;
+
+ if (flags & PAM_SILENT) {
+ return retval;
+ }
+
+ for (; argc-- > 0; ++argv) {
+ if (!strncmp(*argv,"motd=",5)) {
+ motd_path = (char *) strdup(5+*argv);
+ if (motd_path != NULL) {
+ D(("set motd path: %s", motd_path));
+ } else {
+ D(("failed to duplicate motd path - ignored"));
+ }
+ }
+ }
+
+ if (motd_path == NULL)
+ motd_path = DEFAULT_MOTD;
+
+ message.msg_style = PAM_TEXT_INFO;
+
+ if ((fd = open(motd_path, O_RDONLY, 0)) >= 0) {
+ /* fill in message buffer with contents of motd */
+ if ((fstat(fd, &st) < 0) || !st.st_size)
+ return retval;
+ message.msg = mtmp = malloc(st.st_size+1);
+ /* if malloc failed... */
+ if (!message.msg) return retval;
+ read(fd, mtmp, st.st_size);
+ if (mtmp[st.st_size-1] == '\n')
+ mtmp[st.st_size-1] = '\0';
+ else
+ mtmp[st.st_size] = '\0';
+ close(fd);
+ /* Use conversation function to give user contents of motd */
+ pam_get_item(pamh, PAM_CONV, (const void **)&conversation);
+ conversation->conv(1, (const struct pam_message **)&pmessage,
+ &resp, conversation->appdata_ptr);
+ free(mtmp);
+ if (resp)
+ _pam_drop_reply(resp, 1);
+ }
+
+ return retval;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_motd_modstruct = {
+ "pam_motd",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_nologin/.cvsignore b/modules/pam_nologin/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_nologin/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_nologin/Makefile b/modules/pam_nologin/Makefile
new file mode 100644
index 00000000..03d779bf
--- /dev/null
+++ b/modules/pam_nologin/Makefile
@@ -0,0 +1,86 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Michael K. Johnson <johnsonm@redhat.com> 1996/10/24
+#
+
+TITLE=pam_nologin
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ mkdir -p ./dynamic
+endif
+ifdef STATIC
+ mkdir -p ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) -lc
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ mkdir -p $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ install -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_nologin/README b/modules/pam_nologin/README
new file mode 100644
index 00000000..0586de66
--- /dev/null
+++ b/modules/pam_nologin/README
@@ -0,0 +1,12 @@
+# $Id$
+#
+
+This module always lets root in; it lets other users in only if the file
+/etc/nologin doesn't exist. In any case, if /etc/nologin exists, it's
+contents are displayed to the user.
+
+module services provided:
+
+ auth _authentication and _setcred (blank)
+
+Michael K. Johnson
diff --git a/modules/pam_nologin/pam_nologin.c b/modules/pam_nologin/pam_nologin.c
new file mode 100644
index 00000000..306619e1
--- /dev/null
+++ b/modules/pam_nologin/pam_nologin.c
@@ -0,0 +1,130 @@
+/* pam_nologin module */
+
+/*
+ * $Id$
+ *
+ * Written by Michael K. Johnson <johnsonm@redhat.com> 1996/10/24
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:46 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.4 1997/04/05 06:36:47 morgan
+ * display message when the user is unknown
+ *
+ * Revision 1.3 1996/12/01 03:00:54 morgan
+ * added prototype to conversation, gave static structure name of module
+ *
+ * Revision 1.2 1996/11/10 21:02:31 morgan
+ * compile against .53
+ *
+ * Revision 1.1 1996/10/25 03:19:36 morgan
+ * Initial revision
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+
+#include <security/_pam_macros.h>
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ int retval = PAM_SUCCESS;
+ int fd;
+ const char *username;
+ char *mtmp=NULL;
+ struct passwd *user_pwd;
+ struct pam_conv *conversation;
+ struct pam_message message;
+ struct pam_message *pmessage = &message;
+ struct pam_response *resp = NULL;
+ struct stat st;
+
+ if ((fd = open("/etc/nologin", O_RDONLY, 0)) >= 0) {
+ /* root can still log in; lusers cannot */
+ if ((pam_get_user(pamh, &username, NULL) != PAM_SUCCESS)
+ || !username) {
+ return PAM_SERVICE_ERR;
+ }
+ user_pwd = getpwnam(username);
+ if (user_pwd && user_pwd->pw_uid == 0) {
+ message.msg_style = PAM_TEXT_INFO;
+ } else {
+ if (!user_pwd) {
+ retval = PAM_USER_UNKNOWN;
+ } else {
+ retval = PAM_AUTH_ERR;
+ }
+ message.msg_style = PAM_ERROR_MSG;
+ }
+
+ /* fill in message buffer with contents of /etc/nologin */
+ if (fstat(fd, &st) < 0) /* give up trying to display message */
+ return retval;
+ message.msg = mtmp = malloc(st.st_size+1);
+ /* if malloc failed... */
+ if (!message.msg) return retval;
+ read(fd, mtmp, st.st_size);
+ mtmp[st.st_size] = '\000';
+
+ /* Use conversation function to give user contents of /etc/nologin */
+ pam_get_item(pamh, PAM_CONV, (const void **)&conversation);
+ conversation->conv(1, (const struct pam_message **)&pmessage,
+ &resp, conversation->appdata_ptr);
+ free(mtmp);
+ if (resp)
+ _pam_drop_reply(resp, 1);
+ }
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_nologin_modstruct = {
+ "pam_nologin",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_permit/.cvsignore b/modules/pam_permit/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_permit/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_permit/Makefile b/modules/pam_permit/Makefile
new file mode 100644
index 00000000..c291599e
--- /dev/null
+++ b/modules/pam_permit/Makefile
@@ -0,0 +1,132 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:46 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.8 1997/04/05 06:33:25 morgan
+# fakeroot
+#
+# Revision 1.7 1997/02/15 19:02:27 morgan
+# updated email address
+#
+# Revision 1.6 1996/11/10 20:14:34 morgan
+# cross platform support
+#
+# Revision 1.5 1996/09/05 06:32:45 morgan
+# ld --> gcc
+#
+# Revision 1.4 1996/05/26 15:49:25 morgan
+# make dynamic and static dirs
+#
+# Revision 1.3 1996/05/26 04:04:26 morgan
+# automated static support
+#
+# Revision 1.2 1996/03/16 17:56:38 morgan
+# tidied up
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+
+# Convenient defaults for compiling independently of the full source
+# tree.
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+endif
+
+#
+#
+
+TITLE=pam_permit
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(TARGET_ARCH) -c $< -o $@
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+endif
+
+ifdef DYNAMIC
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+endif
+
+ifdef STATIC
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_permit/README b/modules/pam_permit/README
new file mode 100644
index 00000000..c399710c
--- /dev/null
+++ b/modules/pam_permit/README
@@ -0,0 +1,4 @@
+# $Id$
+#
+
+this module always returns PAM_SUCCESS, it ignores all options.
diff --git a/modules/pam_permit/pam_permit.c b/modules/pam_permit/pam_permit.c
new file mode 100644
index 00000000..cf11a2dc
--- /dev/null
+++ b/modules/pam_permit/pam_permit.c
@@ -0,0 +1,128 @@
+/* pam_permit module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:46 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.5 1997/02/15 19:03:15 morgan
+ * fixed email address
+ *
+ * Revision 1.4 1997/02/15 16:03:10 morgan
+ * force a name for user
+ *
+ * Revision 1.3 1996/06/02 08:10:14 morgan
+ * updated for new static protocol
+ *
+ */
+
+#define DEFAULT_USER "nobody"
+
+#include <stdio.h>
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* --- authentication management functions --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval;
+ const char *user=NULL;
+
+ /*
+ * authentication requires we know who the user wants to be
+ */
+ retval = pam_get_user(pamh, &user, NULL);
+ if (retval != PAM_SUCCESS) {
+ D(("get user returned error: %s", pam_strerror(pamh,retval)));
+ return retval;
+ }
+ if (user == NULL || *user == '\0') {
+ D(("username not known"));
+ pam_set_item(pamh, PAM_USER, (const void *) DEFAULT_USER);
+ }
+ user = NULL; /* clean up */
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* --- account management functions --- */
+
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* --- password management --- */
+
+PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* --- session management --- */
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* end of module definition */
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_permit_modstruct = {
+ "pam_permit",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
diff --git a/modules/pam_pwdb/.cvsignore b/modules/pam_pwdb/.cvsignore
new file mode 100644
index 00000000..f0420bac
--- /dev/null
+++ b/modules/pam_pwdb/.cvsignore
@@ -0,0 +1,2 @@
+dynamic
+pwdb_chkpwd
diff --git a/modules/pam_pwdb/BUGS b/modules/pam_pwdb/BUGS
new file mode 100644
index 00000000..e7170b70
--- /dev/null
+++ b/modules/pam_pwdb/BUGS
@@ -0,0 +1,14 @@
+$Id$
+
+$Log$
+Revision 1.1 2000/06/20 22:11:46 agmorgan
+Initial revision
+
+Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+Linux PAM sources pre-0.66
+
+Revision 1.2 1996/09/05 06:36:16 morgan
+revised for .52 to be released
+
+
+As of Linux-PAM-0.52 this is new. No known bugs yet.
diff --git a/modules/pam_pwdb/CHANGELOG b/modules/pam_pwdb/CHANGELOG
new file mode 100644
index 00000000..7bad5cd8
--- /dev/null
+++ b/modules/pam_pwdb/CHANGELOG
@@ -0,0 +1,10 @@
+$Id$
+
+Tue Apr 23 12:28:09 EDT 1996 (Alexander O. Yuriev alex@bach.cis.temple.edu)
+
+ * PAM_DISALLOW_NULL_AUTHTOK implemented in the authentication module
+ * pam_sm_open_session() and pam_sm_close_session() implemented
+ A new "trace" flag added to flags of /etc/pam.conf. Using this
+ flag system administrator is able to make pam_unix module provide
+ very extensive audit trail sent so syslog with LOG_AUTHPRIV level.
+ * pam_sm_set_cred() is done
diff --git a/modules/pam_pwdb/Makefile b/modules/pam_pwdb/Makefile
new file mode 100644
index 00000000..fcb7aec4
--- /dev/null
+++ b/modules/pam_pwdb/Makefile
@@ -0,0 +1,193 @@
+# $Id$
+#
+# This Makefile controls a build process of the pam_unix module
+# for Linux-PAM. You should not modify this Makefile.
+#
+# rewritten to compile new module Andrew Morgan
+# <morgan@parc.power.net> 1996/11/6
+#
+
+#
+# Note, the STATIC module is commented out because it doesn't work.
+# please fix!
+#
+
+ifndef FULL_LINUX_PAM_SOURCE_TREE
+export DYNAMIC=-DPAM_DYNAMIC
+export CC=gcc
+export CFLAGS=-O2 -Dlinux -DLINUX_PAM \
+ -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \
+ -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \
+ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \
+ -Wshadow -pedantic -fPIC
+export MKDIR=mkdir -p
+export LD_D=gcc -shared -Xlinker -x
+export HAVE_PWDBLIB=yes
+endif
+
+ifeq ($(shell if [ -f /lib/libcrypt.so.* ]; then echo yes ; else echo no ; fi),yes)
+EXTRALS += -lcrypt
+endif
+
+ifeq ($(HAVE_PWDBLIB),yes)
+
+TITLE=pam_pwdb
+CHKPWD=pwdb_chkpwd
+
+# compilation flags
+EXTRAS=
+# extra object files
+PLUS=
+# extra files that may be needed to be created
+CREATE=
+
+# NOTE: this module links dynamically to the libpwdb library.
+EXTRALS += -lpwdb
+EXTRAS += -DCHKPWD_HELPER=\"$(SUPLEMENTED)/$(CHKPWD)\"
+
+########################### don't edit below ##########################
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+#LIBOBJS = $(addprefix static/,$(LIBOBJ))
+LIBDEPS = pam_unix_acct.-c pam_unix_auth.-c pam_unix_passwd.-c \
+ pam_unix_sess.-c pam_unix_pwupd.-c support.-c bigcrypt.-c
+
+PLUS += md5_good.o md5_broken.o md5_crypt_good.o md5_crypt_broken.o
+CFLAGS += $(EXTRAS)
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+#ifdef STATIC
+#LIBSTATIC = lib$(TITLE).o
+#endif
+
+all: info dirs $(PLUS) $(LIBSHARED) $(LIBSTATIC) register $(CHKPWD)
+
+dynamic/$(LIBOBJ) : $(LIBSRC) $(LIBDEPS)
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+#static/$(LIBOBJ) : $(LIBSRC) $(LIBDEPS)
+# $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+info:
+ @echo
+ @echo "*** Building PAM_pwdb module..."
+ @echo
+
+$(CHKPWD): pwdb_chkpwd.o md5_good.o md5_broken.o \
+ md5_crypt_good.o md5_crypt_broken.o
+ $(CC) -o $(CHKPWD) $^ -lpwdb
+
+pwdb_chkpwd.o: pwdb_chkpwd.c pam_unix_md.-c bigcrypt.-c
+
+md5_good.o: md5.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -DHIGHFIRST -D'MD5Name(x)=Good##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_broken.o: md5.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_good.o: md5_crypt.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Good##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_broken.o: md5_crypt.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+dirs:
+ifdef DYNAMIC
+ @$(MKDIR) ./dynamic
+endif
+#ifdef STATIC
+# @$(MKDIR) ./static
+#endif
+
+register:
+#ifdef STATIC
+# ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+#endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(PLUS) $(EXTRALS)
+endif
+
+#ifdef STATIC
+#$(LIBOBJS): $(LIBSRC)
+#
+#$(LIBSTATIC): $(LIBOBJS)
+# $(LD) -r -o $@ $(LIBOBJS) $(PLUS) $(EXTRALS)
+#endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SUPLEMENTED)
+ $(INSTALL) -m 4555 -o root -g root $(CHKPWD) $(FAKEROOT)$(SUPLEMENTED)
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(SUPLEMENTED)/$(CHKPWD)
+
+clean:
+ rm -f $(CHKPWD) $(LIBOBJD) $(LIBOBJS) $(MOREDELS) core *~ *.o *.so
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+else
+
+include ../dont_makefile
+
+endif
+
+#####################################################################
+# $Log$
+# Revision 1.1 2000/06/20 22:11:47 agmorgan
+# Initial revision
+#
+# Revision 1.4 1999/08/01 16:18:27 morgan
+# added a conditional for libcrypt
+#
+# Revision 1.3 1999/07/08 05:02:02 morgan
+# glibc fixes (Thorsten Kukuk, Adam J. Richter)
+#
+# Revision 1.2 1999/07/04 23:22:38 morgan
+# Andrey's MD5 (bigendian) work around + cleanup to address problems with
+# applications that let an (ab)user kill them off without giving PAM the
+# opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.7 1997/04/05 06:28:50 morgan
+# fakeroot
+#
+# Revision 1.6 1997/02/15 17:25:32 morgan
+# update for .56 . extra commands for new helper binary
+#
+# Revision 1.5 1997/01/04 20:39:08 morgan
+# conditional on having libpwdb
+#
+# Revision 1.4 1996/12/01 03:02:03 morgan
+# changed banner, removed linking libraries
+#
+# Revision 1.3 1996/11/10 20:14:42 morgan
+# cross platform support
+#
+# Revision 1.2 1996/09/05 06:36:49 morgan
+# options added and use of LD altered
+#
+# Revision 1.1 1996/08/29 13:23:29 morgan
+# Initial revision
+#
+#
diff --git a/modules/pam_pwdb/README b/modules/pam_pwdb/README
new file mode 100644
index 00000000..4f420855
--- /dev/null
+++ b/modules/pam_pwdb/README
@@ -0,0 +1,41 @@
+This is the pam_unix module. It has been significantly rewritten since
+.51 was released (due mostly to the efforts of Cristian Gafton), and
+now takes more options and correctly updates vanilla UNIX/shadow/md5
+passwords.
+
+[Please read the source and make a note of all the warnings there, as
+the license suggests -- use at your own risk.]
+
+So far as I am concerned this module is now pretty stable. If you find
+any bugs, PLEASE tell me! <morgan@linux.kernel.org>
+
+Options recognized by this module are as follows:
+
+ debug - log more debugging info
+ audit - a little more extreme than debug
+ use_first_pass - don't prompt the user for passwords
+ take them from PAM_ items instead
+ try_first_pass - don't prompt the user for the passwords
+ unless PAM_(OLD)AUTHTOK is unset
+ use_authtok - like try_first_pass, but *fail* if the new
+ PAM_AUTHTOK has not been previously set.
+ (intended for stacking password modules only)
+ not_set_pass - don't set the PAM_ items with the passwords
+ used by this module.
+ shadow - try to maintian a shadow based system.
+ unix - when changing passwords, they are placed
+ in the /etc/passwd file
+ md5 - when a user changes their password next,
+ encrypt it with the md5 algorithm.
+ bigcrypt - when a user changes their password next,
+ excrypt it with the DEC C2-algorithm(0).
+ nodelay - used to prevent failed authentication
+ resulting in a delay of about 1 second.
+
+There is some support for building a shadow file on-the-fly from an
+/etc/passwd file. This is VERY alpha. If you want to play with it you
+should read the source to find the appropriate #define that you will
+need.
+
+---------------------
+Andrew Morgan <morgan@linux.kernel.org>
diff --git a/modules/pam_pwdb/TODO b/modules/pam_pwdb/TODO
new file mode 100644
index 00000000..9dc7fc7e
--- /dev/null
+++ b/modules/pam_pwdb/TODO
@@ -0,0 +1,34 @@
+$Id$
+
+ * get NIS working
+ * .. including "nonis" argument
+ * add helper binary
+
+Wed Sep 4 23:40:09 PDT 1996 Andrew G. Morgan
+
+ * verify that it works for everyone
+ * look more seriously at the issue of generating a shadow
+ system on the fly
+ * add some more password flavors
+
+Thu Aug 29 06:26:42 PDT 1996 Andrew G. Morgan
+
+ * check that complete rewrite works! ;^)
+ * complete shadow support to the password changing code.
+ Also some code needed here for session managment?
+ (both pam.conf argument to turn it on/off, and some
+ conditional compilation.)
+ * md5 passwords...
+ * make the exclusive nature of the arguments work. That is,
+ only recognize the flags when appropriate.
+
+Wed May 8 19:08:49 EDT 1996 Alexander O. Yuriev
+
+ * support.c should go.
+
+Tue Apr 23 21:43:55 EDT 1996 Alexander O. Yuriev
+
+ * pam_sm_chauth_tok() should be written
+ * QUICK FIX: pam_sm_setcred() probably returns incorrect error code
+
+
diff --git a/modules/pam_pwdb/bigcrypt.-c b/modules/pam_pwdb/bigcrypt.-c
new file mode 100644
index 00000000..321f2491
--- /dev/null
+++ b/modules/pam_pwdb/bigcrypt.-c
@@ -0,0 +1,114 @@
+/*
+ * This function implements the "bigcrypt" algorithm specifically for
+ * Linux-PAM.
+ *
+ * This algorithm is algorithm 0 (default) shipped with the C2 secure
+ * implementation of Digital UNIX.
+ *
+ * Disclaimer: This work is not based on the source code to Digital
+ * UNIX, nor am I connected to Digital Equipment Corp, in any way
+ * other than as a customer. This code is based on published
+ * interfaces and reasonable guesswork.
+ *
+ * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8
+ * characters or less. Each block is encrypted using the standard UNIX
+ * libc crypt function. The result of the encryption for one block
+ * provides the salt for the suceeding block.
+ *
+ * Restrictions: The buffer used to hold the encrypted result is
+ * statically allocated. (see MAX_PASS_LEN below). This is necessary,
+ * as the returned pointer points to "static data that are overwritten
+ * by each call", (XPG3: XSI System Interface + Headers pg 109), and
+ * this is a drop in replacement for crypt();
+ *
+ * Andy Phillips <atp@mssl.ucl.ac.uk>
+ */
+
+/*
+ * Max cleartext password length in segments of 8 characters this
+ * function can deal with (16 segments of 8 chars= max 128 character
+ * password).
+ */
+
+#define MAX_PASS_LEN 16
+#define SEGMENT_SIZE 8
+#define SALT_SIZE 2
+#define KEYBUF_SIZE ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE)
+#define ESEGMENT_SIZE 11
+#define CBUF_SIZE ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1)
+
+static char *bigcrypt(const char *key, const char *salt)
+{
+ static char dec_c2_cryptbuf[CBUF_SIZE]; /* static storage area */
+
+ unsigned long int keylen,n_seg,j;
+ char *cipher_ptr,*plaintext_ptr,*tmp_ptr,*salt_ptr;
+ char keybuf[KEYBUF_SIZE+1];
+
+ D(("called with key='%s', salt='%s'.", key, salt));
+
+ /* reset arrays */
+ memset(keybuf, 0, KEYBUF_SIZE+1);
+ memset(dec_c2_cryptbuf, 0, CBUF_SIZE);
+
+ /* fill KEYBUF_SIZE with key */
+ strncpy(keybuf, key, KEYBUF_SIZE);
+
+ /* deal with case that we are doing a password check for a
+ conventially encrypted password: the salt will be
+ SALT_SIZE+ESEGMENT_SIZE long. */
+ if (strlen(salt) == (SALT_SIZE+ESEGMENT_SIZE))
+ keybuf[SEGMENT_SIZE] = '\0'; /* terminate password early(?) */
+
+ keylen = strlen(keybuf);
+
+ if (!keylen) {
+ n_seg = 1;
+ } else {
+ /* work out how many segments */
+ n_seg = 1 + ((keylen-1)/SEGMENT_SIZE);
+ }
+
+ if (n_seg > MAX_PASS_LEN)
+ n_seg = MAX_PASS_LEN; /* truncate at max length */
+
+ /* set up some pointers */
+ cipher_ptr = dec_c2_cryptbuf;
+ plaintext_ptr = keybuf;
+
+ /* do the first block with supplied salt */
+ tmp_ptr = crypt(plaintext_ptr,salt); /* libc crypt() */
+
+ /* and place in the static area */
+ strncpy(cipher_ptr, tmp_ptr, 13);
+ cipher_ptr += ESEGMENT_SIZE + SALT_SIZE;
+ plaintext_ptr += SEGMENT_SIZE; /* first block of SEGMENT_SIZE */
+
+ /* change the salt (1st 2 chars of previous block) - this was found
+ by dowsing */
+
+ salt_ptr = cipher_ptr - ESEGMENT_SIZE;
+
+ /* so far this is identical to "return crypt(key, salt);", if
+ there is more than one block encrypt them... */
+
+ if (n_seg > 1) {
+ for (j=2; j <= n_seg; j++) {
+
+ tmp_ptr = crypt(plaintext_ptr, salt_ptr);
+
+ /* skip the salt for seg!=0 */
+ strncpy(cipher_ptr, (tmp_ptr+SALT_SIZE), ESEGMENT_SIZE);
+
+ cipher_ptr += ESEGMENT_SIZE;
+ plaintext_ptr += SEGMENT_SIZE;
+ salt_ptr = cipher_ptr - ESEGMENT_SIZE;
+ }
+ }
+
+ D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf));
+
+ /* this is the <NUL> terminated encrypted password */
+
+ return dec_c2_cryptbuf;
+}
diff --git a/modules/pam_pwdb/md5.c b/modules/pam_pwdb/md5.c
new file mode 100644
index 00000000..d3f47763
--- /dev/null
+++ b/modules/pam_pwdb/md5.c
@@ -0,0 +1,270 @@
+/* $Id$
+ *
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:48 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/04 23:22:38 morgan
+ * Andrey's MD5 (bigendian) work around + cleanup to address problems with
+ * applications that let an (ab)user kill them off without giving PAM the
+ * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.1 1996/09/05 06:43:31 morgan
+ * Initial revision
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32 t;
+ do {
+ t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Name(MD5Init)(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301U;
+ ctx->buf[1] = 0xefcdab89U;
+ ctx->buf[2] = 0x98badcfeU;
+ ctx->buf[3] = 0x10325476U;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len)
+{
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *) ctx->in)[14] = ctx->bits[0];
+ ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16])
+{
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
diff --git a/modules/pam_pwdb/md5.h b/modules/pam_pwdb/md5.h
new file mode 100644
index 00000000..279ce46f
--- /dev/null
+++ b/modules/pam_pwdb/md5.h
@@ -0,0 +1,34 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __alpha
+typedef unsigned int uint32;
+#else
+typedef unsigned long uint32;
+#endif
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+void GoodMD5Init(struct MD5Context *);
+void GoodMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void GoodMD5Final(unsigned char digest[16], struct MD5Context *);
+void GoodMD5Transform(uint32 buf[4], uint32 const in[16]);
+void BrokenMD5Init(struct MD5Context *);
+void BrokenMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void BrokenMD5Final(unsigned char digest[16], struct MD5Context *);
+void BrokenMD5Transform(uint32 buf[4], uint32 const in[16]);
+
+char *Goodcrypt_md5(const char *pw, const char *salt);
+char *Brokencrypt_md5(const char *pw, const char *salt);
+
+/*
+* This is needed to make RSAREF happy on some MS-DOS compilers.
+*/
+
+typedef struct MD5Context MD5_CTX;
+
+#endif /* MD5_H */
diff --git a/modules/pam_pwdb/md5_crypt.c b/modules/pam_pwdb/md5_crypt.c
new file mode 100644
index 00000000..1d755a08
--- /dev/null
+++ b/modules/pam_pwdb/md5_crypt.c
@@ -0,0 +1,153 @@
+/* $Id$
+ *
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * Origin: Id: crypt.c,v 1.3 1995/05/30 05:42:22 rgrimes Exp
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:48 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/04 23:22:38 morgan
+ * Andrey's MD5 (bigendian) work around + cleanup to address problems with
+ * applications that let an (ab)user kill them off without giving PAM the
+ * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.1 1996/09/05 06:43:31 morgan
+ * Initial revision
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void
+to64(char *s, unsigned long v, int n)
+{
+ while (--n >= 0) {
+ *s++ = itoa64[v&0x3f];
+ v >>= 6;
+ }
+}
+
+/*
+ * UNIX password
+ *
+ * Use MD5 for what it is best at...
+ */
+
+char * MD5Name(crypt_md5)(const char *pw, const char *salt)
+{
+ const char *magic = "$1$";
+ /* This string is magic for this algorithm. Having
+ * it this way, we can get get better later on */
+ static char passwd[120], *p;
+ static const char *sp,*ep;
+ unsigned char final[16];
+ int sl,pl,i,j;
+ MD5_CTX ctx,ctx1;
+ unsigned long l;
+
+ /* Refine the Salt first */
+ sp = salt;
+
+ /* If it starts with the magic string, then skip that */
+ if(!strncmp(sp,magic,strlen(magic)))
+ sp += strlen(magic);
+
+ /* It stops at the first '$', max 8 chars */
+ for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++)
+ continue;
+
+ /* get the length of the true salt */
+ sl = ep - sp;
+
+ MD5Name(MD5Init)(&ctx);
+
+ /* The password first, since that is what is most unknown */
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)pw,strlen(pw));
+
+ /* Then our magic string */
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)magic,strlen(magic));
+
+ /* Then the raw salt */
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)sp,sl);
+
+ /* Then just as many characters of the MD5(pw,salt,pw) */
+ MD5Name(MD5Init)(&ctx1);
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ MD5Name(MD5Final)(final,&ctx1);
+ for(pl = strlen(pw); pl > 0; pl -= 16)
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl);
+
+ /* Don't leave anything around in vm they could use. */
+ memset(final,0,sizeof final);
+
+ /* Then something really weird... */
+ for (j=0,i = strlen(pw); i ; i >>= 1)
+ if(i&1)
+ MD5Name(MD5Update)(&ctx, (unsigned const char *)final+j, 1);
+ else
+ MD5Name(MD5Update)(&ctx, (unsigned const char *)pw+j, 1);
+
+ /* Now make the output string */
+ strcpy(passwd,magic);
+ strncat(passwd,sp,sl);
+ strcat(passwd,"$");
+
+ MD5Name(MD5Final)(final,&ctx);
+
+ /*
+ * and now, just to make sure things don't run too fast
+ * On a 60 Mhz Pentium this takes 34 msec, so you would
+ * need 30 seconds to build a 1000 entry dictionary...
+ */
+ for(i=0;i<1000;i++) {
+ MD5Name(MD5Init)(&ctx1);
+ if(i & 1)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ else
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
+
+ if(i % 3)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
+
+ if(i % 7)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+
+ if(i & 1)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
+ else
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ MD5Name(MD5Final)(final,&ctx1);
+ }
+
+ p = passwd + strlen(passwd);
+
+ l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4;
+ l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4;
+ l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4;
+ l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4;
+ l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4;
+ l = final[11] ; to64(p,l,2); p += 2;
+ *p = '\0';
+
+ /* Don't leave anything around in vm they could use. */
+ memset(final,0,sizeof final);
+
+ return passwd;
+}
+
diff --git a/modules/pam_pwdb/pam_pwdb.c b/modules/pam_pwdb/pam_pwdb.c
new file mode 100644
index 00000000..68ca68fd
--- /dev/null
+++ b/modules/pam_pwdb/pam_pwdb.c
@@ -0,0 +1,256 @@
+/*
+ * $Id$
+ *
+ * This is the single file that will be compiled for pam_unix.
+ * it includes each of the modules that have beed defined in the .-c
+ * files in this directory.
+ *
+ * It is a little ugly to do it this way, but it is a simple way of
+ * defining static functions only once, and yet keeping the separate
+ * files modular. If you can think of something better, please email
+ * Andrew Morgan <morgan@linux.kernel.org>
+ *
+ * See the end of this file for Copyright information.
+ */
+
+static const char rcsid[] =
+"$Id$\n"
+" - PWDB Pluggable Authentication module. <morgan@linux.kernel.org>"
+;
+
+/* #define DEBUG */
+
+#define _SVID_SOURCE
+#define _BSD_SOURCE
+#define _BSD_COMPAT
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h> /* for time() */
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <pwdb/pwdb_public.h>
+
+/* indicate the following groups are defined */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+
+#ifndef LINUX_PAM
+#include <security/pam_appl.h>
+#endif /* LINUX_PAM */
+
+#include "./support.-c"
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * authentication module.
+ */
+
+#include "./pam_unix_auth.-c"
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_auth( pamh, ctrl );
+ pwdb_end();
+
+ if ( on(UNIX_LIKE_AUTH, ctrl) ) {
+ D(("recording return code for next time [%d]", retval));
+ pam_set_data(pamh, "pwdb_setcred_return", (void *) &retval, NULL);
+ }
+
+ D(("done. [%s]", pam_strerror(pamh, retval)));
+
+ return retval;
+}
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags
+ , int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_set_credentials(pamh, ctrl);
+ pwdb_end();
+
+ if ( on(UNIX_LIKE_AUTH, ctrl) ) {
+ int *pretval = &retval;
+
+ D(("recovering return code from auth call"));
+ pam_get_data(pamh, "pwdb_setcred_return", (const void **) &pretval);
+ pam_set_data(pamh, "pwdb_setcred_return", NULL, NULL);
+ D(("recovered data indicates that old retval was %d", retval));
+ }
+
+ return retval;
+}
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * account management module.
+ */
+
+#include "./pam_unix_acct.-c"
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_acct_mgmt(pamh, ctrl);
+ pwdb_end();
+
+ D(("done."));
+
+ return retval;
+}
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * session module.
+ */
+
+#include "./pam_unix_sess.-c"
+
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_open_session(pamh, ctrl);
+ pwdb_end();
+
+ return retval;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_close_session(pamh, ctrl);
+ pwdb_end();
+
+ return retval;
+}
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * password changing module.
+ */
+
+#include "./pam_unix_passwd.-c"
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ pwdb_start();
+ ctrl = set_ctrl(flags, argc, argv);
+ retval = _unix_chauthtok(pamh, ctrl);
+ pwdb_end();
+
+ D(("done."));
+
+ return retval;
+}
+
+/* static module data */
+
+#ifdef PAM_STATIC
+struct pam_module _pam_pwdb_modstruct = {
+ "pam_pwdb",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
+
+/*
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_pwdb/pam_unix_acct.-c b/modules/pam_pwdb/pam_unix_acct.-c
new file mode 100644
index 00000000..16869054
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_acct.-c
@@ -0,0 +1,298 @@
+/*
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:49 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.6 1997/01/04 20:37:15 morgan
+ * extra debugging
+ *
+ * Revision 1.5 1996/12/01 03:05:54 morgan
+ * debugging with _pam_macros.h
+ *
+ * Revision 1.4 1996/11/10 21:03:57 morgan
+ * pwdb conversion
+ *
+ * Revision 1.3 1996/09/05 06:45:45 morgan
+ * tidied shadow acct management
+ *
+ * Revision 1.2 1996/09/01 01:13:14 morgan
+ * Cristian Gafton's patches
+ *
+ * Revision 1.1 1996/08/29 13:27:51 morgan
+ * Initial revision
+ *
+ *
+ * See end of file for copyright information
+ */
+
+static const char rcsid_acct[] =
+"$Id$\n"
+" - PAM_PWDB account management <gafton@redhat.com>";
+
+/* the shadow suite has accout managment.. */
+
+static int _shadow_acct_mgmt_exp(pam_handle_t *pamh, unsigned int ctrl,
+ const struct pwdb *pw, const char *uname)
+{
+ const struct pwdb_entry *pwe = NULL;
+ time_t curdays;
+ int last_change, max_change;
+ int retval;
+
+ D(("called."));
+
+ /* Now start the checks */
+
+ curdays = time(NULL)/(60*60*24); /* today */
+
+ /* First: has account expired ? (CG)
+ * - expire < curdays
+ * - or (last_change + max_change + defer_change) < curdays
+ * - in both cases, deny access
+ */
+
+ D(("pwdb_get_entry"));
+ retval = pwdb_get_entry(pw, "expire", &pwe);
+ if (retval == PWDB_SUCCESS) {
+ int expire;
+
+ expire = *( (const int *) pwe->value );
+ (void) pwdb_entry_delete(&pwe); /* no longer needed */
+
+ if ((curdays > expire) && (expire > 0)) {
+
+ _log_err(LOG_NOTICE
+ , "acct: account %s has expired (account expired)"
+ , uname);
+ make_remark(pamh, ctrl, PAM_ERROR_MSG
+ , "Your account has expired; "
+ "please contact your system administrator");
+
+ D(("account expired"));
+ return PAM_ACCT_EXPIRED;
+ }
+ }
+
+ D(("pwdb_get_entry"));
+ retval = pwdb_get_entry(pw, "last_change", &pwe);
+ if ( retval == PWDB_SUCCESS ) {
+ last_change = *( (const int *) pwe->value );
+ } else {
+ last_change = curdays;
+ }
+ (void) pwdb_entry_delete(&pwe);
+
+ D(("pwdb_get_entry"));
+ retval = pwdb_get_entry(pw, "max_change", &pwe);
+ if ( retval == PWDB_SUCCESS ) {
+ max_change = *( (const int *) pwe->value );
+ } else {
+ max_change = -1;
+ }
+ (void) pwdb_entry_delete(&pwe);
+
+ D(("pwdb_get_entry"));
+ retval = pwdb_get_entry(pw, "defer_change", &pwe);
+ if (retval == PWDB_SUCCESS) {
+ int defer_change;
+
+ defer_change = *( (const int *) pwe->value );
+ (void) pwdb_entry_delete(&pwe);
+
+ if ((curdays > (last_change + max_change + defer_change))
+ && (max_change != -1) && (defer_change != -1)
+ && (last_change > 0)) {
+
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_NOTICE, "acct: account %s has expired "
+ "(failed to change password)", uname);
+ }
+ make_remark(pamh, ctrl, PAM_ERROR_MSG
+ , "Your password has expired; "
+ "please see your system administrator");
+
+ D(("account expired2"));
+ return PAM_ACCT_EXPIRED;
+ }
+ }
+
+ /* Now test if the password is expired, but the user still can
+ * change their password. (CG)
+ * - last_change = 0
+ * - last_change + max_change < curdays
+ */
+
+ D(("when was the last change"));
+ if (last_change == 0) {
+
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_NOTICE
+ , "acct: expired password for user %s (root enforced)"
+ , uname);
+ }
+ make_remark(pamh, ctrl, PAM_ERROR_MSG
+ , "You are required to change your password immediately"
+ );
+
+ D(("need a new password"));
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+
+ if (((last_change + max_change) < curdays) &&
+ (max_change < 99999) && (max_change > 0)) {
+
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_DEBUG
+ , "acct: expired password for user %s (password aged)"
+ , uname);
+ }
+ make_remark(pamh, ctrl, PAM_ERROR_MSG
+ , "Your password has expired; please change it!");
+
+ D(("need a new password 2"));
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+
+ /*
+ * Now test if the password is about to expire (CG)
+ * - last_change + max_change - curdays <= warn_change
+ */
+
+ retval = pwdb_get_entry(pw, "warn_change", &pwe);
+ if ( retval == PWDB_SUCCESS ) {
+ int warn_days, daysleft;
+
+ daysleft = last_change + max_change - curdays;
+ warn_days = *((const int *) pwe->value);
+ (void) pwdb_entry_delete(&pwe);
+
+ if ((daysleft <= warn_days) && (warn_days > 0)) {
+ char *s;
+
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_DEBUG
+ , "acct: password for user %s will expire in %d days"
+ , uname, daysleft);
+ }
+
+#define LocalComment "Warning: your password will expire in %d day%s"
+ if ((s = (char *) malloc(30+sizeof(LocalComment))) == NULL) {
+ _log_err(LOG_CRIT, "malloc failure in " __FILE__);
+ retval = PAM_BUF_ERR;
+ } else {
+
+ sprintf(s, LocalComment, daysleft, daysleft == 1 ? "":"s");
+
+ make_remark(pamh, ctrl, PAM_TEXT_INFO, s);
+ free(s);
+ }
+#undef LocalComment
+ }
+ } else {
+ retval = PAM_SUCCESS;
+ }
+
+ D(("all done"));
+ return retval;
+}
+
+
+/*
+ * this function checks for the account details. The user may not be
+ * permitted to log in at this time etc.. Within the context of
+ * vanilla Unix, this function simply does nothing. The shadow suite
+ * added password/account expiry, but PWDB takes care of this
+ * transparently.
+ */
+
+static int _unix_acct_mgmt(pam_handle_t *pamh, unsigned int ctrl)
+{
+ const struct pwdb *pw = NULL;
+
+ char *uname=NULL;
+ int retval;
+
+ D(("called."));
+
+ /* identify user */
+
+ retval = pam_get_item(pamh,PAM_USER,(const void **)&uname);
+ D(("user = `%s'", uname));
+ if (retval != PAM_SUCCESS || uname == NULL) {
+ _log_err(LOG_ALERT
+ , "acct; could not identify user (from uid=%d)"
+ , getuid());
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* get database information for user */
+
+ retval = pwdb_locate("user", PWDB_DEFAULT, uname, PWDB_ID_UNKNOWN, &pw);
+ if (retval != PWDB_SUCCESS || pw == NULL) {
+
+ _log_err(LOG_ALERT, "acct; %s (%s from uid=%d)"
+ , pwdb_strerror(retval), uname, getuid());
+ if ( pw ) {
+ (void) pwdb_delete(&pw);
+ }
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* now check the user's times etc.. */
+
+ retval = _shadow_acct_mgmt_exp(pamh, ctrl, pw, uname);
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "expiry check failed for '%s'", uname);
+ }
+
+ /* Done with pw */
+
+ (void) pwdb_delete(&pw);
+
+ /* all done */
+
+ D(("done."));
+ return retval;
+}
+
+/*
+ * Copyright (c) Elliot Lee, 1996.
+ * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996.
+ * Copyright (c) Cristian Gafton <gafton@redhat.com> 1996.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_pwdb/pam_unix_auth.-c b/modules/pam_pwdb/pam_unix_auth.-c
new file mode 100644
index 00000000..e4dfe136
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_auth.-c
@@ -0,0 +1,131 @@
+/*
+ * $Id$
+ *
+ * See end of file for Copyright information.
+ */
+
+static const char rcsid_auth[] =
+"$Id$: pam_unix_auth.-c,v 1.2 1996/09/05 06:46:53 morgan Exp morgan $\n"
+" - PAM_PWDB authentication functions. <morgan@parc.power.net>";
+
+/*
+ * _unix_auth() is a front-end for UNIX/shadow authentication
+ *
+ * First, obtain the password from the user. Then use a
+ * routine in 'support.-c' to authenticate the user.
+ */
+
+#define _UNIX_AUTHTOK "-UN*X-PASS"
+
+static int _unix_auth(pam_handle_t *pamh, unsigned int ctrl)
+{
+ int retval;
+ const char *name, *p;
+
+ D(("called."));
+
+ /* get the user'name' */
+
+ retval = _unix_get_user(pamh, ctrl, NULL, &name);
+ if (retval != PAM_SUCCESS ) {
+ if (retval != PAM_CONV_AGAIN) {
+ if ( on(UNIX_DEBUG,ctrl) ) {
+ _log_err(LOG_DEBUG, "auth could not identify user");
+ }
+ } else {
+ D(("pam_get_user/conv() function is not ready yet"));
+ /* it is safe to resume this function so we translate this
+ retval to the value that indicates we're happy to resume. */
+ retval = PAM_INCOMPLETE;
+ }
+ return retval;
+ }
+
+ /* if this user does not have a password... */
+
+ if ( _unix_blankpasswd(ctrl, name) ) {
+ D(("user '%s' has blank passwd", name));
+ name = NULL;
+ return PAM_SUCCESS;
+ }
+
+ /* get this user's authentication token */
+
+ retval = _unix_read_password(pamh, ctrl, NULL, "Password: ", NULL
+ , _UNIX_AUTHTOK, &p);
+ if (retval != PAM_SUCCESS) {
+ if (retval != PAM_CONV_AGAIN) {
+ _log_err(LOG_CRIT, "auth could not identify password for [%s]"
+ , name);
+ } else {
+ D(("conversation function is not ready yet"));
+ /* it is safe to resume this function so we translate this
+ retval to the value that indicates we're happy to resume. */
+ retval = PAM_INCOMPLETE;
+ }
+ name = NULL;
+ return retval;
+ }
+ D(("user=%s, password=[%s]", name, p));
+
+ /* verify the password of this user */
+ retval = _unix_verify_password(pamh, name, p, ctrl);
+ name = p = NULL;
+
+ D(("done [%d]", retval));
+
+ return retval;
+}
+
+/*
+ * This function is for setting unix credentials. Sun has indicated
+ * that there are *NO* authentication credentials for unix. The
+ * obvious credentials would be the group membership of the user as
+ * listed in the /etc/group file. However, Sun indicates that it is
+ * the responsibility of the application to set these.
+ */
+
+static int _unix_set_credentials(pam_handle_t *pamh, unsigned int ctrl)
+{
+ D(("called <empty function> returning."));
+
+ return PAM_SUCCESS;
+}
+
+/********************************************************************
+ * Copyright (c) Alexander O. Yuriev, 1996.
+ * Copyright (c) Andrew G. Morgan <morgan@parc.power.net> 1996
+ * Copyright (c) Cristian Gafton <gafton@redhat.com> 1996, 1997
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
diff --git a/modules/pam_pwdb/pam_unix_md.-c b/modules/pam_pwdb/pam_unix_md.-c
new file mode 100644
index 00000000..d9b2c51b
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_md.-c
@@ -0,0 +1,73 @@
+/*
+ * This function is a front-end for the message digest algorithms used
+ * to compute the user's encrypted passwords. No reversible encryption
+ * is used here and I intend to keep it that way.
+ *
+ * While there are many sources of encryption outside the United
+ * States, it *may* be illegal to re-export reversible encryption
+ * computer code. Until such time as it is legal to export encryption
+ * software freely from the US, please do not send me any. (AGM)
+ */
+
+/* this should have been defined in a header file.. Why wasn't it? AGM */
+extern char *crypt(const char *key, const char *salt);
+
+#include "md5.h"
+#include "bigcrypt.-c"
+
+struct cfns {
+ const char *salt;
+ int len;
+ char * (* mdfn)(const char *key, const char *salt);
+};
+
+/* array of non-standard digest algorithms available */
+
+#define N_MDS 1
+const static struct cfns cfn_list[N_MDS] = {
+ { "$1$", 3, Goodcrypt_md5 },
+};
+
+static char *_pam_md(const char *key, const char *salt)
+{
+ char *x,*e=NULL;
+ int i;
+
+ D(("called with key='%s', salt='%s'", key, salt));
+
+ /* check for non-standard salts */
+
+ for (i=0; i<N_MDS; ++i) {
+ if ( !strncmp(cfn_list[i].salt, salt, cfn_list[i].len) ) {
+ e = cfn_list[i].mdfn(key, salt);
+ break;
+ }
+ }
+
+ if ( i >= N_MDS ) {
+ e = bigcrypt(key, salt); /* (defaults to standard algorithm) */
+ }
+
+ x = x_strdup(e); /* put e in malloc()ed memory */
+ _pam_overwrite(e); /* clean up */
+ return x; /* this must be deleted elsewhere */
+}
+
+#ifndef PWDB_NO_MD_COMPAT
+static char *_pam_md_compat(const char *key, const char *salt)
+{
+ char *x,*e=NULL;
+
+ D(("called with key='%s', salt='%s'", key, salt));
+
+ if ( !strncmp("$1$", salt, 3) ) {
+ e = Brokencrypt_md5(key, salt);
+ x = x_strdup(e); /* put e in malloc()ed memory */
+ _pam_overwrite(e); /* clean up */
+ } else {
+ x = x_strdup("*");
+ }
+
+ return x; /* this must be deleted elsewhere */
+}
+#endif /* PWDB_NO_MD_COMPAT */
diff --git a/modules/pam_pwdb/pam_unix_passwd.-c b/modules/pam_pwdb/pam_unix_passwd.-c
new file mode 100644
index 00000000..13801ba3
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_passwd.-c
@@ -0,0 +1,404 @@
+/* $Id$ */
+
+/*
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:50 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/04 23:22:38 morgan
+ * Andrey's MD5 (bigendian) work around + cleanup to address problems with
+ * applications that let an (ab)user kill them off without giving PAM the
+ * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.6 1997/04/05 06:31:06 morgan
+ * mostly a reformat.
+ *
+ * Revision 1.5 1996/12/01 03:05:54 morgan
+ * debugging with _pam_macros.h
+ *
+ * Revision 1.4 1996/11/10 21:04:51 morgan
+ * pwdb conversion
+ *
+ * Revision 1.3 1996/09/05 06:48:15 morgan
+ * A lot has changed. I'd recommend you study the diff.
+ *
+ * Revision 1.2 1996/09/01 16:33:27 morgan
+ * Cristian Gafton's changes
+ *
+ * Revision 1.1 1996/08/29 13:21:27 morgan
+ * Initial revision
+ *
+ */
+
+static const char rcsid_pass[] =
+"$Id$\n"
+" - PAM_PWDB password module <morgan@parc.power.net>"
+;
+
+#include "pam_unix_pwupd.-c"
+
+/* passwd/salt conversion macros */
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* data tokens */
+
+#define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
+#define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
+
+/* Implementation */
+
+/*
+ * i64c - convert an integer to a radix 64 character
+ */
+static int i64c(int i)
+{
+ if (i < 0)
+ return ('.');
+ else if (i > 63)
+ return ('z');
+ if (i == 0)
+ return ('.');
+ if (i == 1)
+ return ('/');
+ if (i >= 2 && i <= 11)
+ return ('0' - 2 + i);
+ if (i >= 12 && i <= 37)
+ return ('A' - 12 + i);
+ if (i >= 38 && i <= 63)
+ return ('a' - 38 + i);
+ return ('\0');
+}
+
+/*
+ * FUNCTION: _pam_unix_chauthtok()
+ *
+ * this function works in two passes. The first, when UNIX__PRELIM is
+ * set, obtains the previous password. It sets the PAM_OLDAUTHTOK item
+ * or stores it as a data item. The second function obtains a new
+ * password (verifying if necessary, that the user types it the same a
+ * second time.) depending on the 'ctrl' flags this new password may
+ * be stored in the PAM_AUTHTOK item or a private data item.
+ *
+ * Having obtained a new password. The function updates the
+ * /etc/passwd (and optionally the /etc/shadow) file(s).
+ *
+ * Provision is made for the creation of a blank shadow file if none
+ * is available, but one is required to update the shadow file -- the
+ * intention being for shadow passwords to be seamlessly implemented
+ * from the generic UNIX scheme. -- THIS BIT IS PRE-ALPHA.. and included
+ * in this release (.52) mostly for the purpose of discussion.
+ */
+
+static int _unix_chauthtok(pam_handle_t *pamh, unsigned int ctrl)
+{
+ int retval;
+ unsigned int lctrl;
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+ const char *pass_old, *pass_new;
+ /* </DO NOT free() THESE> */
+
+ D(("called"));
+
+ /*
+ * First get the name of a user
+ */
+
+ retval = _unix_get_user( pamh, ctrl, "Username: ", &user );
+ if ( retval != PAM_SUCCESS ) {
+ if ( on(UNIX_DEBUG,ctrl) ) {
+ _log_err(LOG_DEBUG, "password - could not identify user");
+ }
+ return retval;
+ }
+
+ if ( on(UNIX__PRELIM, ctrl) ) {
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+
+ char *Announce;
+
+ D(("prelim check"));
+
+ if ( _unix_blankpasswd(ctrl, user) ) {
+
+ return PAM_SUCCESS;
+
+ } else if ( off(UNIX__IAMROOT, ctrl) ) {
+
+ /* instruct user what is happening */
+#define greeting "Changing password for "
+ Announce = (char *) malloc(sizeof(greeting)+strlen(user));
+ if (Announce == NULL) {
+ _log_err(LOG_CRIT, "password - out of memory");
+ return PAM_BUF_ERR;
+ }
+ (void) strcpy(Announce, greeting);
+ (void) strcpy(Announce+sizeof(greeting)-1, user);
+#undef greeting
+
+ lctrl = ctrl;
+ set(UNIX__OLD_PASSWD, lctrl);
+ retval = _unix_read_password( pamh, lctrl
+ , Announce
+ , "(current) UNIX password: "
+ , NULL
+ , _UNIX_OLD_AUTHTOK
+ , &pass_old );
+ free(Announce);
+
+ if ( retval != PAM_SUCCESS ) {
+ _log_err(LOG_NOTICE
+ , "password - (old) token not obtained");
+ return retval;
+ }
+
+ /* verify that this is the password for this user */
+
+ retval = _unix_verify_password(pamh, user, pass_old, ctrl);
+ } else {
+ D(("process run by root so do nothing this time around"));
+ pass_old = NULL;
+ retval = PAM_SUCCESS; /* root doesn't have too */
+ }
+
+ if ( retval != PAM_SUCCESS ) {
+ D(("Authentication failed"));
+ pass_old = NULL;
+ return retval;
+ }
+
+ retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
+ pass_old = NULL;
+ if ( retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
+ }
+
+ } else if ( on( UNIX__UPDATE, ctrl ) ) {
+ /* tpass is used below to store the _pam_md() return; it
+ * should be _pam_delete()'d. */
+
+ char *tpass=NULL;
+
+ /*
+ * obtain the proposed password
+ */
+
+ D(("do update"));
+
+ /*
+ * get the old token back. NULL was ok only if root [at this
+ * point we assume that this has already been enforced on a
+ * previous call to this function].
+ */
+
+ if ( off(UNIX_NOT_SET_PASS, ctrl) ) {
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK
+ , (const void **)&pass_old);
+ } else {
+ retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
+ , (const void **)&pass_old);
+ if (retval == PAM_NO_MODULE_DATA) {
+ retval = PAM_SUCCESS;
+ pass_old = NULL;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "user not authenticated");
+ return retval;
+ }
+
+ D(("get new password now"));
+
+ lctrl = ctrl;
+
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ */
+
+ if ( on(UNIX_USE_AUTHTOK, lctrl) ) {
+ set(UNIX_USE_FIRST_PASS, lctrl);
+ }
+
+ retval = _unix_read_password( pamh, lctrl
+ , NULL
+ , "Enter new UNIX password: "
+ , "Retype new UNIX password: "
+ , _UNIX_NEW_AUTHTOK
+ , &pass_new );
+
+ if ( retval != PAM_SUCCESS ) {
+ if ( on(UNIX_DEBUG,ctrl) ) {
+ _log_err(LOG_ALERT
+ , "password - new password not obtained");
+ }
+ pass_old = NULL; /* tidy up */
+ return retval;
+ }
+
+ D(("returned to _unix_chauthtok"));
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') { /* "\0" password = NULL */
+ pass_new = NULL;
+ }
+
+ retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "new password not acceptable");
+ pass_new = pass_old = NULL; /* tidy up */
+ return retval;
+ }
+
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the password database file.
+ */
+
+ /*
+ * First we encrypt the new password.
+ *
+ * XXX - this is where we might need some code for RADIUS types
+ * of password handling... no encryption needed..
+ */
+
+ if ( on(UNIX_MD5_PASS, ctrl) ) {
+
+ /*
+ * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
+ * removed use of static variables (AGM)
+ */
+
+ struct timeval tv;
+ MD5_CTX ctx;
+ unsigned char result[16];
+ char *cp = (char *)result;
+ unsigned char tmp[16];
+ int i;
+
+ GoodMD5Init(&ctx);
+ gettimeofday(&tv, (struct timezone *) 0);
+ GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
+ i = getpid();
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
+ i = clock();
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
+ GoodMD5Update(&ctx, result, sizeof result);
+ GoodMD5Final(tmp, &ctx);
+ strcpy(cp, "$1$"); /* magic for the MD5 */
+ cp += strlen(cp);
+ for (i = 0; i < 8; i++)
+ *cp++ = i64c(tmp[i] & 077);
+ *cp = '\0';
+
+ /* no longer need cleartext */
+ pass_new = tpass = _pam_md(pass_new, (const char *)result);
+
+ } else {
+ /*
+ * Salt manipulation is stolen from Rick Faith's passwd
+ * program. Sorry Rick :) -- alex
+ */
+
+ time_t tm;
+ char salt[3];
+
+ time(&tm);
+ salt[0] = bin_to_ascii(tm & 0x3f);
+ salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
+ salt[2] = '\0';
+
+ if ( off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8 ) {
+ /* to avoid using the _extensions_ of the bigcrypt()
+ function we truncate the newly entered password */
+ char *temp = malloc(9);
+
+ if (temp == NULL) {
+ _log_err(LOG_CRIT, "out of memory for password");
+ pass_new = pass_old = NULL; /* tidy up */
+ return PAM_BUF_ERR;
+ }
+
+ /* copy first 8 bytes of password */
+ strncpy(temp, pass_new, 8);
+ temp[8] = '\0';
+
+ /* no longer need cleartext */
+ pass_new = tpass = _pam_md( temp, salt );
+
+ _pam_delete(temp); /* tidy up */
+ } else {
+ /* no longer need cleartext */
+ pass_new = tpass = _pam_md( pass_new, salt );
+ }
+ }
+
+ D(("password processed"));
+
+ /* update the password database(s) -- race conditions..? */
+
+ retval = unix_update_db(pamh, ctrl, user, pass_old, pass_new);
+ pass_old = pass_new = NULL;
+
+ } else { /* something has broken with the module */
+
+ _log_err(LOG_ALERT, "password received unknown request");
+ retval = PAM_ABORT;
+
+ }
+
+ return retval;
+}
+
+/* ******************************************************************
+ * Copyright (c) Alexander O. Yuriev (alex@bach.cis.temple.edu), 1996.
+ * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996, 1997.
+ * Copyright (c) Cristian Gafton, <gafton@redhat.com> 1996, 1997.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_pwdb/pam_unix_pwupd.-c b/modules/pam_pwdb/pam_unix_pwupd.-c
new file mode 100644
index 00000000..0f646369
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_pwupd.-c
@@ -0,0 +1,260 @@
+/*
+ * $Id$
+ *
+ * This file contains the routines to update the passwd databases.
+ */
+
+/* Implementation */
+
+static int unix_update_db(pam_handle_t *pamh, int ctrl, const char *user,
+ const char *pass_old, const char *pass_new)
+{
+ const struct pwdb *pw=NULL;
+ const struct pwdb_entry *pwe=NULL;
+ pwdb_flag flag;
+ int retval, i;
+
+ D(("called."));
+
+ /* obtain default user record */
+
+ retval = pwdb_locate("user", PWDB_DEFAULT, user, PWDB_ID_UNKNOWN, &pw);
+ if (retval == PWDB_PASS_PHRASE_REQD) {
+ retval = pwdb_set_entry(pw, "pass_phrase"
+ , pass_old, 1+strlen(pass_old)
+ , NULL, NULL, 0);
+ if (retval == PWDB_SUCCESS)
+ retval = pwdb_locate("user", pw->source, user
+ , PWDB_ID_UNKNOWN, &pw);
+ }
+ pass_old = NULL;
+
+ if ( retval != PWDB_SUCCESS ) {
+ _log_err(LOG_ALERT, "cannot identify user %s (uid=%d)"
+ , user, getuid() );
+ pass_new = NULL;
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* check that we can update all of the default databases */
+
+ retval = pwdb_flags("user", pw->source, &flag);
+
+ if ( retval != PWDB_SUCCESS || ( pwdb_on(flag,PWDB_F_NOUPDATE) ) ) {
+ _log_err(LOG_ERR, "cannot update default database for user %s"
+ , user );
+ pass_new = NULL;
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_PERM_DENIED;
+ }
+
+ /* If there was one, we delete the "last_change" entry */
+ retval = pwdb_get_entry(pw, "last_change", &pwe);
+ if (retval == PWDB_SUCCESS) {
+ (void) pwdb_entry_delete(&pwe);
+ pwdb_set_entry(pw, "last_change", NULL, -1, NULL, NULL, 0);
+ }
+
+ /*
+ * next check for pam.conf specified databases: shadow etc... [In
+ * other words, pam.conf indicates which database the password is
+ * to be subsequently placed in: this is password migration].
+ */
+
+ if ( on(UNIX__SET_DB, ctrl) ) {
+ const char *db_token;
+ pwdb_type pt = _PWDB_MAX_TYPES;
+
+ if ( on(UNIX_UNIX, ctrl) ) {
+ db_token = "U"; /* XXX - should be macro */
+ pt = PWDB_UNIX;
+ } else if ( on(UNIX_SHADOW, ctrl) ) {
+ db_token = "x"; /* XXX - should be macro */
+ pt = PWDB_SHADOW;
+ } else if ( on(UNIX_RADIUS, ctrl) ) {
+ db_token = "R"; /* XXX - is this ok? */
+ pt = PWDB_RADIUS;
+ } else {
+ _log_err(LOG_ALERT
+ , "cannot determine database to use for authtok");
+ pass_new = NULL;
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_ABORT; /* we're in trouble */
+ }
+
+ /*
+ * Attempt to update the indicated database (only)
+ */
+
+ {
+ pwdb_type tpt[2];
+ tpt[0] = pt;
+ tpt[1] = _PWDB_MAX_TYPES;
+
+ /* Can we set entry in database? */
+ retval = pwdb_flags("user", tpt, &flag);
+ if (retval == PWDB_SUCCESS && !pwdb_on(flag,PWDB_F_NOUPDATE)) {
+ /* YES. This database is available.. */
+
+ /* Only update if it is not already in the default list */
+ for (i=0; pw->source[i] != _PWDB_MAX_TYPES
+ && pw->source[i] != pt ; ++i);
+ if (pw->source[i] == _PWDB_MAX_TYPES) {
+ const struct pwdb *tpw=NULL;
+
+ /* copy database entry */
+ if ((retval = pwdb_new(&tpw, 10)) != PWDB_SUCCESS
+ || (retval = pwdb_merge(tpw, pw, PWDB_TRUE))
+ != PWDB_SUCCESS) {
+ _log_err(LOG_CRIT, "failed to obtain new pwdb: %s"
+ , pwdb_strerror(retval));
+ retval = PAM_ABORT;
+ } else
+ retval = PAM_SUCCESS;
+
+ /* set db_token */
+ if (retval == PAM_SUCCESS) {
+ retval = pwdb_set_entry(tpw, "defer_pass", db_token
+ , 1+strlen(db_token)
+ , NULL, NULL, 0);
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "set defer_pass -> %s"
+ , pwdb_strerror(retval));
+ retval = PAM_PERM_DENIED;
+ } else
+ retval = PAM_SUCCESS;
+ }
+
+ /* update specific database */
+ if (retval == PAM_SUCCESS) {
+ retval = pwdb_replace("user", tpt
+ , user, PWDB_ID_UNKNOWN, &tpw);
+ if (retval != PWDB_SUCCESS) {
+ const char *service=NULL;
+ (void) pam_get_item(pamh, PAM_SERVICE
+ , (const void **)&service);
+ _log_err(LOG_ALERT
+ , "(%s) specified database failed: %s"
+ , service
+ , pwdb_strerror(retval));
+ retval = PAM_PERM_DENIED;
+ } else {
+ retval = PAM_SUCCESS;
+ }
+ }
+
+ /* clean up temporary pwdb */
+ if (tpw)
+ (void) pwdb_delete(&tpw);
+ }
+
+ /* we can properly adopt new defer_pass */
+ if (retval == PAM_SUCCESS) {
+ /* failing here will mean we go back to former
+ password location */
+ (void) pwdb_set_entry(pw, "defer_pass", db_token
+ , 1+strlen(db_token), NULL, NULL, 0);
+ }
+ }
+ }
+ }
+
+ /*
+ * the password will now be placed in appropriate (perhaps original) db
+ */
+
+ retval = pwdb_get_entry(pw, "uid", &pwe);
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "no uid!? (%s); %s", user, pwdb_strerror(retval));
+ pass_new = NULL;
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* insert the passwd into the 'pw' structure */
+
+ retval = pwdb_set_entry(pw, "passwd", pass_new, 1+strlen(pass_new)
+ , NULL, NULL, 0);
+ pass_new = NULL;
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "set2 failed; %s", pwdb_strerror(retval));
+ if (pw)
+ (void) pwdb_delete(&pw);
+ return PAM_AUTHTOK_LOCK_BUSY;
+ }
+
+ retval = pwdb_replace("user", pw->source, user
+ , *((uid_t *)pwe->value), &pw);
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "user (%s/%d) update failed; %s"
+ , user, *((uid_t *)pwe->value), pwdb_strerror(retval));
+ if (pw)
+ (void) pwdb_delete(&pw);
+ (void) pwdb_entry_delete(&pwe);
+ return PAM_ABORT;
+ }
+
+ if (retval != PWDB_SUCCESS) {
+
+ _log_err(LOG_ALERT, "user (%s/%d) update failed; %s"
+ , user, *((uid_t *)pwe->value), pwdb_strerror(retval));
+ retval = PAM_ABORT;
+
+ } else {
+ /* password updated */
+
+ _log_err(LOG_INFO, "password for (%s/%d) changed by (%s/%d)"
+ , user, *((uid_t *)pwe->value), getlogin(), getuid());
+ retval = PAM_SUCCESS;
+ }
+
+ /* tidy up */
+
+ (void) pwdb_entry_delete(&pwe);
+ if (pw)
+ (void) pwdb_delete(&pw);
+
+ return retval;
+}
+
+/* ******************************************************************
+ * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996,1997.
+ * Copyright (c) Cristian Gafton, <gafton@redhat.com> 1996, 1997.
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_pwdb/pam_unix_sess.-c b/modules/pam_pwdb/pam_unix_sess.-c
new file mode 100644
index 00000000..e1fe820b
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_sess.-c
@@ -0,0 +1,118 @@
+/*
+ * $Id$
+ *
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:51 agmorgan
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.4 1996/12/01 03:05:54 morgan
+ * debugging with _pam_macros.h
+ *
+ * Revision 1.3 1996/11/10 21:05:33 morgan
+ * pwdb conversion
+ *
+ * Revision 1.2 1996/09/05 06:49:02 morgan
+ * more informative logging
+ *
+ * Revision 1.1 1996/08/29 13:27:51 morgan
+ * Initial revision
+ *
+ *
+ * See end for Copyright information
+ */
+
+static const char rcsid_sess[] =
+"$Id$\n"
+" - PAM_PWDB session management. morgan@parc.power.net";
+
+/* Define internal functions */
+
+static int _unix_open_session(pam_handle_t *pamh, unsigned int ctrl)
+{
+ int retval;
+ char *user_name, *service;
+
+ D(("called."));
+
+ retval = pam_get_item( pamh, PAM_USER, (void *) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ retval = pam_get_item( pamh, PAM_SERVICE, (void*) &service );
+ if ( service == NULL || retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "open_session - error recovering service");
+ return PAM_SESSION_ERR;
+ }
+
+ _log_err(LOG_INFO, "(%s) session opened for user %s by %s(uid=%d)"
+ , service, user_name
+ , getlogin() == NULL ? "":getlogin(), getuid() );
+
+ return PAM_SUCCESS;
+}
+
+static int _unix_close_session(pam_handle_t *pamh, unsigned int ctrl)
+{
+ int retval;
+ char *user_name, *service;
+
+ D(("called."));
+
+ retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "close_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ retval = pam_get_item( pamh, PAM_SERVICE, (void*) &service );
+ if ( service == NULL || retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "close_session - error recovering service");
+ return PAM_SESSION_ERR;
+ }
+
+ _log_err(LOG_INFO, "(%s) session closed for user %s"
+ , service, user_name );
+
+ return PAM_SUCCESS;
+}
+
+/*
+ * Copyright (c) Alexander O. Yuriev, 1996. All rights reserved.
+ * Copyright (c) Andrew G. Morgan, 1996, <morgan@parc.power.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_pwdb/pwdb_chkpwd.c b/modules/pam_pwdb/pwdb_chkpwd.c
new file mode 100644
index 00000000..2c7ba29c
--- /dev/null
+++ b/modules/pam_pwdb/pwdb_chkpwd.c
@@ -0,0 +1,204 @@
+/*
+ * $Id$
+ *
+ * This program is designed to run setuid(root) or with sufficient
+ * privilege to read all of the unix password databases. It is designed
+ * to provide a mechanism for the current user (defined by this
+ * process' real uid) to verify their own password.
+ *
+ * The password is read from the standard input. The exit status of
+ * this program indicates whether the user is authenticated or not.
+ *
+ * Copyright information is located at the end of the file.
+ *
+ */
+
+#define _BSD_SOURCE
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <security/_pam_macros.h>
+
+#define MAXPASS 200 /* the maximum length of a password */
+
+#define UNIX_PASSED (PWDB_SUCCESS)
+#define UNIX_FAILED (PWDB_SUCCESS+1)
+
+#include <pwdb/pwdb_public.h>
+
+/* syslogging function for errors and other information */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pwdb_chkpwd", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+#define PWDB_NO_MD_COMPAT
+#include "pam_unix_md.-c"
+
+static int _unix_verify_passwd(const char *salt, const char *p)
+{
+ char *pp=NULL;
+ int retval;
+
+ if (p == NULL) {
+ if (*salt == '\0') {
+ retval = UNIX_PASSED;
+ } else {
+ retval = UNIX_FAILED;
+ }
+ } else {
+ pp = _pam_md(p, salt);
+ p = NULL; /* no longer needed here */
+
+ if ( strcmp( pp, salt ) == 0 ) {
+ retval = UNIX_PASSED;
+ } else {
+ retval = UNIX_FAILED;
+ }
+ }
+
+ /* clean up */
+ {
+ char *tp = pp;
+ if (pp != NULL) {
+ while(tp && *tp)
+ *tp++ = '\0';
+ free(pp);
+ pp = tp = NULL;
+ }
+ }
+
+ return retval;
+}
+
+int main(void)
+{
+ const struct pwdb *pw=NULL;
+ const struct pwdb_entry *pwe=NULL;
+ char pass[MAXPASS+1];
+ int npass;
+ int retval=UNIX_FAILED;
+
+ /*
+ * we establish that this program is running with non-tty stdin.
+ * this is to discourage casual use. It does *NOT* prevent an
+ * intruder from repeatadly running this program to determine the
+ * password of the current user (brute force attack, but one for
+ * which the attacker must already have gained access to the user's
+ * account).
+ */
+
+ if ( isatty(STDIN_FILENO) ) {
+ _log_err(LOG_NOTICE
+ , "inappropriate use of PWDB helper binary [UID=%d]"
+ , getuid() );
+ fprintf(stderr,
+ "This program is not designed for running in this way\n"
+ "-- the system administrator has been informed\n");
+ exit(UNIX_FAILED);
+ }
+
+ /*
+ * determine the current user's name:
+ */
+
+ retval = pwdb_start();
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "failed to open pwdb");
+ retval = UNIX_FAILED;
+ }
+ if (retval != UNIX_FAILED) {
+ retval = pwdb_locate("user", PWDB_DEFAULT, PWDB_NAME_UNKNOWN
+ , getuid(), &pw);
+ }
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "could not identify user");
+ while (pwdb_end() != PWDB_SUCCESS);
+ exit(UNIX_FAILED);
+ }
+
+ /* read the password from stdin (a pipe from the pam_pwdb module) */
+
+ npass = read(STDIN_FILENO, pass, MAXPASS);
+
+ if (npass < 0) { /* is it a valid password? */
+ _log_err(LOG_DEBUG, "no password supplied");
+ retval = UNIX_FAILED;
+ } else if (npass >= MAXPASS-1) {
+ _log_err(LOG_DEBUG, "password too long");
+ retval = UNIX_FAILED;
+ } else if (pwdb_get_entry(pw, "passwd", &pwe) != PWDB_SUCCESS) {
+ _log_err(LOG_WARNING, "password not found");
+ retval = UNIX_FAILED;
+ } else {
+ if (npass <= 0) {
+ /* the password is NULL */
+
+ retval = _unix_verify_passwd((const char *)(pwe->value), NULL);
+ } else {
+ /* does pass agree with the official one? */
+
+ pass[npass] = '\0'; /* NUL terminate */
+ retval = _unix_verify_passwd((const char *)(pwe->value), pass);
+ }
+ }
+
+ memset(pass, '\0', MAXPASS); /* clear memory of the password */
+ while (pwdb_end() != PWDB_SUCCESS);
+
+ /* return pass or fail */
+
+ exit(retval);
+}
+
+/*
+ * Copyright (c) Andrew G. Morgan, 1997. All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_pwdb/support.-c b/modules/pam_pwdb/support.-c
new file mode 100644
index 00000000..2cbcb576
--- /dev/null
+++ b/modules/pam_pwdb/support.-c
@@ -0,0 +1,938 @@
+/*
+ * $Id$
+ *
+ * Copyright information at end of file.
+ */
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/* type definition for the control options */
+
+typedef struct {
+ const char *token;
+ unsigned int mask; /* shall assume 32 bits of flags */
+ unsigned int flag;
+} UNIX_Ctrls;
+
+/*
+ * macro to determine if a given flag is on
+ */
+
+#define on(x,ctrl) (unix_args[x].flag & ctrl)
+
+/*
+ * macro to determine that a given flag is NOT on
+ */
+
+#define off(x,ctrl) (!on(x,ctrl))
+
+/*
+ * macro to turn on/off a ctrl flag manually
+ */
+
+#define set(x,ctrl) (ctrl = ((ctrl)&unix_args[x].mask)|unix_args[x].flag)
+#define unset(x,ctrl) (ctrl &= ~(unix_args[x].flag))
+
+/* the generic mask */
+
+#define _ALL_ON_ (~0U)
+
+/* end of macro definitions definitions for the control flags */
+
+/* ****************************************************************** *
+ * ctrl flags proper..
+ */
+
+/*
+ * here are the various options recognized by the unix module. They
+ * are enumerated here and then defined below. Internal arguments are
+ * given NULL tokens.
+ */
+
+#define UNIX__OLD_PASSWD 0 /* internal */
+#define UNIX__VERIFY_PASSWD 1 /* internal */
+#define UNIX__IAMROOT 2 /* internal */
+
+#define UNIX_AUDIT 3 /* print more things than debug..
+ some information may be sensitive */
+#define UNIX_USE_FIRST_PASS 4
+#define UNIX_TRY_FIRST_PASS 5
+#define UNIX_NOT_SET_PASS 6 /* don't set the AUTHTOK items */
+
+#define UNIX__PRELIM 7 /* internal */
+#define UNIX__UPDATE 8 /* internal */
+#define UNIX__NONULL 9 /* internal */
+#define UNIX__QUIET 10 /* internal */
+#define UNIX_USE_AUTHTOK 11 /* insist on reading PAM_AUTHTOK */
+#define UNIX_SHADOW 12 /* signal shadow on */
+#define UNIX_MD5_PASS 13 /* force the use of MD5 passwords */
+#define UNIX__NULLOK 14 /* Null token ok */
+#define UNIX_RADIUS 15 /* wish to use RADIUS for password */
+#define UNIX__SET_DB 16 /* internal - signals redirect to db */
+#define UNIX_DEBUG 17 /* send more info to syslog(3) */
+#define UNIX_NODELAY 18 /* admin does not want a fail-delay */
+#define UNIX_UNIX 19 /* wish to use /etc/passwd for pwd */
+#define UNIX_BIGCRYPT 20 /* use DEC-C2 crypt()^x function */
+#define UNIX_LIKE_AUTH 21 /* need to auth for setcred to work */
+/* -------------- */
+#define UNIX_CTRLS_ 22 /* number of ctrl arguments defined */
+
+
+static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = {
+/* symbol token name ctrl mask ctrl *
+ * ------------------ ------------------ -------------- ---------- */
+
+/* UNIX__OLD_PASSWD */ { NULL, _ALL_ON_, 01 },
+/* UNIX__VERIFY_PASSWD */ { NULL, _ALL_ON_, 02 },
+/* UNIX__IAMROOT */ { NULL, _ALL_ON_, 04 },
+/* UNIX_AUDIT */ { "audit", _ALL_ON_, 010 },
+/* UNIX_USE_FIRST_PASS */ { "use_first_pass", _ALL_ON_^(060), 020 },
+/* UNIX_TRY_FIRST_PASS */ { "try_first_pass", _ALL_ON_^(060), 040 },
+/* UNIX_NOT_SET_PASS */ { "not_set_pass", _ALL_ON_, 0100 },
+/* UNIX__PRELIM */ { NULL, _ALL_ON_^(0600), 0200 },
+/* UNIX__UPDATE */ { NULL, _ALL_ON_^(0600), 0400 },
+/* UNIX__NONULL */ { NULL, _ALL_ON_, 01000 },
+/* UNIX__QUIET */ { NULL, _ALL_ON_, 02000 },
+/* UNIX_USE_AUTHTOK */ { "use_authtok", _ALL_ON_, 04000 },
+/* UNIX_SHADOW */ { "shadow", _ALL_ON_^(0140000), 010000 },
+/* UNIX_MD5_PASS */ { "md5", _ALL_ON_^(02000000), 020000 },
+/* UNIX__NULLOK */ { "nullok", _ALL_ON_^(01000), 0 },
+/* UNIX_RADIUS */ { "radius", _ALL_ON_^(0110000), 040000 },
+/* UNIX__SET_DB */ { NULL, _ALL_ON_, 0100000 },
+/* UNIX_DEBUG */ { "debug", _ALL_ON_, 0200000 },
+/* UNIX_NODELAY */ { "nodelay", _ALL_ON_, 0400000 },
+/* UNIX_UNIX */ { "unix", _ALL_ON_^(050000), 01000000 },
+/* UNIX_BIGCRYPT */ { "bigcrypt", _ALL_ON_^(020000), 02000000 },
+/* UNIX_LIKE_AUTH */ { "likeauth", _ALL_ON_, 04000000 },
+};
+
+#define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag)
+
+/* syslogging function for errors and other information */
+
+static void _log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM_pwdb", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* this is a front-end for module-application conversations */
+
+static int converse(pam_handle_t *pamh, int ctrl, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
+ if ( retval == PAM_SUCCESS ) {
+
+ retval = conv->conv(nargs, ( const struct pam_message ** ) message
+ , response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && on(UNIX_DEBUG,ctrl) ) {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ } else if (retval != PAM_CONV_AGAIN) {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ , pam_strerror(pamh, retval));
+ }
+
+ D(("ready to return from module conversation"));
+
+ return retval; /* propagate error status */
+}
+
+static int make_remark(pam_handle_t *pamh, unsigned int ctrl
+ , int type, const char *text)
+{
+ int retval=PAM_SUCCESS;
+
+ if ( off(UNIX__QUIET, ctrl) ) {
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+
+ resp = NULL;
+ retval = converse(pamh, ctrl, 1, pmsg, &resp);
+
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ }
+ return retval;
+}
+
+/*
+ * set the control flags for the UNIX module.
+ */
+
+static int set_ctrl(int flags, int argc, const char **argv)
+{
+ unsigned int ctrl;
+
+ D(("called."));
+
+ ctrl = UNIX_DEFAULTS; /* the default selection of options */
+
+ /* set some flags manually */
+
+ if ( getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK) ) {
+ set(UNIX__IAMROOT, ctrl);
+ }
+ if ( flags & PAM_UPDATE_AUTHTOK ) {
+ set(UNIX__UPDATE, ctrl);
+ }
+ if ( flags & PAM_PRELIM_CHECK ) {
+ set(UNIX__PRELIM, ctrl);
+ }
+ if ( flags & PAM_DISALLOW_NULL_AUTHTOK ) {
+ set(UNIX__NONULL, ctrl);
+ }
+ if ( flags & PAM_SILENT ) {
+ set(UNIX__QUIET, ctrl);
+ }
+
+ /* now parse the arguments to this module */
+
+ while (argc-- > 0) {
+ int j;
+
+ D(("pam_pwdb arg: %s",*argv));
+
+ for (j=0; j<UNIX_CTRLS_; ++j) {
+ if (unix_args[j].token
+ && ! strcmp(*argv, unix_args[j].token) ) {
+ break;
+ }
+ }
+
+ if ( j >= UNIX_CTRLS_ ) {
+ _log_err(LOG_ERR, "unrecognized option [%s]",*argv);
+ } else {
+ ctrl &= unix_args[j].mask; /* for turning things off */
+ ctrl |= unix_args[j].flag; /* for turning things on */
+ }
+
+ ++argv; /* step to next argument */
+ }
+
+ /* these are used for updating passwords in specific places */
+
+ if (on(UNIX_SHADOW,ctrl) || on(UNIX_RADIUS,ctrl) || on(UNIX_UNIX,ctrl)) {
+ set(UNIX__SET_DB, ctrl);
+ }
+
+ /* auditing is a more sensitive version of debug */
+
+ if ( on(UNIX_AUDIT,ctrl) ) {
+ set(UNIX_DEBUG, ctrl);
+ }
+
+ /* return the set of flags */
+
+ D(("done."));
+ return ctrl;
+}
+
+/* use this to free strings. ESPECIALLY password strings */
+
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ _pam_drop(xx);
+ return NULL;
+}
+
+static void _cleanup(pam_handle_t *pamh, void *x, int error_status)
+{
+ x = _pam_delete( (char *) x );
+}
+
+/* ************************************************************** *
+ * Useful non-trivial functions *
+ * ************************************************************** */
+
+#include "pam_unix_md.-c"
+
+/*
+ * the following is used to keep track of the number of times a user fails
+ * to authenticate themself.
+ */
+
+#define FAIL_PREFIX "-UN*X-FAIL-"
+#define UNIX_MAX_RETRIES 3
+
+struct _pam_failed_auth {
+ char *user; /* user that's failed to be authenticated */
+ char *name; /* attempt from user with name */
+ int id; /* uid of name'd user */
+ int count; /* number of failures so far */
+};
+
+#ifndef PAM_DATA_REPLACE
+#error "Need to get an updated libpam 0.52 or better"
+#endif
+
+static void _cleanup_failures(pam_handle_t *pamh, void *fl, int err)
+{
+ int quiet;
+ const char *service=NULL;
+ struct _pam_failed_auth *failure;
+
+ D(("called"));
+
+ quiet = err & PAM_DATA_SILENT; /* should we log something? */
+ err &= PAM_DATA_REPLACE; /* are we just replacing data? */
+ failure = (struct _pam_failed_auth *) fl;
+
+ if ( failure != NULL ) {
+
+ if ( !quiet && !err ) { /* under advisement from Sun,may go away */
+
+ /* log the number of authentication failures */
+ if ( failure->count > 1 ) {
+ (void) pam_get_item(pamh, PAM_SERVICE
+ , (const void **)&service);
+ _log_err(LOG_NOTICE
+ , "%d more authentication failure%s; %s(uid=%d) -> "
+ "%s for %s service"
+ , failure->count-1, failure->count==2 ? "":"s"
+ , failure->name
+ , failure->id
+ , failure->user
+ , service == NULL ? "**unknown**":service
+ );
+ if ( failure->count > UNIX_MAX_RETRIES ) {
+ _log_err(LOG_ALERT
+ , "service(%s) ignoring max retries; %d > %d"
+ , service == NULL ? "**unknown**":service
+ , failure->count
+ , UNIX_MAX_RETRIES );
+ }
+ }
+ }
+ failure->user = _pam_delete(failure->user); /* tidy up */
+ failure->name = _pam_delete(failure->name); /* tidy up */
+ free(failure);
+ }
+}
+
+/*
+ * verify the password of a user
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static int pwdb_run_helper_binary(pam_handle_t *pamh, const char *passwd)
+{
+ int retval, child, fds[2];
+
+ D(("called."));
+ /* create a pipe for the password */
+ if (pipe(fds) != 0) {
+ D(("could not make pipe"));
+ return PAM_AUTH_ERR;
+ }
+
+ /* fork */
+ child = fork();
+ if (child == 0) {
+ static char *args[] = { NULL, NULL };
+ static char *envp[] = { NULL };
+
+ /* XXX - should really tidy up PAM here too */
+ while (pwdb_end() == PWDB_SUCCESS);
+
+ /* reopen stdin as pipe */
+ close(fds[1]);
+ dup2(fds[0], STDIN_FILENO);
+
+ /* exec binary helper */
+ args[0] = x_strdup(CHKPWD_HELPER);
+ execve(CHKPWD_HELPER, args, envp);
+
+ /* should not get here: exit with error */
+ D(("helper binary is not available"));
+ exit(PWDB_SUCCESS+1);
+ } else if (child > 0) {
+ /* wait for child */
+ close(fds[0]);
+ if (passwd != NULL) { /* send the password to the child */
+ write(fds[1], passwd, strlen(passwd)+1);
+ passwd = NULL;
+ } else {
+ write(fds[1], "", 1); /* blank password */
+ }
+ close(fds[1]);
+ (void) waitpid(child, &retval, 0); /* wait for helper to complete */
+ retval = (retval == PWDB_SUCCESS) ? PAM_SUCCESS:PAM_AUTH_ERR;
+ } else {
+ D(("fork failed"));
+ retval = PAM_AUTH_ERR;
+ }
+
+ D(("returning %d", retval));
+ return retval;
+}
+
+static int _unix_verify_password(pam_handle_t *pamh, const char *name
+ , const char *p, unsigned int ctrl)
+{
+ const struct pwdb *pw=NULL;
+ const struct pwdb_entry *pwe=NULL;
+
+ const char *salt;
+ char *pp;
+ char *data_name;
+ int retval;
+ int verify_result;
+
+ D(("called"));
+
+#ifdef HAVE_PAM_FAIL_DELAY
+ if ( off(UNIX_NODELAY, ctrl) ) {
+ D(("setting delay"));
+ (void) pam_fail_delay(pamh, 1000000); /* 1 sec delay for on failure */
+ }
+#endif
+
+ /* locate the entry for this user */
+
+ D(("locating user's record"));
+ retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw);
+ if (retval == PWDB_PASS_PHRASE_REQD) {
+ /*
+ * give the password to the pwdb library. It may be needed to
+ * access the database
+ */
+
+ retval = pwdb_set_entry( pw, "pass_phrase", p, 1+strlen(p)
+ , NULL, NULL, 0);
+ if (retval != PWDB_SUCCESS) {
+ _log_err(LOG_ALERT, "find pass; %s", pwdb_strerror(retval));
+ (void) pwdb_delete(&pw);
+ p = NULL;
+ return PAM_CRED_INSUFFICIENT;
+ }
+
+ retval = pwdb_locate("user", pw->source, name, PWDB_ID_UNKNOWN, &pw);
+ }
+
+ if (retval != PWDB_SUCCESS) {
+ D(("user's record unavailable"));
+ if ( on(UNIX_AUDIT, ctrl) ) {
+ /* this might be a typo and the user has given a password
+ instead of a username. Careful with this. */
+ _log_err(LOG_ALERT, "check pass; user (%s) unknown", name);
+ } else {
+ _log_err(LOG_ALERT, "check pass; user unknown");
+ }
+ (void) pwdb_delete(&pw);
+ p = NULL;
+ return PAM_USER_UNKNOWN;
+ }
+
+ /*
+ * courtesy of PWDB the password for the user is stored in
+ * encrypted form in the "passwd" entry of pw.
+ */
+
+ retval = pwdb_get_entry(pw, "passwd", &pwe);
+ if (retval != PWDB_SUCCESS) {
+ if (geteuid()) {
+ /* we are not root perhaps this is the reason? Run helper */
+ D(("running helper binary"));
+ retval = pwdb_run_helper_binary(pamh, p);
+ } else {
+ retval = PAM_AUTHINFO_UNAVAIL;
+ _log_err(LOG_ALERT, "get passwd; %s", pwdb_strerror(retval));
+ }
+ (void) pwdb_delete(&pw);
+ p = NULL;
+ return retval;
+ }
+ salt = (const char *) pwe->value;
+
+ /*
+ * XXX: Cristian, the above is not the case for RADIUS(?) Some
+ * lines should be added for RADIUS to verify the password in
+ * clear text...
+ */
+
+ data_name = (char *) malloc(sizeof(FAIL_PREFIX)+strlen(name));
+ if ( data_name == NULL ) {
+ _log_err(LOG_CRIT, "no memory for data-name");
+ }
+ strcpy(data_name, FAIL_PREFIX);
+ strcpy(data_name + sizeof(FAIL_PREFIX)-1, name);
+
+ if ( !( (salt && *salt) || (p && *p) ) ) {
+
+ D(("two null passwords to compare"));
+
+ /* the stored password is NULL */
+ pp = NULL;
+ if ( off(UNIX__NONULL, ctrl ) ) { /* this means we've succeeded */
+ verify_result = PAM_SUCCESS;
+ } else {
+ verify_result = PAM_AUTH_ERR;
+ }
+
+ } else if ( !( salt && p ) ) {
+
+ D(("one of the two to compare are NULL"));
+
+ pp = NULL;
+ verify_result = PAM_AUTH_ERR;
+
+ } else {
+
+ pp = _pam_md(p, salt);
+
+ /* the moment of truth -- do we agree with the password? */
+ D(("comparing state of pp[%s] and salt[%s]", pp, salt));
+
+ if ( strcmp( pp, salt ) == 0 ) {
+ verify_result = PAM_SUCCESS;
+ } else {
+ _pam_delete(pp);
+ pp = _pam_md_compat(p, salt);
+ if ( strcmp( pp, salt ) == 0 ) {
+ verify_result = PAM_SUCCESS;
+ } else {
+ verify_result = PAM_AUTH_ERR;
+ }
+ }
+
+ p = NULL; /* no longer needed here */
+
+ }
+
+ if ( verify_result == PAM_SUCCESS ) {
+
+ retval = PAM_SUCCESS;
+ if (data_name) { /* reset failures */
+ pam_set_data(pamh, data_name, NULL, _cleanup_failures);
+ }
+
+ } else {
+
+ retval = PAM_AUTH_ERR;
+ if (data_name != NULL) {
+ struct _pam_failed_auth *new=NULL;
+ const struct _pam_failed_auth *old=NULL;
+
+ /* get a failure recorder */
+
+ new = (struct _pam_failed_auth *)
+ malloc(sizeof(struct _pam_failed_auth));
+
+ if (new != NULL) {
+
+ new->user = x_strdup(name);
+ new->id = getuid();
+ new->name = x_strdup(getlogin() ? getlogin():"" );
+
+ /* any previous failures for this user ? */
+ pam_get_data(pamh, data_name, (const void **)&old );
+
+ if (old != NULL) {
+ new->count = old->count +1;
+ if (new->count >= UNIX_MAX_RETRIES) {
+ retval = PAM_MAXTRIES;
+ }
+ } else {
+ const char *service=NULL;
+ (void) pam_get_item(pamh, PAM_SERVICE
+ , (const void **)&service);
+ _log_err(LOG_NOTICE
+ , "authentication failure; %s(uid=%d) -> "
+ "%s for %s service"
+ , new->name
+ , new->id
+ , new->user
+ , service == NULL ? "**unknown**":service
+ );
+ new->count = 1;
+ }
+
+ pam_set_data(pamh, data_name, new, _cleanup_failures);
+
+ } else {
+ _log_err(LOG_CRIT, "no memory for failure recorder");
+ }
+ }
+
+ }
+
+ (void) pwdb_entry_delete(&pwe);
+ (void) pwdb_delete(&pw);
+ salt = NULL;
+ _pam_delete(data_name);
+ _pam_delete(pp);
+
+ D(("done [%d].", retval));
+
+ return retval;
+}
+
+/*
+ * this function obtains the name of the current user and ensures
+ * that the PAM_USER item is set to this value
+ */
+
+static int _unix_get_user(pam_handle_t *pamh, unsigned int ctrl
+ , const char *prompt, const char **user)
+{
+ int retval;
+
+ D(("called"));
+
+ retval = pam_get_user(pamh, user, prompt);
+ if (retval != PAM_SUCCESS) {
+ D(("trouble reading username"));
+ return retval;
+ }
+
+ /*
+ * Various libraries at various times have had bugs related to
+ * '+' or '-' as the first character of a user name. Don't take
+ * any chances here. Require that the username starts with an
+ * alphanumeric character.
+ */
+
+ if (*user == NULL || !isalnum(**user)) {
+ D(("bad username"));
+ if (on(UNIX_DEBUG,ctrl)) {
+ _log_err(LOG_ERR, "bad username [%s]", *user);
+ }
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (retval == PAM_SUCCESS && on(UNIX_DEBUG,ctrl)) {
+ _log_err(LOG_DEBUG, "username [%s] obtained", *user);
+ }
+
+ return retval;
+}
+
+/*
+ * _unix_blankpasswd() is a quick check for a blank password
+ *
+ * returns TRUE if user does not have a password
+ * - to avoid prompting for one in such cases (CG)
+ */
+
+static int _unix_blankpasswd(unsigned int ctrl, const char *name)
+{
+ const struct pwdb *pw=NULL;
+ const struct pwdb_entry *pwe=NULL;
+ int retval;
+
+ D(("called"));
+
+ /*
+ * This function does not have to be too smart if something goes
+ * wrong, return FALSE and let this case to be treated somewhere
+ * else (CG)
+ */
+
+ if ( on(UNIX__NONULL, ctrl) )
+ return 0; /* will fail but don't let on yet */
+
+ /* find the user's database entry */
+
+ retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw);
+ if (retval != PWDB_SUCCESS || pw == NULL ) {
+
+ retval = 0;
+
+ } else {
+
+ /* Does this user have a password? */
+
+ retval = pwdb_get_entry(pw, "passwd", &pwe);
+ if ( retval != PWDB_SUCCESS || pwe == NULL )
+ retval = 0;
+ else if ( pwe->value == NULL || ((char *)pwe->value)[0] == '\0' )
+ retval = 1;
+ else
+ retval = 0;
+
+ }
+
+ /* tidy up */
+
+ if ( pw ) {
+ (void) pwdb_delete(&pw);
+ if ( pwe )
+ (void) pwdb_entry_delete(&pwe);
+ }
+
+ return retval;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+static int _unix_read_password( pam_handle_t *pamh
+ , unsigned int ctrl
+ , const char *comment
+ , const char *prompt1
+ , const char *prompt2
+ , const char *data_name
+ , const char **pass )
+{
+ int authtok_flag;
+ int retval;
+ const char *item;
+ char *token;
+
+ D(("called"));
+
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+
+ *pass = token = NULL;
+
+ /*
+ * which authentication token are we getting?
+ */
+
+ authtok_flag = on(UNIX__OLD_PASSWD,ctrl) ? PAM_OLDAUTHTOK:PAM_AUTHTOK ;
+
+ /*
+ * should we obtain the password from a PAM item ?
+ */
+
+ if ( on(UNIX_TRY_FIRST_PASS,ctrl) || on(UNIX_USE_FIRST_PASS,ctrl) ) {
+ retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
+ if (retval != PAM_SUCCESS ) {
+ /* very strange. */
+ _log_err(LOG_ALERT
+ , "pam_get_item returned error to unix-read-password"
+ );
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ return PAM_SUCCESS;
+ } else if (on(UNIX_USE_FIRST_PASS,ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on(UNIX_USE_AUTHTOK, ctrl)
+ && off(UNIX__OLD_PASSWD, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ {
+ struct pam_message msg[3],*pmsg[3];
+ struct pam_response *resp;
+ int i, replies;
+
+ /* prepare to converse */
+
+ if ( comment != NULL && off(UNIX__QUIET, ctrl) ) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = comment;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt1;
+ replies = 1;
+
+ if ( prompt2 != NULL ) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt2;
+ ++replies;
+ }
+
+ /* so call the conversation expecting i responses */
+ resp = NULL;
+ retval = converse(pamh, ctrl, i, pmsg, &resp);
+
+ if (resp != NULL) {
+
+ /* interpret the response */
+
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+
+ token = x_strdup(resp[i-replies].resp);
+ if (token != NULL) {
+ if (replies == 2) {
+
+ /* verify that password entered correctly */
+ if (!resp[i-1].resp
+ || strcmp(token,resp[i-1].resp)) {
+ token = _pam_delete(token); /* mistyped */
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ make_remark(pamh, ctrl
+ , PAM_ERROR_MSG, MISTYPED_PASS);
+ }
+ }
+
+ } else {
+ _log_err(LOG_NOTICE
+ , "could not recover authentication token");
+ }
+
+ }
+
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ * -- what is it for anyway? AGM
+ */
+
+ _pam_drop_reply(resp, i);
+
+ } else {
+ retval = (retval == PAM_SUCCESS)
+ ? PAM_AUTHTOK_RECOVER_ERR:retval ;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if ( on(UNIX_DEBUG,ctrl) )
+ _log_err(LOG_DEBUG,"unable to obtain a password");
+ return retval;
+ }
+
+ /* 'token' is the entered password */
+
+ if ( off(UNIX_NOT_SET_PASS, ctrl) ) {
+
+ /* we store this password as an item */
+
+ retval = pam_set_item(pamh, authtok_flag, token);
+ token = _pam_delete(token); /* clean it up */
+ if ( retval != PAM_SUCCESS
+ || (retval = pam_get_item(pamh, authtok_flag
+ , (const void **)&item))
+ != PAM_SUCCESS ) {
+
+ _log_err(LOG_CRIT, "error manipulating password");
+ return retval;
+
+ }
+
+ } else {
+ /*
+ * then store it as data specific to this module. pam_end()
+ * will arrange to clean it up.
+ */
+
+ retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_CRIT, "error manipulating password data [%s]"
+ , pam_strerror(pamh, retval) );
+ token = _pam_delete(token);
+ return retval;
+ }
+ item = token;
+ token = NULL; /* break link to password */
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+static int _pam_unix_approve_pass(pam_handle_t *pamh
+ , unsigned int ctrl
+ , const char *pass_old
+ , const char *pass_new)
+{
+ D(("&new=%p, &old=%p",pass_old,pass_new));
+ D(("new=[%s]",pass_new));
+ D(("old=[%s]",pass_old));
+
+ if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) {
+ if ( on(UNIX_DEBUG, ctrl) ) {
+ _log_err(LOG_DEBUG, "bad authentication token");
+ }
+ make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
+ "No password supplied":"Password unchanged" );
+ return PAM_AUTHTOK_ERR;
+ }
+
+ /*
+ * if one wanted to hardwire authentication token strength
+ * checking this would be the place - AGM
+ */
+
+ return PAM_SUCCESS;
+}
+
+/* ****************************************************************** *
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
diff --git a/modules/pam_radius/.cvsignore b/modules/pam_radius/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_radius/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_radius/Makefile b/modules/pam_radius/Makefile
new file mode 100644
index 00000000..a74b911f
--- /dev/null
+++ b/modules/pam_radius/Makefile
@@ -0,0 +1,99 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Cristian Gafton <gafton@redhat.com> 1996/09/10
+#
+# STATIC modules are not supported
+#
+
+TITLE=pam_radius
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/radius.conf
+export CONFILE
+
+ifeq ($(HAVE_PWDBLIB),yes)
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+#LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+#static/%.o : %.c
+# $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+#ifdef STATIC
+#LIBSTATIC = lib$(TITLE).o
+#endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+#ifdef STATIC
+# $(MKDIR) ./static
+#endif
+
+register:
+#ifdef STATIC
+# ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+#endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) -lpwdb
+endif
+
+#ifdef STATIC
+#$(LIBOBJS): $(LIBSRC)
+#
+#$(LIBSTATIC): $(LIBOBJS)
+# $(LD) -r -o $@ $(LIBOBJS) -lpwdb
+#endif
+
+install: all
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+else
+
+include ../dont_makefile
+
+endif
diff --git a/modules/pam_radius/README b/modules/pam_radius/README
new file mode 100644
index 00000000..253308fd
--- /dev/null
+++ b/modules/pam_radius/README
@@ -0,0 +1,58 @@
+
+pam_radius module:
+ RADIUS session module.
+
+WHAT IT DOES:
+ This module is intended to provide the session service for users
+autheticated with a RADIUS server. At the present stage, the only option
+supported is the use of the RADIUS server as an accounting server. There are
+few things which needs to be cleared out first in the PAM project until one
+will be able to use this module and expect it to magically start pppd in
+response to a RADIUS server command to use PPP for this user, or to initiate
+a telnet connection to another host, or to hang and call back the user using
+parameters provided in the RADIUS server response. Most of these things are
+better suited for the radius login application. I hope to make available
+Real Soon (tm) patches for the login apps to make it work this way.
+
+
+ARGUMENTS RECOGNIZED:
+ debug verbose logging
+
+MODULE SERVICES PROVIDED:
+ session _open_session and _close_session
+
+ When opening a session, this module sends an Accounting-Start
+message to the RADIUS server, which will log/update/whatever a database for
+this user. On close, an Accounting-Stop message is sent to the RADIUS
+server.
+
+This module have no other pre-requisites for making it work. One can install
+a RADIUS server just for fun and use it as a centralized accounting server and
+forget about wtmp/last/sac&comp :-)
+
+USAGE:
+ For the services you need this module (login for example) put
+ the following line in /etc/pam.conf as the last line for that
+ service (usually after the pam_unix session line):
+
+ login session required /lib/security/pam_radius.so
+
+ Replace "login" for each service you are using this module.
+
+ This module make extensive use of the API provided in libpwdb
+ 0.54preB or later. By default, it will read the radius server
+ configuration (hostname and secret) from /etc/raddb/server. This is
+ a default compiled into libpwdb, and curently there is no way to
+ modify this default without recompiling libpwdb. I am working on
+ extending the radius support from libpwdb to provide a possibility
+ to make this runtime-configurable.
+
+ Also please note that libpwdb will require also the RADIUS
+ dictionary to be present (/etc/raddb/dictionary).
+
+TODO:
+ The work is far from complete. Deal with "real" session things.
+
+AUTHOR:
+ Cristian Gafton <gafton@redhat.com>
+
diff --git a/modules/pam_radius/pam_radius.c b/modules/pam_radius/pam_radius.c
new file mode 100644
index 00000000..b412edf9
--- /dev/null
+++ b/modules/pam_radius/pam_radius.c
@@ -0,0 +1,193 @@
+/*
+ * pam_radius
+ * Process an user session according to a RADIUS server response
+ *
+ * 1.0 - initial release - Linux ONLY
+ * 1.1 - revised and reorganized for libpwdb 0.54preB or higher
+ * - removed the conf= parameter, since we use libpwdb exclusively now
+ *
+ * See end for Copyright information
+ */
+
+#if !(defined(linux))
+#error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
+#endif
+
+/* Module defines */
+#define BUFFER_SIZE 1024
+#define LONG_VAL_PTR(ptr) ((*(ptr)<<24)+(*((ptr)+1)<<16)+(*((ptr)+2)<<8)+(*((ptr)+3)))
+
+#define PAM_SM_SESSION
+
+#include "pam_radius.h"
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+static time_t session_time;
+
+/* we need to save these from open_session to close_session, since
+ * when close_session will be called we won't be root anymore and
+ * won't be able to access again the radius server configuration file
+ * -- cristiang */
+
+static RADIUS_SERVER rad_server;
+static char hostname[BUFFER_SIZE];
+static char secret[BUFFER_SIZE];
+
+/* logging */
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_radius", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x0001
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* now the session stuff */
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ char *user_name;
+ int ctrl;
+
+ ctrl = _pam_parse(argc, argv);
+ retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _pam_log(LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "starting RADIUS user session for '%s'",
+ user_name);
+
+ retval = get_server_entries(hostname, secret);
+ if ((retval != PWDB_RADIUS_SUCCESS) ||
+ !strlen(hostname) || !strlen(secret)) {
+ _pam_log(LOG_CRIT, "Could not determine the radius server to talk to");
+ return PAM_IGNORE;
+ }
+ session_time = time(NULL);
+ rad_server.hostname = hostname;
+ rad_server.secret = secret;
+ retval = radius_acct_start(rad_server, user_name);
+ if (retval != PWDB_RADIUS_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "ERROR communicating with the RADIUS server");
+ return PAM_IGNORE;
+ }
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ctrl;
+ char *user_name;
+ int retval;
+
+ ctrl = _pam_parse(argc, argv);
+ retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
+ if ( user_name == NULL || retval != PAM_SUCCESS ) {
+ _pam_log(LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR;
+ }
+
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "closing RADIUS user session for '%s'",
+ user_name);
+
+ if (!strlen(hostname) || !strlen(secret)) {
+ _pam_log(LOG_CRIT, "Could not determine the radius server to talk to");
+ return PAM_IGNORE;
+ }
+ retval = radius_acct_stop(rad_server, user_name,
+ time(NULL) - session_time);
+ if (retval != PWDB_RADIUS_SUCCESS) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG, "ERROR communicating with the RADIUS server");
+ return PAM_IGNORE;
+ }
+
+ return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_radius_modstruct = {
+ "pam_radius",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL
+};
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton, 1996, <gafton@redhat.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_radius/pam_radius.h b/modules/pam_radius/pam_radius.h
new file mode 100644
index 00000000..1f860eb5
--- /dev/null
+++ b/modules/pam_radius/pam_radius.h
@@ -0,0 +1,38 @@
+
+#ifndef PAM_RADIUS_H
+#define PAM_RADIUS_H
+
+#define _GNU_SOURCE
+#include <features.h>
+
+#include <stdio.h>
+
+#ifndef __USE_POSIX2
+#define __USE_POSIX2
+#endif /* __USE_POSIX2 */
+
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <utmp.h>
+#include <time.h>
+#include <netdb.h>
+
+#include <netinet/in.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpc/rpc.h>
+
+#include <pwdb/radius.h>
+#include <pwdb/pwdb_radius.h>
+
+/******************************************************************/
+
+#endif /* PAM_RADIUS_H */
diff --git a/modules/pam_rhosts/.cvsignore b/modules/pam_rhosts/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_rhosts/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_rhosts/Makefile b/modules/pam_rhosts/Makefile
new file mode 100644
index 00000000..431842b2
--- /dev/null
+++ b/modules/pam_rhosts/Makefile
@@ -0,0 +1,98 @@
+# This Makefile controls a build process of the pam_rhosts modules
+# for Linux-PAM. You should not modify this Makefile.
+
+LIBAUTHOBJ = pam_rhosts_auth.o
+LIBAUTHSRC = pam_rhosts_auth.c
+LIBSESSOBJ =
+LIBSESSSRC =
+LIBPASSWDSRC =
+LIBPASSWDOBJ =
+LIBOBJ = $(LIBAUTHOBJ) $(LIBSESSOBJ) $(LIBPASSWDOBJ)
+LIBSRC = $(LIBAUTHSRC) $(LIBSESSSRC) $(LIBPASSWDSRC)
+
+ifdef STATIC
+LIBSTATIC = libpam_rhosts.o
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+endif
+
+ifdef DYNAMIC
+LIBSESSSH =
+LIBAUTHSH = pam_rhosts_auth.so
+LIBPASSWDSH =
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBSHARED = $(LIBSESSSH) $(LIBAUTHSH) $(LIBPASSWDSH)
+endif
+
+ifeq ($(shell if [ -f /usr/include/fsuid.h ]; then echo yes ; fi),yes)
+ CFLAGS += -DNEED_FSUID_H
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; \
+ ./register_static pam_rhosts_auth pam_rhosts/libpam_rhosts.o )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+endif
+
+ifdef DYNAMIC
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+endif
+
+ifdef STATIC
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+
+endif
+
+#.c.o:
+# $(CC) -c $(CFLAGS) $<
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+# tidy up
+
+remove:
+ cd $(FAKEROOT)$(SECUREDIR) && rm -f $(LIBSHARED)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) a.out core *~
+
+extraclean:
+ rm -f *.a *.out *.o *.so *.bak dynamic/* static/*
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
diff --git a/modules/pam_rhosts/README b/modules/pam_rhosts/README
new file mode 100644
index 00000000..527dfd38
--- /dev/null
+++ b/modules/pam_rhosts/README
@@ -0,0 +1,57 @@
+arguments recognized:
+
+"no_hosts_equiv"
+"no_rhosts"
+"debug"
+"nowarn"
+"suppress"
+"promiscuous"
+
+.rhosts/hosts.equiv format:
+
+There are positive entries, when one is matched authentication
+succeeds and terminates. There are negative entries, when one is
+matched authentication fails and terminates. Thus order is
+significant.
+
+Entry hosts.equiv .rhosts
+<host> All users on <host> are ok Same username from <host> is ok
+<host> <user> <user> from <host> is ok ditto
+-<host> No users from <host> are ok ditto
+<host> -<user> <user> from <host> is not ok ditto
+
+<host> can be ip (IPv4) numbers.
+
+Netgroups may be used in either host or user fields, and then applies
+to all hosts, or users, in the netgroup. The syntax is
+
+ +@<ng>
+
+The entries
+
+ <host> +@<ng>
+ +@<ng> +@<ng>
+ +@<ng> <user>
+
+means exactly what you think it does. Negative entries are of the
+form
+
+ -@<ng>
+
+When the "promiscuous" option is given the special character + may be
+used as a wildcard in any field.
+
+ + Allow anyone from any host to connect. DANGEROUS.
+ + + Ditto.
+ + <user> Allow the user to connect from anywhere. DANGEROUS.
+ <host> + Allow any user from the host. Dangerous.
+
+These, perhaps more usefull, forms of the + form is also disallowed
+unless "promiscuous" is specified:
+
+ + -<user> Disallow the user from any host
+ + -@<ng> Disallow all members of the netgroup from any host
+
+When "promiscuous" is not specified a '+' is handled as a negative
+match.
+
diff --git a/modules/pam_rhosts/pam_rhosts_auth.c b/modules/pam_rhosts/pam_rhosts_auth.c
new file mode 100644
index 00000000..f9707cfc
--- /dev/null
+++ b/modules/pam_rhosts/pam_rhosts_auth.c
@@ -0,0 +1,829 @@
+/*----------------------------------------------------------------------
+ * Modified for Linux-PAM by Al Longyear <longyear@netcom.com> 96/5/5
+ * Modifications, Cristian Gafton 97/2/8
+ * Modifications, Peter Allgeyer 97/3
+ * Modifications (netgroups and fixes), Nicolai Langfeldt 97/3/21
+ * Security fix: 97/10/2 - gethostbyname called repeatedly without care
+ * Modification (added privategroup option) Andrew <morgan@transmeta.com>
+ *----------------------------------------------------------------------
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _BSD_SOURCE
+
+#define USER_RHOSTS_FILE "/.rhosts" /* prefixed by user's home dir */
+
+#ifdef linux
+#include <endian.h>
+#endif
+
+#ifdef NEED_FSUID_H
+#include <sys/fsuid.h>
+#endif /* NEED_FSUID_H */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h> /* This is supposed(?) to contain the following */
+int innetgr(const char *, const char *, const char *,const char *);
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#ifndef MAXDNAME
+#define MAXDNAME 256
+#endif
+
+#include <stdarg.h>
+#include <ctype.h>
+
+#include <net/if.h>
+#ifdef linux
+# include <linux/sockios.h>
+# ifndef __USE_MISC
+# define __USE_MISC
+# include <sys/fsuid.h>
+# endif /* __USE_MISC */
+#endif
+
+#include <pwd.h>
+#include <grp.h>
+#include <sys/file.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#ifndef _PATH_HEQUIV
+#define _PATH_HEQUIV "/etc/hosts.equiv"
+#endif /* _PATH_HEQUIV */
+
+#define PAM_SM_AUTH /* only defines this management group */
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+/* to the best of my knowledge, all modern UNIX boxes have 32 bit integers */
+#define U32 unsigned int
+
+
+/*
+ * Options for this module
+ */
+
+struct _options {
+ int opt_no_hosts_equiv;
+ int opt_hosts_equiv_rootok;
+ int opt_no_rhosts;
+ int opt_debug;
+ int opt_nowarn;
+ int opt_disallow_null_authtok;
+ int opt_silent;
+ int opt_promiscuous;
+ int opt_suppress;
+ int opt_private_group;
+ int opt_no_uid_check;
+ const char *superuser;
+ const char *last_error;
+};
+
+/* logging */
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_rhosts_auth", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static void set_option (struct _options *opts, const char *arg)
+{
+ if (strcmp(arg, "no_hosts_equiv") == 0) {
+ opts->opt_no_hosts_equiv = 1;
+ return;
+ }
+
+ if (strcmp(arg, "hosts_equiv_rootok") == 0) {
+ opts->opt_hosts_equiv_rootok = 1;
+ return;
+ }
+
+ if (strcmp(arg, "no_rhosts") == 0) {
+ opts->opt_no_rhosts = 1;
+ return;
+ }
+
+ if (strcmp(arg, "debug") == 0) {
+ D(("debugging enabled"));
+ opts->opt_debug = 1;
+ return;
+ }
+
+ if (strcmp(arg, "no_warn") == 0) {
+ opts->opt_nowarn = 1;
+ return;
+ }
+
+ if (strcmp(arg, "promiscuous") == 0) {
+ opts->opt_promiscuous = 1; /* used to permit '+' in ...hosts file */
+ return;
+ }
+
+ if (strcmp(arg, "suppress") == 0) {
+ opts->opt_suppress = 1; /* used to suppress failure warning message */
+ return;
+ }
+
+ if (strcmp(arg, "privategroup") == 0) {
+ opts->opt_private_group = 1; /* used to permit group write on .rhosts
+ file if group has same name as owner */
+ return;
+ }
+
+ if (strcmp(arg, "no_uid_check") == 0) {
+ opts->opt_no_uid_check = 1; /* NIS optimization */
+ return;
+ }
+
+ if (strcmp(arg, "superuser=") == 0) {
+ opts->superuser = arg+sizeof("superuser=")-1;
+ return;
+ }
+ /*
+ * All other options are ignored at the present time.
+ */
+ _pam_log(LOG_WARNING, "unrecognized option '%s'", arg);
+}
+
+static void set_parameters (struct _options *opts, int flags,
+ int argc, const char **argv)
+{
+ opts->opt_silent = flags & PAM_SILENT;
+ opts->opt_disallow_null_authtok = flags & PAM_DISALLOW_NULL_AUTHTOK;
+
+ while (argc-- > 0) {
+ set_option (opts, *argv);
+ ++argv;
+ }
+}
+
+/*
+ * Obtain the name of the remote host. Currently, this is simply by
+ * requesting the contents of the PAM_RHOST item.
+ */
+
+static int pam_get_rhost(pam_handle_t *pamh, const char **rhost
+ , const char *prompt)
+{
+ int retval;
+ const char *current;
+
+ retval = pam_get_item (pamh, PAM_RHOST, (const void **)&current);
+ if (retval != PAM_SUCCESS)
+ return retval;
+
+ if (current == NULL) {
+ return PAM_AUTH_ERR;
+ }
+ *rhost = current;
+
+ return retval; /* pass on any error from conversation */
+}
+
+/*
+ * Obtain the name of the remote user. Currently, this is simply by
+ * requesting the contents of the PAM_RUSER item.
+ */
+
+static int pam_get_ruser(pam_handle_t *pamh, const char **ruser
+ , const char *prompt)
+{
+ int retval;
+ const char *current;
+
+ retval = pam_get_item (pamh, PAM_RUSER, (const void **)&current);
+ if (retval != PAM_SUCCESS)
+ return retval;
+
+ if (current == NULL) {
+ return PAM_AUTH_ERR;
+ }
+ *ruser = current;
+
+ return retval; /* pass on any error from conversation */
+}
+
+/*
+ * Returns 1 if positive match, 0 if no match, -1 if negative match.
+ */
+
+static int
+__icheckhost (pam_handle_t *pamh, struct _options *opts, U32 raddr
+ , register char *lhost, const char *rhost)
+{
+ struct hostent *hp;
+ U32 laddr;
+ int negate=1; /* Multiply return with this to get -1 instead of 1 */
+ char **pp, *user;
+
+ /* Check nis netgroup. We assume that pam has done all needed
+ paranoia checking before we are handed the rhost */
+ if (strncmp("+@",lhost,2) == 0)
+ return(innetgr(&lhost[2],rhost,NULL,NULL));
+
+ if (strncmp("-@",lhost,2) == 0)
+ return(-innetgr(&lhost[2],rhost,NULL,NULL));
+
+ /* -host */
+ if (strncmp("-",lhost,1) == 0) {
+ negate=-1;
+ lhost++;
+ } else if (strcmp("+",lhost) == 0) {
+ (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
+ D(("user %s has a `+' host entry", user));
+ if (opts->opt_promiscuous)
+ return (1); /* asking for trouble, but ok.. */
+ /* If not promiscuous: handle as negative */
+ return (-1);
+ }
+
+ /* Try for raw ip address first. */
+ if (isdigit(*lhost) && (long)(laddr = inet_addr(lhost)) != -1)
+ return (negate*(! (raddr ^ laddr)));
+
+ /* Better be a hostname. */
+ hp = gethostbyname(lhost);
+ if (hp == NULL)
+ return (0);
+
+ /* Spin through ip addresses. */
+ for (pp = hp->h_addr_list; *pp; ++pp)
+ if (!memcmp (&raddr, *pp, sizeof (U32)))
+ return (negate);
+
+ /* No match. */
+ return (0);
+}
+
+/* Returns 1 on positive match, 0 on no match, -1 on negative match */
+
+static int __icheckuser(pam_handle_t *pamh, struct _options *opts
+ , const char *luser, const char *ruser
+ , const char *rhost)
+{
+ /*
+ luser is user entry from .rhosts/hosts.equiv file
+ ruser is user id on remote host
+ rhost is the remote host name
+ */
+ char *user;
+
+ /* [-+]@netgroup */
+ if (strncmp("+@",luser,2) == 0)
+ return (innetgr(&luser[2],NULL,ruser,NULL));
+
+ if (strncmp("-@",luser,2) == 0)
+ return (-innetgr(&luser[2],NULL,ruser,NULL));
+
+ /* -user */
+ if (strncmp("-",luser,1) == 0)
+ return(-(strcmp(&luser[1],ruser) == 0));
+
+ /* + */
+ if (strcmp("+",luser) == 0) {
+ (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
+ _pam_log(LOG_WARNING, "user %s has a `+' user entry", user);
+ if (opts->opt_promiscuous)
+ return(1);
+ /* If not promiscuous we handle it as a negative match */
+ return(-1);
+ }
+
+ /* simple string match */
+ return (strcmp(ruser, luser) == 0);
+}
+
+/*
+ * Returns 1 for blank lines (or only comment lines) and 0 otherwise
+ */
+
+static int __isempty(char *p)
+{
+ while (*p && isspace(*p)) {
+ ++p;
+ }
+
+ return (*p == '\0' || *p == '#') ? 1:0 ;
+}
+
+/*
+ * Returns 0 if positive match, 1 if _not_ ok.
+ */
+
+static int
+__ivaliduser (pam_handle_t *pamh, struct _options *opts,
+ FILE *hostf, U32 raddr,
+ const char *luser, const char *ruser, const char *rhost)
+{
+ register const char *user;
+ register char *p;
+ int hcheck, ucheck;
+ char buf[MAXHOSTNAMELEN + 128]; /* host + login */
+
+ buf[sizeof (buf)-1] = '\0'; /* terminate line */
+
+ while (fgets(buf, sizeof(buf), hostf) != NULL) { /* hostf file line */
+ p = buf; /* from beginning of file.. */
+
+ /* Skip empty or comment lines */
+ if (__isempty(p)) {
+ continue;
+ }
+
+ /* Skip lines that are too long. */
+ if (strchr(p, '\n') == NULL) {
+ int ch = getc(hostf);
+
+ while (ch != '\n' && ch != EOF)
+ ch = getc(hostf);
+ continue;
+ }
+
+ /*
+ * If there is a hostname at the start of the line. Set it to
+ * lower case. A leading ' ' or '\t' indicates no hostname
+ */
+
+ for (;*p && !isspace(*p); ++p) {
+ *p = tolower(*p);
+ }
+
+ /*
+ * next we want to find the permitted name for the remote user
+ */
+
+ if (*p == ' ' || *p == '\t') {
+
+ /* <nul> terminate hostname and skip spaces */
+ for (*p++='\0'; *p && isspace(*p); ++p);
+
+ user = p; /* this is the user's name */
+ while (*p && !isspace(*p))
+ ++p; /* find end of user's name */
+ } else
+ user = p;
+
+ *p = '\0'; /* <nul> terminate username (+host?) */
+
+ /* buf -> host(?) ; user -> username(?) */
+
+ /* First check host part */
+ hcheck=__icheckhost(pamh, opts, raddr, buf, rhost);
+
+ if (hcheck<0)
+ return(1);
+
+ if (hcheck) {
+ /* Then check user part */
+ if (! (*user))
+ user = luser;
+
+ ucheck=__icheckuser(pamh, opts, user, ruser, rhost);
+
+ /* Positive 'host user' match? */
+ if (ucheck>0)
+ return(0);
+
+ /* Negative 'host -user' match? */
+ if (ucheck<0)
+ return(1);
+
+ /* Neither, go on looking for match */
+ }
+ }
+
+ return (1);
+}
+
+/*
+ * New .rhosts strategy: We are passed an ip address. We spin through
+ * hosts.equiv and .rhosts looking for a match. When the .rhosts only
+ * has ip addresses, we don't have to trust a nameserver. When it
+ * contains hostnames, we spin through the list of addresses the nameserver
+ * gives us and look for a match.
+ *
+ * Returns 0 if ok, -1 if not ok.
+ */
+
+static int
+pam_iruserok(pam_handle_t *pamh,
+ struct _options *opts, U32 raddr, int superuser,
+ const char *ruser, const char *luser, const char *rhost)
+{
+ const char *cp;
+ struct stat sbuf;
+ struct passwd *pwd;
+ FILE *hostf;
+ uid_t uid;
+ int answer;
+ char pbuf[MAXPATHLEN]; /* potential buffer overrun */
+
+ if ((!superuser||opts->opt_hosts_equiv_rootok) && !opts->opt_no_hosts_equiv ) {
+
+ /* try to open system hosts.equiv file */
+ hostf = fopen (_PATH_HEQUIV, "r");
+ if (hostf) {
+ answer = __ivaliduser(pamh, opts, hostf, raddr, luser
+ , ruser, rhost);
+ (void) fclose(hostf);
+ if (answer == 0)
+ return 0; /* remote host is equivalent to localhost */
+ } /* else {
+ No hosts.equiv file on system.
+ } */
+ }
+
+ if ( opts->opt_no_rhosts )
+ return 1;
+
+ /*
+ * Identify user's local .rhosts file
+ */
+
+ pwd = getpwnam(luser);
+ if (pwd == NULL) {
+ /*
+ * luser is assumed to be valid because of an earlier check for uid = 0
+ * we don't log this error twice. However, this shouldn't happen !
+ * --cristiang
+ */
+ return(1);
+ }
+
+ /* check for buffer overrun */
+ if (strlen(pwd->pw_dir) + sizeof(USER_RHOSTS_FILE) + 2 >= MAXPATHLEN) {
+ if (opts->opt_debug)
+ _pam_log(LOG_DEBUG,"home directory for `%s' is too long", luser);
+ return 1; /* to dangerous to try */
+ }
+
+ (void) strcpy(pbuf, pwd->pw_dir);
+ (void) strcat(pbuf, USER_RHOSTS_FILE);
+
+ /*
+ * Change effective uid while _reading_ .rhosts. (not just
+ * opening). If root and reading an NFS mounted file system,
+ * can't read files that are 0600 as .rhosts files should be.
+ */
+
+ /* We are root, this will not fail */
+#ifdef linux
+ /* If we are on linux the better way is setfsuid */
+ uid = setfsuid(pwd->pw_uid);
+ hostf = fopen(pbuf, "r");
+#else
+ uid = geteuid();
+ (void) seteuid(pwd->pw_uid);
+ hostf = fopen(pbuf, "r");
+#endif
+
+ if (hostf == NULL) {
+ if (opts->opt_debug)
+ _pam_log(LOG_DEBUG,"Could not open %s file",pbuf);
+ answer = 1;
+ goto exit_function;
+ }
+
+ /*
+ * If not a regular file, or is owned by someone other than
+ * user or root or if writeable by anyone but the owner, quit.
+ */
+
+ cp = NULL;
+ if (lstat(pbuf, &sbuf) < 0 || !S_ISREG(sbuf.st_mode))
+ cp = ".rhosts not regular file";
+ else if (fstat(fileno(hostf), &sbuf) < 0)
+ cp = ".rhosts fstat failed";
+ else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
+ cp = "bad .rhosts owner";
+ else if (sbuf.st_mode & S_IWOTH)
+ cp = ".rhosts writable by other!";
+ else if (sbuf.st_mode & S_IWGRP) {
+
+ /* private group caveat */
+ if (opts->opt_private_group) {
+ struct group *grp = getgrgid(sbuf.st_gid);
+
+ if (NULL == grp || NULL == grp->gr_name
+ || strcmp(luser,grp->gr_name)) {
+ cp = ".rhosts writable by public group";
+ } else if (grp->gr_mem) {
+ int gcount;
+
+ /* require at most one member (luser) of this group */
+ for (gcount=0; grp->gr_mem[gcount]; ++gcount) {
+ if (strcmp(grp->gr_mem[gcount], luser)) {
+ gcount = -1;
+ break;
+ }
+ }
+ if (gcount < 0) {
+ cp = ".rhosts writable by other members of group";
+ }
+ }
+ } else {
+ cp = ".rhosts writable by group";
+ }
+
+ } /* It is _NOT_ safe to append an else here... Do so prior to
+ * S_IWGRP check */
+
+ /* If there were any problems, quit. */
+ if (cp) {
+ opts->last_error = cp;
+ answer = 1;
+ goto exit_function;
+ }
+
+ answer = __ivaliduser (pamh, opts, hostf, raddr, luser, ruser, rhost);
+
+exit_function:
+ /*
+ * Go here to exit after the fsuid/euid has been adjusted so that
+ * they are reset before we exit.
+ */
+
+#ifdef linux
+ setfsuid(uid);
+#else
+ (void)seteuid(uid);
+#endif
+
+ if (hostf != NULL)
+ (void) fclose(hostf);
+
+ return answer;
+}
+
+static int
+pam_ruserok (pam_handle_t *pamh,
+ struct _options *opts, const char *rhost, int superuser,
+ const char *ruser, const char *luser)
+{
+ struct hostent *hp;
+ int answer = 1; /* default to failure */
+ U32 *addrs;
+ int n, i;
+
+ opts->last_error = (char *) 0;
+ hp = gethostbyname(rhost); /* identify host */
+
+ if (hp != NULL) {
+ /* First of all check the address length */
+ if (hp->h_length != 4) {
+ _pam_log(LOG_ALERT, "pam_rhosts module can't work with not IPv4 "
+ "addresses");
+ return 1; /* not allowed */
+ }
+
+ /* loop though address list */
+ for (n = 0; hp->h_addr_list[n]; n++);
+ D(("rhosts: %d addresses", n));
+
+ if (n) {
+ addrs = calloc (n, hp->h_length);
+ for (i = 0; i < n; i++)
+ memcpy (addrs+i, hp->h_addr_list[i], hp->h_length);
+
+ for (i = 0; i < n && answer; i++) {
+ D(("rhosts: address %d is %04x", i, addrs[i]));
+ answer = pam_iruserok(pamh, opts, addrs[i], superuser,
+ ruser, luser, rhost);
+ /* answer == 0 means success */
+ }
+
+ free (addrs);
+ }
+ }
+
+ return answer;
+}
+
+/*
+ * Internal function to do authentication
+ */
+
+static int _pam_auth_rhosts (pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv)
+{
+ int retval;
+ const char *luser;
+ const char *ruser,*rhost;
+ struct _options opts;
+ int as_root = 0;
+ /*
+ * Look at the options and set the flags accordingly.
+ */
+ memset (&opts, 0, sizeof (opts));
+ set_parameters (&opts, flags, argc, argv);
+ /*
+ * Obtain the parameters for the various items
+ */
+ for (;;) { /* abuse loop to avoid goto */
+
+ /* get the remotehost */
+ retval = pam_get_rhost(pamh, &rhost, NULL);
+ (void) pam_set_item(pamh, PAM_RHOST, rhost);
+ if (retval != PAM_SUCCESS) {
+ if (opts.opt_debug) {
+ _pam_log(LOG_DEBUG, "could not get the remote host name");
+ }
+ break;
+ }
+
+ /* get the remote user */
+ retval = pam_get_ruser(pamh, &ruser, NULL);
+ (void) pam_set_item(pamh, PAM_RUSER, ruser);
+ if (retval != PAM_SUCCESS) {
+ if (opts.opt_debug)
+ _pam_log(LOG_DEBUG, "could not get the remote username");
+ break;
+ }
+
+ /* get the local user */
+ retval = pam_get_user(pamh, &luser, NULL);
+
+ if (retval != PAM_SUCCESS) {
+ if (opts.opt_debug)
+ _pam_log(LOG_DEBUG, "could not determine name of local user");
+ break;
+ }
+
+ if (opts.superuser && !strcmp(opts.superuser, luser)) {
+ as_root = 1;
+ }
+
+ /* check if the luser uid == 0... --cristiang */
+ if (! opts.opt_no_uid_check) {
+ struct passwd *luser_pwd;
+
+ luser_pwd = getpwnam(luser);
+ if (luser_pwd == NULL) {
+ if (opts.opt_debug)
+ _pam_log(LOG_DEBUG, "user '%s' unknown to this system",
+ luser);
+ retval = PAM_AUTH_ERR;
+ break;
+ }
+ if (luser_pwd->pw_uid == 0)
+ as_root = 1;
+ luser_pwd = NULL; /* forget */
+ }
+/*
+ * Validate the account information.
+ */
+ if (pam_ruserok (pamh, &opts, rhost, as_root, ruser, luser) != 0) {
+ if ( !opts.opt_suppress ) {
+ _pam_log(LOG_WARNING, "denied to %s@%s as %s: %s",
+ ruser, rhost, luser, (opts.last_error==NULL) ?
+ "access not allowed":opts.last_error);
+ }
+ retval = PAM_AUTH_ERR;
+ } else {
+ _pam_log(LOG_NOTICE, "allowed to %s@%s as %s",
+ ruser, rhost, luser);
+ }
+ break;
+ }
+
+ return retval;
+}
+
+/* --- authentication management functions --- */
+
+PAM_EXTERN
+int pam_sm_authenticate (pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv)
+{
+ int retval;
+
+ if (sizeof(U32) != 4) {
+ _pam_log (LOG_ALERT, "pam_rhosts module can\'t work on this hardware "
+ "(yet)");
+ return PAM_AUTH_ERR;
+ }
+ sethostent(1);
+ retval = _pam_auth_rhosts (pamh, flags, argc, argv);
+ endhostent();
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,
+ const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/* end of module definition */
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_rhosts_auth_modstruct = {
+ "pam_rhosts_auth",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/*
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:56 agmorgan
+ * Initial revision
+ *
+ * Revision 1.4 1999/11/08 05:46:58 morgan
+ * fixes for .71
+ *
+ * Revision 1.3 1999/10/09 05:12:49 morgan
+ * added hosts_equiv_rootok support
+ *
+ * Revision 1.2 1998/12/14 05:47:58 morgan
+ * added a couple of options: specify the name of root, and don't do the
+ * user-known check.
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.12 1997/09/27 14:34:01 morgan
+ * fixed comment and renamed iruserok to pam_iruserok.
+ *
+ * Revision 1.11 1997/04/05 06:26:39 morgan
+ * fairly major fixes and enhancements (see CHANGELOG for 0.57 release)
+ *
+ * Revision 1.10 1997/02/09 02:09:30 morgan
+ * - implementation of 'debug' argument (Cristian Gafton)
+ * - we check for uid=0 accounts instead of hardcoded 'root' (Cristian Gafton)
+ *
+ * Revision 1.9 1996/12/01 03:09:47 morgan
+ * *** empty log message ***
+ *
+ * Revision 1.8 1996/11/12 06:08:59 morgan
+ * Oliver Crow's "rootok" patch plus a little clean up of set_option
+ * (AGM)
+ *
+ * Revision 1.7 1996/11/10 20:15:56 morgan
+ * cross platform support
+ *
+ * Revision 1.6 1996/08/09 05:46:29 morgan
+ * removed code for manually setting the remote username etc..
+ *
+ */
diff --git a/modules/pam_rootok/.cvsignore b/modules/pam_rootok/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_rootok/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_rootok/Makefile b/modules/pam_rootok/Makefile
new file mode 100644
index 00000000..f3f2e53d
--- /dev/null
+++ b/modules/pam_rootok/Makefile
@@ -0,0 +1,117 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:56 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.7 1997/04/05 06:25:20 morgan
+# fakeroot
+#
+# Revision 1.6 1997/02/15 19:15:50 morgan
+# fixed email
+#
+# Revision 1.5 1996/11/10 20:16:10 morgan
+# cross platform support
+#
+# Revision 1.4 1996/09/05 06:29:36 morgan
+# ld --> gcc
+#
+# Revision 1.3 1996/05/26 15:47:46 morgan
+# make dynamic/static dirs!
+#
+# Revision 1.2 1996/05/26 04:04:53 morgan
+# automated static support
+#
+# Revision 1.1 1996/05/05 17:14:15 morgan
+# Initial revision
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/5/5
+#
+
+TITLE=pam_rootok
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_rootok/README b/modules/pam_rootok/README
new file mode 100644
index 00000000..7120b164
--- /dev/null
+++ b/modules/pam_rootok/README
@@ -0,0 +1,18 @@
+# $Id$
+#
+
+this module is an authentication module that performs one task: if the
+id of the user is '0' then it returns 'PAM_SUCCESS' with the
+'sufficient' /etc/pam.conf control flag it can be used to allow
+password free access to some service for 'root'
+
+Recognized arguments:
+
+ debug write a message to syslog indicating success or
+ failure.
+
+module services provided:
+
+ auth _authetication and _setcred (blank)
+
+Andrew Morgan
diff --git a/modules/pam_rootok/pam_rootok.c b/modules/pam_rootok/pam_rootok.c
new file mode 100644
index 00000000..a275e6eb
--- /dev/null
+++ b/modules/pam_rootok/pam_rootok.c
@@ -0,0 +1,109 @@
+/* pam_rootok module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-rootok", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 01
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int ctrl;
+ int retval = PAM_AUTH_ERR;
+
+ ctrl = _pam_parse(argc, argv);
+ if (getuid() == 0)
+ retval = PAM_SUCCESS;
+
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_DEBUG, "authetication %s"
+ , retval==PAM_SUCCESS ? "succeeded":"failed" );
+ }
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_rootok_modstruct = {
+ "pam_rootok",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_securetty/.cvsignore b/modules/pam_securetty/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_securetty/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_securetty/Makefile b/modules/pam_securetty/Makefile
new file mode 100644
index 00000000..d8a09ea1
--- /dev/null
+++ b/modules/pam_securetty/Makefile
@@ -0,0 +1,83 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+
+TITLE=pam_securetty
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_securetty/README b/modules/pam_securetty/README
new file mode 100644
index 00000000..1df095c9
--- /dev/null
+++ b/modules/pam_securetty/README
@@ -0,0 +1,9 @@
+pam_securetty:
+ Allows root logins only if the user is logging in on a
+ "secure" tty, as defined by the listing in /etc/securetty
+
+ Also checks to make sure that /etc/securetty is a plain
+ file and not world writable.
+
+ - Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ July 25, 1996.
diff --git a/modules/pam_securetty/pam_securetty.c b/modules/pam_securetty/pam_securetty.c
new file mode 100644
index 00000000..9e6121e8
--- /dev/null
+++ b/modules/pam_securetty/pam_securetty.c
@@ -0,0 +1,191 @@
+/* pam_securetty module */
+
+#define SECURETTY_FILE "/etc/securetty"
+#define TTY_PREFIX "/dev/"
+
+/*
+ * by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ * July 25, 1996.
+ * This code shamelessly ripped from the pam_rootok module.
+ * Slight modifications AGM. 1996/12/3
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <pwd.h>
+#include <string.h>
+
+#define PAM_SM_AUTH
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-securetty", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x0001
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval = PAM_AUTH_ERR;
+ const char *username;
+ char *uttyname;
+ char ttyfileline[256];
+ struct stat ttyfileinfo;
+ struct passwd *user_pwd;
+ FILE *ttyfile;
+ int ctrl;
+
+ /* parse the arguments */
+ ctrl = _pam_parse(argc, argv);
+
+ retval = pam_get_user(pamh, &username, NULL);
+ if (retval != PAM_SUCCESS || username == NULL) {
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_WARNING, "cannot determine username");
+ }
+ return (retval == PAM_CONV_AGAIN
+ ? PAM_INCOMPLETE:PAM_SERVICE_ERR);
+ }
+
+ retval = pam_get_item(pamh, PAM_TTY, (const void **)&uttyname);
+ if (retval != PAM_SUCCESS || uttyname == NULL) {
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_WARNING, "cannot determine user's tty");
+ }
+ return PAM_SERVICE_ERR;
+ }
+
+ /* The PAM_TTY item may be prefixed with "/dev/" - skip that */
+ if (strncmp(TTY_PREFIX, uttyname, sizeof(TTY_PREFIX)-1) == 0)
+ uttyname += sizeof(TTY_PREFIX)-1;
+
+ user_pwd = getpwnam(username);
+ if (user_pwd == NULL) {
+ return PAM_IGNORE;
+ } else if (user_pwd->pw_uid != 0) { /* If the user is not root,
+ securetty's does not apply
+ to them */
+ return PAM_SUCCESS;
+ }
+
+ if (stat(SECURETTY_FILE, &ttyfileinfo)) {
+ _pam_log(LOG_NOTICE, "Couldn't open " SECURETTY_FILE);
+ return PAM_SUCCESS; /* for compatibility with old securetty handling,
+ this needs to succeed. But we still log the
+ error. */
+ }
+
+ if ((ttyfileinfo.st_mode & S_IWOTH)
+ || !S_ISREG(ttyfileinfo.st_mode)) {
+ /* If the file is world writable or is not a
+ normal file, return error */
+ _pam_log(LOG_ERR, SECURETTY_FILE
+ " is either world writable or not a normal file");
+ return PAM_AUTH_ERR;
+ }
+
+ ttyfile = fopen(SECURETTY_FILE,"r");
+ if(ttyfile == NULL) { /* Check that we opened it successfully */
+ _pam_log(LOG_ERR,
+ "Error opening " SECURETTY_FILE);
+ return PAM_SERVICE_ERR;
+ }
+ /* There should be no more errors from here on */
+ retval=PAM_AUTH_ERR;
+ /* This loop assumes that PAM_SUCCESS == 0
+ and PAM_AUTH_ERR != 0 */
+ while((fgets(ttyfileline,sizeof(ttyfileline)-1, ttyfile) != NULL)
+ && retval) {
+ if(ttyfileline[strlen(ttyfileline) - 1] == '\n')
+ ttyfileline[strlen(ttyfileline) - 1] = '\0';
+ retval = strcmp(ttyfileline,uttyname);
+ }
+ fclose(ttyfile);
+ if(retval) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_WARNING, "access denied: tty '%s' is not secure !",
+ uttyname);
+ retval = PAM_AUTH_ERR;
+ }
+ if ((retval == PAM_SUCCESS) && (ctrl & PAM_DEBUG_ARG))
+ _pam_log(LOG_DEBUG, "access allowed for '%s' on '%s'",
+ username, uttyname);
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_securetty_modstruct = {
+ "pam_securetty",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_shells/.cvsignore b/modules/pam_shells/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_shells/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_shells/Makefile b/modules/pam_shells/Makefile
new file mode 100644
index 00000000..121b19a0
--- /dev/null
+++ b/modules/pam_shells/Makefile
@@ -0,0 +1,84 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+
+TITLE=pam_shells
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_shells/README b/modules/pam_shells/README
new file mode 100644
index 00000000..cbd5bfb5
--- /dev/null
+++ b/modules/pam_shells/README
@@ -0,0 +1,10 @@
+pam_shells:
+ Authentication is granted if the users shell is listed in
+ /etc/shells. If no shell is in /etc/passwd (empty), the
+ /bin/sh is used (following ftpd's convention).
+
+ Also checks to make sure that /etc/shells is a plain
+ file and not world writable.
+
+ - Erik Troan <ewt@redhat.com>, Red Hat Software.
+ August 5, 1996.
diff --git a/modules/pam_shells/pam_shells.c b/modules/pam_shells/pam_shells.c
new file mode 100644
index 00000000..f460ee5f
--- /dev/null
+++ b/modules/pam_shells/pam_shells.c
@@ -0,0 +1,133 @@
+/* pam_shells module */
+
+#define SHELL_FILE "/etc/shells"
+
+/*
+ * by Erik Troan <ewt@redhat.com>, Red Hat Software.
+ * August 5, 1996.
+ * This code shamelessly ripped from the pam_securetty module.
+ */
+
+#define _BSD_SOURCE
+
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <unistd.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-shells", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int retval = PAM_AUTH_ERR;
+ const char *userName;
+ char *userShell;
+ char shellFileLine[256];
+ struct stat sb;
+ struct passwd * pw;
+ FILE * shellFile;
+
+ retval = pam_get_user(pamh,&userName,NULL);
+ if(retval != PAM_SUCCESS)
+ return PAM_SERVICE_ERR;
+
+ if(!userName || (strlen(userName) <= 0)) {
+ /* Don't let them use a NULL username... */
+ pam_get_user(pamh,&userName,NULL);
+ if (retval != PAM_SUCCESS)
+ return PAM_SERVICE_ERR;
+ }
+
+ pw = getpwnam(userName);
+ if (!pw)
+ return PAM_AUTH_ERR; /* user doesn't exist */
+ userShell = pw->pw_shell;
+
+ if(stat(SHELL_FILE,&sb)) {
+ _pam_log(LOG_ERR, SHELL_FILE, " cannot be stat'd (it probably does "
+ "not exist)");
+ return PAM_AUTH_ERR; /* must have /etc/shells */
+ }
+
+ if((sb.st_mode & S_IWOTH) || !S_ISREG(sb.st_mode)) {
+ _pam_log(LOG_ERR,
+ SHELL_FILE " is either world writable or not a normal file");
+ return PAM_AUTH_ERR;
+ }
+
+ shellFile = fopen(SHELL_FILE,"r");
+ if(shellFile == NULL) { /* Check that we opened it successfully */
+ _pam_log(LOG_ERR,
+ "Error opening " SHELL_FILE);
+ return PAM_SERVICE_ERR;
+ }
+ /* There should be no more errors from here on */
+ retval=PAM_AUTH_ERR;
+ /* This loop assumes that PAM_SUCCESS == 0
+ and PAM_AUTH_ERR != 0 */
+ while((fgets(shellFileLine,255,shellFile) != NULL)
+ && retval) {
+ if (shellFileLine[strlen(shellFileLine) - 1] == '\n')
+ shellFileLine[strlen(shellFileLine) - 1] = '\0';
+ retval = strcmp(shellFileLine, userShell);
+ }
+ fclose(shellFile);
+ if(retval)
+ retval = PAM_AUTH_ERR;
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_shells_modstruct = {
+ "pam_shells",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/* end of module definition */
diff --git a/modules/pam_stress/.cvsignore b/modules/pam_stress/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_stress/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_stress/Makefile b/modules/pam_stress/Makefile
new file mode 100644
index 00000000..1bcfa502
--- /dev/null
+++ b/modules/pam_stress/Makefile
@@ -0,0 +1,115 @@
+#
+# $Id$
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/3/11
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:11:57 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.7 1997/04/05 06:23:08 morgan
+# fakeroot
+#
+# Revision 1.6 1997/02/15 19:05:55 morgan
+# fixed email
+#
+# Revision 1.5 1996/11/10 20:17:55 morgan
+# cross platform support
+#
+# Revision 1.4 1996/09/05 06:31:09 morgan
+# ld --> gcc
+#
+# Revision 1.3 1996/05/26 15:50:43 morgan
+# make dynamic and static dirs
+#
+# Revision 1.2 1996/05/26 04:11:56 morgan
+# automated static support
+#
+#
+#
+
+TITLE=pam_stress
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_stress/README b/modules/pam_stress/README
new file mode 100644
index 00000000..74a297b2
--- /dev/null
+++ b/modules/pam_stress/README
@@ -0,0 +1,66 @@
+#
+# $Id$
+#
+# This describes the behavior of this module with respect to the
+# /etc/pam.conf file.
+#
+# writen by Andrew Morgan <morgan@parc.power.net>
+#
+
+This module recognizes the following arguments.
+
+debug put lots of information in syslog.
+ *NOTE* this option writes passwords to syslog, so
+ don't use anything sensitive when testing.
+
+no_warn don't give warnings about things (otherwise warnings are issued
+ via the conversation function)
+
+use_first_pass don't prompt for a password, for pam_sm_authentication
+ function just use item PAM_AUTHTOK.
+
+try_first_pass don't prompt for a password unless there has been no
+ previous authentication token (item PAM_AUTHTOK is NULL)
+
+rootok This is intended for the pam_sm_chauthtok function and
+ it instructs this function to permit root to change
+ the user's password without entering the old password.
+
+The following arguments are acted on by the module. They are intended
+to make the module give the impression of failing as a fully
+functioning module might.
+
+expired an argument intended for the account and chauthtok module
+ parts. It instructs the module to act as if the user's
+ password has expired
+
+fail_1 this instructs the module to make its first function fail.
+
+fail_2 this instructs the module to make its second function (if there
+ is one) fail.
+
+ The function break up is indicated in the Module
+ Developers' Guide. Listed here it is:
+
+ service function 1 function 2
+ ------- ---------- ----------
+ auth pam_sm_authenticate pam_sm_setcred
+ password pam_sm_chauthtok
+ session pam_sm_open_session pam_sm_close_session
+ account pam_sm_acct_mgmt
+
+prelim for pam_sm_chauthtok, means fail on PAM_PRELIM_CHECK.
+
+required for pam_sm_chauthtok, means fail if the user hasn't already
+ been authenticated by this module. (See stress_new_pwd data
+ item below.)
+
+#
+# data strings that this module uses are the following:
+#
+
+data name value(s) Comments
+--------- -------- --------
+stress_new_pwd yes tells pam_sm_chauthtok that
+ pam_sm_acct_mgmt says we need a new
+ password
diff --git a/modules/pam_stress/pam_stress.c b/modules/pam_stress/pam_stress.c
new file mode 100644
index 00000000..e5813579
--- /dev/null
+++ b/modules/pam_stress/pam_stress.c
@@ -0,0 +1,565 @@
+/* pam_stress module */
+
+/* $Id$
+ *
+ * created by Andrew Morgan <morgan@linux.kernel.org> 1996/3/12
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_SESSION
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+static char *_strdup(const char *x)
+{
+ char *new;
+ new = malloc(strlen(x)+1);
+ strcpy(new,x);
+ return new;
+}
+
+/* log errors */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-stress", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* ---------- */
+
+/* an internal function to turn all possible test arguments into bits
+ of a ctrl number */
+
+/* generic options */
+
+#define PAM_ST_DEBUG 01
+#define PAM_ST_NO_WARN 02
+#define PAM_ST_USE_PASS1 04
+#define PAM_ST_TRY_PASS1 010
+#define PAM_ST_ROOTOK 020
+
+/* simulation options */
+
+#define PAM_ST_EXPIRED 040
+#define PAM_ST_FAIL_1 0100
+#define PAM_ST_FAIL_2 0200
+#define PAM_ST_PRELIM 0400
+#define PAM_ST_REQUIRE_PWD 01000
+
+/* some syslogging */
+
+static void _pam_report(int ctrl, const char *name, int flags,
+ int argc, const char **argv)
+{
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG, "CALLED: %s", name);
+ _pam_log(LOG_DEBUG, "FLAGS : 0%o%s", flags,
+ (flags & PAM_SILENT) ? " (silent)":"");
+ _pam_log(LOG_DEBUG, "CTRL = 0%o",ctrl);
+ _pam_log(LOG_DEBUG, "ARGV :");
+ while (argc--) {
+ _pam_log(LOG_DEBUG, " \"%s\"", *argv++);
+ }
+ }
+}
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_ST_DEBUG;
+ else if (!strcmp(*argv,"no_warn"))
+ ctrl |= PAM_ST_NO_WARN;
+ else if (!strcmp(*argv,"use_first_pass"))
+ ctrl |= PAM_ST_USE_PASS1;
+ else if (!strcmp(*argv,"try_first_pass"))
+ ctrl |= PAM_ST_TRY_PASS1;
+ else if (!strcmp(*argv,"rootok"))
+ ctrl |= PAM_ST_ROOTOK;
+
+ /* simulation options */
+
+ else if (!strcmp(*argv,"expired")) /* signal password needs
+ renewal */
+ ctrl |= PAM_ST_EXPIRED;
+ else if (!strcmp(*argv,"fail_1")) /* instruct fn 1 to fail */
+ ctrl |= PAM_ST_FAIL_1;
+ else if (!strcmp(*argv,"fail_2")) /* instruct fn 2 to fail */
+ ctrl |= PAM_ST_FAIL_2;
+ else if (!strcmp(*argv,"prelim")) /* instruct pam_sm_setcred
+ to fail on first call */
+ ctrl |= PAM_ST_PRELIM;
+ else if (!strcmp(*argv,"required")) /* module is fussy about the
+ user being authenticated */
+ ctrl |= PAM_ST_REQUIRE_PWD;
+
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+static int converse(pam_handle_t *pamh, int nargs
+ , struct pam_message **message
+ , struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ if ((retval = pam_get_item(pamh,PAM_CONV,(const void **)&conv))
+ == PAM_SUCCESS) {
+ retval = conv->conv(nargs, (const struct pam_message **) message
+ , response, conv->appdata_ptr);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR,"(pam_stress) converse returned %d",retval);
+ _pam_log(LOG_ERR,"that is: %s",pam_strerror(pamh, retval));
+ }
+ } else {
+ _pam_log(LOG_ERR,"(pam_stress) converse failed to get pam_conv");
+ }
+
+ return retval;
+}
+
+/* authentication management functions */
+
+static int stress_get_password(pam_handle_t *pamh, int flags
+ , int ctrl, char **password)
+{
+ char *pass;
+
+ if ( (ctrl & (PAM_ST_TRY_PASS1|PAM_ST_USE_PASS1))
+ && (pam_get_item(pamh,PAM_AUTHTOK,(const void **)&pass)
+ == PAM_SUCCESS)
+ && (pass != NULL) ) {
+ pass = _strdup(pass);
+ } else if ((ctrl & PAM_ST_USE_PASS1)) {
+ _pam_log(LOG_WARNING, "pam_stress: no forwarded password");
+ return PAM_PERM_DENIED;
+ } else { /* we will have to get one */
+ struct pam_message msg[1],*pmsg[1];
+ struct pam_response *resp;
+ int retval;
+
+ /* set up conversation call */
+
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[0].msg = "STRESS Password: ";
+ resp = NULL;
+
+ if ((retval = converse(pamh,1,pmsg,&resp)) != PAM_SUCCESS) {
+ return retval;
+ }
+
+ if (resp) {
+ if ((resp[0].resp == NULL) && (ctrl & PAM_ST_DEBUG)) {
+ _pam_log(LOG_DEBUG,
+ "pam_sm_authenticate: NULL authtok given");
+ }
+ if ((flags & PAM_DISALLOW_NULL_AUTHTOK)
+ && resp[0].resp == NULL) {
+ free(resp);
+ return PAM_AUTH_ERR;
+ }
+
+ pass = resp[0].resp; /* remember this! */
+
+ resp[0].resp = NULL;
+ } else if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG,"pam_sm_authenticate: no error reported");
+ _pam_log(LOG_DEBUG,"getting password, but NULL returned!?");
+ return PAM_CONV_ERR;
+ }
+ free(resp);
+ }
+
+ *password = pass; /* this *MUST* be free()'d by this module */
+
+ return PAM_SUCCESS;
+}
+
+/* function to clean up data items */
+
+static void wipe_up(pam_handle_t *pamh, void *data, int error)
+{
+ free(data);
+}
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ int retval=PAM_SUCCESS;
+ char *pass;
+ int ctrl;
+
+ D(("called."));
+
+ ctrl = _pam_parse(argc,argv);
+ _pam_report(ctrl, "pam_sm_authenticate", flags, argc, argv);
+
+ /* try to get the username */
+
+ retval = pam_get_user(pamh, &username, "username: ");
+ if ((ctrl & PAM_ST_DEBUG) && (retval == PAM_SUCCESS)) {
+ _pam_log(LOG_DEBUG, "pam_sm_authenticate: username = %s", username);
+ } else if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "pam_sm_authenticate: failed to get username");
+ return retval;
+ }
+
+ /* now get the password */
+
+ retval = stress_get_password(pamh,flags,ctrl,&pass);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "pam_sm_authenticate: "
+ "failed to get a password");
+ return retval;
+ }
+
+ /* try to set password item */
+
+ retval = pam_set_item(pamh,PAM_AUTHTOK,pass);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_WARNING, "pam_sm_authenticate: "
+ "failed to store new password");
+ _pam_overwrite(pass);
+ free(pass);
+ return retval;
+ }
+
+ /* clean up local copy of password */
+
+ _pam_overwrite(pass);
+ free(pass);
+ pass = NULL;
+
+ /* if we are debugging then we print the password */
+
+ if (ctrl & PAM_ST_DEBUG) {
+ (void) pam_get_item(pamh,PAM_AUTHTOK,(const void **)&pass);
+ _pam_log(LOG_DEBUG,
+ "pam_st_authenticate: password entered is: [%s]\n",pass);
+ pass = NULL;
+ }
+
+ /* if we signal a fail for this function then fail */
+
+ if ((ctrl & PAM_ST_FAIL_1) && retval == PAM_SUCCESS)
+ return PAM_PERM_DENIED;
+
+ return retval;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl, "pam_sm_setcred", flags, argc, argv);
+
+ if (ctrl & PAM_ST_FAIL_2)
+ return PAM_CRED_ERR;
+
+ return PAM_SUCCESS;
+}
+
+/* account management functions */
+
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl,"pam_sm_acct_mgmt", flags, argc, argv);
+
+ if (ctrl & PAM_ST_FAIL_1)
+ return PAM_PERM_DENIED;
+ else if (ctrl & PAM_ST_EXPIRED) {
+ void *text = malloc(sizeof("yes")+1);
+ strcpy(text,"yes");
+ pam_set_data(pamh,"stress_new_pwd",text,wipe_up);
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG,"pam_sm_acct_mgmt: need a new password");
+ }
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ char *username,*service;
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl,"pam_sm_open_session", flags, argc, argv);
+
+ if ((pam_get_item(pamh, PAM_USER, (const void **) &username)
+ != PAM_SUCCESS)
+ || (pam_get_item(pamh, PAM_SERVICE, (const void **) &service)
+ != PAM_SUCCESS)) {
+ _pam_log(LOG_WARNING,"pam_sm_open_session: for whom?");
+ return PAM_SESSION_ERR;
+ }
+
+ _pam_log(LOG_NOTICE,"pam_stress: opened [%s] session for user [%s]"
+ , service, username);
+
+ if (ctrl & PAM_ST_FAIL_1)
+ return PAM_SESSION_ERR;
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username,*service;
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl,"pam_sm_close_session", flags, argc, argv);
+
+ if ((pam_get_item(pamh, PAM_USER, (const void **)&username)
+ != PAM_SUCCESS)
+ || (pam_get_item(pamh, PAM_SERVICE, (const void **)&service)
+ != PAM_SUCCESS)) {
+ _pam_log(LOG_WARNING,"pam_sm_close_session: for whom?");
+ return PAM_SESSION_ERR;
+ }
+
+ _pam_log(LOG_NOTICE,"pam_stress: closed [%s] session for user [%s]"
+ , service, username);
+
+ if (ctrl & PAM_ST_FAIL_2)
+ return PAM_SESSION_ERR;
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ int ctrl = _pam_parse(argc,argv);
+
+ D(("called. [post parsing]"));
+
+ _pam_report(ctrl,"pam_sm_chauthtok", flags, argc, argv);
+
+ /* this function should be called twice by the Linux-PAM library */
+
+ if (flags & PAM_PRELIM_CHECK) { /* first call */
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG,"pam_sm_chauthtok: prelim check");
+ }
+ if (ctrl & PAM_ST_PRELIM)
+ return PAM_TRY_AGAIN;
+
+ return PAM_SUCCESS;
+ } else if (flags & PAM_UPDATE_AUTHTOK) { /* second call */
+ struct pam_message msg[3],*pmsg[3];
+ struct pam_response *resp;
+ const char *text;
+ char *txt=NULL;
+ int i;
+
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG,"pam_sm_chauthtok: alter password");
+ }
+
+ if (ctrl & PAM_ST_FAIL_1)
+ return PAM_AUTHTOK_LOCK_BUSY;
+
+ if ( !(ctrl && PAM_ST_EXPIRED)
+ && (flags & PAM_CHANGE_EXPIRED_AUTHTOK)
+ && (pam_get_data(pamh,"stress_new_pwd",(const void **)&text)
+ != PAM_SUCCESS || strcmp(text,"yes"))) {
+ return PAM_SUCCESS; /* the token has not expired */
+ }
+
+ /* the password should be changed */
+
+ if ((ctrl & PAM_ST_REQUIRE_PWD)
+ && !(getuid() == 0 && (ctrl & PAM_ST_ROOTOK))
+ ) { /* first get old one? */
+ char *pass;
+
+ if (ctrl & PAM_ST_DEBUG) {
+ _pam_log(LOG_DEBUG
+ ,"pam_sm_chauthtok: getting old password");
+ }
+ retval = stress_get_password(pamh,flags,ctrl,&pass);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_DEBUG
+ ,"pam_sm_chauthtok: no password obtained");
+ return retval;
+ }
+ retval = pam_set_item(pamh, PAM_OLDAUTHTOK, pass);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_DEBUG
+ ,"pam_sm_chauthtok: could not set OLDAUTHTOK");
+ _pam_overwrite(pass);
+ free(pass);
+ return retval;
+ }
+ _pam_overwrite(pass);
+ free(pass);
+ }
+
+ /* set up for conversation */
+
+ if (!(flags & PAM_SILENT)) {
+ char *username;
+
+ if ( pam_get_item(pamh, PAM_USER, (const void **)&username)
+ || username == NULL ) {
+ _pam_log(LOG_ERR,"no username set");
+ return PAM_USER_UNKNOWN;
+ }
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+#define _LOCAL_STRESS_COMMENT "Changing STRESS password for "
+ txt = (char *) malloc(sizeof(_LOCAL_STRESS_COMMENT)
+ +strlen(username)+1);
+ strcpy(txt, _LOCAL_STRESS_COMMENT);
+#undef _LOCAL_STRESS_COMMENT
+ strcat(txt, username);
+ msg[0].msg = txt;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = "Enter new STRESS password: ";
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = "Retype new STRESS password: ";
+ resp = NULL;
+
+ retval = converse(pamh,i,pmsg,&resp);
+ if (txt) {
+ free(txt);
+ txt = NULL; /* clean up */
+ }
+ if (retval != PAM_SUCCESS) {
+ return retval;
+ }
+
+ if (resp == NULL) {
+ _pam_log(LOG_ERR, "pam_sm_chauthtok: no response from conv");
+ return PAM_CONV_ERR;
+ }
+
+ /* store the password */
+
+ if (resp[i-2].resp && resp[i-1].resp) {
+ if (strcmp(resp[i-2].resp,resp[i-1].resp)) {
+ /* passwords are not the same; forget and return error */
+
+ _pam_drop_reply(resp, i);
+
+ if (!(flags & PAM_SILENT) && !(ctrl & PAM_ST_NO_WARN)) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_ERROR_MSG;
+ msg[0].msg = "Verification mis-typed; "
+ "password unchaged";
+ resp = NULL;
+ (void) converse(pamh,1,pmsg,&resp);
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ }
+ return PAM_AUTHTOK_ERR;
+ }
+
+ if (pam_get_item(pamh,PAM_AUTHTOK,(const void **)&text)
+ == PAM_SUCCESS) {
+ (void) pam_set_item(pamh,PAM_OLDAUTHTOK,text);
+ text = NULL;
+ }
+ (void) pam_set_item(pamh,PAM_AUTHTOK,resp[0].resp);
+ } else {
+ _pam_log(LOG_DEBUG,"pam_sm_chauthtok: problem with resp");
+ retval = PAM_SYSTEM_ERR;
+ }
+
+ _pam_drop_reply(resp, i); /* clean up the passwords */
+ } else {
+ _pam_log(LOG_ERR,"pam_sm_chauthtok: this must be a Linux-PAM error");
+ return PAM_SYSTEM_ERR;
+ }
+
+ return retval;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_stress_modstruct = {
+ "pam_stress",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
diff --git a/modules/pam_tally/.cvsignore b/modules/pam_tally/.cvsignore
new file mode 100644
index 00000000..e1a4f48f
--- /dev/null
+++ b/modules/pam_tally/.cvsignore
@@ -0,0 +1,2 @@
+dynamic
+pam_tally
diff --git a/modules/pam_tally/Makefile b/modules/pam_tally/Makefile
new file mode 100644
index 00000000..78ce621d
--- /dev/null
+++ b/modules/pam_tally/Makefile
@@ -0,0 +1,109 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module and
+# application for Linux-PAM. You should not modify this Makefile
+# (unless you know what you are doing!).
+#
+#
+
+TITLE=pam_tally
+
+#
+## Additional rules for making (and moving) the application added.
+## Assuming that all modules' applications are called $TITLE
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+APPSRC = $(TITLE)_app.c
+APPOBJ = $(TITLE)_app.o
+APPOBJD = $(addprefix dynamic/,$(APPOBJ))
+APPOBJS = $(addprefix static/,$(APPOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+APPLICATION = $(TITLE)
+APPMODE = 755
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+# we're not yet in a position to build this. If you want it, build it
+# separately...
+# $(APPLICATION)
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+
+$(APPLICATION): $(APPOBJD)
+ $(CC) $(CFLAGS) -o $@ $< $(LOADLIBES)
+
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+
+$(APPLICATION): $(APPOBJS)
+ $(CC) $(CFLAGS) -o $@ $< $(LOADLIBES)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SUPLEMENTED)
+# $(INSTALL) -m $(APPMODE) $(APPLICATION) $(FAKEROOT)$(SUPLEMENTED)
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(SUPLEMENTED)/$(TITLE)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) $(APPOBJD) $(APPOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/* $(APPLICATION)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
diff --git a/modules/pam_tally/README b/modules/pam_tally/README
new file mode 100644
index 00000000..b58b24e4
--- /dev/null
+++ b/modules/pam_tally/README
@@ -0,0 +1,95 @@
+SUMMARY:
+ pam_tally:
+
+ Maintains a count of attempted accesses, can reset count on success,
+ can deny access if too many attempts fail.
+
+ Options:
+
+ * onerr=[succeed|fail] (if something weird happens
+ such as unable to open the file, what to do?)
+ * file=/where/to/keep/counts (default /var/log/faillog)
+
+ (auth)
+ Authentication phase increments attempted login counter.
+ * no_magic_root (root DOES increment counter. Use for
+ daemon-based stuff, like telnet/rsh/login)
+
+ (account)
+ Account phase can deny access and/or reset attempts counter.
+ * deny=n (deny access if tally for this user exceeds n;
+ The presence of deny=n changes the default for
+ reset/no_reset to reset, unless the user trying to
+ gain access is root and the no_magic_root option
+ has NOT been specified.)
+
+ * no_magic_root (access attempts by root DON'T ignore deny.
+ Use this for daemon-based stuff, like telnet/rsh/login)
+ * even_deny_root_account (Root can become unavailable. BEWARE.
+ Note that magic root trying to gain root bypasses this,
+ but normal users can be locked out.)
+
+ * reset (reset count to 0 on successful entry, even for
+ magic root)
+ * no_reset (don't reset count on successful entry)
+ This is the default unless deny exists and the
+ user attempting access is NOT magic root.
+
+ * per_user (If /var/log/faillog contains a non-zero
+ .fail_max field for this user then use it
+ instead of deny=n parameter)
+
+ * no_lock_time (Don't use .fail_locktime filed in
+ /var/log/faillog for this user)
+
+ Also checks to make sure that the counts file is a plain
+ file and not world writable.
+
+ - Tim Baverstock <warwick@sable.demon.co.uk>, v0.1 5 March 1997
+
+LONGER:
+
+pam_tally comes in two parts: pam_tally.so and pam_tally.
+
+pam_tally.so sits in a pam config file, in the auth and account sections.
+
+In the auth section, it increments a per-uid counter for each attempted
+login, in the account section, it denies access if attempted logins
+exceed some threashold and/or resets that counter to zero on successful
+login.
+
+Root is treated specially:
+
+1. When a process already running as root tries to access some service, the
+access is `magic', and bypasses pam_tally's checks: handy for `su'ing from
+root into an account otherwise blocked. However, for services like telnet or
+login which always effectively run from the root account, root (ie everyone)
+shouldn't be granted this magic status, and the flag `no_magic_root' should
+be set in this situation, as noted in the summary above. [This option may
+be obsolete, with `sufficient root' processing.]
+
+2. Normally, failed attempts to access root will NOT cause the root
+account to become blocked, to prevent denial-of-service: if your users aren't
+given shell accounts and root may only login via `su' or at the machine
+console (not telnet/rsh, etc), this is safe. If you really want root to be
+blocked for some given service, use even_deny_root_account.
+
+pam_tally is an (optional) application which can be used to interrogate and
+manipulate the counter file. It can display users' counts, set individual
+counts, or clear all counts. Setting artificially high counts may be useful
+for blocking users without changing their passwords. I found it useful to
+clear all counts every midnight from a cron..
+
+The counts file is organised as a binary-word array, indexed by uid. You
+can probably make sense of it with `od', if you don't want to use the
+supplied appliction.
+
+BUGS:
+
+pam_tally is very dependant on getpw*(): a database of usernames
+would be much more flexible.
+
+The (4.0 Redhat) utilities seem to do funny things with uid, and I'm
+not wholly sure I understood what I should have been doing anyway so
+the `keep a count of current logins' bit has been #ifdef'd out and you
+can only reset the counter on successful authentication, for now.
diff --git a/modules/pam_tally/faillog.h b/modules/pam_tally/faillog.h
new file mode 100644
index 00000000..7f704713
--- /dev/null
+++ b/modules/pam_tally/faillog.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1989 - 1994, Julianne Frances Haugh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * faillog.h - login failure logging file format
+ *
+ * $Id$
+ *
+ * The login failure file is maintained by login(1) and faillog(8)
+ * Each record in the file represents a separate UID and the file
+ * is indexed in that fashion.
+ */
+
+#ifndef _FAILLOG_H
+#define _FAILLOG_H
+
+struct faillog {
+ short fail_cnt; /* failures since last success */
+ short fail_max; /* failures before turning account off */
+ char fail_line[12]; /* last failure occured here */
+ time_t fail_time; /* last failure occured then */
+ /*
+ * If nonzero, the account will be re-enabled if there are no
+ * failures for fail_locktime seconds since last failure.
+ */
+ long fail_locktime;
+};
+
+#endif
diff --git a/modules/pam_tally/pam_tally.c b/modules/pam_tally/pam_tally.c
new file mode 100644
index 00000000..ff75697d
--- /dev/null
+++ b/modules/pam_tally/pam_tally.c
@@ -0,0 +1,689 @@
+/*
+ * pam_tally.c
+ *
+ * $Id$
+ */
+
+
+/* By Tim Baverstock <warwick@mmm.co.uk>, Multi Media Machine Ltd.
+ * 5 March 1997
+ *
+ * Stuff stolen from pam_rootok and pam_listfile
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include "faillog.h"
+
+#ifndef TRUE
+#define TRUE 1L
+#define FALSE 0L
+#endif
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+/* #define PAM_SM_SESSION */
+/* #define PAM_SM_PASSWORD */
+
+#include <security/pam_modules.h>
+
+/*---------------------------------------------------------------------*/
+
+#define DEFAULT_LOGFILE "/var/log/faillog"
+#define MODULE_NAME "pam_tally"
+
+enum TALLY_RESET {
+ TALLY_RESET_DEFAULT,
+ TALLY_RESET_RESET,
+ TALLY_RESET_NO_RESET
+};
+
+#define tally_t unsigned short int
+#define TALLY_FMT "%hu"
+#define TALLY_HI ((tally_t)~0L)
+
+#define UID_FMT "%hu"
+
+#ifndef FILENAME_MAX
+# define FILENAME_MAX MAXPATHLEN
+#endif
+
+static struct faillog faillog;
+static time_t fail_time;
+
+/*---------------------------------------------------------------------*/
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+#ifdef MAIN
+ vfprintf(stderr,format,args);
+#else
+ openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ closelog();
+#endif
+ va_end(args);
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: get uid (and optionally username) from PAM or
+ cline_user --- */
+
+#ifdef MAIN
+static char *cline_user=0; /* cline_user is used in the administration prog */
+#endif
+
+static int pam_get_uid( pam_handle_t *pamh, uid_t *uid, const char **userp )
+ {
+ const char *user;
+ struct passwd *pw;
+
+#ifdef MAIN
+ user = cline_user;
+#else
+ pam_get_user( pamh, &user, NULL );
+#endif
+
+ if ( !user || !*user ) {
+ _pam_log(LOG_ERR, MODULE_NAME ": pam_get_uid; user?");
+ return PAM_AUTH_ERR;
+ }
+
+ if ( ! ( pw = getpwnam( user ) ) ) {
+ _pam_log(LOG_ERR,MODULE_NAME ": pam_get_uid; no such user %s",user);
+ return PAM_USER_UNKNOWN;
+ }
+
+ if ( uid ) *uid = pw->pw_uid;
+ if ( userp ) *userp = user;
+ return PAM_SUCCESS;
+ }
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: open/create tallyfile and return tally for uid --- */
+
+/* If on entry *tally==TALLY_HI, tallyfile is opened READONLY */
+/* Otherwise, if on entry tallyfile doesn't exist, creation is attempted. */
+
+static int get_tally( tally_t *tally,
+ uid_t uid,
+ const char *filename,
+ FILE **TALLY )
+ {
+ struct stat fileinfo;
+ int lstat_ret = lstat(filename,&fileinfo);
+
+ if ( lstat_ret && *tally!=TALLY_HI ) {
+ int oldmask = umask(077);
+ *TALLY=fopen(filename, "a");
+ /* Create file, or append-open in pathological case. */
+ umask(oldmask);
+ if ( !*TALLY ) {
+ _pam_log(LOG_ALERT, "Couldn't create %s",filename);
+ return PAM_AUTH_ERR;
+ }
+ lstat_ret = fstat(fileno(*TALLY),&fileinfo);
+ fclose(*TALLY);
+ }
+
+ if ( lstat_ret ) {
+ _pam_log(LOG_ALERT, "Couldn't stat %s",filename);
+ return PAM_AUTH_ERR;
+ }
+
+ if((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) {
+ /* If the file is world writable or is not a
+ normal file, return error */
+ _pam_log(LOG_ALERT,
+ "%s is either world writable or not a normal file",
+ filename);
+ return PAM_AUTH_ERR;
+ }
+
+ if ( ! ( *TALLY = fopen(filename,(*tally!=TALLY_HI)?"r+":"r") ) ) {
+ _pam_log(LOG_ALERT, "Error opening %s for update", filename);
+
+/* Discovering why account service fails: e/uid are target user.
+ *
+ * perror(MODULE_NAME);
+ * fprintf(stderr,"uid %d euid %d\n",getuid(), geteuid());
+ */
+ return PAM_AUTH_ERR;
+ }
+
+ if ( fseek( *TALLY, uid * sizeof faillog, SEEK_SET ) ) {
+ _pam_log(LOG_ALERT, "fseek failed %s", filename);
+ return PAM_AUTH_ERR;
+ }
+
+
+ if ( ( fread((char *) &faillog, sizeof faillog, 1, *TALLY) )==0 ) {
+ *tally=0; /* Assuming a gappy filesystem */
+ }
+ *tally = faillog.fail_cnt;
+
+ return PAM_SUCCESS;
+ }
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: update and close tallyfile with tally!=TALLY_HI --- */
+
+static int set_tally( tally_t tally,
+ uid_t uid,
+ const char *filename,
+ FILE **TALLY )
+ {
+ if ( tally!=TALLY_HI )
+ {
+ if ( fseek( *TALLY, uid * sizeof faillog, SEEK_SET ) ) {
+ _pam_log(LOG_ALERT, "fseek failed %s", filename);
+ return PAM_AUTH_ERR;
+ }
+ faillog.fail_cnt = tally;
+ if ( fwrite((char *) &faillog, sizeof faillog, 1, *TALLY)==0 ) {
+ _pam_log(LOG_ALERT, "tally update (fputc) failed.", filename);
+ return PAM_AUTH_ERR;
+ }
+ }
+
+ if ( fclose(*TALLY) ) {
+ _pam_log(LOG_ALERT, "tally update (fclose) failed.", filename);
+ return PAM_AUTH_ERR;
+ }
+ *TALLY=NULL;
+ return PAM_SUCCESS;
+ }
+
+/*---------------------------------------------------------------------*/
+
+/* --- PAM bits --- */
+
+#ifndef MAIN
+
+#define PAM_FUNCTION(name) \
+ PAM_EXTERN int name (pam_handle_t *pamh,int flags,int argc,const char **argv)
+
+#define RETURN_ERROR(i) return ((fail_on_error)?(i):(PAM_SUCCESS))
+
+/*---------------------------------------------------------------------*/
+
+/* --- tally bump function: bump tally for uid by (signed) inc --- */
+
+static int tally_bump (int inc,
+ pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv) {
+ uid_t uid;
+
+ int
+ fail_on_error = FALSE;
+ tally_t
+ tally = 0; /* !TALLY_HI --> Log opened for update */
+
+ char
+ no_magic_root = FALSE;
+
+ char
+ filename[ FILENAME_MAX ] = DEFAULT_LOGFILE;
+
+ /* Should probably decode the parameters before anything else. */
+
+ {
+ for ( ; argc-- > 0; ++argv ) {
+
+ /* generic options.. um, ignored. :] */
+
+ if ( ! strcmp( *argv, "no_magic_root" ) ) {
+ no_magic_root = TRUE;
+ }
+ else if ( ! strncmp( *argv, "file=", 5 ) ) {
+ char const
+ *from = (*argv)+5;
+ char
+ *to = filename;
+ if ( *from!='/' || strlen(from)>FILENAME_MAX-1 ) {
+ _pam_log(LOG_ERR,
+ MODULE_NAME ": filename not /rooted or too long; ",
+ *argv);
+ RETURN_ERROR( PAM_AUTH_ERR );
+ }
+ while ( ( *to++ = *from++ ) );
+ }
+ else if ( ! strcmp( *argv, "onerr=fail" ) ) {
+ fail_on_error=TRUE;
+ }
+ else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
+ fail_on_error=FALSE;
+ }
+ else {
+ _pam_log(LOG_ERR, MODULE_NAME ": unknown option; %s",*argv);
+ }
+ } /* for() */
+ }
+
+ {
+ FILE
+ *TALLY = NULL;
+ const char
+ *user = NULL,
+ *remote_host = NULL;
+
+ int i=pam_get_uid(pamh, &uid, &user);
+ if ( i != PAM_SUCCESS ) RETURN_ERROR( i );
+
+ i=get_tally( &tally, uid, filename, &TALLY );
+ fail_time = faillog.fail_time; /* to remember old fail time (for locktime) */
+ faillog.fail_time = (time_t) time( (time_t *) 0);
+ (void) pam_get_item(pamh, PAM_RHOST, (const void **)&remote_host);
+ if (!remote_host)
+ {
+ strcpy(faillog.fail_line, "unknown");
+ }
+ else
+ {
+ strncpy(faillog.fail_line, remote_host, (size_t)sizeof(faillog.fail_line));
+ faillog.fail_line[sizeof(faillog.fail_line)-1] = 0;
+ }
+ if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+
+ if ( no_magic_root || getuid() ) { /* no_magic_root kills uid test */
+
+ tally+=inc;
+
+ if ( tally==TALLY_HI ) { /* Overflow *and* underflow. :) */
+ tally-=inc;
+ _pam_log(LOG_ALERT,"Tally %sflowed for user %s",
+ (inc<0)?"under":"over",user);
+ }
+ }
+
+ i=set_tally( tally, uid, filename, &TALLY );
+ if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+ }
+
+ return PAM_SUCCESS;
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- authentication management functions (only) --- */
+
+#ifdef PAM_SM_AUTH
+
+PAM_FUNCTION( pam_sm_authenticate ) {
+ return tally_bump( 1, pamh, flags, argc, argv);
+}
+
+/* --- Seems to need this function. Ho hum. --- */
+
+PAM_FUNCTION( pam_sm_setcred ) { return PAM_SUCCESS; }
+
+#endif
+
+/*---------------------------------------------------------------------*/
+
+/* --- session management functions (only) --- */
+
+/*
+ * Unavailable until .so files can be suid
+ */
+
+#ifdef PAM_SM_SESSION
+
+/* To maintain a balance-tally of successful login/outs */
+
+PAM_FUNCTION( pam_sm_open_session ) {
+ return tally_bump( 1, pamh, flags, argc, argv);
+}
+
+PAM_FUNCTION( pam_sm_close_session ) {
+ return tally_bump(-1, pamh, flags, argc, argv);
+}
+
+#endif
+
+/*---------------------------------------------------------------------*/
+
+/* --- authentication management functions (only) --- */
+
+#ifdef PAM_SM_AUTH
+
+/* To lock out a user with an unacceptably high tally */
+
+PAM_FUNCTION( pam_sm_acct_mgmt ) {
+ uid_t
+ uid;
+
+ int
+ fail_on_error = FALSE;
+ tally_t
+ deny = 0;
+ tally_t
+ tally = 0; /* !TALLY_HI --> Log opened for update */
+
+ char
+ no_magic_root = FALSE,
+ even_deny_root_account = FALSE;
+ char per_user = FALSE; /* if true then deny=.fail_max for user */
+ char no_lock_time = FALSE; /* if true then don't use .fail_locktime */
+
+ const char
+ *user = NULL;
+
+ enum TALLY_RESET
+ reset = TALLY_RESET_DEFAULT;
+
+ char
+ filename[ FILENAME_MAX ] = DEFAULT_LOGFILE;
+
+ /* Should probably decode the parameters before anything else. */
+
+ {
+ for ( ; argc-- > 0; ++argv ) {
+
+ /* generic options.. um, ignored. :] */
+
+ if ( ! strcmp( *argv, "no_magic_root" ) ) {
+ no_magic_root = TRUE;
+ }
+ else if ( ! strcmp( *argv, "even_deny_root_account" ) ) {
+ even_deny_root_account = TRUE;
+ }
+ else if ( ! strcmp( *argv, "reset" ) ) {
+ reset = TALLY_RESET_RESET;
+ }
+ else if ( ! strcmp( *argv, "no_reset" ) ) {
+ reset = TALLY_RESET_NO_RESET;
+ }
+ else if ( ! strncmp( *argv, "file=", 5 ) ) {
+ char const
+ *from = (*argv)+5;
+ char
+ *to = filename;
+ if ( *from != '/' || strlen(from) > FILENAME_MAX-1 ) {
+ _pam_log(LOG_ERR,
+ MODULE_NAME ": filename not /rooted or too long; ",
+ *argv);
+ RETURN_ERROR( PAM_AUTH_ERR );
+ }
+ while ( ( *to++ = *from++ ) );
+ }
+ else if ( ! strncmp( *argv, "deny=", 5 ) ) {
+ if ( sscanf((*argv)+5,TALLY_FMT,&deny) != 1 ) {
+ _pam_log(LOG_ERR,"bad number supplied; %s",*argv);
+ RETURN_ERROR( PAM_AUTH_ERR );
+ }
+ }
+ else if ( ! strcmp( *argv, "onerr=fail" ) ) {
+ fail_on_error=TRUE;
+ }
+ else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
+ fail_on_error=FALSE;
+ }
+ else if ( ! strcmp( *argv, "per_user" ) )
+ {
+ per_user = TRUE;
+ }
+ else if ( ! strcmp( *argv, "no_lock_time") )
+ {
+ no_lock_time = TRUE;
+ }
+ else {
+ _pam_log(LOG_ERR, MODULE_NAME ": unknown option; %s",*argv);
+ }
+ } /* for() */
+ }
+
+ {
+ FILE *TALLY=0;
+ int i=pam_get_uid(pamh, &uid, &user);
+ if ( i != PAM_SUCCESS ) RETURN_ERROR( i );
+
+ i=get_tally( &tally, uid, filename, &TALLY );
+ if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+
+ if ( no_magic_root || getuid() ) { /* no_magic_root kills uid test */
+
+ /* To deny or not to deny; that is the question */
+
+ /* if there's .fail_max entry and per_user=TRUE then deny=.fail_max */
+
+ if ( (faillog.fail_max) && (per_user) ) {deny = faillog.fail_max;}
+ if (faillog.fail_locktime && fail_time && (!no_lock_time) )
+ {
+ if ( (faillog.fail_locktime + fail_time) > (time_t)time((time_t)0) )
+ {
+ _pam_log(LOG_NOTICE,"user %s ("UID_FMT") has time limit [%lds left] since last failure.",user,uid,fail_time+faillog.fail_locktime-(time_t)time((time_t)0));
+ return PAM_AUTH_ERR;
+ }
+ }
+ if (
+ ( deny != 0 ) && /* deny==0 means no deny */
+ ( tally > deny ) && /* tally>deny means exceeded */
+ ( even_deny_root_account || uid ) /* even_deny stops uid check */
+ ) {
+ _pam_log(LOG_NOTICE,"user %s ("UID_FMT") tally "TALLY_FMT", deny "TALLY_FMT,
+ user, uid, tally, deny);
+ return PAM_AUTH_ERR; /* Only unconditional failure */
+ }
+
+ /* resets for explicit reset
+ * or by default if deny exists and not magic-root
+ */
+
+ if ( ( reset == TALLY_RESET_RESET ) ||
+ ( reset == TALLY_RESET_DEFAULT && deny ) ) { tally=0; }
+ }
+ else /* is magic root */ {
+
+ /* Magic root skips deny test... */
+
+ /* Magic root only resets on explicit reset, regardless of deny */
+
+ if ( reset == TALLY_RESET_RESET ) { tally=0; }
+ }
+ if (tally == 0)
+ {
+ faillog.fail_time = (time_t) 0;
+ strcpy(faillog.fail_line, "");
+ }
+ i=set_tally( tally, uid, filename, &TALLY );
+ if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+ }
+
+ return PAM_SUCCESS;
+}
+
+#endif /* #ifdef PAM_SM_AUTH */
+
+/*-----------------------------------------------------------------------*/
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_tally_modstruct = {
+ MODULE_NAME,
+#ifdef PAM_SM_AUTH
+ pam_sm_authenticate,
+ pam_sm_setcred,
+#else
+ NULL,
+ NULL,
+#endif
+#ifdef PAM_SM_ACCOUNT
+ pam_sm_acct_mgmt,
+#else
+ NULL,
+#endif
+#ifdef PAM_SM_SESSION
+ pam_sm_open_session,
+ pam_sm_close_session,
+#else
+ NULL,
+ NULL,
+#endif
+#ifdef PAM_SM_PASSWORD
+ pam_sm_chauthtok,
+#else
+ NULL,
+#endif
+};
+
+#endif /* #ifdef PAM_STATIC */
+
+/*-----------------------------------------------------------------------*/
+
+#else /* #ifndef MAIN */
+
+static const char *cline_filename = DEFAULT_LOGFILE;
+static tally_t cline_reset = TALLY_HI; /* Default is `interrogate only' */
+static int cline_quiet = 0;
+
+/*
+ * Not going to link with pamlib just for these.. :)
+ */
+
+static const char * pam_errors( int i ) {
+ switch (i) {
+ case PAM_AUTH_ERR: return "Authentication error";
+ case PAM_SERVICE_ERR: return "Service error";
+ case PAM_USER_UNKNOWN: return "Unknown user";
+ default: return "Unknown error";
+ }
+}
+
+static int getopts( int argc, char **argv ) {
+ const char *pname = *argv;
+ for ( ; *argv ; (void)(*argv && ++argv) ) {
+ if ( !strcmp (*argv,"--file") ) cline_filename=*++argv;
+ else if ( !strncmp(*argv,"--file=",7) ) cline_filename=*argv+7;
+ else if ( !strcmp (*argv,"--user") ) cline_user=*++argv;
+ else if ( !strncmp(*argv,"--user=",7) ) cline_user=*argv+7;
+ else if ( !strcmp (*argv,"--reset") ) cline_reset=0;
+ else if ( !strncmp(*argv,"--reset=",8)) {
+ if ( sscanf(*argv+8,TALLY_FMT,&cline_reset) != 1 )
+ fprintf(stderr,"%s: Bad number given to --reset=\n",pname), exit(0);
+ }
+ else if ( !strcmp (*argv,"--quiet") ) cline_quiet=1;
+ else {
+ fprintf(stderr,"%s: Unrecognised option %s\n",pname,*argv);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+int main ( int argc, char **argv ) {
+
+ if ( ! getopts( argc, argv+1 ) ) {
+ printf("%s: [--file rooted-filename] [--user username] "
+ "[--reset[=n]] [--quiet]\n",
+ *argv);
+ exit(0);
+ }
+
+ umask(077);
+
+ /*
+ * Major difference between individual user and all users:
+ * --user just handles one user, just like PAM.
+ * --user=* handles all users, sniffing cline_filename for nonzeros
+ */
+
+ if ( cline_user ) {
+ uid_t uid;
+ tally_t tally=cline_reset;
+ FILE *TALLY=0;
+ int i=pam_get_uid( NULL, &uid, NULL);
+ if ( i != PAM_SUCCESS ) {
+ fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
+ exit(0);
+ }
+
+ i=get_tally( &tally, uid, cline_filename, &TALLY );
+ if ( i != PAM_SUCCESS ) {
+ if (TALLY) fclose(TALLY);
+ fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
+ exit(0);
+ }
+
+ if ( !cline_quiet )
+ printf("User %s\t("UID_FMT")\t%s "TALLY_FMT"\n",cline_user,uid,
+ (cline_reset!=TALLY_HI)?"had":"has",tally);
+
+ i=set_tally( cline_reset, uid, cline_filename, &TALLY );
+ if ( i != PAM_SUCCESS ) {
+ if (TALLY) fclose(TALLY);
+ fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
+ exit(0);
+ }
+ }
+ else /* !cline_user (ie, operate on all users) */ {
+ FILE *TALLY=fopen(cline_filename, "r");
+ uid_t uid=0;
+ if ( !TALLY ) perror(*argv), exit(0);
+
+ for ( ; !feof(TALLY); uid++ ) {
+ tally_t tally;
+ struct passwd *pw;
+ if ( ! fread((char *) &faillog, sizeof faillog, 1, TALLY) || ! faillog.fail_cnt ) {
+ tally=faillog.fail_cnt;
+ continue;
+ }
+ tally = faillog.fail_cnt;
+
+ if ( ( pw=getpwuid(uid) ) ) {
+ printf("User %s\t("UID_FMT")\t%s "TALLY_FMT"\n",pw->pw_name,uid,
+ (cline_reset!=TALLY_HI)?"had":"has",tally);
+ }
+ else {
+ printf("User [NONAME]\t("UID_FMT")\t%s "TALLY_FMT"\n",uid,
+ (cline_reset!=TALLY_HI)?"had":"has",tally);
+ }
+ }
+ fclose(TALLY);
+ if ( cline_reset!=0 && cline_reset!=TALLY_HI ) {
+ fprintf(stderr,"%s: Can't reset all users to non-zero\n",*argv);
+ }
+ else if ( !cline_reset ) {
+ TALLY=fopen(cline_filename, "w");
+ if ( !TALLY ) perror(*argv), exit(0);
+ fclose(TALLY);
+ }
+ }
+ return 0;
+}
+
+
+#endif
diff --git a/modules/pam_tally/pam_tally_app.c b/modules/pam_tally/pam_tally_app.c
new file mode 100644
index 00000000..9e6e1faf
--- /dev/null
+++ b/modules/pam_tally/pam_tally_app.c
@@ -0,0 +1,7 @@
+/*
+ # This seemed like such a good idea at the time. :)
+ */
+
+#define MAIN
+#include "pam_tally.c"
+
diff --git a/modules/pam_time/.cvsignore b/modules/pam_time/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_time/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_time/Makefile b/modules/pam_time/Makefile
new file mode 100644
index 00000000..03105bb1
--- /dev/null
+++ b/modules/pam_time/Makefile
@@ -0,0 +1,98 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Andrew Morgan <morgan@linux.kernel.org> 1996/6/11
+#
+
+TITLE=pam_time
+CONFD=$(CONFIGED)/security
+export CONFD
+CONFILE=$(CONFD)/time.conf
+export CONFILE
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+DEFS=-DCONFILE=\"$(CONFILE)\"
+
+CFLAGS += $(DEFS)
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ifdef DYNAMIC
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+ $(MKDIR) $(FAKEROOT)$(SCONFIGED)
+ bash -f ./install_conf
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+ rm -f $(FAKEROOT)$(CONFILE)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+ rm -f ./.ignore_age
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_time/README b/modules/pam_time/README
new file mode 100644
index 00000000..dcbe35e4
--- /dev/null
+++ b/modules/pam_time/README
@@ -0,0 +1,43 @@
+$Id$
+
+This is a help file for the pam_time module. It explains the need for
+pam_time and also the syntax of the /etc/security/time.conf file.
+[a lot of the syntax is freely adapted from the porttime file of the
+shadow suite.]
+
+1. Introduction
+===============
+
+It is desirable to restrict access to a system and or specific
+applications at various times of the day and on specific days or over
+various terminal lines.
+
+The pam_time module is intended to offer a configurable module that
+satisfies this purpose, within the context of Linux-PAM.
+
+2. the /etc/security/time.conf file
+===================================
+
+This file is the configuration script for defining time/port access
+control to the system/applications.
+
+Its syntax is described in the sample ./time.conf provided in this
+directory.
+
+unrecognised rules are ignored (but an error is logged to syslog(3))
+
+--------------------
+Bugs to Andrew <morgan@parc.power.net> or the list <pam-list@redhat.com>
+
+########################################################################
+# $Log$
+# Revision 1.1 2000/06/20 22:12:00 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.3 1997/01/04 20:42:43 morgan
+# I want email on parc now
+#
+# \ No newline at end of file
diff --git a/modules/pam_time/install_conf b/modules/pam_time/install_conf
new file mode 100755
index 00000000..051d8b70
--- /dev/null
+++ b/modules/pam_time/install_conf
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+CONFILE=$FAKEROOT"$CONFILE"
+IGNORE_AGE=./.ignore_age
+QUIET_INSTALL=../../.quiet_install
+CONF=./time.conf
+MODULE=pam_time
+
+echo
+
+if [ -f "$QUIET_INSTALL" ]; then
+ if [ ! -f "$CONFILE" ]; then
+ yes="y"
+ else
+ yes="skip"
+ fi
+elif [ -f "$IGNORE_AGE" ]; then
+ echo "you don't want to be bothered with the age of your $CONFILE file"
+ yes="n"
+elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then
+ if [ -f "$CONFILE" ]; then
+ echo "An older $MODULE configuration file already exists ($CONFILE)"
+ echo "Do you wish to copy the $CONF file in this distribution"
+ echo "to $CONFILE ? (y/n) [skip] "
+ read yes
+ else
+ yes="y"
+ fi
+else
+ yes="skip"
+fi
+
+if [ "$yes" = "y" ]; then
+ mkdir -p $FAKEROOT$CONFD
+ echo " copying $CONF to $CONFILE"
+ cp $CONF $CONFILE
+else
+ echo " Skipping $CONF installation"
+ if [ "$yes" = "n" ]; then
+ touch "$IGNORE_AGE"
+ fi
+fi
+
+echo
+
+exit 0
diff --git a/modules/pam_time/pam_time.c b/modules/pam_time/pam_time.c
new file mode 100644
index 00000000..2ca51859
--- /dev/null
+++ b/modules/pam_time/pam_time.c
@@ -0,0 +1,612 @@
+/* pam_time module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/6/22
+ * (File syntax and much other inspiration from the shadow package
+ * shadow-960129)
+ */
+
+const static char rcsid[] =
+"$Id$;\n"
+"\t\tVersion 0.22 for Linux-PAM\n"
+"Copyright (C) Andrew G. Morgan 1996 <morgan@linux.kernel.org>\n";
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <time.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define PAM_TIME_CONF CONFILE /* from external define */
+#define PAM_TIME_BUFLEN 1000
+#define FIELD_SEPARATOR ';' /* this is new as of .02 */
+
+typedef enum { FALSE, TRUE } boolean;
+typedef enum { AND, OR } operator;
+
+/*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+ * but strongly encouraged generally) they are used to instruct the
+ * modules include file to define their prototypes.
+ */
+
+#define PAM_SM_ACCOUNT
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+
+/* --- static functions for checking whether the user should be let in --- */
+
+static void _log_err(const char *format, ... )
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_time", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(LOG_CRIT, format, args);
+ va_end(args);
+ closelog();
+}
+
+static void shift_bytes(char *mem, int from, int by)
+{
+ while (by-- > 0) {
+ *mem = mem[from];
+ ++mem;
+ }
+}
+
+static int read_field(int fd, char **buf, int *from, int *to)
+{
+ /* is buf set ? */
+
+ if (! *buf) {
+ *buf = (char *) malloc(PAM_TIME_BUFLEN);
+ if (! *buf) {
+ _log_err("out of memory");
+ D(("no memory"));
+ return -1;
+ }
+ *from = *to = 0;
+ fd = open(PAM_TIME_CONF, O_RDONLY);
+ }
+
+ /* do we have a file open ? return error */
+
+ if (fd < 0 && *to <= 0) {
+ _log_err( PAM_TIME_CONF " not opened");
+ memset(*buf, 0, PAM_TIME_BUFLEN);
+ _pam_drop(*buf);
+ return -1;
+ }
+
+ /* check if there was a newline last time */
+
+ if ((*to > *from) && (*to > 0)
+ && ((*buf)[*from] == '\0')) { /* previous line ended */
+ (*from)++;
+ (*buf)[0] = '\0';
+ return fd;
+ }
+
+ /* ready for more data: first shift the buffer's remaining data */
+
+ *to -= *from;
+ shift_bytes(*buf, *from, *to);
+ *from = 0;
+ (*buf)[*to] = '\0';
+
+ while (fd >= 0 && *to < PAM_TIME_BUFLEN) {
+ int i;
+
+ /* now try to fill the remainder of the buffer */
+
+ i = read(fd, *to + *buf, PAM_TIME_BUFLEN - *to);
+ if (i < 0) {
+ _log_err("error reading " PAM_TIME_CONF);
+ return -1;
+ } else if (!i) {
+ close(fd);
+ fd = -1; /* end of file reached */
+ } else
+ *to += i;
+
+ /*
+ * contract the buffer. Delete any comments, and replace all
+ * multiple spaces with single commas
+ */
+
+ i = 0;
+#ifdef DEBUG_DUMP
+ D(("buffer=<%s>",*buf));
+#endif
+ while (i < *to) {
+ if ((*buf)[i] == ',') {
+ int j;
+
+ for (j=++i; j<*to && (*buf)[j] == ','; ++j);
+ if (j!=i) {
+ shift_bytes(i + (*buf), j-i, (*to) - j);
+ *to -= j-i;
+ }
+ }
+ switch ((*buf)[i]) {
+ int j,c;
+ case '#':
+ for (j=i; j < *to && (c = (*buf)[j]) != '\n'; ++j);
+ if (j >= *to) {
+ (*buf)[*to = ++i] = '\0';
+ } else if (c == '\n') {
+ shift_bytes(i + (*buf), j-i, (*to) - j);
+ *to -= j-i;
+ ++i;
+ } else {
+ _log_err("internal error in " __FILE__
+ " at line %d", __LINE__ );
+ return -1;
+ }
+ break;
+ case '\\':
+ if ((*buf)[i+1] == '\n') {
+ shift_bytes(i + *buf, 2, *to - (i+2));
+ *to -= 2;
+ }
+ break;
+ case '!':
+ case ' ':
+ case '\t':
+ if ((*buf)[i] != '!')
+ (*buf)[i] = ',';
+ /* delete any trailing spaces */
+ for (j=++i; j < *to && ( (c = (*buf)[j]) == ' '
+ || c == '\t' ); ++j);
+ shift_bytes(i + *buf, j-i, (*to)-j );
+ *to -= j-i;
+ break;
+ default:
+ ++i;
+ }
+ }
+ }
+
+ (*buf)[*to] = '\0';
+
+ /* now return the next field (set the from/to markers) */
+ {
+ int i;
+
+ for (i=0; i<*to; ++i) {
+ switch ((*buf)[i]) {
+ case '#':
+ case '\n': /* end of the line/file */
+ (*buf)[i] = '\0';
+ *from = i;
+ return fd;
+ case FIELD_SEPARATOR: /* end of the field */
+ (*buf)[i] = '\0';
+ *from = ++i;
+ return fd;
+ }
+ }
+ *from = i;
+ (*buf)[*from] = '\0';
+ }
+
+ if (*to <= 0) {
+ D(("[end of text]"));
+ *buf = NULL;
+ }
+
+ return fd;
+}
+
+/* read a member from a field */
+
+static int logic_member(const char *string, int *at)
+{
+ int len,c,to;
+ int done=0;
+ int token=0;
+
+ len=0;
+ to=*at;
+ do {
+ c = string[to++];
+
+ switch (c) {
+
+ case '\0':
+ --to;
+ done = 1;
+ break;
+
+ case '&':
+ case '|':
+ case '!':
+ if (token) {
+ --to;
+ }
+ done = 1;
+ break;
+
+ default:
+ if (isalpha(c) || c == '*' || isdigit(c) || c == '_'
+ || c == '-' || c == '.') {
+ token = 1;
+ } else if (token) {
+ --to;
+ done = 1;
+ } else {
+ ++*at;
+ }
+ }
+ } while (!done);
+
+ return to - *at;
+}
+
+typedef enum { VAL, OP } expect;
+
+static boolean logic_field(const void *me, const char *x, int rule,
+ boolean (*agrees)(const void *, const char *
+ , int, int))
+{
+ boolean left=FALSE, right, not=FALSE;
+ operator oper=OR;
+ int at=0, l;
+ expect next=VAL;
+
+ while ((l = logic_member(x,&at))) {
+ int c = x[at];
+
+ if (next == VAL) {
+ if (c == '!')
+ not = !not;
+ else if (isalpha(c) || c == '*') {
+ right = not ^ agrees(me, x+at, l, rule);
+ if (oper == AND)
+ left &= right;
+ else
+ left |= right;
+ next = OP;
+ } else {
+ _log_err("garbled syntax; expected name (rule #%d)", rule);
+ return FALSE;
+ }
+ } else { /* OP */
+ switch (c) {
+ case '&':
+ oper = AND;
+ break;
+ case '|':
+ oper = OR;
+ break;
+ default:
+ _log_err("garbled syntax; expected & or | (rule #%d)"
+ , rule);
+ D(("%c at %d",c,at));
+ return FALSE;
+ }
+ next = VAL;
+ }
+ at += l;
+ }
+
+ return left;
+}
+
+static boolean is_same(const void *A, const char *b, int len, int rule)
+{
+ int i;
+ const char *a;
+
+ a = A;
+ for (i=0; len > 0; ++i, --len) {
+ if (b[i] != a[i]) {
+ if (b[i++] == '*') {
+ return (!--len || !strncmp(b+i,a+strlen(a)-len,len));
+ } else
+ return FALSE;
+ }
+ }
+ return ( !len );
+}
+
+typedef struct {
+ int day; /* array of 7 bits, one set for today */
+ int minute; /* integer, hour*100+minute for now */
+} TIME;
+
+struct day {
+ const char *d;
+ int bit;
+} static const days[11] = {
+ { "su", 01 },
+ { "mo", 02 },
+ { "tu", 04 },
+ { "we", 010 },
+ { "th", 020 },
+ { "fr", 040 },
+ { "sa", 0100 },
+ { "wk", 076 },
+ { "wd", 0101 },
+ { "al", 0177 },
+ { NULL, 0 }
+};
+
+static TIME time_now(void)
+{
+ struct tm *local;
+ time_t the_time;
+ TIME this;
+
+ the_time = time((time_t *)0); /* get the current time */
+ local = localtime(&the_time);
+ this.day = days[local->tm_wday].bit;
+ this.minute = local->tm_hour*100 + local->tm_min;
+
+ D(("day: 0%o, time: %.4d", this.day, this.minute));
+ return this;
+}
+
+/* take the current date and see if the range "date" passes it */
+static boolean check_time(const void *AT, const char *times, int len, int rule)
+{
+ boolean not,pass;
+ int marked_day, time_start, time_end;
+ const TIME *at;
+ int i,j=0;
+
+ at = AT;
+ D(("chcking: 0%o/%.4d vs. %s", at->day, at->minute, times));
+
+ if (times == NULL) {
+ /* this should not happen */
+ _log_err("internal error: " __FILE__ " line %d", __LINE__);
+ return FALSE;
+ }
+
+ if (times[j] == '!') {
+ ++j;
+ not = TRUE;
+ } else {
+ not = FALSE;
+ }
+
+ for (marked_day = 0; len > 0 && isalpha(times[j]); --len) {
+ int this_day=-1;
+
+ D(("%c%c ?", times[j], times[j+1]));
+ for (i=0; days[i].d != NULL; ++i) {
+ if (tolower(times[j]) == days[i].d[0]
+ && tolower(times[j+1]) == days[i].d[1] ) {
+ this_day = days[i].bit;
+ break;
+ }
+ }
+ j += 2;
+ if (this_day == -1) {
+ _log_err("bad day specified (rule #%d)", rule);
+ return FALSE;
+ }
+ marked_day ^= this_day;
+ }
+ if (marked_day == 0) {
+ _log_err("no day specified");
+ return FALSE;
+ }
+ D(("day range = 0%o", marked_day));
+
+ time_start = 0;
+ for (i=0; len > 0 && i < 4 && isdigit(times[i+j]); ++i, --len) {
+ time_start *= 10;
+ time_start += times[i+j]-'0'; /* is this portable? */
+ }
+ j += i;
+
+ if (times[j] == '-') {
+ time_end = 0;
+ for (i=1; len > 0 && i < 5 && isdigit(times[i+j]); ++i, --len) {
+ time_end *= 10;
+ time_end += times[i+j]-'0'; /* is this portable */
+ }
+ j += i;
+ } else
+ time_end = -1;
+
+ D(("i=%d, time_end=%d, times[j]='%c'", i, time_end, times[j]));
+ if (i != 5 || time_end == -1) {
+ _log_err("no/bad times specified (rule #%d)", rule);
+ return TRUE;
+ }
+ D(("times(%d to %d)", time_start,time_end));
+ D(("marked_day = 0%o", marked_day));
+
+ /* compare with the actual time now */
+
+ pass = FALSE;
+ if (time_start < time_end) { /* start < end ? --> same day */
+ if ((at->day & marked_day) && (at->minute >= time_start)
+ && (at->minute < time_end)) {
+ D(("time is listed"));
+ pass = TRUE;
+ }
+ } else { /* spans two days */
+ if ((at->day & marked_day) && (at->minute >= time_start)) {
+ D(("caught on first day"));
+ pass = TRUE;
+ } else {
+ marked_day <<= 1;
+ marked_day |= (marked_day & 0200) ? 1:0;
+ D(("next day = 0%o", marked_day));
+ if ((at->day & marked_day) && (at->minute <= time_end)) {
+ D(("caught on second day"));
+ pass = TRUE;
+ }
+ }
+ }
+
+ return (not ^ pass);
+}
+
+static int check_account(const char *service
+ , const char *tty, const char *user)
+{
+ int from=0,to=0,fd=-1;
+ char *buffer=NULL;
+ int count=0;
+ TIME here_and_now;
+ int retval=PAM_SUCCESS;
+
+ here_and_now = time_now(); /* find current time */
+ do {
+ boolean good=TRUE,intime;
+
+ /* here we get the service name field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+
+ if (!buffer || !buffer[0]) {
+ /* empty line .. ? */
+ continue;
+ }
+ ++count;
+
+ good = logic_field(service, buffer, count, is_same);
+ D(("with service: %s", good ? "passes":"fails" ));
+
+ /* here we get the terminal name field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_TIME_CONF "; no tty entry #%d", count);
+ continue;
+ }
+ good &= logic_field(tty, buffer, count, is_same);
+ D(("with tty: %s", good ? "passes":"fails" ));
+
+ /* here we get the username field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_TIME_CONF "; no user entry #%d", count);
+ continue;
+ }
+ good &= logic_field(user, buffer, count, is_same);
+ D(("with user: %s", good ? "passes":"fails" ));
+
+ /* here we get the time field */
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (!buffer || !buffer[0]) {
+ _log_err(PAM_TIME_CONF "; no time entry #%d", count);
+ continue;
+ }
+
+ intime = logic_field(&here_and_now, buffer, count, check_time);
+ D(("with time: %s", intime ? "passes":"fails" ));
+
+ fd = read_field(fd,&buffer,&from,&to);
+ if (buffer && buffer[0]) {
+ _log_err(PAM_TIME_CONF "; poorly terminated rule #%d", count);
+ continue;
+ }
+
+ if (good && !intime) {
+ /*
+ * for security parse whole file.. also need to ensure
+ * that the buffer is free()'d and the file is closed.
+ */
+ retval = PAM_PERM_DENIED;
+ } else {
+ D(("rule passed"));
+ }
+ } while (buffer);
+
+ return retval;
+}
+
+/* --- public account management functions --- */
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ const char *service=NULL, *tty=NULL;
+ const char *user=NULL;
+
+ /* set service name */
+
+ if (pam_get_item(pamh, PAM_SERVICE, (const void **)&service)
+ != PAM_SUCCESS || service == NULL) {
+ _log_err("cannot find the current service name");
+ return PAM_ABORT;
+ }
+
+ /* set username */
+
+ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
+ || *user == '\0') {
+ _log_err("cannot determine the user's name");
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* set tty name */
+
+ if (pam_get_item(pamh, PAM_TTY, (const void **)&tty) != PAM_SUCCESS
+ || tty == NULL) {
+ D(("PAM_TTY not set, probing stdin"));
+ tty = ttyname(STDIN_FILENO);
+ if (tty == NULL) {
+ _log_err("couldn't get the tty name");
+ return PAM_ABORT;
+ }
+ if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
+ _log_err("couldn't set tty name");
+ return PAM_ABORT;
+ }
+ }
+
+ if (strncmp("/dev/",tty,5) == 0) { /* strip leading /dev/ */
+ tty += 5;
+ }
+
+ /* good, now we have the service name, the user and the terminal name */
+
+ D(("service=%s", service));
+ D(("user=%s", user));
+ D(("tty=%s", tty));
+
+ return check_account(service,tty,user);
+}
+
+/* end of module definition */
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_time_modstruct = {
+ "pam_time",
+ NULL,
+ NULL,
+ pam_sm_acct_mgmt,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
diff --git a/modules/pam_time/time.conf b/modules/pam_time/time.conf
new file mode 100644
index 00000000..d2062fdb
--- /dev/null
+++ b/modules/pam_time/time.conf
@@ -0,0 +1,64 @@
+# this is an example configuration file for the pam_time module. Its syntax
+# was initially based heavily on that of the shadow package (shadow-960129).
+#
+# the syntax of the lines is as follows:
+#
+# services;ttys;users;times
+#
+# white space is ignored and lines maybe extended with '\\n' (escaped
+# newlines). As should be clear from reading these comments,
+# text following a '#' is ignored to the end of the line.
+#
+# the combination of individual users/terminals etc is a logic list
+# namely individual tokens that are optionally prefixed with '!' (logical
+# not) and separated with '&' (logical and) and '|' (logical or).
+#
+# services
+# is a logic list of PAM service names that the rule applies to.
+#
+# ttys
+# is a logic list of terminal names that this rule applies to.
+#
+# users
+# is a logic list of users to whom this rule applies.
+#
+# NB. For these items the simple wildcard '*' may be used only once.
+#
+# times
+# the format here is a logic list of day/time-range
+# entries the days are specified by a sequence of two character
+# entries, MoTuSa for example is Monday Tuesday and Saturday. Note
+# that repeated days are unset MoMo = no day, and MoWk = all weekdays
+# bar Monday. The two character combinations accepted are
+#
+# Mo Tu We Th Fr Sa Su Wk Wd Al
+#
+# the last two being week-end days and all 7 days of the week
+# respectively. As a final example, AlFr means all days except Friday.
+#
+# each day/time-range can be prefixed with a '!' to indicate "anything
+# but"
+#
+# The time-range part is two 24-hour times HHMM separated by a hyphen
+# indicating the start and finish time (if the finish time is smaller
+# than the start time it is deemed to apply on the following day).
+#
+# for a rule to be active, ALL of service+ttys+users must be satisfied
+# by the applying process.
+#
+
+#
+# Here is a simple example: running blank on tty* (any ttyXXX device),
+# the users 'you' and 'me' are denied service all of the time
+#
+
+#blank;tty* & !ttyp*;you|me;!Al0000-2400
+
+# Another silly example, user 'root' is denied xsh access
+# from pseudo terminals at the weekend and on mondays.
+
+#xsh;ttyp*;root;!WdMo0000-2400
+
+#
+# End of example file.
+# \ No newline at end of file
diff --git a/modules/pam_unix/.cvsignore b/modules/pam_unix/.cvsignore
new file mode 100644
index 00000000..64c5ce5c
--- /dev/null
+++ b/modules/pam_unix/.cvsignore
@@ -0,0 +1,4 @@
+dynamic
+unix_chkpwd
+*.so
+*~
diff --git a/modules/pam_unix/CHANGELOG b/modules/pam_unix/CHANGELOG
new file mode 100644
index 00000000..1476b579
--- /dev/null
+++ b/modules/pam_unix/CHANGELOG
@@ -0,0 +1,55 @@
+$Id$
+
+* Mon Aug 16 1999 Jan Rêkorajski <baggins@pld.org.pl>
+- fixed reentrancy problems
+
+* Sun Jul 4 21:03:42 PDT 1999
+
+- temporarily removed the crypt16 stuff. I'm really paranoid about
+ crypto stuff and exporting it, and there are a few too many 's-box'
+ references in the code for my liking..
+
+* Wed Jun 30 1999 Steve Langasek <vorlon@netexpress.net>
+- further NIS+ fixes
+
+* Sun Jun 27 1999 Steve Langasek <vorlon@netexpress.net>
+- fix to uid-handling code for NIS+
+
+* Sat Jun 26 1999 Jan Rêkorajski <baggins@mimuw.edu.pl>
+- merged MD5 fix and early failure syslog
+ by Andrey Vladimirovich Savochkin <saw@msu.ru>
+- minor fixes
+- added signal handler to unix_chkpwd
+
+* Fri Jun 25 1999 Stephen Langasek <vorlon@netexpress.net>
+- reorganized the code to let it build as separate C files
+
+* Sun Jun 20 1999 Jan Rêkorajski <baggins@mimuw.edu.pl>
+- fixes in pam_unix_auth, it incorrectly saved and restored return
+ value when likeauth option was used
+
+* Tue Jun 15 1999 Jan Rêkorajski <baggins@mimuw.edu.pl>
+- added NIS+ support
+
+* Mon Jun 14 1999 Jan Rêkorajski <baggins@mimuw.edu.pl>
+- total rewrite based on pam_pwdb module, now there is ONE pam_unix.so
+ module, it accepts the same options as pam_pwdb - all of them correctly ;)
+ (pam_pwdb dosn't understand what DISALLOW_NULL_AUTHTOK means)
+
+* Tue Apr 20 1999 Jan Rêkorajski <baggins@mimuw.edu.pl>
+- Arghhh, pam_unix_passwd was not updating /etc/shadow when used with
+ pam_cracklib.
+
+* Mon Apr 19 1999 Jan Rêkorajski <baggins@mimuw.edu.pl>
+- added "remember=XXX" option that means 'remember XXX old passwords'
+ Old passwords are stored in /etc/security/opasswd, there can be
+ maximum of 400 passwords per user.
+
+* Sat Mar 27 1999 Jan Rêkorajski <baggins@mimuw.edu.pl>
+- added crypt16 to pam_unix_auth and pam_unix_passwd (check only, this algorithm
+ is too lame to use it in real life)
+
+* Sun Mar 21 1999 Jan Rêkorajski <baggins@mimuw.edu.pl>
+- pam_unix_auth now correctly behave when user has NULL AUTHTOK
+- pam_unix_auth returns PAM_PERM_DENIED when seteuid fails
+
diff --git a/modules/pam_unix/Makefile b/modules/pam_unix/Makefile
new file mode 100644
index 00000000..89b33cfd
--- /dev/null
+++ b/modules/pam_unix/Makefile
@@ -0,0 +1,155 @@
+# $Id$
+#
+# This Makefile controls a build process of the pam_unix modules
+# for Linux-PAM. You should not modify this Makefile.
+#
+
+########################################################################
+# some options... uncomment to take effect
+########################################################################
+
+# do you want cracklib?
+ifeq ($(HAVE_CRACKLIB),yes)
+USE_CRACKLIB=-D"USE_CRACKLIB"
+endif
+
+# do you want to use lckpwdf?
+USE_LCKPWDF=-D"USE_LCKPWDF"
+
+# do you need to include the locking functions in the source?
+#NEED_LCKPWDF=-D"NEED_LCKPWDF"
+
+ifeq ($(shell ./need_nsl.sh),yes)
+LIBNSL = -lnsl
+endif
+
+CHKPWD=unix_chkpwd
+
+EXTRAS += -DCHKPWD_HELPER=\"$(SUPLEMENTED)/$(CHKPWD)\"
+
+########################################################################
+
+CFLAGS += $(USE_CRACKLIB) $(USE_LCKPWDF) $(NEED_LCKPWDF) $(EXTRAS)
+LDLIBS = $(EXTRALS)
+
+ifdef USE_CRACKLIB
+CRACKLIB = -lcrack
+endif
+
+
+LIBOBJ = pam_unix_auth.o pam_unix_acct.o pam_unix_sess.o pam_unix_passwd.o \
+ support.o
+LIBSRC = pam_unix_auth.c pam_unix_acct.c pam_unix_sess.c pam_unix_passwd.c \
+ support.c
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+PLUS = md5_good.o md5_broken.o md5_crypt_good.o md5_crypt_broken.o \
+ yppasswd_xdr.o bigcrypt.o
+
+ifdef DYNAMIC
+LIBSHARED = pam_unix.so
+endif
+ifdef STATIC
+LIBSTATIC = libpam_unix.o
+endif
+
+
+########################### don't edit below #######################
+
+all: dirs info $(PLUS) $(LIBSHARED) $(LIBSTATIC) $(CHKPWD) register
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o: %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+dummy:
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+info:
+ @echo
+ @echo "*** Building pam-unix module of the framework..."
+ @echo
+
+dirs:
+ifdef DYNAMIC
+ mkdir -p ./dynamic
+endif
+ifdef STATIC
+ mkdir -p ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static pam_unix_auth pam_unix/$(LIBSTATIC) ; \
+ ./register_static pam_unix_acct "" ; \
+ ./register_static pam_unix_session "" ; \
+ ./register_static pam_unix_passwd "" ; \
+ )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(PLUS) $(CRACKLIB) $(LDLIBS) $(LIBNSL)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS) $(PLUS) $(CRACKLIB) $(LDLIBS) $(LIBNSL)
+endif
+
+$(CHKPWD): unix_chkpwd.o md5_good.o md5_broken.o \
+ md5_crypt_good.o md5_crypt_broken.o \
+ bigcrypt.o
+ $(CC) -o $(CHKPWD) $^ $(LDLIBS)
+
+unix_chkpwd.o: unix_chkpwd.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+md5_good.o: md5.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -DHIGHFIRST -D'MD5Name(x)=Good##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_broken.o: md5.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_good.o: md5_crypt.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Good##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+md5_crypt_broken.o: md5_crypt.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \
+ $(TARGET_ARCH) -c $< -o $@
+
+install: all
+ mkdir -p $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ install -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+ ln -sf $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)/pam_unix_auth.so
+ ln -sf $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)/pam_unix_acct.so
+ ln -sf $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)/pam_unix_passwd.so
+ ln -sf $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)/pam_unix_session.so
+endif
+ install $(CHKPWD) $(FAKEROOT)$(SUPLEMENTED)
+
+remove:
+ cd $(FAKEROOT)$(SECUREDIR) && rm -f $(LIBSHARED)
+ rm -f $(FAKEROOT)$(SUPLEMENTED)/$(CHKPWD)
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) $(CHKPWD) *.o *.so core
+
+extraclean: clean
+ rm -f *~ *.a *.out *.bak
+
+.c.o:
+ $(CC) -c $(CFLAGS) $<
+
diff --git a/modules/pam_unix/README b/modules/pam_unix/README
new file mode 100644
index 00000000..ad4bc35e
--- /dev/null
+++ b/modules/pam_unix/README
@@ -0,0 +1,39 @@
+This is the README for pam_unix in Linux-PAM-0.67.
+--------------------------------------------------
+
+pam_unix now comes as one module pam_unix.so.
+
+The following links are left for compatibility with old versions:
+pam_unix_auth: authentication module providing
+ pam_authenticate() and pam_setcred() hooks
+pam_unix_sess: session module, providing session logging
+pam_unix_acct: account management, providing shadow account
+ managment features, password aging etc..
+pam_unix_passwd: password updating facilities providing
+ cracklib password strength checking facilities.
+
+The following options are recognized:
+ debug - log more debugging info
+ audit - a little more extreme than debug
+ use_first_pass - don 't prompt the user for passwords
+ take them from PAM_ items instead
+ try_first_pass - don 't prompt the user for the passwords
+ unless PAM_(OLD)AUTHTOK is unset
+ use_authtok - like try_first_pass, but * fail * if the new
+ PAM_AUTHTOK has not been previously set.
+ (intended for stacking password modules only)
+ not_set_pass - don 't set the PAM_ items with the passwords
+ used by this module.
+ shadow - try to maintian a shadow based system.
+ md5 - when a user changes their password next,
+ encrypt it with the md5 algorithm.
+ bigcrypt - when a user changes their password next,
+ excrypt it with the DEC C2 - algorithm(0).
+ nodelay - used to prevent failed authentication
+ resulting in a delay of about 1 second.
+ nis - use NIS RPC for setting new password
+ remember=X - remember X old passwords, they are kept in
+ /etc/security/opasswd in MD5 crypted form
+
+ invalid arguments are logged to syslog.
+
diff --git a/modules/pam_unix/bigcrypt.c b/modules/pam_unix/bigcrypt.c
new file mode 100644
index 00000000..b1568d6b
--- /dev/null
+++ b/modules/pam_unix/bigcrypt.c
@@ -0,0 +1,119 @@
+/*
+ * This function implements the "bigcrypt" algorithm specifically for
+ * Linux-PAM.
+ *
+ * This algorithm is algorithm 0 (default) shipped with the C2 secure
+ * implementation of Digital UNIX.
+ *
+ * Disclaimer: This work is not based on the source code to Digital
+ * UNIX, nor am I connected to Digital Equipment Corp, in any way
+ * other than as a customer. This code is based on published
+ * interfaces and reasonable guesswork.
+ *
+ * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8
+ * characters or less. Each block is encrypted using the standard UNIX
+ * libc crypt function. The result of the encryption for one block
+ * provides the salt for the suceeding block.
+ *
+ * Restrictions: The buffer used to hold the encrypted result is
+ * statically allocated. (see MAX_PASS_LEN below). This is necessary,
+ * as the returned pointer points to "static data that are overwritten
+ * by each call", (XPG3: XSI System Interface + Headers pg 109), and
+ * this is a drop in replacement for crypt();
+ *
+ * Andy Phillips <atp@mssl.ucl.ac.uk>
+ */
+
+#include <string.h>
+#include <security/_pam_macros.h>
+
+char *crypt(const char *key, const char *salt);
+char *bigcrypt(const char *key, const char *salt);
+
+/*
+ * Max cleartext password length in segments of 8 characters this
+ * function can deal with (16 segments of 8 chars= max 128 character
+ * password).
+ */
+
+#define MAX_PASS_LEN 16
+#define SEGMENT_SIZE 8
+#define SALT_SIZE 2
+#define KEYBUF_SIZE ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE)
+#define ESEGMENT_SIZE 11
+#define CBUF_SIZE ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1)
+
+char *bigcrypt(const char *key, const char *salt)
+{
+ static char dec_c2_cryptbuf[CBUF_SIZE]; /* static storage area */
+
+ unsigned long int keylen, n_seg, j;
+ char *cipher_ptr, *plaintext_ptr, *tmp_ptr, *salt_ptr;
+ char keybuf[KEYBUF_SIZE + 1];
+
+ D(("called with key='%s', salt='%s'.", key, salt));
+
+ /* reset arrays */
+ memset(keybuf, 0, KEYBUF_SIZE + 1);
+ memset(dec_c2_cryptbuf, 0, CBUF_SIZE);
+
+ /* fill KEYBUF_SIZE with key */
+ strncpy(keybuf, key, KEYBUF_SIZE);
+
+ /* deal with case that we are doing a password check for a
+ conventially encrypted password: the salt will be
+ SALT_SIZE+ESEGMENT_SIZE long. */
+ if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE))
+ keybuf[SEGMENT_SIZE] = '\0'; /* terminate password early(?) */
+
+ keylen = strlen(keybuf);
+
+ if (!keylen) {
+ n_seg = 1;
+ } else {
+ /* work out how many segments */
+ n_seg = 1 + ((keylen - 1) / SEGMENT_SIZE);
+ }
+
+ if (n_seg > MAX_PASS_LEN)
+ n_seg = MAX_PASS_LEN; /* truncate at max length */
+
+ /* set up some pointers */
+ cipher_ptr = dec_c2_cryptbuf;
+ plaintext_ptr = keybuf;
+
+ /* do the first block with supplied salt */
+ tmp_ptr = crypt(plaintext_ptr, salt); /* libc crypt() */
+
+ /* and place in the static area */
+ strncpy(cipher_ptr, tmp_ptr, 13);
+ cipher_ptr += ESEGMENT_SIZE + SALT_SIZE;
+ plaintext_ptr += SEGMENT_SIZE; /* first block of SEGMENT_SIZE */
+
+ /* change the salt (1st 2 chars of previous block) - this was found
+ by dowsing */
+
+ salt_ptr = cipher_ptr - ESEGMENT_SIZE;
+
+ /* so far this is identical to "return crypt(key, salt);", if
+ there is more than one block encrypt them... */
+
+ if (n_seg > 1) {
+ for (j = 2; j <= n_seg; j++) {
+
+ tmp_ptr = crypt(plaintext_ptr, salt_ptr);
+
+ /* skip the salt for seg!=0 */
+ strncpy(cipher_ptr, (tmp_ptr + SALT_SIZE), ESEGMENT_SIZE);
+
+ cipher_ptr += ESEGMENT_SIZE;
+ plaintext_ptr += SEGMENT_SIZE;
+ salt_ptr = cipher_ptr - ESEGMENT_SIZE;
+ }
+ }
+ D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf));
+
+ /* this is the <NUL> terminated encrypted password */
+
+ return dec_c2_cryptbuf;
+}
diff --git a/modules/pam_unix/lckpwdf.-c b/modules/pam_unix/lckpwdf.-c
new file mode 100644
index 00000000..b5ff4585
--- /dev/null
+++ b/modules/pam_unix/lckpwdf.-c
@@ -0,0 +1,117 @@
+/*
+ * This is a hack, but until libc and glibc both include this function
+ * by default (libc only includes it if nys is not being used, at the
+ * moment, and glibc doesn't appear to have it at all) we need to have
+ * it here, too. :-(
+ *
+ * This should not become an official part of PAM.
+ *
+ * BEGIN_HACK
+ */
+
+/*
+ * lckpwdf.c -- prevent simultaneous updates of password files
+ *
+ * Before modifying any of the password files, call lckpwdf(). It may block
+ * for up to 15 seconds trying to get the lock. Return value is 0 on success
+ * or -1 on failure. When you are done, call ulckpwdf() to release the lock.
+ * The lock is also released automatically when the process exits. Only one
+ * process at a time may hold the lock.
+ *
+ * These functions are supposed to be conformant with AT&T SVID Issue 3.
+ *
+ * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
+ * public domain.
+ */
+
+#include <fcntl.h>
+#include <signal.h>
+
+#define LOCKFILE "/etc/.pwd.lock"
+#define TIMEOUT 15
+
+static int lockfd = -1;
+
+static int set_close_on_exec(int fd)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+ if (flags == -1)
+ return -1;
+ flags |= FD_CLOEXEC;
+ return fcntl(fd, F_SETFD, flags);
+}
+
+static int do_lock(int fd)
+{
+ struct flock fl;
+
+ memset(&fl, 0, sizeof fl);
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ return fcntl(fd, F_SETLKW, &fl);
+}
+
+static void alarm_catch(int sig)
+{
+/* does nothing, but fcntl F_SETLKW will fail with EINTR */
+}
+
+static int lckpwdf(void)
+{
+ struct sigaction act, oldact;
+ sigset_t set, oldset;
+
+ if (lockfd != -1)
+ return -1;
+
+ lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
+ if (lockfd == -1)
+ return -1;
+ if (set_close_on_exec(lockfd) == -1)
+ goto cleanup_fd;
+
+ memset(&act, 0, sizeof act);
+ act.sa_handler = alarm_catch;
+ act.sa_flags = 0;
+ sigfillset(&act.sa_mask);
+ if (sigaction(SIGALRM, &act, &oldact) == -1)
+ goto cleanup_fd;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+ if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1)
+ goto cleanup_sig;
+
+ alarm(TIMEOUT);
+ if (do_lock(lockfd) == -1)
+ goto cleanup_alarm;
+ alarm(0);
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ sigaction(SIGALRM, &oldact, NULL);
+ return 0;
+
+ cleanup_alarm:
+ alarm(0);
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ cleanup_sig:
+ sigaction(SIGALRM, &oldact, NULL);
+ cleanup_fd:
+ close(lockfd);
+ lockfd = -1;
+ return -1;
+}
+
+static int ulckpwdf(void)
+{
+ unlink(LOCKFILE);
+ if (lockfd == -1)
+ return -1;
+
+ if (close(lockfd) == -1) {
+ lockfd = -1;
+ return -1;
+ }
+ lockfd = -1;
+ return 0;
+}
+/* END_HACK */
diff --git a/modules/pam_unix/md5.c b/modules/pam_unix/md5.c
new file mode 100644
index 00000000..d88d6810
--- /dev/null
+++ b/modules/pam_unix/md5.c
@@ -0,0 +1,256 @@
+/*
+ * $Id$
+ *
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32 t;
+ do {
+ t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Name(MD5Init)(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301U;
+ ctx->buf[1] = 0xefcdab89U;
+ ctx->buf[2] = 0x98badcfeU;
+ ctx->buf[3] = 0x10325476U;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len)
+{
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *) ctx->in)[14] = ctx->bits[0];
+ ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16])
+{
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
diff --git a/modules/pam_unix/md5.h b/modules/pam_unix/md5.h
new file mode 100644
index 00000000..469e5bd1
--- /dev/null
+++ b/modules/pam_unix/md5.h
@@ -0,0 +1,35 @@
+
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __alpha
+typedef unsigned int uint32;
+#else
+typedef unsigned long uint32;
+#endif
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+void GoodMD5Init(struct MD5Context *);
+void GoodMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void GoodMD5Final(unsigned char digest[16], struct MD5Context *);
+void GoodMD5Transform(uint32 buf[4], uint32 const in[16]);
+void BrokenMD5Init(struct MD5Context *);
+void BrokenMD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void BrokenMD5Final(unsigned char digest[16], struct MD5Context *);
+void BrokenMD5Transform(uint32 buf[4], uint32 const in[16]);
+
+char *Goodcrypt_md5(const char *pw, const char *salt);
+char *Brokencrypt_md5(const char *pw, const char *salt);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+
+typedef struct MD5Context MD5_CTX;
+
+#endif /* MD5_H */
diff --git a/modules/pam_unix/md5_crypt.c b/modules/pam_unix/md5_crypt.c
new file mode 100644
index 00000000..a7243a2e
--- /dev/null
+++ b/modules/pam_unix/md5_crypt.c
@@ -0,0 +1,149 @@
+/*
+ * $Id$
+ *
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * Origin: Id: crypt.c,v 1.3 1995/05/30 05:42:22 rgrimes Exp
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void to64(char *s, unsigned long v, int n)
+{
+ while (--n >= 0) {
+ *s++ = itoa64[v & 0x3f];
+ v >>= 6;
+ }
+}
+
+/*
+ * UNIX password
+ *
+ * Use MD5 for what it is best at...
+ */
+
+char *MD5Name(crypt_md5)(const char *pw, const char *salt)
+{
+ const char *magic = "$1$";
+ /* This string is magic for this algorithm. Having
+ * it this way, we can get get better later on */
+ static char passwd[120], *p;
+ static const char *sp, *ep;
+ unsigned char final[16];
+ int sl, pl, i, j;
+ MD5_CTX ctx, ctx1;
+ unsigned long l;
+
+ /* Refine the Salt first */
+ sp = salt;
+
+ /* If it starts with the magic string, then skip that */
+ if (!strncmp(sp, magic, strlen(magic)))
+ sp += strlen(magic);
+
+ /* It stops at the first '$', max 8 chars */
+ for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
+ continue;
+
+ /* get the length of the true salt */
+ sl = ep - sp;
+
+ MD5Name(MD5Init)(&ctx);
+
+ /* The password first, since that is what is most unknown */
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)pw,strlen(pw));
+
+ /* Then our magic string */
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)magic,strlen(magic));
+
+ /* Then the raw salt */
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)sp,sl);
+
+ /* Then just as many characters of the MD5(pw,salt,pw) */
+ MD5Name(MD5Init)(&ctx1);
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ MD5Name(MD5Final)(final,&ctx1);
+ for (pl = strlen(pw); pl > 0; pl -= 16)
+ MD5Name(MD5Update)(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl);
+
+ /* Don't leave anything around in vm they could use. */
+ memset(final, 0, sizeof final);
+
+ /* Then something really weird... */
+ for (j = 0, i = strlen(pw); i; i >>= 1)
+ if (i & 1)
+ MD5Name(MD5Update)(&ctx, (unsigned const char *)final+j, 1);
+ else
+ MD5Name(MD5Update)(&ctx, (unsigned const char *)pw+j, 1);
+
+ /* Now make the output string */
+ strcpy(passwd, magic);
+ strncat(passwd, sp, sl);
+ strcat(passwd, "$");
+
+ MD5Name(MD5Final)(final,&ctx);
+
+ /*
+ * and now, just to make sure things don't run too fast
+ * On a 60 Mhz Pentium this takes 34 msec, so you would
+ * need 30 seconds to build a 1000 entry dictionary...
+ */
+ for (i = 0; i < 1000; i++) {
+ MD5Name(MD5Init)(&ctx1);
+ if (i & 1)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ else
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
+
+ if (i % 3)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
+
+ if (i % 7)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+
+ if (i & 1)
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
+ else
+ MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
+ MD5Name(MD5Final)(final,&ctx1);
+ }
+
+ p = passwd + strlen(passwd);
+
+ l = (final[0] << 16) | (final[6] << 8) | final[12];
+ to64(p, l, 4);
+ p += 4;
+ l = (final[1] << 16) | (final[7] << 8) | final[13];
+ to64(p, l, 4);
+ p += 4;
+ l = (final[2] << 16) | (final[8] << 8) | final[14];
+ to64(p, l, 4);
+ p += 4;
+ l = (final[3] << 16) | (final[9] << 8) | final[15];
+ to64(p, l, 4);
+ p += 4;
+ l = (final[4] << 16) | (final[10] << 8) | final[5];
+ to64(p, l, 4);
+ p += 4;
+ l = final[11];
+ to64(p, l, 2);
+ p += 2;
+ *p = '\0';
+
+ /* Don't leave anything around in vm they could use. */
+ memset(final, 0, sizeof final);
+
+ return passwd;
+}
diff --git a/modules/pam_unix/need_nsl.sh b/modules/pam_unix/need_nsl.sh
new file mode 100755
index 00000000..23f38ec0
--- /dev/null
+++ b/modules/pam_unix/need_nsl.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+list=`/bin/ls /lib/libnsl.so.* 2> /dev/null`
+if [ -z "$list" ]; then
+ echo no
+else
+ echo yes
+fi
diff --git a/modules/pam_unix/pam_unix_acct.c b/modules/pam_unix/pam_unix_acct.c
new file mode 100644
index 00000000..f86f56e5
--- /dev/null
+++ b/modules/pam_unix/pam_unix_acct.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright Elliot Lee, 1996. All rights reserved.
+ * Copyright Jan Rêkorajski, 1999. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _BSD_SOURCE
+
+#ifdef linux
+#define _GNU_SOURCE
+#include <features.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <time.h> /* for time() */
+
+
+#include <security/_pam_macros.h>
+
+/* indicate that the following groups are defined */
+
+#define PAM_SM_ACCOUNT
+
+#include <security/pam_modules.h>
+
+#ifndef LINUX_PAM
+#include <security/pam_appl.h>
+#endif /* LINUX_PAM */
+
+#include "support.h"
+
+/*
+ * PAM framework looks for this entry-point to pass control to the
+ * account management module.
+ */
+
+PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ const char *uname;
+ int retval, daysleft;
+ time_t curdays;
+ struct spwd *spent;
+ struct passwd *pwent;
+ char buf[80];
+
+ D(("called."));
+
+ ctrl = _set_ctrl(flags, NULL, argc, argv);
+
+ retval = pam_get_item(pamh, PAM_USER, (const void **) &uname);
+ D(("user = `%s'", uname));
+ if (retval != PAM_SUCCESS || uname == NULL) {
+ _log_err(LOG_ALERT
+ ,"could not identify user (from uid=%d)"
+ ,getuid());
+ return PAM_USER_UNKNOWN;
+ }
+
+ pwent = getpwnam(uname);
+ if (!pwent) {
+ _log_err(LOG_ALERT
+ ,"could not identify user (from getpwnam(%s))"
+ ,uname);
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (!strcmp( pwent->pw_passwd, "*NP*" )) { /* NIS+ */
+ uid_t save_euid, save_uid;
+
+ save_euid = geteuid();
+ save_uid = getuid();
+ if (save_uid == pwent->pw_uid)
+ setreuid( save_euid, save_uid );
+ else {
+ setreuid( 0, -1 );
+ if (setreuid( -1, pwent->pw_uid ) == -1) {
+ setreuid( -1, 0 );
+ setreuid( 0, -1 );
+ if(setreuid( -1, pwent->pw_uid ) == -1)
+ return PAM_CRED_INSUFFICIENT;
+ }
+ }
+ spent = getspnam( uname );
+ if (save_uid == pwent->pw_uid)
+ setreuid( save_uid, save_euid );
+ else {
+ if (setreuid( -1, 0 ) == -1)
+ setreuid( save_uid, -1 );
+ setreuid( -1, save_euid );
+ }
+
+ } else if (!strcmp( pwent->pw_passwd, "x" )) {
+ spent = getspnam(uname);
+ } else {
+ return PAM_SUCCESS;
+ }
+
+ if (!spent)
+ return PAM_AUTHINFO_UNAVAIL; /* Couldn't get username from shadow */
+
+ curdays = time(NULL) / (60 * 60 * 24);
+ D(("today is %d, last change %d", curdays, spent->sp_lstchg));
+ if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)
+ && (spent->sp_lstchg != 0)) {
+ _log_err(LOG_NOTICE
+ ,"account %s has expired (account expired)"
+ ,uname);
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ "Your account has expired; please contact your system administrator");
+ D(("account expired"));
+ return PAM_ACCT_EXPIRED;
+ }
+ if ((curdays > (spent->sp_lstchg + spent->sp_max + spent->sp_inact))
+ && (spent->sp_max != -1) && (spent->sp_inact != -1)
+ && (spent->sp_lstchg != 0)) {
+ _log_err(LOG_NOTICE
+ ,"account %s has expired (failed to change password)"
+ ,uname);
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ "Your account has expired; please contact your system administrator");
+ D(("account expired 2"));
+ return PAM_ACCT_EXPIRED;
+ }
+ D(("when was the last change"));
+ if (spent->sp_lstchg == 0) {
+ _log_err(LOG_NOTICE
+ ,"expired password for user %s (root enforced)"
+ ,uname);
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ "You are required to change your password immediately (root enforced)");
+ D(("need a new password"));
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+ if (((spent->sp_lstchg + spent->sp_max) < curdays) && (spent->sp_max != -1)) {
+ _log_err(LOG_DEBUG
+ ,"expired password for user %s (password aged)"
+ ,uname);
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ "You are required to change your password immediately (password aged)");
+ D(("need a new password 2"));
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+ if ((curdays > (spent->sp_lstchg + spent->sp_max - spent->sp_warn))
+ && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
+ daysleft = (spent->sp_lstchg + spent->sp_max) - curdays;
+ _log_err(LOG_DEBUG
+ ,"password for user %s will expire in %d days"
+ ,uname, daysleft);
+ snprintf(buf, 80, "Warning: your password will expire in %d day%.2s",
+ daysleft, daysleft == 1 ? "" : "s");
+ _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
+ }
+
+ D(("all done"));
+
+ return PAM_SUCCESS;
+}
+
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_unix_acct_modstruct = {
+ "pam_unix_acct",
+ NULL,
+ NULL,
+ pam_sm_acct_mgmt,
+ NULL,
+ NULL,
+ NULL,
+};
+#endif
diff --git a/modules/pam_unix/pam_unix_auth.c b/modules/pam_unix/pam_unix_auth.c
new file mode 100644
index 00000000..3c301df0
--- /dev/null
+++ b/modules/pam_unix/pam_unix_auth.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright Alexander O. Yuriev, 1996. All rights reserved.
+ * NIS+ support by Thorsten Kukuk <kukuk@weber.uni-paderborn.de>
+ * Copyright Jan Rêkorajski, 1999. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* #define DEBUG */
+
+#ifdef linux
+#define _GNU_SOURCE
+#include <features.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* indicate the following groups are defined */
+
+#define PAM_SM_AUTH
+
+#define _PAM_EXTERN_FUNCTIONS
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+
+#ifndef LINUX_PAM
+#include <security/pam_appl.h>
+#endif /* LINUX_PAM */
+
+#include "support.h"
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * authentication module.
+ */
+
+/* Fun starts here :)
+
+ * pam_sm_authenticate() performs UNIX/shadow authentication
+ *
+ * First, if shadow support is available, attempt to perform
+ * authentication using shadow passwords. If shadow is not
+ * available, or user does not have a shadow password, fallback
+ * onto a normal UNIX authentication
+ */
+
+#define _UNIX_AUTHTOK "-UN*X-PASS"
+
+#define AUTH_RETURN \
+{ \
+ if (on(UNIX_LIKE_AUTH, ctrl)) { \
+ D(("recording return code for next time [%d]", \
+ retval)); \
+ pam_set_data(pamh, "unix_setcred_return", \
+ (void *) &retval, NULL); \
+ } \
+ D(("done. [%s]", pam_strerror(pamh, retval))); \
+ return retval; \
+}
+
+PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags
+ ,int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+ const char *name, *p;
+
+ D(("called."));
+
+ ctrl = _set_ctrl(flags, NULL, argc, argv);
+
+ /* get the user'name' */
+
+ retval = pam_get_user(pamh, &name, "login: ");
+ if (retval == PAM_SUCCESS) {
+ /*
+ * Various libraries at various times have had bugs related to
+ * '+' or '-' as the first character of a user name. Don't take
+ * any chances here. Require that the username starts with an
+ * alphanumeric character.
+ */
+ if (name == NULL || !isalnum(*name)) {
+ _log_err(LOG_ERR, "bad username [%s]", name);
+ retval = PAM_USER_UNKNOWN;
+ AUTH_RETURN
+ }
+ if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
+ D(("username [%s] obtained", name));
+ } else {
+ D(("trouble reading username"));
+ if (retval == PAM_CONV_AGAIN) {
+ D(("pam_get_user/conv() function is not ready yet"));
+ /* it is safe to resume this function so we translate this
+ * retval to the value that indicates we're happy to resume.
+ */
+ retval = PAM_INCOMPLETE;
+ }
+ AUTH_RETURN
+ }
+
+ /* if this user does not have a password... */
+
+ if (_unix_blankpasswd(ctrl, name)) {
+ D(("user '%s' has blank passwd", name));
+ name = NULL;
+ retval = PAM_SUCCESS;
+ AUTH_RETURN
+ }
+ /* get this user's authentication token */
+
+ retval = _unix_read_password(pamh, ctrl, NULL, "Password: ", NULL
+ ,_UNIX_AUTHTOK, &p);
+ if (retval != PAM_SUCCESS) {
+ if (retval != PAM_CONV_AGAIN) {
+ _log_err(LOG_CRIT, "auth could not identify password for [%s]"
+ ,name);
+ } else {
+ D(("conversation function is not ready yet"));
+ /*
+ * it is safe to resume this function so we translate this
+ * retval to the value that indicates we're happy to resume.
+ */
+ retval = PAM_INCOMPLETE;
+ }
+ name = NULL;
+ AUTH_RETURN
+ }
+ D(("user=%s, password=[%s]", name, p));
+
+ /* verify the password of this user */
+ retval = _unix_verify_password(pamh, name, p, ctrl);
+ name = p = NULL;
+
+ AUTH_RETURN
+}
+
+
+/*
+ * The only thing _pam_set_credentials_unix() does is initialization of
+ * UNIX group IDs.
+ *
+ * Well, everybody but me on linux-pam is convinced that it should not
+ * initialize group IDs, so I am not doing it but don't say that I haven't
+ * warned you. -- AOY
+ */
+
+PAM_EXTERN int pam_sm_setcred(pam_handle_t * pamh, int flags
+ ,int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ /* FIXME: it shouldn't be necessary to parse the arguments again. The
+ only argument we need is UNIX_LIKE_AUTH: if it was set,
+ pam_get_data will succeed. If it wasn't, it will fail, and we
+ return PAM_SUCCESS. -SRL */
+ ctrl = _set_ctrl(flags, NULL, argc, argv);
+ retval = PAM_SUCCESS;
+
+ if (on(UNIX_LIKE_AUTH, ctrl)) {
+ int *pretval = &retval;
+
+ D(("recovering return code from auth call"));
+ pam_get_data(pamh, "unix_setcred_return", (const void **) &pretval);
+ pam_set_data(pamh, "unix_setcred_return", NULL, NULL);
+ D(("recovered data indicates that old retval was %d", retval));
+ }
+ return retval;
+}
+
+#ifdef PAM_STATIC
+struct pam_module _pam_unix_auth_modstruct = {
+ "pam_unix_auth",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+#endif
diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c
new file mode 100644
index 00000000..cfa294f8
--- /dev/null
+++ b/modules/pam_unix/pam_unix_passwd.c
@@ -0,0 +1,985 @@
+/*
+ * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
+ * Copyright (C) 1996.
+ * Copyright (c) Jan Rêkorajski, 1999.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _BSD_SOURCE
+#define __USE_SVID
+
+#ifdef linux
+#define _GNU_SOURCE
+#include <features.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <shadow.h>
+#include <time.h> /* for time() */
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+#ifdef USE_CRACKLIB
+#include <crack.h>
+#endif
+
+#include <security/_pam_macros.h>
+
+/* indicate the following groups are defined */
+
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+
+#ifndef LINUX_PAM
+#include <security/pam_appl.h>
+#endif /* LINUX_PAM */
+
+#include "yppasswd.h"
+#include "md5.h"
+#include "support.h"
+
+#if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
+extern int getrpcport(const char *host, unsigned long prognum,
+ unsigned long versnum, unsigned int proto);
+#endif /* GNU libc 2.1 */
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * password changing module.
+ */
+
+#ifdef NEED_LCKPWDF
+#include "./lckpwdf.-c"
+#endif
+
+extern char *bigcrypt(const char *key, const char *salt);
+
+/*
+ How it works:
+ Gets in username (has to be done) from the calling program
+ Does authentication of user (only if we are not running as root)
+ Gets new password/checks for sanity
+ Sets it.
+ */
+
+/* passwd/salt conversion macros */
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* data tokens */
+
+#define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
+#define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
+
+#define MAX_PASSWD_TRIES 3
+#define PW_TMPFILE "/etc/npasswd"
+#define SH_TMPFILE "/etc/nshadow"
+#define CRACKLIB_DICTS "/usr/share/dict/cracklib_dict"
+#define OPW_TMPFILE "/etc/security/nopasswd"
+#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+
+/*
+ * i64c - convert an integer to a radix 64 character
+ */
+static int i64c(int i)
+{
+ if (i < 0)
+ return ('.');
+ else if (i > 63)
+ return ('z');
+ if (i == 0)
+ return ('.');
+ if (i == 1)
+ return ('/');
+ if (i >= 2 && i <= 11)
+ return ('0' - 2 + i);
+ if (i >= 12 && i <= 37)
+ return ('A' - 12 + i);
+ if (i >= 38 && i <= 63)
+ return ('a' - 38 + i);
+ return ('\0');
+}
+
+static char *crypt_md5_wrapper(const char *pass_new)
+{
+ /*
+ * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
+ * removed use of static variables (AGM)
+ */
+
+ struct timeval tv;
+ MD5_CTX ctx;
+ unsigned char result[16];
+ char *cp = (char *) result;
+ unsigned char tmp[16];
+ int i;
+ char *x, *e = NULL;
+
+ GoodMD5Init(&ctx);
+ gettimeofday(&tv, (struct timezone *) 0);
+ GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
+ i = getpid();
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
+ i = clock();
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
+ GoodMD5Update(&ctx, result, sizeof result);
+ GoodMD5Final(tmp, &ctx);
+ strcpy(cp, "$1$"); /* magic for the MD5 */
+ cp += strlen(cp);
+ for (i = 0; i < 8; i++)
+ *cp++ = i64c(tmp[i] & 077);
+ *cp = '\0';
+
+ /* no longer need cleartext */
+ e = Goodcrypt_md5(pass_new, (const char *) result);
+ x = x_strdup(e); /* put e in malloc()ed memory */
+ _pam_overwrite(e); /* clean up */
+
+ return x;
+}
+
+static char *getNISserver(void)
+{
+ char *master;
+ char *domainname;
+ int port, err;
+
+ if ((err = yp_get_default_domain(&domainname)) != 0) {
+ _log_err(LOG_WARNING, "can't get local yp domain: %s\n",
+ yperr_string(err));
+ return NULL;
+ }
+ if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
+ _log_err(LOG_WARNING, "can't find the master ypserver: %s\n",
+ yperr_string(err));
+ return NULL;
+ }
+ port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
+ if (port == 0) {
+ _log_err(LOG_WARNING, "yppasswdd not running on NIS master host\n");
+ return NULL;
+ }
+ if (port >= IPPORT_RESERVED) {
+ _log_err(LOG_WARNING, "yppasswd daemon running on illegal port.\n");
+ return NULL;
+ }
+ return master;
+}
+
+static int check_old_password(const char *forwho, const char *newpass)
+{
+ static char buf[16384];
+ char *s_luser, *s_uid, *s_npas, *s_pas;
+ int retval = PAM_SUCCESS;
+ FILE *opwfile;
+
+ opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+ if (opwfile == NULL)
+ return PAM_AUTHTOK_ERR;
+
+ while (fgets(buf, 16380, opwfile)) {
+ if (!strncmp(buf, forwho, strlen(forwho))) {
+ buf[strlen(buf) - 1] = '\0';
+ s_luser = strtok(buf, ":,");
+ s_uid = strtok(NULL, ":,");
+ s_npas = strtok(NULL, ":,");
+ s_pas = strtok(NULL, ":,");
+ while (s_pas != NULL) {
+ if (!strcmp(Goodcrypt_md5(newpass, s_pas), s_pas)) {
+ retval = PAM_AUTHTOK_ERR;
+ break;
+ }
+ s_pas = strtok(NULL, ":,");
+ }
+ break;
+ }
+ }
+ fclose(opwfile);
+
+ return retval;
+}
+
+static int save_old_password(const char *forwho, const char *oldpass, int howmany)
+{
+ static char buf[16384];
+ static char nbuf[16384];
+ char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
+ int retval = 0, npas;
+ FILE *pwfile, *opwfile;
+ int err = 0;
+ int oldmask;
+ int found = 0;
+ struct passwd *pwd = NULL;
+
+ if (howmany < 0)
+ return retval;
+
+ if (oldpass == NULL)
+ return retval;
+
+ oldmask = umask(077);
+ pwfile = fopen(OPW_TMPFILE, "w");
+ umask(oldmask);
+ opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+ if (pwfile == NULL || opwfile == NULL)
+ return PAM_AUTHTOK_ERR;
+ chown(OPW_TMPFILE, 0, 0);
+ chmod(OPW_TMPFILE, 0600);
+
+ while (fgets(buf, 16380, opwfile)) {
+ if (!strncmp(buf, forwho, strlen(forwho))) {
+ buf[strlen(buf) - 1] = '\0';
+ s_luser = strtok(buf, ":");
+ s_uid = strtok(NULL, ":");
+ s_npas = strtok(NULL, ":");
+ s_pas = strtok(NULL, ":");
+ npas = strtol(s_npas, NULL, 10) + 1;
+ while (npas > howmany) {
+ s_pas = strpbrk(s_pas, ",");
+ if (s_pas != NULL)
+ s_pas++;
+ npas--;
+ }
+ pass = crypt_md5_wrapper(oldpass);
+ if (s_pas == NULL)
+ sprintf(nbuf, "%s:%s:%d:%s\n", s_luser, s_uid, npas, pass);
+ else
+ sprintf(nbuf, "%s:%s:%d:%s,%s\n", s_luser, s_uid, npas, s_pas, pass);
+ if (fputs(nbuf, pwfile) < 0) {
+ retval = PAM_AUTHTOK_ERR;
+ err = 1;
+ break;
+ }
+ found = 1;
+ } else if (fputs(buf, pwfile) < 0) {
+ retval = PAM_AUTHTOK_ERR;
+ err = 1;
+ break;
+ }
+ }
+ fclose(opwfile);
+ if (!found) {
+ pwd = getpwnam(forwho);
+ if (pwd == NULL) {
+ retval = PAM_AUTHTOK_ERR;
+ err = 1;
+ } else {
+ pass = crypt_md5_wrapper(oldpass);
+ sprintf(nbuf, "%s:%d:1:%s\n", forwho, pwd->pw_uid, pass);
+ if (fputs(nbuf, pwfile) < 0) {
+ retval = PAM_AUTHTOK_ERR;
+ err = 1;
+ }
+ }
+ }
+ if (fclose(pwfile)) {
+ fprintf(stderr, "error writing entries to old passwords file: %s\n",
+ strerror(errno));
+ retval = PAM_AUTHTOK_ERR;
+ err = 1;
+ }
+ if (!err)
+ rename(OPW_TMPFILE, OLD_PASSWORDS_FILE);
+ else
+ unlink(OPW_TMPFILE);
+
+ return retval;
+}
+
+static int _update_passwd(const char *forwho, char *towhat)
+{
+ struct passwd *tmpent = NULL;
+ FILE *pwfile, *opwfile;
+ int retval = 0;
+ int err = 0;
+ int oldmask;
+
+ oldmask = umask(077);
+ pwfile = fopen(PW_TMPFILE, "w");
+ umask(oldmask);
+ opwfile = fopen("/etc/passwd", "r");
+ if (pwfile == NULL || opwfile == NULL)
+ return PAM_AUTHTOK_ERR;
+ chown(PW_TMPFILE, 0, 0);
+ chmod(PW_TMPFILE, 0644);
+ tmpent = fgetpwent(opwfile);
+ while (tmpent) {
+ if (!strcmp(tmpent->pw_name, forwho)) {
+ tmpent->pw_passwd = towhat;
+ }
+ if (putpwent(tmpent, pwfile)) {
+ fprintf(stderr, "error writing entry to password file: %s\n",
+ strerror(errno));
+ err = 1;
+ retval = PAM_AUTHTOK_ERR;
+ break;
+ }
+ tmpent = fgetpwent(opwfile);
+ }
+ fclose(opwfile);
+
+ if (fclose(pwfile)) {
+ fprintf(stderr, "error writing entries to password file: %s\n",
+ strerror(errno));
+ retval = PAM_AUTHTOK_ERR;
+ err = 1;
+ }
+ if (!err)
+ rename(PW_TMPFILE, "/etc/passwd");
+ else
+ unlink(PW_TMPFILE);
+
+ return retval;
+}
+
+static int _update_shadow(const char *forwho, char *towhat)
+{
+ struct spwd *spwdent = NULL, *stmpent = NULL;
+ FILE *pwfile, *opwfile;
+ int retval = 0;
+ int err = 0;
+ int oldmask;
+
+ spwdent = getspnam(forwho);
+ if (spwdent == NULL)
+ return PAM_USER_UNKNOWN;
+ oldmask = umask(077);
+ pwfile = fopen(SH_TMPFILE, "w");
+ umask(oldmask);
+ opwfile = fopen("/etc/shadow", "r");
+ if (pwfile == NULL || opwfile == NULL)
+ return PAM_AUTHTOK_ERR;
+ chown(SH_TMPFILE, 0, 0);
+ chmod(SH_TMPFILE, 0600);
+ stmpent = fgetspent(opwfile);
+ while (stmpent) {
+ if (!strcmp(stmpent->sp_namp, forwho)) {
+ stmpent->sp_pwdp = towhat;
+ stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
+
+ D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
+ }
+ if (putspent(stmpent, pwfile)) {
+ fprintf(stderr, "error writing entry to shadow file: %s\n",
+ strerror(errno));
+ err = 1;
+ retval = PAM_AUTHTOK_ERR;
+ break;
+ }
+ stmpent = fgetspent(opwfile);
+ }
+ fclose(opwfile);
+
+ if (fclose(pwfile)) {
+ fprintf(stderr, "error writing entries to shadow file: %s\n",
+ strerror(errno));
+ retval = PAM_AUTHTOK_ERR;
+ err = 1;
+ }
+ if (!err)
+ rename(SH_TMPFILE, "/etc/shadow");
+ else
+ unlink(SH_TMPFILE);
+
+ return retval;
+}
+
+static int _do_setpass(const char *forwho, char *fromwhat, char *towhat,
+ unsigned int ctrl, int remember)
+{
+ struct passwd *pwd = NULL;
+ int retval = 0;
+
+ D(("called"));
+
+ setpwent();
+ pwd = getpwnam(forwho);
+ endpwent();
+
+ if (pwd == NULL)
+ return PAM_AUTHTOK_ERR;
+
+ if (on(UNIX_NIS, ctrl)) {
+ struct timeval timeout;
+ struct yppasswd yppwd;
+ CLIENT *clnt;
+ char *master;
+ int status;
+ int err = 0;
+
+ /* Make RPC call to NIS server */
+ if ((master = getNISserver()) == NULL)
+ return PAM_TRY_AGAIN;
+
+ /* Initialize password information */
+ yppwd.newpw.pw_passwd = pwd->pw_passwd;
+ yppwd.newpw.pw_name = pwd->pw_name;
+ yppwd.newpw.pw_uid = pwd->pw_uid;
+ yppwd.newpw.pw_gid = pwd->pw_gid;
+ yppwd.newpw.pw_gecos = pwd->pw_gecos;
+ yppwd.newpw.pw_dir = pwd->pw_dir;
+ yppwd.newpw.pw_shell = pwd->pw_shell;
+ yppwd.oldpass = fromwhat;
+ yppwd.newpw.pw_passwd = towhat;
+
+ D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
+
+ /* The yppasswd.x file said `unix authentication required',
+ * so I added it. This is the only reason it is in here.
+ * My yppasswdd doesn't use it, but maybe some others out there
+ * do. --okir
+ */
+ clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
+ clnt->cl_auth = authunix_create_default();
+ memset((char *) &status, '\0', sizeof(status));
+ timeout.tv_sec = 25;
+ timeout.tv_usec = 0;
+ err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
+ (xdrproc_t) xdr_yppasswd, (char *) &yppwd,
+ (xdrproc_t) xdr_int, (char *) &status,
+ timeout);
+
+ if (err) {
+ clnt_perrno(err);
+ retval = PAM_TRY_AGAIN;
+ } else if (status) {
+ fprintf(stderr, "Error while changing NIS password.\n");
+ retval = PAM_TRY_AGAIN;
+ }
+ printf("\nThe password has%s been changed on %s.\n",
+ (err || status) ? " not" : "", master);
+
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ if ((err || status) != 0) {
+ retval = PAM_TRY_AGAIN;
+ }
+#ifdef DEBUG
+ sleep(5);
+#endif
+ return retval;
+ }
+ /* first, save old password */
+ if (save_old_password(forwho, fromwhat, remember)) {
+ return PAM_AUTHTOK_ERR;
+ }
+ if (on(UNIX_SHADOW, ctrl) || (strcmp(pwd->pw_passwd, "x") == 0)) {
+ retval = _update_shadow(forwho, towhat);
+ if (retval == PAM_SUCCESS)
+ retval = _update_passwd(forwho, "x");
+ } else {
+ retval = _update_passwd(forwho, towhat);
+ }
+
+ return retval;
+}
+
+static int _unix_verify_shadow(const char *user, unsigned int ctrl)
+{
+ struct passwd *pwd = NULL; /* Password and shadow password */
+ struct spwd *spwdent = NULL; /* file entries for the user */
+ time_t curdays;
+ int retval = PAM_SUCCESS;
+
+ /* UNIX passwords area */
+ setpwent();
+ pwd = getpwnam(user); /* Get password file entry... */
+ endpwent();
+ if (pwd == NULL)
+ return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */
+
+ if (strcmp(pwd->pw_passwd, "x") == 0) {
+ /* ...and shadow password file entry for this user, if shadowing
+ is enabled */
+ setspent();
+ spwdent = getspnam(user);
+ endspent();
+
+ if (spwdent == NULL)
+ return PAM_AUTHINFO_UNAVAIL;
+ } else {
+ if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
+ uid_t save_uid;
+
+ save_uid = geteuid();
+ seteuid (pwd->pw_uid);
+ spwdent = getspnam( user );
+ seteuid (save_uid);
+
+ if (spwdent == NULL)
+ return PAM_AUTHINFO_UNAVAIL;
+ } else
+ spwdent = NULL;
+ }
+
+ if (spwdent != NULL) {
+ /* We have the user's information, now let's check if their account
+ has expired (60 * 60 * 24 = number of seconds in a day) */
+
+ if (off(UNIX__IAMROOT, ctrl)) {
+ /* Get the current number of days since 1970 */
+ curdays = time(NULL) / (60 * 60 * 24);
+ if ((curdays < (spwdent->sp_lstchg + spwdent->sp_min))
+ && (spwdent->sp_min != -1))
+ retval = PAM_AUTHTOK_ERR;
+ else if ((curdays > (spwdent->sp_lstchg + spwdent->sp_max + spwdent->sp_inact))
+ && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
+ && (spwdent->sp_lstchg != 0))
+ /*
+ * Their password change has been put off too long,
+ */
+ retval = PAM_ACCT_EXPIRED;
+ else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
+ && (spwdent->sp_lstchg != 0))
+ /*
+ * OR their account has just plain expired
+ */
+ retval = PAM_ACCT_EXPIRED;
+ }
+ }
+ return retval;
+}
+
+static int _pam_unix_approve_pass(pam_handle_t * pamh
+ ,unsigned int ctrl
+ ,const char *pass_old
+ ,const char *pass_new)
+{
+ const char *user;
+ char *remark = NULL;
+ int retval = PAM_SUCCESS;
+
+ D(("&new=%p, &old=%p", pass_old, pass_new));
+ D(("new=[%s]", pass_new));
+ D(("old=[%s]", pass_old));
+
+ if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
+ if (on(UNIX_DEBUG, ctrl)) {
+ _log_err(LOG_DEBUG, "bad authentication token");
+ }
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
+ "No password supplied" : "Password unchanged");
+ return PAM_AUTHTOK_ERR;
+ }
+ /*
+ * if one wanted to hardwire authentication token strength
+ * checking this would be the place - AGM
+ */
+
+ retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
+ if (retval != PAM_SUCCESS) {
+ if (on(UNIX_DEBUG, ctrl)) {
+ _log_err(LOG_ERR, "Can not get username");
+ return PAM_AUTHTOK_ERR;
+ }
+ }
+ if (off(UNIX__IAMROOT, ctrl)) {
+#ifdef USE_CRACKLIB
+ remark = FascistCheck(pass_new, CRACKLIB_DICTS);
+ D(("called cracklib [%s]", remark));
+#else
+ if (strlen(pass_new) < 6)
+ remark = "You must choose a longer password";
+ D(("lenth check [%s]", remark));
+#endif
+ if (on(UNIX_REMEMBER_PASSWD, ctrl))
+ if ((retval = check_old_password(user, pass_new)) != PAM_SUCCESS)
+ remark = "Password has been already used. Choose another.";
+ }
+ if (remark) {
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
+ retval = PAM_AUTHTOK_ERR;
+ }
+ return retval;
+}
+
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl, lctrl;
+ int retval;
+ int remember = -1;
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+ char *pass_old, *pass_new;
+ /* </DO NOT free() THESE> */
+
+ D(("called."));
+
+#ifdef USE_LCKPWDF
+ /* our current locking system requires that we lock the
+ entire password database. This avoids both livelock
+ and deadlock. */
+ lckpwdf();
+#endif
+ ctrl = _set_ctrl(flags, &remember, argc, argv);
+
+ /*
+ * First get the name of a user
+ */
+ retval = pam_get_user(pamh, &user, "Username: ");
+ if (retval == PAM_SUCCESS) {
+ /*
+ * Various libraries at various times have had bugs related to
+ * '+' or '-' as the first character of a user name. Don't take
+ * any chances here. Require that the username starts with an
+ * alphanumeric character.
+ */
+ if (user == NULL || !isalnum(*user)) {
+ _log_err(LOG_ERR, "bad username [%s]", user);
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return PAM_USER_UNKNOWN;
+ }
+ if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
+ _log_err(LOG_DEBUG, "username [%s] obtained", user);
+ } else {
+ if (on(UNIX_DEBUG, ctrl))
+ _log_err(LOG_DEBUG, "password - could not identify user");
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+
+ D(("Got username of %s", user));
+
+ /*
+ * This is not an AUTH module!
+ */
+ if (on(UNIX__NONULL, ctrl))
+ set(UNIX__NULLOK, ctrl);
+
+ if (on(UNIX__PRELIM, ctrl)) {
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+ char *Announce;
+
+ D(("prelim check"));
+
+ if (_unix_blankpasswd(ctrl, user)) {
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return PAM_SUCCESS;
+ } else if (off(UNIX__IAMROOT, ctrl)) {
+
+ /* instruct user what is happening */
+#define greeting "Changing password for "
+ Announce = (char *) malloc(sizeof(greeting) + strlen(user));
+ if (Announce == NULL) {
+ _log_err(LOG_CRIT, "password - out of memory");
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return PAM_BUF_ERR;
+ }
+ (void) strcpy(Announce, greeting);
+ (void) strcpy(Announce + sizeof(greeting) - 1, user);
+#undef greeting
+
+ lctrl = ctrl;
+ set(UNIX__OLD_PASSWD, lctrl);
+ retval = _unix_read_password(pamh, lctrl
+ ,Announce
+ ,"(current) UNIX password: "
+ ,NULL
+ ,_UNIX_OLD_AUTHTOK
+ ,(const char **) &pass_old);
+ free(Announce);
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE
+ ,"password - (old) token not obtained");
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+ /* verify that this is the password for this user */
+
+ retval = _unix_verify_password(pamh, user, pass_old, ctrl);
+ } else {
+ D(("process run by root so do nothing this time around"));
+ pass_old = NULL;
+ retval = PAM_SUCCESS; /* root doesn't have too */
+ }
+
+ if (retval != PAM_SUCCESS) {
+ D(("Authentication failed"));
+ pass_old = NULL;
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+ retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
+ pass_old = NULL;
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
+ }
+ retval = _unix_verify_shadow(user, ctrl);
+ if (retval == PAM_AUTHTOK_ERR) {
+ if (off(UNIX__IAMROOT, ctrl))
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ "You must wait longer to change your password");
+ else
+ retval = PAM_SUCCESS;
+ }
+ } else if (on(UNIX__UPDATE, ctrl)) {
+ /*
+ * tpass is used below to store the _pam_md() return; it
+ * should be _pam_delete()'d.
+ */
+
+ char *tpass = NULL;
+ int retry = 0;
+
+ /*
+ * obtain the proposed password
+ */
+
+ D(("do update"));
+
+ /*
+ * get the old token back. NULL was ok only if root [at this
+ * point we assume that this has already been enforced on a
+ * previous call to this function].
+ */
+
+ if (off(UNIX_NOT_SET_PASS, ctrl)) {
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK
+ ,(const void **) &pass_old);
+ } else {
+ retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
+ ,(const void **) &pass_old);
+ if (retval == PAM_NO_MODULE_DATA) {
+ retval = PAM_SUCCESS;
+ pass_old = NULL;
+ }
+ }
+ D(("pass_old [%s]", pass_old));
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "user not authenticated");
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+ retval = _unix_verify_shadow(user, ctrl);
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "user not authenticated 2");
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+ D(("get new password now"));
+
+ lctrl = ctrl;
+
+ if (on(UNIX_USE_AUTHTOK, lctrl)) {
+ set(UNIX_USE_FIRST_PASS, lctrl);
+ }
+ retry = 0;
+ retval = PAM_AUTHTOK_ERR;
+ while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ */
+
+ retval = _unix_read_password(pamh, lctrl
+ ,NULL
+ ,"Enter new UNIX password: "
+ ,"Retype new UNIX password: "
+ ,_UNIX_NEW_AUTHTOK
+ ,(const char **) &pass_new);
+
+ if (retval != PAM_SUCCESS) {
+ if (on(UNIX_DEBUG, ctrl)) {
+ _log_err(LOG_ALERT
+ ,"password - new password not obtained");
+ }
+ pass_old = NULL; /* tidy up */
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+ D(("returned to _unix_chauthtok"));
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') { /* "\0" password = NULL */
+ pass_new = NULL;
+ }
+ retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
+ }
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "new password not acceptable");
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_new = pass_old = NULL; /* tidy up */
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+ }
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the password database file.
+ */
+
+ /*
+ * First we encrypt the new password.
+ */
+
+ if (on(UNIX_MD5_PASS, ctrl)) {
+ tpass = crypt_md5_wrapper(pass_new);
+ } else {
+ /*
+ * Salt manipulation is stolen from Rick Faith's passwd
+ * program. Sorry Rick :) -- alex
+ */
+
+ time_t tm;
+ char salt[3];
+
+ time(&tm);
+ salt[0] = bin_to_ascii(tm & 0x3f);
+ salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
+ salt[2] = '\0';
+
+ if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
+ /*
+ * to avoid using the _extensions_ of the bigcrypt()
+ * function we truncate the newly entered password
+ */
+ char *temp = malloc(9);
+ char *e;
+
+ if (temp == NULL) {
+ _log_err(LOG_CRIT, "out of memory for password");
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_new = pass_old = NULL; /* tidy up */
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return PAM_BUF_ERR;
+ }
+ /* copy first 8 bytes of password */
+ strncpy(temp, pass_new, 8);
+ temp[8] = '\0';
+
+ /* no longer need cleartext */
+ e = bigcrypt(temp, salt);
+ tpass = x_strdup(e);
+
+ _pam_overwrite(e);
+ _pam_delete(temp); /* tidy up */
+ } else {
+ char *e;
+
+ /* no longer need cleartext */
+ e = bigcrypt(pass_new, salt);
+ tpass = x_strdup(e);
+
+ _pam_overwrite(e);
+ }
+ }
+
+ D(("password processed"));
+
+ /* update the password database(s) -- race conditions..? */
+
+ retval = _do_setpass(user, pass_old, tpass, ctrl, remember);
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ _pam_delete(tpass);
+ pass_old = pass_new = NULL;
+ } else { /* something has broken with the module */
+ _log_err(LOG_ALERT, "password received unknown request");
+ retval = PAM_ABORT;
+ }
+
+ D(("retval was %d", retval));
+
+#ifdef USE_LCKPWDF
+ ulckpwdf();
+#endif
+ return retval;
+}
+
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_unix_passwd_modstruct = {
+ "pam_unix_passwd",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok,
+};
+#endif
+
diff --git a/modules/pam_unix/pam_unix_sess.c b/modules/pam_unix/pam_unix_sess.c
new file mode 100644
index 00000000..ec658453
--- /dev/null
+++ b/modules/pam_unix/pam_unix_sess.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright Alexander O. Yuriev, 1996. All rights reserved.
+ * Copyright Jan Rêkorajski, 1999. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef linux
+#define _GNU_SOURCE
+#include <features.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* indicate the following groups are defined */
+
+#define PAM_SM_SESSION
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+
+#ifndef LINUX_PAM
+#include <security/pam_appl.h>
+#endif /* LINUX_PAM */
+
+#include "support.h"
+
+/*
+ * PAM framework looks for these entry-points to pass control to the
+ * session module.
+ */
+
+PAM_EXTERN int pam_sm_open_session(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
+{
+ char *user_name, *service;
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ ctrl = _set_ctrl(flags, NULL, argc, argv);
+
+ retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
+ if (user_name == NULL || retval != PAM_SUCCESS) {
+ _log_err(LOG_CRIT, "open_session - error recovering username");
+ return PAM_SESSION_ERR; /* How did we get authenticated with
+ no username?! */
+ }
+ retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service);
+ if (service == NULL || retval != PAM_SUCCESS) {
+ _log_err(LOG_CRIT, "open_session - error recovering service");
+ return PAM_SESSION_ERR;
+ }
+ _log_err(LOG_INFO, "(%s) session opened for user %s by %s(uid=%d)"
+ ,service, user_name
+ ,PAM_getlogin() == NULL ? "" : PAM_getlogin(), getuid());
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN int pam_sm_close_session(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
+{
+ char *user_name, *service;
+ unsigned int ctrl;
+ int retval;
+
+ D(("called."));
+
+ ctrl = _set_ctrl(flags, NULL, argc, argv);
+
+ retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
+ if (user_name == NULL || retval != PAM_SUCCESS) {
+ _log_err(LOG_CRIT, "close_session - error recovering username");
+ return PAM_SESSION_ERR; /* How did we get authenticated with
+ no username?! */
+ }
+ retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service);
+ if (service == NULL || retval != PAM_SUCCESS) {
+ _log_err(LOG_CRIT, "close_session - error recovering service");
+ return PAM_SESSION_ERR;
+ }
+ _log_err(LOG_INFO, "(%s) session closed for user %s"
+ ,service, user_name);
+
+ return PAM_SUCCESS;
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_unix_session_modstruct = {
+ "pam_unix_session",
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ NULL,
+};
+#endif
+
diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c
new file mode 100644
index 00000000..610b29a7
--- /dev/null
+++ b/modules/pam_unix/support.c
@@ -0,0 +1,821 @@
+/*
+ * $Id$
+ *
+ * Copyright information at end of file.
+ */
+
+#define _BSD_SOURCE
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <malloc.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <limits.h>
+#include <utmp.h>
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+
+#include "md5.h"
+#include "support.h"
+
+extern char *crypt(const char *key, const char *salt);
+extern char *bigcrypt(const char *key, const char *salt);
+
+/* syslogging function for errors and other information */
+
+void _log_err(int err, const char *format,...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM_unix", LOG_CONS | LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* this is a front-end for module-application conversations */
+
+static int converse(pam_handle_t * pamh, int ctrl, int nargs
+ ,struct pam_message **message
+ ,struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("begin to converse"));
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (retval == PAM_SUCCESS) {
+
+ retval = conv->conv(nargs, (const struct pam_message **) message
+ ,response, conv->appdata_ptr);
+
+ D(("returned from application's conversation function"));
+
+ if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+ } else if (retval != PAM_CONV_AGAIN) {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+ D(("ready to return from module conversation"));
+
+ return retval; /* propagate error status */
+}
+
+int _make_remark(pam_handle_t * pamh, unsigned int ctrl
+ ,int type, const char *text)
+{
+ int retval = PAM_SUCCESS;
+
+ if (off(UNIX__QUIET, ctrl)) {
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+
+ resp = NULL;
+ retval = converse(pamh, ctrl, 1, pmsg, &resp);
+
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ }
+ return retval;
+}
+
+ /*
+ * Beacause getlogin() is fucked in a weird way, and
+ * sometimes it just don't work, we reimplement it here.
+ */
+char *PAM_getlogin(void)
+{
+ struct utmp *ut, line;
+ char *curr_tty, *retval;
+ static char curr_user[UT_NAMESIZE + 4];
+
+ retval = NULL;
+
+ curr_tty = ttyname(0);
+ if (curr_tty != NULL) {
+ D(("PAM_getlogin ttyname: %s", curr_tty));
+ curr_tty += 5;
+ setutent();
+ strncpy(line.ut_line, curr_tty, sizeof line.ut_line);
+ if ((ut = getutline(&line)) != NULL) {
+ strncpy(curr_user, ut->ut_user, UT_NAMESIZE);
+ retval = curr_user;
+ }
+ endutent();
+ }
+ D(("PAM_getlogin retval: %s", retval));
+
+ return retval;
+}
+
+/*
+ * set the control flags for the UNIX module.
+ */
+
+int _set_ctrl(int flags, int *remember, int argc, const char **argv)
+{
+ unsigned int ctrl;
+
+ D(("called."));
+
+ ctrl = UNIX_DEFAULTS; /* the default selection of options */
+
+ /* set some flags manually */
+
+ if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
+ D(("IAMROOT"));
+ set(UNIX__IAMROOT, ctrl);
+ }
+ if (flags & PAM_UPDATE_AUTHTOK) {
+ D(("UPDATE_AUTHTOK"));
+ set(UNIX__UPDATE, ctrl);
+ }
+ if (flags & PAM_PRELIM_CHECK) {
+ D(("PRELIM_CHECK"));
+ set(UNIX__PRELIM, ctrl);
+ }
+ if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
+ D(("DISALLOW_NULL_AUTHTOK"));
+ set(UNIX__NONULL, ctrl);
+ }
+ if (flags & PAM_SILENT) {
+ D(("SILENT"));
+ set(UNIX__QUIET, ctrl);
+ }
+ /* now parse the arguments to this module */
+
+ while (argc-- > 0) {
+ int j;
+
+ D(("pam_unix arg: %s", *argv));
+
+ for (j = 0; j < UNIX_CTRLS_; ++j) {
+ if (unix_args[j].token
+ && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
+ break;
+ }
+ }
+
+ if (j >= UNIX_CTRLS_) {
+ _log_err(LOG_ERR, "unrecognized option [%s]", *argv);
+ } else {
+ ctrl &= unix_args[j].mask; /* for turning things off */
+ ctrl |= unix_args[j].flag; /* for turning things on */
+
+ if (remember != NULL) {
+ if (j == UNIX_REMEMBER_PASSWD) {
+ *remember = strtol(*argv + 9, NULL, 10);
+ if ((*remember == LONG_MIN) || (*remember == LONG_MAX))
+ *remember = -1;
+ if (*remember > 400)
+ *remember = 400;
+ }
+ }
+ }
+
+ ++argv; /* step to next argument */
+ }
+
+ /* auditing is a more sensitive version of debug */
+
+ if (on(UNIX_AUDIT, ctrl)) {
+ set(UNIX_DEBUG, ctrl);
+ }
+ /* return the set of flags */
+
+ D(("done."));
+ return ctrl;
+}
+
+static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
+{
+ _pam_delete(x);
+}
+
+/* ************************************************************** *
+ * Useful non-trivial functions *
+ * ************************************************************** */
+
+ /*
+ * the following is used to keep track of the number of times a user fails
+ * to authenticate themself.
+ */
+
+#define FAIL_PREFIX "-UN*X-FAIL-"
+#define UNIX_MAX_RETRIES 3
+
+struct _pam_failed_auth {
+ char *user; /* user that's failed to be authenticated */
+ char *name; /* attempt from user with name */
+ int id; /* uid of name'd user */
+ int count; /* number of failures so far */
+};
+
+#ifndef PAM_DATA_REPLACE
+#error "Need to get an updated libpam 0.52 or better"
+#endif
+
+static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
+{
+ int quiet;
+ const char *service = NULL;
+ struct _pam_failed_auth *failure;
+
+ D(("called"));
+
+ quiet = err & PAM_DATA_SILENT; /* should we log something? */
+ err &= PAM_DATA_REPLACE; /* are we just replacing data? */
+ failure = (struct _pam_failed_auth *) fl;
+
+ if (failure != NULL) {
+
+ if (!quiet && !err) { /* under advisement from Sun,may go away */
+
+ /* log the number of authentication failures */
+ if (failure->count > 1) {
+ (void) pam_get_item(pamh, PAM_SERVICE
+ ,(const void **) &service);
+ _log_err(LOG_NOTICE
+ ,"%d more authentication failure%s; %s(uid=%d) -> "
+ "%s for %s service"
+ ,failure->count - 1, failure->count == 2 ? "" : "s"
+ ,failure->name
+ ,failure->id
+ ,failure->user
+ ,service == NULL ? "**unknown**" : service
+ );
+ if (failure->count > UNIX_MAX_RETRIES) {
+ _log_err(LOG_ALERT
+ ,"service(%s) ignoring max retries; %d > %d"
+ ,service == NULL ? "**unknown**" : service
+ ,failure->count
+ ,UNIX_MAX_RETRIES);
+ }
+ }
+ }
+ _pam_delete(failure->user); /* tidy up */
+ _pam_delete(failure->name); /* tidy up */
+ free(failure);
+ }
+}
+
+/*
+ * _unix_blankpasswd() is a quick check for a blank password
+ *
+ * returns TRUE if user does not have a password
+ * - to avoid prompting for one in such cases (CG)
+ */
+
+int _unix_blankpasswd(unsigned int ctrl, const char *name)
+{
+ struct passwd *pwd = NULL;
+ struct spwd *spwdent = NULL;
+ char *salt = NULL;
+ int retval;
+
+ D(("called"));
+
+ /*
+ * This function does not have to be too smart if something goes
+ * wrong, return FALSE and let this case to be treated somewhere
+ * else (CG)
+ */
+
+ if (on(UNIX__NONULL, ctrl))
+ return 0; /* will fail but don't let on yet */
+
+ /* UNIX passwords area */
+ pwd = getpwnam(name); /* Get password file entry... */
+
+ if (pwd != NULL) {
+ if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
+ { /* NIS+ */
+ uid_t save_euid, save_uid;
+
+ save_euid = geteuid();
+ save_uid = getuid();
+ if (save_uid == pwd->pw_uid)
+ setreuid( save_euid, save_uid );
+ else {
+ setreuid( 0, -1 );
+ if (setreuid( -1, pwd->pw_uid ) == -1) {
+ setreuid( -1, 0 );
+ setreuid( 0, -1 );
+ if(setreuid( -1, pwd->pw_uid ) == -1)
+ /* Will fail elsewhere. */
+ return 0;
+ }
+ }
+
+ spwdent = getspnam( name );
+ if (save_uid == pwd->pw_uid)
+ setreuid( save_uid, save_euid );
+ else {
+ if (setreuid( -1, 0 ) == -1)
+ setreuid( save_uid, -1 );
+ setreuid( -1, save_euid );
+ }
+ } else if (strcmp(pwd->pw_passwd, "x") == 0) {
+ /*
+ * ...and shadow password file entry for this user,
+ * if shadowing is enabled
+ */
+ spwdent = getspnam(name);
+ }
+ if (spwdent)
+ salt = x_strdup(spwdent->sp_pwdp);
+ else
+ salt = x_strdup(pwd->pw_passwd);
+ }
+ /* Does this user have a password? */
+ if (salt == NULL) {
+ retval = 0;
+ } else {
+ if (strlen(salt) == 0)
+ retval = 1;
+ else
+ retval = 0;
+ }
+
+ /* tidy up */
+
+ if (salt)
+ _pam_delete(salt);
+
+ return retval;
+}
+
+/*
+ * verify the password of a user
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, unsigned int ctrl)
+{
+ int retval, child, fds[2];
+
+ D(("called."));
+ /* create a pipe for the password */
+ if (pipe(fds) != 0) {
+ D(("could not make pipe"));
+ return PAM_AUTH_ERR;
+ }
+
+ /* fork */
+ child = fork();
+ if (child == 0) {
+ static char *args[] = { NULL, NULL };
+ static char *envp[] = { NULL };
+
+ /* XXX - should really tidy up PAM here too */
+
+ /* reopen stdin as pipe */
+ close(fds[1]);
+ dup2(fds[0], STDIN_FILENO);
+
+ /* exec binary helper */
+ args[0] = x_strdup(CHKPWD_HELPER);
+ execve(CHKPWD_HELPER, args, envp);
+
+ /* should not get here: exit with error */
+ D(("helper binary is not available"));
+ exit(PAM_AUTHINFO_UNAVAIL);
+ } else if (child > 0) {
+ /* wait for child */
+ close(fds[0]);
+ /* if the stored password is NULL */
+ if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
+ write(fds[1], "nullok\0\0", 8);
+ } else {
+ write(fds[1], "nonull\0\0", 8);
+ }
+ if (passwd != NULL) { /* send the password to the child */
+ write(fds[1], passwd, strlen(passwd)+1);
+ passwd = NULL;
+ } else {
+ write(fds[1], "", 1); /* blank password */
+ }
+ close(fds[1]);
+ (void) waitpid(child, &retval, 0); /* wait for helper to complete */
+ retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
+ } else {
+ D(("fork failed"));
+ retval = PAM_AUTH_ERR;
+ }
+
+ D(("returning %d", retval));
+ return retval;
+}
+
+int _unix_verify_password(pam_handle_t * pamh, const char *name
+ ,const char *p, unsigned int ctrl)
+{
+ struct passwd *pwd = NULL;
+ struct spwd *spwdent = NULL;
+ char *salt = NULL;
+ char *pp = NULL;
+ char *data_name;
+ int retval;
+
+ D(("called"));
+
+#ifdef HAVE_PAM_FAIL_DELAY
+ if (off(UNIX_NODELAY, ctrl)) {
+ D(("setting delay"));
+ (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
+ }
+#endif
+
+ /* locate the entry for this user */
+
+ D(("locating user's record"));
+
+ /* UNIX passwords area */
+ pwd = getpwnam(name); /* Get password file entry... */
+
+ if (pwd != NULL) {
+ if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
+ { /* NIS+ */
+ uid_t save_euid, save_uid;
+
+ save_euid = geteuid();
+ save_uid = getuid();
+ if (save_uid == pwd->pw_uid)
+ setreuid( save_euid, save_uid );
+ else {
+ setreuid( 0, -1 );
+ if (setreuid( -1, pwd->pw_uid ) == -1) {
+ setreuid( -1, 0 );
+ setreuid( 0, -1 );
+ if(setreuid( -1, pwd->pw_uid ) == -1)
+ return PAM_CRED_INSUFFICIENT;
+ }
+ }
+
+ spwdent = getspnam( name );
+ if (save_uid == pwd->pw_uid)
+ setreuid( save_uid, save_euid );
+ else {
+ if (setreuid( -1, 0 ) == -1)
+ setreuid( save_uid, -1 );
+ setreuid( -1, save_euid );
+ }
+ } else if (strcmp(pwd->pw_passwd, "x") == 0) {
+ /*
+ * ...and shadow password file entry for this user,
+ * if shadowing is enabled
+ */
+ spwdent = getspnam(name);
+ }
+ if (spwdent)
+ salt = x_strdup(spwdent->sp_pwdp);
+ else
+ salt = x_strdup(pwd->pw_passwd);
+ }
+
+ data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
+ if (data_name == NULL) {
+ _log_err(LOG_CRIT, "no memory for data-name");
+ } else {
+ strcpy(data_name, FAIL_PREFIX);
+ strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
+ }
+
+ retval = PAM_SUCCESS;
+ if (pwd == NULL || salt == NULL || !strcmp(salt, "x")) {
+ if (geteuid()) {
+ /* we are not root perhaps this is the reason? Run helper */
+ D(("running helper binary"));
+ retval = _unix_run_helper_binary(pamh, p, ctrl);
+ } else {
+ D(("user's record unavailable"));
+ if (on(UNIX_AUDIT, ctrl)) {
+ /* this might be a typo and the user has given a password
+ instead of a username. Careful with this. */
+ _log_err(LOG_ALERT, "check pass; user (%s) unknown", name);
+ } else {
+ _log_err(LOG_ALERT, "check pass; user unknown");
+ }
+ p = NULL;
+ retval = PAM_AUTHINFO_UNAVAIL;
+ }
+ } else {
+ if (!strlen(salt)) {
+ /* the stored password is NULL */
+ if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
+ D(("user has empty password - access granted"));
+ retval = PAM_SUCCESS;
+ } else {
+ D(("user has empty password - access denied"));
+ retval = PAM_AUTH_ERR;
+ }
+ } else {
+ if (!strncmp(salt, "$1$", 3)) {
+ pp = Goodcrypt_md5(p, salt);
+ if (strcmp(pp, salt) != 0) {
+ pp = Brokencrypt_md5(p, salt);
+ }
+ } else {
+ pp = bigcrypt(p, salt);
+ }
+ p = NULL; /* no longer needed here */
+
+ /* the moment of truth -- do we agree with the password? */
+ D(("comparing state of pp[%s] and salt[%s]", pp, salt));
+
+ if (strcmp(pp, salt) == 0) {
+ retval = PAM_SUCCESS;
+ } else {
+ retval = PAM_AUTH_ERR;
+ }
+ }
+ }
+
+ if (retval == PAM_SUCCESS) {
+ if (data_name) /* reset failures */
+ pam_set_data(pamh, data_name, NULL, _cleanup_failures);
+ } else {
+ if (data_name != NULL) {
+ struct _pam_failed_auth *new = NULL;
+ const struct _pam_failed_auth *old = NULL;
+
+ /* get a failure recorder */
+
+ new = (struct _pam_failed_auth *)
+ malloc(sizeof(struct _pam_failed_auth));
+
+ if (new != NULL) {
+
+ new->user = x_strdup(name);
+ new->id = getuid();
+ new->name = x_strdup(PAM_getlogin()? PAM_getlogin() : "");
+
+ /* any previous failures for this user ? */
+ pam_get_data(pamh, data_name, (const void **) &old);
+
+ if (old != NULL) {
+ new->count = old->count + 1;
+ if (new->count >= UNIX_MAX_RETRIES) {
+ retval = PAM_MAXTRIES;
+ }
+ } else {
+ const char *service=NULL;
+ (void) pam_get_item(pamh, PAM_SERVICE,
+ (const void **)&service);
+ _log_err(LOG_NOTICE
+ ,"authentication failure; %s(uid=%d) -> "
+ "%s for %s service"
+ ,new->name
+ ,new->id
+ ,new->user
+ ,service == NULL ? "**unknown**":service
+ );
+ new->count = 1;
+ }
+
+ pam_set_data(pamh, data_name, new, _cleanup_failures);
+
+ } else {
+ _log_err(LOG_CRIT, "no memory for failure recorder");
+ }
+ }
+ }
+
+ if (data_name)
+ _pam_delete(data_name);
+ if (salt)
+ _pam_delete(salt);
+ if (pp)
+ _pam_overwrite(pp);
+
+ D(("done [%d].", retval));
+
+ return retval;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+int _unix_read_password(pam_handle_t * pamh
+ ,unsigned int ctrl
+ ,const char *comment
+ ,const char *prompt1
+ ,const char *prompt2
+ ,const char *data_name
+ ,const char **pass)
+{
+ int authtok_flag;
+ int retval;
+ const char *item;
+ char *token;
+
+ D(("called"));
+
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+
+ *pass = token = NULL;
+
+ /*
+ * which authentication token are we getting?
+ */
+
+ authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
+
+ /*
+ * should we obtain the password from a PAM item ?
+ */
+
+ if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
+ retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _log_err(LOG_ALERT
+ ,"pam_get_item returned error to unix-read-password"
+ );
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ return PAM_SUCCESS;
+ } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on(UNIX_USE_AUTHTOK, ctrl)
+ && off(UNIX__OLD_PASSWD, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ {
+ struct pam_message msg[3], *pmsg[3];
+ struct pam_response *resp;
+ int i, replies;
+
+ /* prepare to converse */
+
+ if (comment != NULL && off(UNIX__QUIET, ctrl)) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = comment;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt1;
+ replies = 1;
+
+ if (prompt2 != NULL) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt2;
+ ++replies;
+ }
+ /* so call the conversation expecting i responses */
+ resp = NULL;
+ retval = converse(pamh, ctrl, i, pmsg, &resp);
+
+ if (resp != NULL) {
+
+ /* interpret the response */
+
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+
+ token = x_strdup(resp[i - replies].resp);
+ if (token != NULL) {
+ if (replies == 2) {
+
+ /* verify that password entered correctly */
+ if (!resp[i - 1].resp
+ || strcmp(token, resp[i - 1].resp)) {
+ _pam_delete(token); /* mistyped */
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ _make_remark(pamh, ctrl
+ ,PAM_ERROR_MSG, MISTYPED_PASS);
+ }
+ }
+ } else {
+ _log_err(LOG_NOTICE
+ ,"could not recover authentication token");
+ }
+
+ }
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ * -- what is it for anyway? AGM
+ */
+
+ _pam_drop_reply(resp, i);
+
+ } else {
+ retval = (retval == PAM_SUCCESS)
+ ? PAM_AUTHTOK_RECOVER_ERR : retval;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (on(UNIX_DEBUG, ctrl))
+ _log_err(LOG_DEBUG, "unable to obtain a password");
+ return retval;
+ }
+ /* 'token' is the entered password */
+
+ if (off(UNIX_NOT_SET_PASS, ctrl)) {
+
+ /* we store this password as an item */
+
+ retval = pam_set_item(pamh, authtok_flag, token);
+ _pam_delete(token); /* clean it up */
+ if (retval != PAM_SUCCESS
+ || (retval = pam_get_item(pamh, authtok_flag
+ ,(const void **) &item))
+ != PAM_SUCCESS) {
+
+ _log_err(LOG_CRIT, "error manipulating password");
+ return retval;
+
+ }
+ } else {
+ /*
+ * then store it as data specific to this module. pam_end()
+ * will arrange to clean it up.
+ */
+
+ retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_CRIT, "error manipulating password data [%s]"
+ ,pam_strerror(pamh, retval));
+ _pam_delete(token);
+ return retval;
+ }
+ item = token;
+ token = NULL; /* break link to password */
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+/* ****************************************************************** *
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h
new file mode 100644
index 00000000..68686738
--- /dev/null
+++ b/modules/pam_unix/support.h
@@ -0,0 +1,139 @@
+#ifndef _PAM_UNIX_SUPPORT_H
+#define _PAM_UNIX_SUPPORT_H
+
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/* type definition for the control options */
+
+typedef struct {
+ const char *token;
+ unsigned int mask; /* shall assume 32 bits of flags */
+ unsigned int flag;
+} UNIX_Ctrls;
+
+/*
+ * macro to determine if a given flag is on
+ */
+
+#define on(x,ctrl) (unix_args[x].flag & ctrl)
+
+/*
+ * macro to determine that a given flag is NOT on
+ */
+
+#define off(x,ctrl) (!on(x,ctrl))
+
+/*
+ * macro to turn on/off a ctrl flag manually
+ */
+
+#define set(x,ctrl) (ctrl = ((ctrl)&unix_args[x].mask)|unix_args[x].flag)
+#define unset(x,ctrl) (ctrl &= ~(unix_args[x].flag))
+
+/* the generic mask */
+
+#define _ALL_ON_ (~0U)
+
+/* end of macro definitions definitions for the control flags */
+
+/* ****************************************************************** *
+ * ctrl flags proper..
+ */
+
+/*
+ * here are the various options recognized by the unix module. They
+ * are enumerated here and then defined below. Internal arguments are
+ * given NULL tokens.
+ */
+
+#define UNIX__OLD_PASSWD 0 /* internal */
+#define UNIX__VERIFY_PASSWD 1 /* internal */
+#define UNIX__IAMROOT 2 /* internal */
+
+#define UNIX_AUDIT 3 /* print more things than debug..
+ some information may be sensitive */
+#define UNIX_USE_FIRST_PASS 4
+#define UNIX_TRY_FIRST_PASS 5
+#define UNIX_NOT_SET_PASS 6 /* don't set the AUTHTOK items */
+
+#define UNIX__PRELIM 7 /* internal */
+#define UNIX__UPDATE 8 /* internal */
+#define UNIX__NONULL 9 /* internal */
+#define UNIX__QUIET 10 /* internal */
+#define UNIX_USE_AUTHTOK 11 /* insist on reading PAM_AUTHTOK */
+#define UNIX_SHADOW 12 /* signal shadow on */
+#define UNIX_MD5_PASS 13 /* force the use of MD5 passwords */
+#define UNIX__NULLOK 14 /* Null token ok */
+#define UNIX_DEBUG 15 /* send more info to syslog(3) */
+#define UNIX_NODELAY 16 /* admin does not want a fail-delay */
+#define UNIX_NIS 17 /* wish to use NIS for pwd */
+#define UNIX_BIGCRYPT 18 /* use DEC-C2 crypt()^x function */
+#define UNIX_LIKE_AUTH 19 /* need to auth for setcred to work */
+#define UNIX_REMEMBER_PASSWD 20 /* Remember N previous passwords */
+/* -------------- */
+#define UNIX_CTRLS_ 21 /* number of ctrl arguments defined */
+
+
+static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
+{
+/* symbol token name ctrl mask ctrl *
+ * ----------------------- ------------------- --------------------- -------- */
+
+/* UNIX__OLD_PASSWD */ {NULL, _ALL_ON_, 01},
+/* UNIX__VERIFY_PASSWD */ {NULL, _ALL_ON_, 02},
+/* UNIX__IAMROOT */ {NULL, _ALL_ON_, 04},
+/* UNIX_AUDIT */ {"audit", _ALL_ON_, 010},
+/* UNIX_USE_FIRST_PASS */ {"use_first_pass", _ALL_ON_^(060), 020},
+/* UNIX_TRY_FIRST_PASS */ {"try_first_pass", _ALL_ON_^(060), 040},
+/* UNIX_NOT_SET_PASS */ {"not_set_pass", _ALL_ON_, 0100},
+/* UNIX__PRELIM */ {NULL, _ALL_ON_^(0600), 0200},
+/* UNIX__UPDATE */ {NULL, _ALL_ON_^(0600), 0400},
+/* UNIX__NONULL */ {NULL, _ALL_ON_, 01000},
+/* UNIX__QUIET */ {NULL, _ALL_ON_, 02000},
+/* UNIX_USE_AUTHTOK */ {"use_authtok", _ALL_ON_, 04000},
+/* UNIX_SHADOW */ {"shadow", _ALL_ON_, 010000},
+/* UNIX_MD5_PASS */ {"md5", _ALL_ON_^(0400000), 020000},
+/* UNIX__NULLOK */ {"nullok", _ALL_ON_^(01000), 0},
+/* UNIX_DEBUG */ {"debug", _ALL_ON_, 040000},
+/* UNIX_NODELAY */ {"nodelay", _ALL_ON_, 0100000},
+/* UNIX_NIS */ {"nis", _ALL_ON_^(010000), 0200000},
+/* UNIX_BIGCRYPT */ {"bigcrypt", _ALL_ON_^(020000), 0400000},
+/* UNIX_LIKE_AUTH */ {"likeauth", _ALL_ON_, 01000000},
+/* UNIX_REMEMBER_PASSWD */ {"remember=", _ALL_ON_, 02000000},
+};
+
+#define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag)
+
+
+/* use this to free strings. ESPECIALLY password strings */
+
+#define _pam_delete(xx) \
+{ \
+ _pam_overwrite(xx); \
+ _pam_drop(xx); \
+}
+
+extern char *PAM_getlogin(void);
+extern void _log_err(int err, const char *format,...);
+extern int _make_remark(pam_handle_t * pamh, unsigned int ctrl
+ ,int type, const char *text);
+extern int _set_ctrl(int flags, int *remember, int argc, const char **argv);
+extern int _unix_blankpasswd(unsigned int ctrl, const char *name);
+extern int _unix_verify_password(pam_handle_t * pamh, const char *name
+ ,const char *p, unsigned int ctrl);
+extern int _unix_read_password(pam_handle_t * pamh
+ ,unsigned int ctrl
+ ,const char *comment
+ ,const char *prompt1
+ ,const char *prompt2
+ ,const char *data_name
+ ,const char **pass);
+
+#endif /* _PAM_UNIX_SUPPORT_H */
+
diff --git a/modules/pam_unix/unix_chkpwd.c b/modules/pam_unix/unix_chkpwd.c
new file mode 100644
index 00000000..66c0ad7f
--- /dev/null
+++ b/modules/pam_unix/unix_chkpwd.c
@@ -0,0 +1,324 @@
+/*
+ * $Id$
+ *
+ * This program is designed to run setuid(root) or with sufficient
+ * privilege to read all of the unix password databases. It is designed
+ * to provide a mechanism for the current user (defined by this
+ * process' uid) to verify their own password.
+ *
+ * The password is read from the standard input. The exit status of
+ * this program indicates whether the user is authenticated or not.
+ *
+ * Copyright information is located at the end of the file.
+ *
+ */
+
+#define _BSD_SOURCE
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <signal.h>
+
+#define MAXPASS 200 /* the maximum length of a password */
+
+#include <security/_pam_macros.h>
+
+#include "md5.h"
+
+extern char *crypt(const char *key, const char *salt);
+extern char *bigcrypt(const char *key, const char *salt);
+
+#define UNIX_PASSED 0
+#define UNIX_FAILED 1
+
+/* syslogging function for errors and other information */
+
+static void _log_err(int err, const char *format,...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static void su_sighandler(int sig)
+{
+ if (sig > 0) {
+ _log_err(LOG_NOTICE, "caught signal %d.", sig);
+ exit(sig);
+ }
+}
+
+static void setup_signals(void)
+{
+ struct sigaction action; /* posix signal structure */
+
+ /*
+ * Setup signal handlers
+ */
+ (void) memset((void *) &action, 0, sizeof(action));
+ action.sa_handler = su_sighandler;
+ action.sa_flags = SA_RESETHAND;
+ (void) sigaction(SIGILL, &action, NULL);
+ (void) sigaction(SIGTRAP, &action, NULL);
+ (void) sigaction(SIGBUS, &action, NULL);
+ (void) sigaction(SIGSEGV, &action, NULL);
+ action.sa_handler = SIG_IGN;
+ action.sa_flags = 0;
+ (void) sigaction(SIGTERM, &action, NULL);
+ (void) sigaction(SIGHUP, &action, NULL);
+ (void) sigaction(SIGINT, &action, NULL);
+ (void) sigaction(SIGQUIT, &action, NULL);
+}
+
+static int _unix_verify_password(const char *name, const char *p, int opt)
+{
+ struct passwd *pwd = NULL;
+ struct spwd *spwdent = NULL;
+ char *salt = NULL;
+ char *pp = NULL;
+ int retval = UNIX_FAILED;
+
+ /* UNIX passwords area */
+ setpwent();
+ pwd = getpwnam(name); /* Get password file entry... */
+ endpwent();
+ if (pwd != NULL) {
+ if (strcmp(pwd->pw_passwd, "x") == 0) {
+ /*
+ * ...and shadow password file entry for this user,
+ * if shadowing is enabled
+ */
+ setspent();
+ spwdent = getspnam(name);
+ endspent();
+ if (spwdent != NULL)
+ salt = x_strdup(spwdent->sp_pwdp);
+ else
+ pwd = NULL;
+ } else {
+ if (strcmp(pwd->pw_passwd, "*NP*") == 0) { /* NIS+ */
+ uid_t save_uid;
+
+ save_uid = geteuid();
+ seteuid(pwd->pw_uid);
+ spwdent = getspnam(name);
+ seteuid(save_uid);
+
+ salt = x_strdup(spwdent->sp_pwdp);
+ } else {
+ salt = x_strdup(pwd->pw_passwd);
+ }
+ }
+ }
+ if (pwd == NULL || salt == NULL) {
+ _log_err(LOG_ALERT, "check pass; user unknown");
+ p = NULL;
+ return retval;
+ }
+
+ if (strlen(salt) == 0)
+ return (opt == 0) ? UNIX_FAILED : UNIX_PASSED;
+
+ /* the moment of truth -- do we agree with the password? */
+ retval = UNIX_FAILED;
+ if (!strncmp(salt, "$1$", 3)) {
+ pp = Goodcrypt_md5(p, salt);
+ if (strcmp(pp, salt) == 0) {
+ retval = UNIX_PASSED;
+ } else {
+ pp = Brokencrypt_md5(p, salt);
+ if (strcmp(pp, salt) == 0)
+ retval = UNIX_PASSED;
+ }
+ } else {
+ pp = bigcrypt(p, salt);
+ if (strcmp(pp, salt) == 0) {
+ retval = UNIX_PASSED;
+ }
+ }
+ p = NULL; /* no longer needed here */
+
+ /* clean up */
+ {
+ char *tp = pp;
+ if (pp != NULL) {
+ while (tp && *tp)
+ *tp++ = '\0';
+ }
+ pp = tp = NULL;
+ }
+
+ return retval;
+}
+
+static char *getuidname(uid_t uid)
+{
+ struct passwd *pw;
+#if 0
+ char *envname;
+
+ envname = getenv("LOGNAME");
+ if (envname == NULL)
+ return NULL;
+
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ return NULL;
+
+ if (strcmp(envname, pw->pw_name))
+ return NULL;
+
+ return envname;
+#else
+ static char username[32];
+
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ return NULL;
+
+ memset(username, 0, 32);
+ strncpy(username, pw->pw_name, 32);
+ username[31] = '\0';
+
+ return username;
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+ char pass[MAXPASS + 1];
+ char option[8];
+ int npass, opt;
+ int retval = UNIX_FAILED;
+ char *user;
+
+ /*
+ * Catch or ignore as many signal as possible.
+ */
+ setup_signals();
+
+ /*
+ * we establish that this program is running with non-tty stdin.
+ * this is to discourage casual use. It does *NOT* prevent an
+ * intruder from repeatadly running this program to determine the
+ * password of the current user (brute force attack, but one for
+ * which the attacker must already have gained access to the user's
+ * account).
+ */
+
+ if (isatty(STDIN_FILENO)) {
+
+ _log_err(LOG_NOTICE
+ ,"inappropriate use of Unix helper binary [UID=%d]"
+ ,getuid());
+ fprintf(stderr
+ ,"This binary is not designed for running in this way\n"
+ "-- the system administrator has been informed\n");
+ sleep(10); /* this should discourage/annoy the user */
+ return UNIX_FAILED;
+ }
+ /*
+ * determine the current user's name is
+ * 1. supplied as a environment variable as LOGNAME
+ * 2. the uid has to match the one associated with the LOGNAME.
+ */
+ user = getuidname(getuid());
+
+ /* read the nollok/nonull option */
+
+ npass = read(STDIN_FILENO, option, 8);
+
+ if (npass < 0) {
+ _log_err(LOG_DEBUG, "no option supplied");
+ return UNIX_FAILED;
+ } else {
+ option[7] = '\0';
+ if (strncmp(option, "nullok", 8) == 0)
+ opt = 1;
+ else
+ opt = 0;
+ }
+
+ /* read the password from stdin (a pipe from the pam_unix module) */
+
+ npass = read(STDIN_FILENO, pass, MAXPASS);
+
+ if (npass < 0) { /* is it a valid password? */
+
+ _log_err(LOG_DEBUG, "no password supplied");
+
+ } else if (npass >= MAXPASS) {
+
+ _log_err(LOG_DEBUG, "password too long");
+
+ } else {
+ if (npass == 0) {
+ /* the password is NULL */
+
+ retval = _unix_verify_password(user, NULL, opt);
+
+ } else {
+ /* does pass agree with the official one? */
+
+ pass[npass] = '\0'; /* NUL terminate */
+ retval = _unix_verify_password(user, pass, opt);
+
+ }
+ }
+
+ memset(pass, '\0', MAXPASS); /* clear memory of the password */
+
+ /* return pass or fail */
+
+ return retval;
+}
+
+/*
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_unix/yppasswd.h b/modules/pam_unix/yppasswd.h
new file mode 100644
index 00000000..6b414be0
--- /dev/null
+++ b/modules/pam_unix/yppasswd.h
@@ -0,0 +1,51 @@
+/*
+ * yppasswdd
+ * Copyright 1994, 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
+ *
+ * This program is covered by the GNU General Public License, version 2.
+ * It is provided in the hope that it is useful. However, the author
+ * disclaims ALL WARRANTIES, expressed or implied. See the GPL for details.
+ *
+ * This file was generated automatically by rpcgen from yppasswd.x, and
+ * editied manually.
+ */
+
+#ifndef _YPPASSWD_H_
+#define _YPPASSWD_H_
+
+#define YPPASSWDPROG ((u_long)100009)
+#define YPPASSWDVERS ((u_long)1)
+#define YPPASSWDPROC_UPDATE ((u_long)1)
+
+/*
+ * The password struct passed by the update call. I renamed it to
+ * xpasswd to avoid a type clash with the one defined in <pwd.h>.
+ */
+#ifndef __sgi
+typedef struct xpasswd {
+ char *pw_name;
+ char *pw_passwd;
+ int pw_uid;
+ int pw_gid;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+} xpasswd;
+
+#else
+#include <pwd.h>
+typedef struct xpasswd xpasswd;
+#endif
+
+/* The updated password information, plus the old password.
+ */
+typedef struct yppasswd {
+ char *oldpass;
+ xpasswd newpw;
+} yppasswd;
+
+/* XDR encoding/decoding routines */
+bool_t xdr_xpasswd(XDR * xdrs, xpasswd * objp);
+bool_t xdr_yppasswd(XDR * xdrs, yppasswd * objp);
+
+#endif /* _YPPASSWD_H_ */
diff --git a/modules/pam_unix/yppasswd_xdr.c b/modules/pam_unix/yppasswd_xdr.c
new file mode 100644
index 00000000..eeb36423
--- /dev/null
+++ b/modules/pam_unix/yppasswd_xdr.c
@@ -0,0 +1,41 @@
+/*
+ * yppasswdd
+ * Copyright 1994, 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
+ *
+ * This program is covered by the GNU General Public License, version 2.
+ * It is provided in the hope that it is useful. However, the author
+ * disclaims ALL WARRANTIES, expressed or implied. See the GPL for details.
+ *
+ * This file was generated automatically by rpcgen from yppasswd.x, and
+ * editied manually.
+ */
+
+#ifdef linux
+# define _GNU_SOURCE
+# include <features.h>
+#endif
+
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include "yppasswd.h"
+
+bool_t
+xdr_xpasswd(XDR * xdrs, xpasswd * objp)
+{
+ return xdr_string(xdrs, &objp->pw_name, ~0)
+ && xdr_string(xdrs, &objp->pw_passwd, ~0)
+ && xdr_int(xdrs, &objp->pw_uid)
+ && xdr_int(xdrs, &objp->pw_gid)
+ && xdr_string(xdrs, &objp->pw_gecos, ~0)
+ && xdr_string(xdrs, &objp->pw_dir, ~0)
+ && xdr_string(xdrs, &objp->pw_shell, ~0);
+}
+
+
+bool_t
+xdr_yppasswd(XDR * xdrs, yppasswd * objp)
+{
+ return xdr_string(xdrs, &objp->oldpass, ~0)
+ && xdr_xpasswd(xdrs, &objp->newpw);
+}
diff --git a/modules/pam_userdb/.cvsignore b/modules/pam_userdb/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_userdb/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_userdb/Makefile b/modules/pam_userdb/Makefile
new file mode 100644
index 00000000..39226242
--- /dev/null
+++ b/modules/pam_userdb/Makefile
@@ -0,0 +1,81 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+
+# $Id$
+# Created by Cristian Gafton <gafton@redhat.com>
+
+WHICH_DB=$(shell ./libdbfound.sh)
+ifeq ($(WHICH_DB),none)
+
+include ../dont_makefile
+
+else
+
+TITLE=pam_userdb
+
+LIBSRC = $(TITLE).c conv.c
+LIBOBJ = $(TITLE).o conv.o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+#LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+EXTRALS += -ldb
+CFLAGS += $(WHICH_DB)
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+#static/%.o : %.c
+# $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(EXTRALS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~ *.so
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+.PHONY: register
+
+test:
+ install -m 755 $(TITLE).so /tmp
+
+endif
diff --git a/modules/pam_userdb/README b/modules/pam_userdb/README
new file mode 100644
index 00000000..09d65edd
--- /dev/null
+++ b/modules/pam_userdb/README
@@ -0,0 +1,30 @@
+pam_userdb:
+ Look up users in a .db database and verify their password against
+ what is contained in that database.
+
+RECOGNIZED ARGUMENTS:
+ debug write a message to syslog indicating success or
+ failure.
+
+ db=[path] use the [path] database for performing lookup. There
+ is no default; the module will return PAM_IGNORE if
+ no database is provided.
+
+ icase make the password verification to be case insensitive
+ (ie when working with registration numbers and such)
+
+ dump dump all the entries in the database to the log (eek,
+ don't do this by default!)
+
+MODULE SERVICES PROVIDED:
+ auth _authetication and _setcred (blank)
+
+EXAMPLE USE:
+ auth sufficient pam_userdb.so icase db=/tmp/dbtest.db
+
+AUTHOR:
+ Cristian Gafton <gafton@redhat.com>
+
+
+
+$Id$
diff --git a/modules/pam_userdb/conv.c b/modules/pam_userdb/conv.c
new file mode 100644
index 00000000..0f13d03a
--- /dev/null
+++ b/modules/pam_userdb/conv.c
@@ -0,0 +1,125 @@
+/*
+ * Conversation related functions
+ */
+
+/* $Id */
+/* Copyright at the end of the file */
+
+#define _BSD_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+#include "pam_userdb.h"
+
+/*
+ * dummy conversation function sending exactly one prompt
+ * and expecting exactly one response from the other party
+ */
+static int converse(pam_handle_t *pamh,
+ struct pam_message **message,
+ struct pam_response **response)
+{
+ int retval;
+ const struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
+ if (retval == PAM_SUCCESS)
+ retval = conv->conv(1, (const struct pam_message **)message,
+ response, conv->appdata_ptr);
+
+ return retval; /* propagate error status */
+}
+
+
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ _pam_drop(xx);
+ return NULL;
+}
+
+/*
+ * This is a conversation function to obtain the user's password
+ */
+int conversation(pam_handle_t *pamh)
+{
+ struct pam_message msg[2],*pmsg[2];
+ struct pam_response *resp;
+ int retval;
+ char * token = NULL;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[0].msg = "Password: ";
+
+ /* so call the conversation expecting i responses */
+ resp = NULL;
+ retval = converse(pamh, pmsg, &resp);
+
+ if (resp != NULL) {
+ const char * item;
+ /* interpret the response */
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+ token = x_strdup(resp[0].resp);
+ if (token == NULL) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+
+ /* set the auth token */
+ retval = pam_set_item(pamh, PAM_AUTHTOK, token);
+ token = _pam_delete(token); /* clean it up */
+ if ( (retval != PAM_SUCCESS) ||
+ (retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&item))
+ != PAM_SUCCESS ) {
+ return retval;
+ }
+
+ _pam_drop_reply(resp, 1);
+ } else {
+ retval = (retval == PAM_SUCCESS)
+ ? PAM_AUTHTOK_RECOVER_ERR:retval ;
+ }
+
+ return retval;
+}
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1999
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_userdb/create.pl b/modules/pam_userdb/create.pl
new file mode 100644
index 00000000..046b55f0
--- /dev/null
+++ b/modules/pam_userdb/create.pl
@@ -0,0 +1,23 @@
+#!/usr/bin/perl
+# this program creates a database in ARGV[1] from pairs given on
+# stdandard input
+#
+# $Id$
+
+use DB_File;
+
+my $database = $ARGV[0];
+die "Use: check,pl <database>\n" unless ($database);
+print "Using database: $database\n";
+
+my %lusers = ();
+
+tie %lusers, 'DB_File', $database, O_RDWR|O_CREAT, 0644, $DB_HASH ;
+while (<STDIN>) {
+ my ($user, $pass) = split;
+
+ $lusers{$user} = $pass;
+}
+untie %lusers;
+
+
diff --git a/modules/pam_userdb/libdbfound.sh b/modules/pam_userdb/libdbfound.sh
new file mode 100755
index 00000000..d6a1723b
--- /dev/null
+++ b/modules/pam_userdb/libdbfound.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+if [ -f "/usr/include/ndbm.h" ]; then
+ echo "-DUSE_NDBM_H"
+ exit 0
+fi
+
+list=`/bin/ls /lib/libdb.so.* 2> /dev/null`
+if [ -n "$list" ]; then
+ echo ""
+ exit 0
+fi
+
+echo "none"
+exit 0
diff --git a/modules/pam_userdb/pam_userdb.c b/modules/pam_userdb/pam_userdb.c
new file mode 100644
index 00000000..20ca5d10
--- /dev/null
+++ b/modules/pam_userdb/pam_userdb.c
@@ -0,0 +1,302 @@
+/* pam_userdb module */
+
+/*
+ * $Id$
+ * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10
+ * See the end of the file for Copyright Information
+ */
+
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
+#include <features.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "pam_userdb.h"
+
+#ifdef USE_NDBM_H
+# include <ndbm.h>
+#else /* USE_NDBM_H */
+# define DB_DBM_HSEARCH 1 /* use the dbm interface */
+# include <db.h>
+#endif /* USE_NDBM_H */
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+
+#include <security/pam_modules.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+char * database = NULL;
+static int ctrl = 0;
+
+static int _pam_parse(int argc, const char **argv)
+{
+ /* step through arguments */
+ for (ctrl = 0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strcasecmp(*argv, "icase"))
+ ctrl |= PAM_ICASE_ARG;
+ else if (!strcasecmp(*argv, "dump"))
+ ctrl |= PAM_DUMP_ARG;
+ else if (!strncasecmp(*argv,"db=", 3)) {
+ database = strdup((*argv) + 3);
+ if (database == NULL)
+ _pam_log(LOG_ERR, "pam_parse: could not parse argument \"%s\"",
+ *argv);
+ } else {
+ _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
+ }
+ }
+
+ return ctrl;
+}
+
+
+/*
+ * Looks up an user name in a database and checks the password
+ *
+ * return values:
+ * 1 = User not found
+ * 0 = OK
+ * -1 = Password incorrect
+ * -2 = System error
+ */
+static int user_lookup(const char *user, const char *pass)
+{
+ DBM *dbm;
+ datum key, data;
+
+ /* Open the DB file. */
+ dbm = dbm_open(database, O_RDONLY, 0644);
+ if (dbm == NULL) {
+ _pam_log(LOG_ERR, "user_lookup: could not open database `%s'",
+ database);
+ return -2;
+ }
+
+ if (ctrl &PAM_DUMP_ARG) {
+ _pam_log(LOG_INFO, "Database dump:");
+ for (key = dbm_firstkey(dbm); key.dptr != NULL;
+ key = dbm_nextkey(dbm)) {
+ data = dbm_fetch(dbm, key);
+ _pam_log(LOG_INFO, "key[len=%d] = `%s', data[len=%d] = `%s'",
+ key.dsize, key.dptr, data.dsize, data.dptr);
+ }
+ }
+ /* do some more init work */
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.dptr = x_strdup(user);
+ key.dsize = strlen(user);
+ user = NULL;
+
+ if (key.dptr) {
+ data = dbm_fetch(dbm, key);
+ memset(key.dptr, 0, key.dsize);
+ free(key.dptr);
+ }
+
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_INFO, "password in database is [%p]`%s', len is %d",
+ data.dptr, (char *) data.dptr, data.dsize);
+ }
+
+ if (data.dptr != NULL) {
+ int compare = 0;
+ /* bingo, got it */
+ if (ctrl & PAM_ICASE_ARG)
+ compare = strncasecmp(pass, data.dptr, data.dsize);
+ else
+ compare = strncmp(pass, data.dptr, data.dsize);
+ dbm_close(dbm);
+ if (compare == 0)
+ return 0; /* match */
+ else
+ return -1; /* wrong */
+ } else {
+ if (ctrl & PAM_DEBUG_ARG) {
+ _pam_log(LOG_INFO, "error returned by dbm_fetch: %s",
+ strerror(errno));
+ }
+ dbm_close(dbm);
+ /* probably we should check dbm_error() here */
+ return 1; /* not found */
+ }
+
+ /* NOT REACHED */
+ return -2;
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ const char *password;
+ int retval = PAM_AUTH_ERR;
+
+ /* parse arguments */
+ ctrl = _pam_parse(argc, argv);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Converse just to be sure we have the password */
+ retval = conversation(pamh);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "could not obtain password for `%s'",
+ username);
+ return -2;
+ }
+
+ /* Get the password */
+ retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&password);
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "Could not retrive user's password");
+ return -2;
+ }
+
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
+ username, password);
+
+ /* Now use the username to look up password in the database file */
+ retval = user_lookup(username, password);
+ switch (retval) {
+ case -2:
+ /* some sort of system error. The log was already printed */
+ return PAM_SERVICE_ERR;
+ case -1:
+ /* incorrect password */
+ _pam_log(LOG_WARNING,
+ "user `%s' denied access (incorrect password)",
+ username);
+ return PAM_AUTH_ERR;
+ case 1:
+ /* the user does not exist in the database */
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "user `%s' not found in the database",
+ username);
+ return PAM_USER_UNKNOWN;
+ case 0:
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' granted acces", username);
+ return PAM_SUCCESS;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(LOG_ERR,
+ "internal module error (retval = %d, user = `%s'",
+ retval, username);
+ return PAM_SERVICE_ERR;
+ }
+
+ /* should not be reached */
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_wheel_modstruct = {
+ MODULE_NAME,
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1999
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_userdb/pam_userdb.h b/modules/pam_userdb/pam_userdb.h
new file mode 100644
index 00000000..911a7622
--- /dev/null
+++ b/modules/pam_userdb/pam_userdb.h
@@ -0,0 +1,61 @@
+
+#ifndef _PAM_USERSDB_H
+#define _PAM_USERSDB_H
+/* $Id$ */
+
+/* Header files */
+#include <security/pam_appl.h>
+
+/* argument parsing */
+#define PAM_DEBUG_ARG 0x0001
+#define PAM_ICASE_ARG 0x0002
+#define PAM_DUMP_ARG 0x0004
+
+/* Useful macros */
+#define x_strdup(s) ( (s) ? strdup(s):NULL )
+
+/* The name of the module we are compiling */
+#ifndef MODULE_NAME
+#define MODULE_NAME "pam_userdb"
+#endif /* MODULE_NAME */
+
+/* function prototypes */
+int conversation(pam_handle_t *);
+
+#endif /* _PAM_USERSDB_H */
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1999
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/pam_warn/.cvsignore b/modules/pam_warn/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_warn/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_warn/Makefile b/modules/pam_warn/Makefile
new file mode 100644
index 00000000..7b48689c
--- /dev/null
+++ b/modules/pam_warn/Makefile
@@ -0,0 +1,102 @@
+#
+# $Id$
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# $Log$
+# Revision 1.1 2000/06/20 22:12:10 agmorgan
+# Initial revision
+#
+# Revision 1.1.1.1 1998/07/12 05:17:17 morgan
+# Linux PAM sources pre-0.66
+#
+# Revision 1.2 1997/04/05 06:20:16 morgan
+# fixed fakeroot
+#
+# Revision 1.1 1996/12/01 03:12:22 morgan
+# Initial revision
+#
+#
+# Created by Andrew Morgan <morgan@parc.power.net> 1996/11/14
+#
+
+TITLE=pam_warn
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+static/%.o : %.c
+ $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+ifdef STATIC
+LIBSTATIC = lib$(TITLE).o
+endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+ifdef STATIC
+ $(MKDIR) ./static
+endif
+
+register:
+ifdef STATIC
+ ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD)
+endif
+
+ifdef STATIC
+$(LIBOBJS): $(LIBSRC)
+
+$(LIBSTATIC): $(LIBOBJS)
+ $(LD) -r -o $@ $(LIBOBJS)
+endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_warn/README b/modules/pam_warn/README
new file mode 100644
index 00000000..6d484bdf
--- /dev/null
+++ b/modules/pam_warn/README
@@ -0,0 +1,26 @@
+# $Id$
+#
+
+This module is an authentication module that does not authenticate.
+Instead it always returns PAM_IGNORE, indicating that it does not want
+to affect the authentication process.
+
+Its purpose is to log a message to the syslog indicating the
+pam_item's available at the time it was invoked. It is a diagnostic
+tool.
+
+Recognized arguments:
+
+ <none>
+
+module services provided:
+
+ auth _authenticate and _setcred (blank)
+ acct _acct_mgmt [mapped to _authenticate]
+ session _open_session and
+ _close_session [mapped to _authenticate ]
+ password _chauthtok [mapped to _authenticate]
+
+
+Andrew Morgan
+1996/11/14
diff --git a/modules/pam_warn/pam_warn.c b/modules/pam_warn/pam_warn.c
new file mode 100644
index 00000000..2e390ca4
--- /dev/null
+++ b/modules/pam_warn/pam_warn.c
@@ -0,0 +1,132 @@
+/* pam_warn module */
+
+/*
+ * $Id$
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
+ */
+
+#define _BSD_SOURCE
+
+#include <stdio.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+#define PAM_SM_PASSWORD
+
+#include <security/pam_modules.h>
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-warn", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc
+ , const char **argv)
+{
+ const char *service=NULL, *user=NULL, *terminal=NULL
+ , *rhost=NULL, *ruser=NULL;
+
+ (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
+ (void) pam_get_item(pamh, PAM_TTY, (const void **)&terminal);
+ _pam_log(LOG_NOTICE, "service: %s [on terminal: %s]"
+ , service ? service : "<unknown>"
+ , terminal ? terminal : "<unknown>"
+ );
+ (void) pam_get_user(pamh, &user, "Who are you? ");
+ (void) pam_get_item(pamh, PAM_RUSER, (const void **)&ruser);
+ (void) pam_get_item(pamh, PAM_RHOST, (const void **)&rhost);
+ _pam_log(LOG_NOTICE, "user: (uid=%d) -> %s [remote: %s@%s]"
+ , getuid()
+ , user ? user : "<unknown>"
+ , ruser ? ruser : "?nobody"
+ , rhost ? rhost : "?nowhere"
+ );
+
+ /* we are just a fly on the wall */
+
+ return PAM_IGNORE;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ , const char **argv)
+{
+ return PAM_IGNORE;
+}
+
+/* password updating functions */
+
+PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc
+ , const char **argv)
+{
+ /* map to the authentication function... */
+
+ return pam_sm_authenticate(pamh, flags, argc, argv);
+}
+
+PAM_EXTERN int
+pam_sm_acct_mgmt (pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ /* map to the authentication function... */
+
+ return pam_sm_authenticate(pamh, flags, argc, argv);
+}
+
+PAM_EXTERN int
+pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ /* map to the authentication function... */
+
+ return pam_sm_authenticate(pamh, flags, argc, argv);
+}
+
+PAM_EXTERN int
+pam_sm_close_session (pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ /* map to the authentication function... */
+
+ return pam_sm_authenticate(pamh, flags, argc, argv);
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_warn_modstruct = {
+ "pam_warn",
+ 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/modules/pam_wheel/.cvsignore b/modules/pam_wheel/.cvsignore
new file mode 100644
index 00000000..380a834a
--- /dev/null
+++ b/modules/pam_wheel/.cvsignore
@@ -0,0 +1 @@
+dynamic
diff --git a/modules/pam_wheel/Makefile b/modules/pam_wheel/Makefile
new file mode 100644
index 00000000..a262babf
--- /dev/null
+++ b/modules/pam_wheel/Makefile
@@ -0,0 +1,86 @@
+#
+# This Makefile controls a build process of $(TITLE) module for
+# Linux-PAM. You should not modify this Makefile (unless you know
+# what you are doing!).
+#
+# Created by Cristian Gafton <gafton@sorosis.ro> 1996/09/10
+#
+
+TITLE=pam_wheel
+
+#
+
+LIBSRC = $(TITLE).c
+LIBOBJ = $(TITLE).o
+LIBOBJD = $(addprefix dynamic/,$(LIBOBJ))
+#LIBOBJS = $(addprefix static/,$(LIBOBJ))
+
+dynamic/%.o : %.c
+ $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+#static/%.o : %.c
+# $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
+
+
+ifdef DYNAMIC
+LIBSHARED = $(TITLE).so
+endif
+
+#ifdef STATIC
+#LIBSTATIC = lib$(TITLE).o
+#endif
+
+####################### don't edit below #######################
+
+dummy:
+
+ @echo "**** This is not a top-level Makefile "
+ exit
+
+all: dirs $(LIBSHARED) $(LIBSTATIC) register
+
+dirs:
+ifdef DYNAMIC
+ $(MKDIR) ./dynamic
+endif
+#ifdef STATIC
+# $(MKDIR) ./static
+#endif
+
+register:
+#ifdef STATIC
+# ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) )
+#endif
+
+ifdef DYNAMIC
+$(LIBOBJD): $(LIBSRC)
+
+$(LIBSHARED): $(LIBOBJD)
+ $(LD_D) -o $@ $(LIBOBJD) $(EXTRALS)
+endif
+
+#ifdef STATIC
+#$(LIBOBJS): $(LIBSRC)
+#
+#$(LIBSTATIC): $(LIBOBJS)
+# $(LD) -r -o $@ $(LIBOBJS) $(EXTRALS)
+#endif
+
+install: all
+ $(MKDIR) $(FAKEROOT)$(SECUREDIR)
+ifdef DYNAMIC
+ $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)
+endif
+
+remove:
+ rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so
+
+clean:
+ rm -f $(LIBOBJD) $(LIBOBJS) core *~ *.so
+
+extraclean: clean
+ rm -f *.a *.o *.so *.bak dynamic/* static/*
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
diff --git a/modules/pam_wheel/README b/modules/pam_wheel/README
new file mode 100644
index 00000000..336bb31e
--- /dev/null
+++ b/modules/pam_wheel/README
@@ -0,0 +1,33 @@
+
+pam_wheel:
+ only permit root authentication too members of wheel group
+
+RECOGNIZED ARGUMENTS:
+ debug write a message to syslog indicating success or
+ failure.
+
+ use_uid the check for wheel membership will be done against
+ the current uid instead of the original one
+ (useful when jumping with su from one account to
+ another for example)
+
+ trust the pam_wheel module will return PAM_SUCCESS instead
+ of PAM_IGNORE if the user is a member of the wheel
+ group (thus with a little play stacking the modules
+ the wheel members may be able to su to root without
+ being prompted for a passwd).
+
+ deny Reverse the sense of the auth operation: if the user
+ is trying to get UID 0 access and is a member of the
+ wheel group, deny access (well, kind of nonsense, but
+ for use in conjunction with 'group' argument... :-)
+
+ group=xxxx Instead of checking the GID 0 group, use the xxxx
+ group to perform the authentification.
+
+MODULE SERVICES PROVIDED:
+ auth _authetication and _setcred (blank)
+
+AUTHOR:
+ Cristian Gafton <gafton@sorosis.ro>
+
diff --git a/modules/pam_wheel/pam_wheel.c b/modules/pam_wheel/pam_wheel.c
new file mode 100644
index 00000000..417fa5b8
--- /dev/null
+++ b/modules/pam_wheel/pam_wheel.c
@@ -0,0 +1,275 @@
+/* pam_wheel module */
+
+/*
+ * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10
+ * See the end of the file for Copyright Information
+ *
+ *
+ * 1.2 - added 'deny' and 'group=' options
+ * 1.1 - added 'trust' option
+ * 1.0 - the code is working for at least another person, so... :-)
+ * 0.1 - use vsyslog instead of vfprintf/syslog in _pam_log
+ * - return PAM_IGNORE on success (take care of sloppy sysadmins..)
+ * - use pam_get_user instead of pam_get_item(...,PAM_USER,...)
+ * - a new arg use_uid to auth the current uid instead of the
+ * initial (logged in) one.
+ * 0.0 - first release
+ *
+ * TODO:
+ * - try to use make_remark from pam_unix/support.c
+ * - consider returning on failure PAM_FAIL_NOW if the user is not
+ * a wheel member.
+ */
+
+#define _BSD_SOURCE
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#define PAM_SM_AUTH
+
+#include <security/pam_modules.h>
+
+/* variables */
+static char use_group[BUFSIZ];
+
+/* some syslogging */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("PAM-Wheel", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+/* checks if a user is on a list of members of the GID 0 group */
+
+static int is_on_list(char * const *list, const char *member)
+{
+ while (*list) {
+ if (strcmp(*list, member) == 0)
+ return 1;
+ list++;
+ }
+ return 0;
+}
+
+/* argument parsing */
+
+#define PAM_DEBUG_ARG 0x0001
+#define PAM_USE_UID_ARG 0x0002
+#define PAM_TRUST_ARG 0x0004
+#define PAM_DENY_ARG 0x0010
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl=0;
+
+ /* step through arguments */
+ for (ctrl=0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= PAM_DEBUG_ARG;
+ else if (!strcmp(*argv,"use_uid"))
+ ctrl |= PAM_USE_UID_ARG;
+ else if (!strcmp(*argv,"trust"))
+ ctrl |= PAM_TRUST_ARG;
+ else if (!strcmp(*argv,"deny"))
+ ctrl |= PAM_DENY_ARG;
+ else if (!strncmp(*argv,"group=",6))
+ strcpy(use_group,*argv+6);
+ else {
+ _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+ }
+ }
+
+ return ctrl;
+}
+
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ int ctrl;
+ const char *username;
+ char *fromsu;
+ struct passwd *pwd, *tpwd;
+ struct group *grp;
+ int retval = PAM_AUTH_ERR;
+
+ /* Init the optional group */
+ bzero(use_group,sizeof(use_group));
+
+ ctrl = _pam_parse(argc, argv);
+ retval = pam_get_user(pamh,&username,NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ /* su to a uid 0 account ? */
+ pwd = getpwnam(username);
+ if (!pwd) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE,"unknown user %s",username);
+ return PAM_USER_UNKNOWN;
+ }
+
+ /* Now we know that the username exists, pass on to other modules...
+ * the call to pam_get_user made this obsolete, so is commented out
+ *
+ * pam_set_item(pamh,PAM_USER,(const void *)username);
+ */
+
+ /* is this user an UID 0 account ? */
+ if(pwd->pw_uid) {
+ /* no need to check for wheel */
+ return PAM_IGNORE;
+ }
+
+ if (ctrl & PAM_USE_UID_ARG) {
+ tpwd = getpwuid(getuid());
+ if (!tpwd) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE,"who is running me ?!");
+ return PAM_SERVICE_ERR;
+ }
+ fromsu = tpwd->pw_name;
+ } else {
+ fromsu = getlogin();
+ if (!fromsu) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE,"who is running me ?!");
+ return PAM_SERVICE_ERR;
+ }
+ }
+
+ if (!use_group[0]) {
+ if ((grp = getgrnam("wheel")) == NULL) {
+ grp = getgrgid(0);
+ }
+ } else
+ grp = getgrnam(use_group);
+
+ if (!grp || !grp->gr_mem) {
+ if (ctrl & PAM_DEBUG_ARG) {
+ if (!use_group[0])
+ _pam_log(LOG_NOTICE,"no members in a GID 0 group");
+ else
+ _pam_log(LOG_NOTICE,"no members in '%s' group",use_group);
+ }
+ if (ctrl & PAM_DENY_ARG)
+ /* if this was meant to deny access to the members
+ * of this group and the group does not exist, allow
+ * access
+ */
+ return PAM_IGNORE;
+ else
+ return PAM_AUTH_ERR;
+ }
+
+ if (is_on_list(grp->gr_mem, fromsu)) {
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE,"Access %s to '%s' for '%s'",
+ (ctrl & PAM_DENY_ARG)?"denied":"granted",
+ fromsu,username);
+ if (ctrl & PAM_DENY_ARG)
+ return PAM_PERM_DENIED;
+ else
+ if (ctrl & PAM_TRUST_ARG)
+ return PAM_SUCCESS;
+ else
+ return PAM_IGNORE;
+ }
+
+ if (ctrl & PAM_DEBUG_ARG)
+ _pam_log(LOG_NOTICE,"Access %s for '%s' to '%s'",
+ (ctrl & PAM_DENY_ARG)?"granted":"denied",fromsu,username);
+ if (ctrl & PAM_DENY_ARG)
+ return PAM_SUCCESS;
+ else
+ return PAM_PERM_DENIED;
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
+ ,const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_wheel_modstruct = {
+ "pam_wheel",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif
+
+/*
+ * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1996, 1997
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/modules/register_static b/modules/register_static
new file mode 100755
index 00000000..2067ac7e
--- /dev/null
+++ b/modules/register_static
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+if [ `basename $PWD` != "modules" ]; then
+ echo "$0 must be run from the .../modules directory"
+ exit 1
+fi
+
+merge_line ()
+{
+ if [ $# != 3 ]; then
+ echo "usage: merge_line token filename 'new line'"
+ fi
+ if [ -f $2 ]; then
+# remove any existing entry...
+ grep -v "$1" $2 > tmp.$2
+ rm -f $2
+ mv {tmp.,}$2
+ fi
+ cat << EOT >> $2
+$3
+EOT
+
+}
+
+
+if [ $# -ne 2 ]; then
+
+ cat << EOT 2>&1
+$0: this script takes TWO arguments:
+ the 'alphanumeric label' of the module and the location of
+ its object file from the .../modules/ directory
+EOT
+ exit 1
+
+else
+ echo "
+ *> registering static module: $1 ($2) <*
+"
+ merge_line "$1" _static_module_list "\
+extern struct pam_module _$1_modstruct;"
+
+ merge_line "$1" _static_module_entry " &_$1_modstruct,"
+ if [ -n "$2" ]; then
+ merge_line "$2" _static_module_objects "../modules/$2"
+ fi
+
+fi
+
+exit 0
diff --git a/pgp.keys.asc b/pgp.keys.asc
new file mode 100644
index 00000000..a516c379
--- /dev/null
+++ b/pgp.keys.asc
@@ -0,0 +1,103 @@
+Type Bits/KeyID Date User ID
+pub 1024/2A398175 1996/11/17 Andrew G. Morgan <morgan@linux.kernel.org>
+ Andrew G. Morgan <morgan@transmeta.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: 2.6.3a
+
+mQCNAzKOhJ4AAAEEAJ9xYnZSD1kYanF+8GUBhHf/gx6hGd8ZNmS5qIC8Qb8rMcTI
++E16nV+FnNRlPRbShITYjq1TPvVK8gTliZf41N9LRQZw0rywRt1NQyhdfKgDWYxB
+kSOwK67oDjkzzC56XS2rrGI6K3Rz/VtYElRyuQ6ZyaKTGcgU/TTwrUUqOYF1AAUR
+tCpBbmRyZXcgRy4gTW9yZ2FuIDxtb3JnYW5AbGludXgua2VybmVsLm9yZz6JAJUD
+BRA2iFK0NPCtRSo5gXUBAalqA/9s3Hx8BUESiC9PpL88KSVe3ENoO0ogAuMDK3vj
+k2a17Twxi92Dc/NPXr8ewEKF/h1GiRetLBVPGaSVC+602+2cr5SHqzUzAeyF2Xa6
+VAxCskxkAssTxIW7nyAMWaOB5A/1xm3YChawVQx3XIvbIp+HXHDNr/60COtlGm7I
+IcHftbQnQW5kcmV3IEcuIE1vcmdhbiA8bW9yZ2FuQHRyYW5zbWV0YS5jb20+iQCV
+AwUQNohVmTTwrUUqOYF1AQEgWwP+K94N0OO+I2A7lnP5Jp7O+kfMJCFxPZOeozrq
+O8uKsAs03ekS+kDJ3p2ec65BOzZyweHEu1HtOtdZbXsN3zynLKBwJrvvaHBQpAqv
+BrjfNsl9a+NFmfa4fmdPWTzCaG2rmFlaQvZ6FP7QrHXB/1+VlH0gJ90FOgAd3Qyp
+4hhW9g8=
+=qQJI
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID Date User ID
+pub 1024/4536A8DD 1996/01/28 Michael K. Johnson <johnsonm@redhat.com>
+ Michael K. Johnson <johnsonm@nigel.vnet.net>
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: 2.6.3a
+
+mQCNAzEK0l0AAAEEAMWweYcS6ov1RISP6E7lb3vgQOrmhBy6S/8zkuHo92IkQWXm
+V9AcMUY/eJPRJH6yI6o1ZKN4InT4uCkSIQOd2C8XyeIK5jFhpmP9DhoucacNL5H7
+oCV4wtFGhUDaDl9VeTtbWLSMESxJ4T/fL/IfkW95/Q2dF7zIDid5aW9FNqjdAAUR
+tChNaWNoYWVsIEsuIEpvaG5zb24gPGpvaG5zb25tQHJlZGhhdC5jb20+iQCVAwUQ
+MuqeiDTwrUUqOYF1AQEjywP/bCWLybbZSI8plyUSWD3yxwjsE+8BiOPGRu1AARUz
+GbVZq9LqPDyjFtH9DqgXULyZtCAk8ebZonH/h/0EnZTi4tiZg3BHKXhIlWQnNz4D
+QRdtUEmMNQzi9+3mU99CBGigsrDQnNrnI88ejo/0YY3gdt6752g5HAvY13h9A0ZP
+MFWJAJUDBRAxgAouJ3lpb0U2qN0BActVA/9vgBOUheUpLPiIry/+2qqJv+e+LnHw
+DgZqROpli9bhJ4wfb1sXPYkFzchR8BUeU0NY6HvAwxEilSNPE1yQoaJuy8POtTuu
+aFO4wvuLp0v5LuatXaU8EsncwjrBsWqRB6Dqd+jyq24Pjx0YKNSRJxceiBE8SBDW
+HESAhYTYCBLy77QsTWljaGFlbCBLLiBKb2huc29uIDxqb2huc29ubUBuaWdlbC52
+bmV0Lm5ldD6JAJUDBRAxGljWe01Ojay67k0BAf3qA/48N9OvgGk9nNR+Pg6aW3rK
+2Dy8t2RQdFGd4b7gBtZeXUAklq9ppYZtS+cXFHoQ8d7K8XBjHh+rgF2oOSBQUrQf
+eb8XkKSZQxB7DZVdi1gAsOzSwCrn4TWSSKc28P4Mjuj1Jr2f1FGST1+cGIl7JbhV
+kLGjmvOIgs7lS8FE0Hhm/4kAlQMFEDEWclxEcVNogr/H7QEBN1QD/1iY+KYQyOTz
+fgaBsx+Bt11kstmOlYhXx23yK2etG0p8XCD2r3aojGOTR/e3o2bLiJo4xe+iMhOM
+dvdSzxSPGQ20wX3jGJaRrRiSClFTQbZSelGG0FcOGfM3mL5zeHaXzRcRciK3VDkD
+IFzTQ3J5NJVBIVlAkxTMIxho758lR2SjiQCVAwUQMREqFnoDqzGe1QXFAQFdpAP/
+VPPoYO50seo1rLL28AA2PVKqo6BJwj0ZMsC14MDJEKryBbj/E4Ma25uSlzBjj+t9
+rbygoz0XWUQMLh8XPAEps3nE3n8FWROsdlucGzGiDGKVEygLPzCsjR7aGEspN1Y7
+4qOZPxbpGG7B5exOLur4ACY75m6oBh+PN+Q1liCIYXKJAJUDBRAxDpk1iGe2nxKR
+G10BAeQjBACmx4DyJacQXxuckDaKMTXa8v2Q7lQpPDyHdn1oAUsx1mrbSL55v2AI
+Q0riFWcFRTERpjAToCLgQjK1pKpmJcduiXURj6TPVKd88hYkuCIpn2hIaI7SCkd8
+HZlfFiuaxVN29UbbzHv3C+mseydpkPRrovqmOSuj2xAGFALo6Vl9U4kAlQMFEDEN
+eD5EFXDNRmtCiQEBRmoEAJAuyY0F5hbweDOdeAhxLWeiTl9jGwQYDS3T5B5/9ZpC
+bJ1yX7Pk2o7LvR9tg/Ji5sfMMvIpH48DNT4kyjmmChFXCUBccwd+33ugdTcYDwLR
+Cdt7k9r2yXz1LEH+lVNKOEIhuIq8/sX61hvFR7+qSABthTLrvvynycD5n2pG3F7L
+=aGjw
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID Date User ID
+pub 1024/D4F4D901 1997/03/05 Cristian Gafton <gafton@sorosis.ro>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: 2.6.3a
+
+mQCNAzMdU6sAAAEEAKLF73rRJ3RUtl+y4bLUOVOV7ataJ46ZHxDZeGAVi+/suwT9
+Kq7QdaeFc4Xwaq8PVWv7pZ4/qTwHUkdbjBVeLt+KOlprvKuadyAh9aG/SqmKkEvA
+hCS3yZDwNmeSLO7VIN5ko1nIwVD4kPJvS3xX6kn6jd4mvv/qGfGvxKXU9NkBAAUR
+tCNDcmlzdGlhbiBHYWZ0b24gPGdhZnRvbkBzb3Jvc2lzLnJvPokAlQMFEDMeTlI0
+8K1FKjmBdQEBmgQD/02JxAU6+fiaBKwRIFDdsLYTy8mPgYaoul9RIX450W5D5nY/
+/696F6TfmFUzvnrvTbZUDyLxHB0mnh4SrdKRKo57i7RDrdx3Mqlt/xP4R6nHwFed
+yTMvz3KB9tYuWfC1fJp69/VRIkMrw448zKkgqHUnAKxMIHvXnV3M9jd6lXSYiQCV
+AwUQMx1Tq/GvxKXU9NkBAQE3/gP/RZMe59OkBWS4whc9c6eac6zwcC/hNc1vyiZ5
+2TEHJ10PgtNtHchD7j3xsDO17/DGEZB23OQiPAeLdqnBr+y2uiSlQfYdpVHBHX3A
+uX3onc69LpEHmUAJAVOvfU1scnDtOH/KeVN3nwc6PWLxzLWzXfUbwLNK+LiPMNMV
+1qygu+s=
+=J4G2
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID Date User ID
+pub 1024/A5D75B79 1997/03/01 Andrey V. Savochkin <saw@msu.ru>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: 2.6.3a
+
+mQCNAzMYf1MAAAEEAK1S5jgmWnn8IS9mKoSpXu87f2soQhVZ3XdvsBCK2V7BojlU
+0+JJrK+2gMH5tavyFsQ6cKch6I4xH54cS4P4tNE9M7OtfoXOxejtp9U9KZio8T0X
+gM8qOS4fTQEfmdHSA5ETe5Vv+WPZ+/3SCo5kD1uIUUwppHDgJH+l396l11t5AAUR
+tCBBbmRyZXkgVi4gU2F2b2Noa2luIDxzYXdAbXN1LnJ1PokAlQMFEDaIUh008K1F
+KjmBdQEBFtkD/38mraXdr4aEYC6lxlG3cF+59XB6FjyBYhtwgNshpI2mB5XLr25p
+f4jMFNUqnY/bGjXWKwbNguzJ0ukD8TgOg1ZXQZztRso1t1Y2M1KPbwlqj8ib1bZG
+inQO/eqLrVwFH6F9CTiF0Fgy7faAIHN6BfE0o8earrcIwjT7sxRej3lziQCVAwUQ
+M35653fqPT1smcpJAQHeqgQAlXMOru6Rz1TkslVrWD0n7dvBUHQxs0HS1pcWJnZJ
+6kcYMLSA2RBi1fRabwzuOtzK60tOmfmnD7btcGBMMflOtfSulEg/xKNw2awEsNQK
+ULEIBsvrpMr0UN4hWkxTggDXaykg7rQqgrbAsicoLuTtPDIbc+yhQcFEVGJiPO/I
+tqiJAJUDBRAzfnUef89/VVw/1FkBAQ2lA/9q6FQM4RZzp75qxZ7jqAwUy9RFAKhp
+L63YFJX3i1JsUjNoO51pjj5pEAxVVQsorqbdsmpC2aOUTf1AufEcs1kLojb3tc19
+MhXPyHTJs66QqWutdP/yOW+CLzmILAsbEgI6O+toVZ0rHVXjEtRgKUnYReHLrlYj
+RKlBnkVc3NtPcIkAlQMFEDMYf1N/pd/epddbeQEBfKYD/3x/PkH2e+Cy7YXsfwxb
+y/n+6eNIbfakSYjkwN5tDOeaKhdQKUJBKVwAzD2yrLmMDx6uW+FUOTucb6Anau6R
+iKrAJq/a4DcpAeymo7cAthVU7en7HWwebQcL4wZGao1BJI+ulynki4sIqkfbGP83
+DK775eovl5X195ZkE/wNJvoi
+=V5TY
+-----END PGP PUBLIC KEY BLOCK-----