From ea488580c42e8918445a945484de3c8a5addc761 Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Tue, 20 Jun 2000 22:10:38 +0000 Subject: Initial revision --- .cvsignore | 4 + CHANGELOG | 1244 +++++++++++++++++++ Copyright | 41 + Makefile | 283 +++++ README | 167 +++ bin/.cvsignore | 3 + bin/README | 45 + conf/.cvsignore | 2 + conf/Makefile | 34 + conf/install | 178 +++ conf/install_conf | 36 + conf/md5itall | 51 + conf/mkdirp | 50 + conf/pam.conf | 126 ++ conf/pam_conv1/.cvsignore | 3 + conf/pam_conv1/Makefile | 41 + conf/pam_conv1/README | 10 + conf/pam_conv1/pam_conv.lex | 42 + conf/pam_conv1/pam_conv.y | 203 +++ defs/debian.defs | 40 + defs/hpux.defs | 36 + defs/linux.defs | 32 + defs/morgan.defs | 36 + defs/redhat.defs | 36 + defs/redhat4.defs | 35 + defs/solaris-2.1.5.defs | 45 + defs/solaris.defs | 48 + defs/sunos.defs | 37 + defs/suse.defs | 36 + doc/.cvsignore | 2 + doc/CREDITS | 48 + doc/Makefile | 101 ++ doc/NOTES | 16 + doc/figs/pam_orient.txt | 23 + doc/html/.cvsignore | 1 + doc/html/index.html | 21 + doc/man/pam.8 | 279 +++++ doc/man/pam.conf.8 | 1 + doc/man/pam.d.8 | 1 + doc/man/pam_authenticate.3 | 91 ++ doc/man/pam_chauthtok.3 | 101 ++ doc/man/pam_close_session.3 | 1 + doc/man/pam_end.3 | 1 + doc/man/pam_fail_delay.3 | 130 ++ doc/man/pam_open_session.3 | 99 ++ doc/man/pam_setcred.3 | 79 ++ doc/man/pam_start.3 | 98 ++ doc/man/pam_strerror.3 | 51 + doc/man/template-man | 52 + doc/modules/README | 13 + doc/modules/module.sgml-template | 170 +++ doc/modules/pam_access.sgml | 93 ++ doc/modules/pam_chroot.sgml | 86 ++ doc/modules/pam_cracklib.sgml | 259 ++++ doc/modules/pam_deny.sgml | 179 +++ doc/modules/pam_env.sgml | 141 +++ doc/modules/pam_filter.sgml | 150 +++ doc/modules/pam_ftp.sgml | 93 ++ doc/modules/pam_group.sgml | 108 ++ doc/modules/pam_issue.sgml | 120 ++ doc/modules/pam_krb4.sgml | 126 ++ doc/modules/pam_lastlog.sgml | 119 ++ doc/modules/pam_limits.sgml | 197 +++ doc/modules/pam_listfile.sgml | 138 +++ doc/modules/pam_mail.sgml | 137 ++ doc/modules/pam_mkhomedir.sgml | 83 ++ doc/modules/pam_motd.sgml | 77 ++ doc/modules/pam_nologin.sgml | 75 ++ doc/modules/pam_permit.sgml | 83 ++ doc/modules/pam_pwdb.sgml | 252 ++++ doc/modules/pam_radius.sgml | 117 ++ doc/modules/pam_rhosts.sgml | 164 +++ doc/modules/pam_rootok.sgml | 85 ++ doc/modules/pam_securetty.sgml | 72 ++ doc/modules/pam_time.sgml | 166 +++ doc/modules/pam_unix.sgml | 289 +++++ doc/modules/pam_userdb.sgml | 112 ++ doc/modules/pam_warn.sgml | 67 + doc/modules/pam_wheel.sgml | 125 ++ doc/pam_appl.sgml | 1643 ++++++++++++++++++++++++ doc/pam_modules.sgml | 1476 ++++++++++++++++++++++ doc/pam_source.sgml | 1173 ++++++++++++++++++ doc/ps/.cvsignore | 1 + doc/ps/README | 3 + doc/ps/missfont.log | 36 + doc/specs/.cvsignore | 1 + doc/specs/draft-morgan-pam.raw | 702 +++++++++++ doc/specs/formatter/.cvsignore | 3 + doc/specs/formatter/Makefile | 16 + doc/specs/formatter/parse.lex | 11 + doc/specs/formatter/parse.y | 293 +++++ doc/specs/rfc86.0.txt | 1851 ++++++++++++++++++++++++++++ doc/txts/.cvsignore | 1 + doc/txts/README | 3 + examples/.cvsignore | 3 + examples/Makefile | 42 + examples/blank.c | 182 +++ examples/check_user.c | 71 ++ examples/test.c | 105 ++ examples/vpass.c | 47 + examples/xsh.c | 148 +++ libpam/.cvsignore | 2 + libpam/Makefile | 162 +++ libpam/include/security/_pam_compat.h | 122 ++ libpam/include/security/_pam_macros.h | 166 +++ libpam/include/security/_pam_types.h | 316 +++++ libpam/include/security/pam_appl.h | 92 ++ libpam/include/security/pam_malloc.h | 79 ++ libpam/include/security/pam_modules.h | 195 +++ libpam/pam_account.c | 13 + libpam/pam_auth.c | 67 + libpam/pam_data.c | 105 ++ libpam/pam_delay.c | 168 +++ libpam/pam_dispatch.c | 282 +++++ libpam/pam_end.c | 72 ++ libpam/pam_env.c | 389 ++++++ libpam/pam_handlers.c | 896 ++++++++++++++ libpam/pam_item.c | 304 +++++ libpam/pam_log.c | 382 ++++++ libpam/pam_malloc.c | 404 ++++++ libpam/pam_map.c | 85 ++ libpam/pam_misc.c | 305 +++++ libpam/pam_password.c | 52 + libpam/pam_private.h | 312 +++++ libpam/pam_second.c | 46 + libpam/pam_session.c | 41 + libpam/pam_start.c | 112 ++ libpam/pam_static.c | 141 +++ libpam/pam_strerror.c | 112 ++ libpam/pam_tokens.h | 111 ++ libpam_misc/.cvsignore | 7 + libpam_misc/Makefile | 96 ++ libpam_misc/help_env.c | 118 ++ libpam_misc/misc_conv.c | 344 ++++++ libpam_misc/pam_misc.h | 56 + libpam_misc/xstrdup.c | 31 + libpamc/.cvsignore | 3 + libpamc/License | 42 + libpamc/Makefile | 110 ++ libpamc/include/security/pam_client.h | 186 +++ libpamc/libpamc.h | 63 + libpamc/pamc_client.c | 189 +++ libpamc/pamc_converse.c | 211 ++++ libpamc/pamc_load.c | 477 +++++++ libpamc/test/agents/secret@here | 305 +++++ libpamc/test/modules/Makefile | 9 + libpamc/test/modules/pam_secret.c | 670 ++++++++++ libpamc/test/regress/Makefile | 7 + libpamc/test/regress/run_test.sh | 6 + libpamc/test/regress/test.libpamc.c | 334 +++++ libpamc/test/regress/test.secret@here | 152 +++ modules/Makefile | 70 ++ modules/README | 55 + modules/dont_makefile | 19 + modules/download-all | 30 + modules/pam_access/.cvsignore | 1 + modules/pam_access/Makefile | 106 ++ modules/pam_access/README | 40 + modules/pam_access/access.conf | 52 + modules/pam_access/install_conf | 46 + modules/pam_access/pam_access.c | 424 +++++++ modules/pam_cracklib/.cvsignore | 1 + modules/pam_cracklib/Makefile | 118 ++ modules/pam_cracklib/README | 21 + modules/pam_cracklib/pam_cracklib.c | 776 ++++++++++++ modules/pam_deny/.cvsignore | 1 + modules/pam_deny/Makefile | 131 ++ modules/pam_deny/README | 4 + modules/pam_deny/pam_deny.c | 100 ++ modules/pam_env/.cvsignore | 1 + modules/pam_env/Makefile | 111 ++ modules/pam_env/README | 72 ++ modules/pam_env/install_conf | 46 + modules/pam_env/pam_env.c | 839 +++++++++++++ modules/pam_env/pam_env.conf-example | 72 ++ modules/pam_filter/.cvsignore | 2 + modules/pam_filter/.upperLOWER | 1 + modules/pam_filter/Makefile | 140 +++ modules/pam_filter/README | 94 ++ modules/pam_filter/include/pam_filter.h | 32 + modules/pam_filter/pam_filter.c | 738 +++++++++++ modules/pam_filter/upperLOWER/.cvsignore | 1 + modules/pam_filter/upperLOWER/Makefile | 64 + modules/pam_filter/upperLOWER/upperLOWER.c | 174 +++ modules/pam_ftp/.cvsignore | 1 + modules/pam_ftp/Makefile | 88 ++ modules/pam_ftp/README | 18 + modules/pam_ftp/pam_ftp.c | 299 +++++ modules/pam_group/.cvsignore | 1 + modules/pam_group/Makefile | 98 ++ modules/pam_group/group.conf | 60 + modules/pam_group/install_conf | 46 + modules/pam_group/pam_group.c | 843 +++++++++++++ modules/pam_issue/.cvsignore | 1 + modules/pam_issue/Makefile | 86 ++ modules/pam_issue/pam_issue.c | 265 ++++ modules/pam_lastlog/.cvsignore | 1 + modules/pam_lastlog/Makefile | 112 ++ modules/pam_lastlog/pam_lastlog.c | 463 +++++++ modules/pam_limits/.cvsignore | 1 + modules/pam_limits/Makefile | 98 ++ modules/pam_limits/README | 87 ++ modules/pam_limits/install_conf | 46 + modules/pam_limits/limits.skel | 42 + modules/pam_limits/pam_limits.c | 635 ++++++++++ modules/pam_listfile/.cvsignore | 1 + modules/pam_listfile/Makefile | 84 ++ modules/pam_listfile/README | 25 + modules/pam_listfile/pam_listfile.c | 437 +++++++ modules/pam_mail/.cvsignore | 1 + modules/pam_mail/Makefile | 113 ++ modules/pam_mail/README | 17 + modules/pam_mail/pam_mail.c | 504 ++++++++ modules/pam_mkhomedir/.cvsignore | 1 + modules/pam_mkhomedir/Makefile | 90 ++ modules/pam_mkhomedir/pam_mkhomedir.c | 370 ++++++ modules/pam_motd/.cvsignore | 1 + modules/pam_motd/Makefile | 90 ++ modules/pam_motd/pam_motd.c | 119 ++ modules/pam_nologin/.cvsignore | 1 + modules/pam_nologin/Makefile | 86 ++ modules/pam_nologin/README | 12 + modules/pam_nologin/pam_nologin.c | 130 ++ modules/pam_permit/.cvsignore | 1 + modules/pam_permit/Makefile | 132 ++ modules/pam_permit/README | 4 + modules/pam_permit/pam_permit.c | 128 ++ modules/pam_pwdb/.cvsignore | 2 + modules/pam_pwdb/BUGS | 14 + modules/pam_pwdb/CHANGELOG | 10 + modules/pam_pwdb/Makefile | 193 +++ modules/pam_pwdb/README | 41 + modules/pam_pwdb/TODO | 34 + modules/pam_pwdb/bigcrypt.-c | 114 ++ modules/pam_pwdb/md5.c | 270 ++++ modules/pam_pwdb/md5.h | 34 + modules/pam_pwdb/md5_crypt.c | 153 +++ modules/pam_pwdb/pam_pwdb.c | 256 ++++ modules/pam_pwdb/pam_unix_acct.-c | 298 +++++ modules/pam_pwdb/pam_unix_auth.-c | 131 ++ modules/pam_pwdb/pam_unix_md.-c | 73 ++ modules/pam_pwdb/pam_unix_passwd.-c | 404 ++++++ modules/pam_pwdb/pam_unix_pwupd.-c | 260 ++++ modules/pam_pwdb/pam_unix_sess.-c | 118 ++ modules/pam_pwdb/pwdb_chkpwd.c | 204 +++ modules/pam_pwdb/support.-c | 938 ++++++++++++++ modules/pam_radius/.cvsignore | 1 + modules/pam_radius/Makefile | 99 ++ modules/pam_radius/README | 58 + modules/pam_radius/pam_radius.c | 193 +++ modules/pam_radius/pam_radius.h | 38 + modules/pam_rhosts/.cvsignore | 1 + modules/pam_rhosts/Makefile | 98 ++ modules/pam_rhosts/README | 57 + modules/pam_rhosts/pam_rhosts_auth.c | 829 +++++++++++++ modules/pam_rootok/.cvsignore | 1 + modules/pam_rootok/Makefile | 117 ++ modules/pam_rootok/README | 18 + modules/pam_rootok/pam_rootok.c | 109 ++ modules/pam_securetty/.cvsignore | 1 + modules/pam_securetty/Makefile | 83 ++ modules/pam_securetty/README | 9 + modules/pam_securetty/pam_securetty.c | 191 +++ modules/pam_shells/.cvsignore | 1 + modules/pam_shells/Makefile | 84 ++ modules/pam_shells/README | 10 + modules/pam_shells/pam_shells.c | 133 ++ modules/pam_stress/.cvsignore | 1 + modules/pam_stress/Makefile | 115 ++ modules/pam_stress/README | 66 + modules/pam_stress/pam_stress.c | 565 +++++++++ modules/pam_tally/.cvsignore | 2 + modules/pam_tally/Makefile | 109 ++ modules/pam_tally/README | 95 ++ modules/pam_tally/faillog.h | 55 + modules/pam_tally/pam_tally.c | 689 +++++++++++ modules/pam_tally/pam_tally_app.c | 7 + modules/pam_time/.cvsignore | 1 + modules/pam_time/Makefile | 98 ++ modules/pam_time/README | 43 + modules/pam_time/install_conf | 46 + modules/pam_time/pam_time.c | 612 +++++++++ modules/pam_time/time.conf | 64 + modules/pam_unix/.cvsignore | 4 + modules/pam_unix/CHANGELOG | 55 + modules/pam_unix/Makefile | 155 +++ modules/pam_unix/README | 39 + modules/pam_unix/bigcrypt.c | 119 ++ modules/pam_unix/lckpwdf.-c | 117 ++ modules/pam_unix/md5.c | 256 ++++ modules/pam_unix/md5.h | 35 + modules/pam_unix/md5_crypt.c | 149 +++ modules/pam_unix/need_nsl.sh | 7 + modules/pam_unix/pam_unix_acct.c | 210 ++++ modules/pam_unix/pam_unix_auth.c | 220 ++++ modules/pam_unix/pam_unix_passwd.c | 985 +++++++++++++++ modules/pam_unix/pam_unix_sess.c | 138 +++ modules/pam_unix/support.c | 821 ++++++++++++ modules/pam_unix/support.h | 139 +++ modules/pam_unix/unix_chkpwd.c | 324 +++++ modules/pam_unix/yppasswd.h | 51 + modules/pam_unix/yppasswd_xdr.c | 41 + modules/pam_userdb/.cvsignore | 1 + modules/pam_userdb/Makefile | 81 ++ modules/pam_userdb/README | 30 + modules/pam_userdb/conv.c | 125 ++ modules/pam_userdb/create.pl | 23 + modules/pam_userdb/libdbfound.sh | 15 + modules/pam_userdb/pam_userdb.c | 302 +++++ modules/pam_userdb/pam_userdb.h | 61 + modules/pam_warn/.cvsignore | 1 + modules/pam_warn/Makefile | 102 ++ modules/pam_warn/README | 26 + modules/pam_warn/pam_warn.c | 132 ++ modules/pam_wheel/.cvsignore | 1 + modules/pam_wheel/Makefile | 86 ++ modules/pam_wheel/README | 33 + modules/pam_wheel/pam_wheel.c | 275 +++++ modules/register_static | 49 + pgp.keys.asc | 103 ++ 320 files changed, 48373 insertions(+) create mode 100644 .cvsignore create mode 100644 CHANGELOG create mode 100644 Copyright create mode 100644 Makefile create mode 100644 README create mode 100644 bin/.cvsignore create mode 100644 bin/README create mode 100644 conf/.cvsignore create mode 100644 conf/Makefile create mode 100755 conf/install create mode 100755 conf/install_conf create mode 100755 conf/md5itall create mode 100755 conf/mkdirp create mode 100644 conf/pam.conf create mode 100644 conf/pam_conv1/.cvsignore create mode 100644 conf/pam_conv1/Makefile create mode 100644 conf/pam_conv1/README create mode 100644 conf/pam_conv1/pam_conv.lex create mode 100644 conf/pam_conv1/pam_conv.y create mode 100644 defs/debian.defs create mode 100644 defs/hpux.defs create mode 100644 defs/linux.defs create mode 100644 defs/morgan.defs create mode 100644 defs/redhat.defs create mode 100644 defs/redhat4.defs create mode 100644 defs/solaris-2.1.5.defs create mode 100644 defs/solaris.defs create mode 100644 defs/sunos.defs create mode 100644 defs/suse.defs create mode 100644 doc/.cvsignore create mode 100644 doc/CREDITS create mode 100644 doc/Makefile create mode 100644 doc/NOTES create mode 100644 doc/figs/pam_orient.txt create mode 100644 doc/html/.cvsignore create mode 100644 doc/html/index.html create mode 100644 doc/man/pam.8 create mode 100644 doc/man/pam.conf.8 create mode 100644 doc/man/pam.d.8 create mode 100644 doc/man/pam_authenticate.3 create mode 100644 doc/man/pam_chauthtok.3 create mode 100644 doc/man/pam_close_session.3 create mode 100644 doc/man/pam_end.3 create mode 100644 doc/man/pam_fail_delay.3 create mode 100644 doc/man/pam_open_session.3 create mode 100644 doc/man/pam_setcred.3 create mode 100644 doc/man/pam_start.3 create mode 100644 doc/man/pam_strerror.3 create mode 100644 doc/man/template-man create mode 100644 doc/modules/README create mode 100644 doc/modules/module.sgml-template create mode 100644 doc/modules/pam_access.sgml create mode 100644 doc/modules/pam_chroot.sgml create mode 100644 doc/modules/pam_cracklib.sgml create mode 100644 doc/modules/pam_deny.sgml create mode 100644 doc/modules/pam_env.sgml create mode 100644 doc/modules/pam_filter.sgml create mode 100644 doc/modules/pam_ftp.sgml create mode 100644 doc/modules/pam_group.sgml create mode 100644 doc/modules/pam_issue.sgml create mode 100644 doc/modules/pam_krb4.sgml create mode 100644 doc/modules/pam_lastlog.sgml create mode 100644 doc/modules/pam_limits.sgml create mode 100644 doc/modules/pam_listfile.sgml create mode 100644 doc/modules/pam_mail.sgml create mode 100644 doc/modules/pam_mkhomedir.sgml create mode 100644 doc/modules/pam_motd.sgml create mode 100644 doc/modules/pam_nologin.sgml create mode 100644 doc/modules/pam_permit.sgml create mode 100644 doc/modules/pam_pwdb.sgml create mode 100644 doc/modules/pam_radius.sgml create mode 100644 doc/modules/pam_rhosts.sgml create mode 100644 doc/modules/pam_rootok.sgml create mode 100644 doc/modules/pam_securetty.sgml create mode 100644 doc/modules/pam_time.sgml create mode 100644 doc/modules/pam_unix.sgml create mode 100644 doc/modules/pam_userdb.sgml create mode 100644 doc/modules/pam_warn.sgml create mode 100644 doc/modules/pam_wheel.sgml create mode 100644 doc/pam_appl.sgml create mode 100644 doc/pam_modules.sgml create mode 100644 doc/pam_source.sgml create mode 100644 doc/ps/.cvsignore create mode 100644 doc/ps/README create mode 100644 doc/ps/missfont.log create mode 100644 doc/specs/.cvsignore create mode 100644 doc/specs/draft-morgan-pam.raw create mode 100644 doc/specs/formatter/.cvsignore create mode 100644 doc/specs/formatter/Makefile create mode 100644 doc/specs/formatter/parse.lex create mode 100644 doc/specs/formatter/parse.y create mode 100644 doc/specs/rfc86.0.txt create mode 100644 doc/txts/.cvsignore create mode 100644 doc/txts/README create mode 100644 examples/.cvsignore create mode 100644 examples/Makefile create mode 100644 examples/blank.c create mode 100644 examples/check_user.c create mode 100644 examples/test.c create mode 100644 examples/vpass.c create mode 100644 examples/xsh.c create mode 100644 libpam/.cvsignore create mode 100644 libpam/Makefile create mode 100644 libpam/include/security/_pam_compat.h create mode 100644 libpam/include/security/_pam_macros.h create mode 100644 libpam/include/security/_pam_types.h create mode 100644 libpam/include/security/pam_appl.h create mode 100644 libpam/include/security/pam_malloc.h create mode 100644 libpam/include/security/pam_modules.h create mode 100644 libpam/pam_account.c create mode 100644 libpam/pam_auth.c create mode 100644 libpam/pam_data.c create mode 100644 libpam/pam_delay.c create mode 100644 libpam/pam_dispatch.c create mode 100644 libpam/pam_end.c create mode 100644 libpam/pam_env.c create mode 100644 libpam/pam_handlers.c create mode 100644 libpam/pam_item.c create mode 100644 libpam/pam_log.c create mode 100644 libpam/pam_malloc.c create mode 100644 libpam/pam_map.c create mode 100644 libpam/pam_misc.c create mode 100644 libpam/pam_password.c create mode 100644 libpam/pam_private.h create mode 100644 libpam/pam_second.c create mode 100644 libpam/pam_session.c create mode 100644 libpam/pam_start.c create mode 100644 libpam/pam_static.c create mode 100644 libpam/pam_strerror.c create mode 100644 libpam/pam_tokens.h create mode 100644 libpam_misc/.cvsignore create mode 100644 libpam_misc/Makefile create mode 100644 libpam_misc/help_env.c create mode 100644 libpam_misc/misc_conv.c create mode 100644 libpam_misc/pam_misc.h create mode 100644 libpam_misc/xstrdup.c create mode 100644 libpamc/.cvsignore create mode 100644 libpamc/License create mode 100644 libpamc/Makefile create mode 100644 libpamc/include/security/pam_client.h create mode 100644 libpamc/libpamc.h create mode 100644 libpamc/pamc_client.c create mode 100644 libpamc/pamc_converse.c create mode 100644 libpamc/pamc_load.c create mode 100755 libpamc/test/agents/secret@here create mode 100644 libpamc/test/modules/Makefile create mode 100644 libpamc/test/modules/pam_secret.c create mode 100644 libpamc/test/regress/Makefile create mode 100755 libpamc/test/regress/run_test.sh create mode 100644 libpamc/test/regress/test.libpamc.c create mode 100755 libpamc/test/regress/test.secret@here create mode 100644 modules/Makefile create mode 100644 modules/README create mode 100644 modules/dont_makefile create mode 100755 modules/download-all create mode 100644 modules/pam_access/.cvsignore create mode 100644 modules/pam_access/Makefile create mode 100644 modules/pam_access/README create mode 100644 modules/pam_access/access.conf create mode 100755 modules/pam_access/install_conf create mode 100644 modules/pam_access/pam_access.c create mode 100644 modules/pam_cracklib/.cvsignore create mode 100644 modules/pam_cracklib/Makefile create mode 100644 modules/pam_cracklib/README create mode 100644 modules/pam_cracklib/pam_cracklib.c create mode 100644 modules/pam_deny/.cvsignore create mode 100644 modules/pam_deny/Makefile create mode 100644 modules/pam_deny/README create mode 100644 modules/pam_deny/pam_deny.c create mode 100644 modules/pam_env/.cvsignore create mode 100644 modules/pam_env/Makefile create mode 100644 modules/pam_env/README create mode 100755 modules/pam_env/install_conf create mode 100644 modules/pam_env/pam_env.c create mode 100644 modules/pam_env/pam_env.conf-example create mode 100644 modules/pam_filter/.cvsignore create mode 100644 modules/pam_filter/.upperLOWER create mode 100644 modules/pam_filter/Makefile create mode 100644 modules/pam_filter/README create mode 100644 modules/pam_filter/include/pam_filter.h create mode 100644 modules/pam_filter/pam_filter.c create mode 100644 modules/pam_filter/upperLOWER/.cvsignore create mode 100644 modules/pam_filter/upperLOWER/Makefile create mode 100644 modules/pam_filter/upperLOWER/upperLOWER.c create mode 100644 modules/pam_ftp/.cvsignore create mode 100644 modules/pam_ftp/Makefile create mode 100644 modules/pam_ftp/README create mode 100644 modules/pam_ftp/pam_ftp.c create mode 100644 modules/pam_group/.cvsignore create mode 100644 modules/pam_group/Makefile create mode 100644 modules/pam_group/group.conf create mode 100755 modules/pam_group/install_conf create mode 100644 modules/pam_group/pam_group.c create mode 100644 modules/pam_issue/.cvsignore create mode 100644 modules/pam_issue/Makefile create mode 100644 modules/pam_issue/pam_issue.c create mode 100644 modules/pam_lastlog/.cvsignore create mode 100644 modules/pam_lastlog/Makefile create mode 100644 modules/pam_lastlog/pam_lastlog.c create mode 100644 modules/pam_limits/.cvsignore create mode 100644 modules/pam_limits/Makefile create mode 100644 modules/pam_limits/README create mode 100755 modules/pam_limits/install_conf create mode 100644 modules/pam_limits/limits.skel create mode 100644 modules/pam_limits/pam_limits.c create mode 100644 modules/pam_listfile/.cvsignore create mode 100644 modules/pam_listfile/Makefile create mode 100644 modules/pam_listfile/README create mode 100644 modules/pam_listfile/pam_listfile.c create mode 100644 modules/pam_mail/.cvsignore create mode 100644 modules/pam_mail/Makefile create mode 100644 modules/pam_mail/README create mode 100644 modules/pam_mail/pam_mail.c create mode 100644 modules/pam_mkhomedir/.cvsignore create mode 100644 modules/pam_mkhomedir/Makefile create mode 100644 modules/pam_mkhomedir/pam_mkhomedir.c create mode 100644 modules/pam_motd/.cvsignore create mode 100644 modules/pam_motd/Makefile create mode 100644 modules/pam_motd/pam_motd.c create mode 100644 modules/pam_nologin/.cvsignore create mode 100644 modules/pam_nologin/Makefile create mode 100644 modules/pam_nologin/README create mode 100644 modules/pam_nologin/pam_nologin.c create mode 100644 modules/pam_permit/.cvsignore create mode 100644 modules/pam_permit/Makefile create mode 100644 modules/pam_permit/README create mode 100644 modules/pam_permit/pam_permit.c create mode 100644 modules/pam_pwdb/.cvsignore create mode 100644 modules/pam_pwdb/BUGS create mode 100644 modules/pam_pwdb/CHANGELOG create mode 100644 modules/pam_pwdb/Makefile create mode 100644 modules/pam_pwdb/README create mode 100644 modules/pam_pwdb/TODO create mode 100644 modules/pam_pwdb/bigcrypt.-c create mode 100644 modules/pam_pwdb/md5.c create mode 100644 modules/pam_pwdb/md5.h create mode 100644 modules/pam_pwdb/md5_crypt.c create mode 100644 modules/pam_pwdb/pam_pwdb.c create mode 100644 modules/pam_pwdb/pam_unix_acct.-c create mode 100644 modules/pam_pwdb/pam_unix_auth.-c create mode 100644 modules/pam_pwdb/pam_unix_md.-c create mode 100644 modules/pam_pwdb/pam_unix_passwd.-c create mode 100644 modules/pam_pwdb/pam_unix_pwupd.-c create mode 100644 modules/pam_pwdb/pam_unix_sess.-c create mode 100644 modules/pam_pwdb/pwdb_chkpwd.c create mode 100644 modules/pam_pwdb/support.-c create mode 100644 modules/pam_radius/.cvsignore create mode 100644 modules/pam_radius/Makefile create mode 100644 modules/pam_radius/README create mode 100644 modules/pam_radius/pam_radius.c create mode 100644 modules/pam_radius/pam_radius.h create mode 100644 modules/pam_rhosts/.cvsignore create mode 100644 modules/pam_rhosts/Makefile create mode 100644 modules/pam_rhosts/README create mode 100644 modules/pam_rhosts/pam_rhosts_auth.c create mode 100644 modules/pam_rootok/.cvsignore create mode 100644 modules/pam_rootok/Makefile create mode 100644 modules/pam_rootok/README create mode 100644 modules/pam_rootok/pam_rootok.c create mode 100644 modules/pam_securetty/.cvsignore create mode 100644 modules/pam_securetty/Makefile create mode 100644 modules/pam_securetty/README create mode 100644 modules/pam_securetty/pam_securetty.c create mode 100644 modules/pam_shells/.cvsignore create mode 100644 modules/pam_shells/Makefile create mode 100644 modules/pam_shells/README create mode 100644 modules/pam_shells/pam_shells.c create mode 100644 modules/pam_stress/.cvsignore create mode 100644 modules/pam_stress/Makefile create mode 100644 modules/pam_stress/README create mode 100644 modules/pam_stress/pam_stress.c create mode 100644 modules/pam_tally/.cvsignore create mode 100644 modules/pam_tally/Makefile create mode 100644 modules/pam_tally/README create mode 100644 modules/pam_tally/faillog.h create mode 100644 modules/pam_tally/pam_tally.c create mode 100644 modules/pam_tally/pam_tally_app.c create mode 100644 modules/pam_time/.cvsignore create mode 100644 modules/pam_time/Makefile create mode 100644 modules/pam_time/README create mode 100755 modules/pam_time/install_conf create mode 100644 modules/pam_time/pam_time.c create mode 100644 modules/pam_time/time.conf create mode 100644 modules/pam_unix/.cvsignore create mode 100644 modules/pam_unix/CHANGELOG create mode 100644 modules/pam_unix/Makefile create mode 100644 modules/pam_unix/README create mode 100644 modules/pam_unix/bigcrypt.c create mode 100644 modules/pam_unix/lckpwdf.-c create mode 100644 modules/pam_unix/md5.c create mode 100644 modules/pam_unix/md5.h create mode 100644 modules/pam_unix/md5_crypt.c create mode 100755 modules/pam_unix/need_nsl.sh create mode 100644 modules/pam_unix/pam_unix_acct.c create mode 100644 modules/pam_unix/pam_unix_auth.c create mode 100644 modules/pam_unix/pam_unix_passwd.c create mode 100644 modules/pam_unix/pam_unix_sess.c create mode 100644 modules/pam_unix/support.c create mode 100644 modules/pam_unix/support.h create mode 100644 modules/pam_unix/unix_chkpwd.c create mode 100644 modules/pam_unix/yppasswd.h create mode 100644 modules/pam_unix/yppasswd_xdr.c create mode 100644 modules/pam_userdb/.cvsignore create mode 100644 modules/pam_userdb/Makefile create mode 100644 modules/pam_userdb/README create mode 100644 modules/pam_userdb/conv.c create mode 100644 modules/pam_userdb/create.pl create mode 100755 modules/pam_userdb/libdbfound.sh create mode 100644 modules/pam_userdb/pam_userdb.c create mode 100644 modules/pam_userdb/pam_userdb.h create mode 100644 modules/pam_warn/.cvsignore create mode 100644 modules/pam_warn/Makefile create mode 100644 modules/pam_warn/README create mode 100644 modules/pam_warn/pam_warn.c create mode 100644 modules/pam_wheel/.cvsignore create mode 100644 modules/pam_wheel/Makefile create mode 100644 modules/pam_wheel/README create mode 100644 modules/pam_wheel/pam_wheel.c create mode 100755 modules/register_static create mode 100644 pgp.keys.asc 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 + +* 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=. + 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 + +* 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 + +* 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 + +* 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 + +* 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 + +* 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 + +* 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. + + + +* 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 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 + +* 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 + +* 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 + +* 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 + +* 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 + + **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 + +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<----------------- +/* + * + * 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. + * + * + */ +/* #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 + + 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 ) + +* 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 ) + +* 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 ) + +* 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 ) + +*** 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 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 '\'. +* '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 ) + +* fixed module makefiles to create needed dynamic/static subdirectories + +Saturday May 25 20:30:27.8 PST 1996 (Andrew Morgan ) + +* 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 ) + +* 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 ) + +* 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 ) + +* 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 ) + +* 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 ) + +* 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 ) + +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 + - 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 ) + +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 ) + +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 + + 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, , and applications with + . + +* I have tried to remove all the compiler warnings from the updated + "pamlib/*.c" files. On my system, (with a slightly modified + 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 +] + +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 +or me + +To see about joining the mailing list, send the following email: +-------------------------------- +To: pam-list-request@redhat.com +Subject: help + +-------------------------------- + +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 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 # +# ---------------------------------------------------------------------------# +# $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 + * + * 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 \n"; + + extern int current_line; +%} + +%% + +"#"[^\n]* ; /* skip comments (sorry) */ + +"\\\n" { + ++current_line; +} + +([^\n\t ]|[\\][^\n])+ { + return TOK; +} + +[ \t]+ ; /* Ignore */ + +<> { + 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 + * + * 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 \n"; + +#include +#include +#include +#include + + 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 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 +## +# 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 +## +# 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 +## +# 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 +## +# 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 +## +# 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 +## +# 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 +## +# 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 +## +# 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 @@ + +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 '/^/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-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 @@ + + + +Linux-PAM - Pluggable Authentication Modules for Linux + + + +

+Here is the documentation for Linux-PAM. As you will see it is +currently not complete. However, in order of decreasing length: + +

+ +
+

+REVISION: $Id$ + 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 +.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: `\\'. 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 +.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 +.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 +.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 +.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 +.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 +.br +or, +.br +.B #include +.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 +.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 +.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 +.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 +.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 +.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 +.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 +.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 +.br +or, +.br +.B #include +.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 +.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 +.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 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 @@ + + + [*Familiar full name of module*, eg. The "allow all" module.] + +Synopsis + +

+ + +Module Name: +[ + insert the name of the module + + Blank is not permitted. +] + +Author[s]: + +[ + Insert author names here + + Blank is not permitted. If in doubt, put "unknown" if the + author wishes to remain anonymous, put "anonymous". +] + +Maintainer: + +[ + Insert names and date-begun of most recent maintainer. +] + +Management groups provided: + +[ + 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! +] + +Cryptographically sensitive: + +[ + 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. +] + +Security rating: + +[ + 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. +] + +Clean code base: + +[ + 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 :) +] + +System dependencies: + +[ + here we list config files, dynamic libraries needed, system + resources, kernel options.. etc. + + Blank = nothing more than libc required. +] + +Network aware: + +[ + Does the module base its behavior on probing a network + connection? Does it expect to be protected by the + application? + + Blank = Ignorance of network. +] + + + +Overview of module + +[ + some text describing the intended actions of the module + general comments mainly (specifics in sections + below). +] + +[ + + [ now we have a level subsection for each of the + management groups. Include as many as there are groups + listed above in the synopsis ] + +[ Account | Authentication | Password | Session ] component + +

+ + +Recognized arguments: + +[ + 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! +] + +Description: + +[ + This component of the module performs the task of ... +] + +Examples/suggested usage: + +[ + Here we list some doos and don'ts 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 @@ + + + The access module + +Synopsis + +

+ + +Module Name: + +pam_access + + +Author[s]: + +Alexei Nogin <alexei@nogin.dnttm.ru> + +Maintainer: + +Author + +Management groups provided: + +account + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +Requires a configuration file /etc/security/access.conf +Network aware: + +Through + +Overview of module + +

+Provides logdaemon style login access control. + + Account component + +

+ + +Recognized arguments: + +Description: + +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 Examples/suggested usage: + +Use of module is recommended, for example, on administrative machines +such as /etc/pam.d style configurations where your modules live +in /lib/security, start by adding the following line to +/etc/pam.d/login, /etc/pam.d/rlogin, +/etc/pam.d/rsh and /etc/pam.d/ftp: + + + +account required /lib/security/pam_access.so + + + +Note that use of this module is not effective unless your system ignores +.rhosts files. See the the pam_rhosts_auth documentation. + +A sample access.conf configuration file is included with the +distribution. + + + 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 @@ + + +Chroot + +Synopsis + +

+ + +Module Name: +Author: +Bruce Campbell <brucec@humbug.org.au> + +Maintainer: +Author; proposed on 20/11/96 - email for status + +Management groups provided: +account; session; authentication + +Cryptographically sensitive: + +Security rating: + +Clean code base: +Unwritten. + +System dependencies: + +Network aware: +Expects localhost. + + + +Overview of module + +

+This module is intended to provide a transparent wrapper around the +average user, one that puts them in a fake file-system (eg, their +'/' is really /some/where/else). + +

+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. + +Account component: + +

+Authentication component: + +

+Session component: + +

+ + + +Recognized arguments: +Arguments and logging levels for the PAM version are being worked on. + +Description: + +Examples/suggested usage: +Do provide a reasonable list of programs - just tossing 'cat', 'ls', 'rm', +'cp' and 'ed' in there is a bit... +

+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.) + + + + 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 @@ + + +Cracklib pluggable password strength-checker + +Synopsis + +

+ + +Module Name: + +pam_cracklib + +Author: + +Cristian Gafton <gafton@redhat.com> + +Maintainer: + +Author. + +Management groups provided: + +password + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: + +Requires the system library /usr/lib/cracklib_dict. + +Network aware: + + + +Overview of module + +

+This module can be plugged into the +This module works in the following manner: it first calls the +Cracklib 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: + + + + +

+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. + +Password component + +

+ + +Recognized arguments: + +Description: + +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. + +

+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. + +

+The default action may be modified in a number of ways using the +arguments recognized by the module: + + + other, +upper, lower and Cracklib 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 minlen. 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. + + + +Examples/suggested usage: + +(At the time of writing, this module can only be stacked before the + +For an example of the use of this module, we show how it may be +stacked with the password component of + +# +# 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 + + + +

+Another example (in the /etc/pam.d/passwd format) is for the +case that you want to use md5 password encryption: + + +#%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 + + + + + + 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 @@ + + +The locking-out module + +Synopsis + +

+ + +Module Name: +pam_deny + +Author: +Andrew G. Morgan <morgan@parc.power.net> + +Maintainer: +current Management groups provided: +account; authentication; password; session + +Cryptographically sensitive: + +Security rating: + +Clean code base: +clean. + +System dependencies: + +Network aware: + + + +Overview of module + +

+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 , this module +might be suitable for using for default (the Account component + +

+ + +Recognized arguments: + +Description: + +This component does nothing other than return a failure. The +failure type is Examples/suggested usage: + +Stacking this module with type +The following example would make it impossible to login: + + +# +# add this line to your other login entries to disable all accounts +# +login account required pam_deny.so + + + + + +Authentication component + +

+ + +Recognized arguments: + +Description: + +This component does nothing other than return a failure. The failure +type is Examples/suggested usage: + +To deny access to default applications with this component of the + + +# +# add this line to your existing OTHER entries to prevent +# authentication succeeding with default applications. +# +OTHER auth required pam_deny.so + + + + + +Password component + +

+ + +Recognized arguments: + +Description: + +This component of the module denies the user the opportunity to change +their password. It always responds with Examples/suggested usage: + +This module should be used to prevent an application from updating the +applicant user's password. For example, to prevent + +# +# 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 + + + + + +Session component + +

+ + +Recognized arguments: + +Description: + +This aspect of the module prevents an application from starting a +session on the host computer. + +Examples/suggested usage: + +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 + +# +# 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 + + + + + + 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 @@ + + +Set/unset environment variables + +Synopsis + +

+ + +Module Name: +Author: +Dave Kinchlea <kinch@kinch.ark.com> + +Maintainer: +Author + +Management groups provided: +Authentication (setcred) + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +/etc/security/pam_env.conf + +Network aware: + + + +Overview of module + +

+This module allows the (un)setting of environment variables. Supported +is the use of previously set environment variables as well as +PAM_ITEMs such as PAM_RHOST. + +Authentication component + +

+ + +Recognized arguments: +Description: +This module allows you to (un)set arbitrary environment variables +using fixed strings, the value of previously set environment variables +and/or +All is controlled via a configuration file (by default, +/etc/security/pam_env.conf but can be overriden with +connfile argument). 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) @{string} syntax. Both the $ +and @ characters can be backslash-escaped to be used +as literal values (as in \$. Double quotes may +be used in values (but not environment variable names) when white +space is needed the full value must be delimited by the quotes and +embedded or escaped quotes are not supported. + +

+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 +The behavior of this module can be modified with one of the following +flags: + +

+ + +/etc/security/pam_env.conf is used as +the configuration file. This option overrides the default. You must +supply a complete path + file name. + +/etc/environment is used to load KEY=VAL +pairs directly into the env. This option overrides the default. You must +supply a complete path + file name. + + + +Examples/suggested usage: + +See sample pam_env.conf for more information and examples. + + + + + + + + + + + + + + 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 @@ + + +The filter module + +Synopsis + +

+ + +Module Name: + +pam_filter + +Author: + +Andrew G. Morgan <morgan@parc.power.net> + +Maintainer: + +Author. + +Management groups provided: + +account; authentication; password; session + +Cryptographically sensitive: + +Not yet. + +Security rating: + +Clean code base: + +This module compiles cleanly on Linux based systems. + +System dependencies: + +To function it requires Network aware: + + + +Overview of module + +

+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). + +Account+Authentication+Password+Session components + +

+ + +Recognized arguments: + +Description: + +Each component of the module has the potential to invoke the desired +filter. The filter is always +The behavior of the module can be significantly altered by the +arguments passed to it in the + +Permitted values for +For the case of the account component. Either +For the case of the password component, + +Examples/suggested usage: + +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 + + +# +# 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 + + + + + + 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 @@ + + +Anonymous access module + +Synopsis + +

+ + +Module Name: +Author: +Andrew G. Morgan <morgan@linux.kernel.org> + +Maintainer: +Author. + +Management groups provided: +authentication + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: + +Network aware: +prompts for email address of user; easily spoofed (XXX - needs work) + + + +Overview of module + +

+The purpose of this module is to provide a pluggable anonymous ftp +mode of access. + +Authentication component + +

+ + +Recognized arguments: +Description: + +This module intercepts the user's name and password. If the name is +`` +The behavior of the module can be modified with the following flags: + + + +Examples/suggested usage: + +An example of the use of this module is provided in the configuration +file section . With care, this +module could be used to provide new/temporary account anonymous +login. + + + + 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 @@ + + +The group access module + +Synopsis + +

+ + +Module Name: +Author: +Andrew G. Morgan <morgan@parc.power.net> + +Maintainer: +Author. + +Management groups provided: +authentication + +Cryptographically sensitive: + +Security rating: +Sensitive to Clean code base: + +System dependencies: +Requires an /etc/security/group.conf file. Can be compiled +with or without Network aware: +Only through correctly set + +Overview of module + +

+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. + +Authentication component + +

+ + +Recognized arguments: + +Description: + +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 /etc/security/group.conf file. + +Examples/suggested usage: + +For this module to function correctly there must be a correctly +formatted /etc/security/groups.conf 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 +` + +services ; ttys ; users ; times ; groups + + +Here the first four fields share the syntax of the pam_time +configuration file; /etc/security/pam_time.conf, and the last +field, the +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 +The pam_group module fuctions in parallel with the +/etc/group file. If the user is granted any groups based on +the behavior of this module, they are granted in addition to +those entries /etc/group (or equivalent). + + + + 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 @@ + + +Add issue file to user prompt + +Synopsis + +

+ + +Module Name: +Author: +Ben Collins <bcollins@debian.org> + +Maintainer: +Author + +Management groups provided: +Authentication (pam_sm_authenticate) + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: + +Network aware: + + + +Overview of module + +

+This module prepends the issue file (/etc/issue by default) when +prompting for a username. + +Authentication component + +

+ + +Recognized arguments: +Description: +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 \x format). +

+Recognized escapes: + + + + +

+The behavior of this module can be modified with one of the following +flags: + +

+ + + + +Examples/suggested usage: + +login auth pam_issue.so issue=/etc/issue + + + + 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 @@ + + +The Kerberos 4 module. + +Synopsis + +

+ + +Module Name: +Author: +Derrick J. Brashear <shadow@dementia.org> + +Maintainer: +Author. + +Management groups provided: +authentication; password; session + +Cryptographically sensitive: +uses API + +Security rating: + +Clean code base: + +System dependencies: +libraries - Network aware: +Gets Kerberos ticket granting ticket via a Kerberos key distribution +center reached via the network. + + + +Overview of module + +

+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. + + Session component + +

+ + +Recognized arguments: + +Description: + +This component of the module currently sets the user's Examples/suggested usage: + +This part of the module won't be terribly useful until we can change +the environment from within a + + Password component + +

+ + +Recognized arguments: +Description: + +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. + +Examples/suggested usage: + +This should only be used with a real Kerberos v4 + + Authentication component + +

+ + +Recognized arguments: +Description: + +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 Examples/suggested usage: + +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. + + + + 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 @@ + + +The last login module + +Synopsis + +

+ + +Module Name: +Author: +Andrew G. Morgan <morgan@parc.power.net> + +Maintainer: +Author + +Management groups provided: +auth + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +uses information contained in the /var/log/lastlog file. + +Network aware: + + + +Overview of module + +

+This session module maintains the /var/log/lastlog file. Adding +an open entry when called via the pam_open_seesion() function +and completing it when pam_close_session() 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. + +Authentication component + +

+ + +Recognized arguments: +Description: + +

+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 +/var/log/lastlog file. + +

+The behavior of this module can be modified with one of the following +flags: + +

+ +/var/log/lastlog file. + +/var/log/lastlog file does not contain any old entries +for the user, indicate that the user has never previously logged in +with a ``welcome..." message. + + + +Examples/suggested usage: + +This module can be used to indicate that the user has new mail when +they /etc/pam.conf file: + + +# +# do we have any mail? +# +login session optional pam_lastlog.so + + + +

+Note, some applications may perform this function themselves. In such +cases, this module is not necessary. + + + + 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 @@ + + +The resource limits module + +Synopsis + +

+ + +Module Name: +Authors: +Cristian Gafton <gafton@redhat.com> +Thanks are also due to Elliot Lee <sopwith@redhat.com> +for his comments on improving this module. + +Maintainer: +Cristian Gafton - 1996/11/20 + +Management groups provided: +session + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +requires an /etc/security/limits.conf file and kernel support +for resource limits. Also uses the library, Network aware: + + + +Overview of module + +

+This module, through the Session component + +

+ + +Recognized arguments: +conf=/path/to/file.conf + +Description: + +Through the contents of the configuration file, +/etc/security/limits.conf, resource limits are placed on +users' sessions. Users of +The behavior of this module can be modified with the following +arguments: + + +conf=/path/to/file.conf - +indicate an alternative + +Examples/suggested usage: + +In order to use this module the system administrator must first create +a /etc/security/limits.conf). This file describes the resource +limits the superuser wishes to impose on users and groups. No limits +are imposed on +Each line of the configuration file describes a limit for a user in +the form: + + + + + + +

+The fields listed above should be filled as follows... +<domain> can be: + + a username + a groupname, with @group syntax + the wild-card + +

+<type> can have the two values: + + + + +

+<item> can be one of the following: + + + +

+To completely disable limits for a user (or a group), a single dash +(-) will do (Example: `` +Also, please note that all limit settings are set +In the +The +The following is an example configuration file: + + +# EXAMPLE /etc/security/limits.conf 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 + + +Note, the use of +For the services that 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: + + +# +# Resource limits imposed on login sessions via pam_limits +# +login session required pam_limits.so + + + + + + 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 @@ + + +The list-file module + +Synopsis + +

+ + +Module Name: +Author: +Elliot Lee <sopwith@cuc.edu> + +Maintainer: +Red Hat Software: +Michael K. Johnson <johnsonm@redhat.com> 1996/11/18 +(if unavailable, contact Elliot Lee <sopwith@cuc.edu>). + +Management groups provided: +authentication + +Cryptographically sensitive: + +Security rating: + +Clean code base: +clean + +System dependencies: + +Network aware: + + + +Overview of module + +

+The list-file module provides a way to deny or allow services based on +an arbitrary file. + +Authentication component + +

+ + +Recognized arguments: + +onerr=succeed|fail; +sense=allow|deny; +file=filename; +item=user|tty|rhost|ruser|group|shell +apply=user|@group + +Description: + +The module gets the item of the type specified -- user specifies +the username, PAM_USER; tty specifies the name of the terminal +over which the request has been made, PAM_TTY; rhost specifies +the name of the remote host (if any) from which the request was made, +PAM_RHOST; and ruser specifies the name of the remote user +(if available) who made the request, PAM_RUSER -- and looks for +an instance of that item in the file filename. filename +contains one line per item listed. If the item is found, then if +sense=allow, PAM_SUCCESS is returned, causing the +authorization request to succeed; else if sense=deny, +PAM_AUTH_ERR is returned, causing the authorization +request to fail. + +

+If an error is encountered (for instance, if filename +does not exist, or a poorly-constructed argument is encountered), +then if onerr=succeed, PAM_SUCCESS is returned, +otherwise if onerr=fail, PAM_AUTH_ERR or +PAM_SERVICE_ERR (as appropriate) will be returned. + +

+An additional argument, apply=, can be used to restrict the +application of the above to a specific user +(apply=username) or a given group +(apply=@groupname). This added restriction is only +meaningful when used with the +Besides this last one, all arguments should be specified; do not count +on any default behavior, as it is subject to change. + +

+No credentials are awarded by this module. + +Examples/suggested usage: + +Classic ``ftpusers'' authentication can be implemented with this entry +in /etc/pam.conf: + + +# +# 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 + + +Note, users listed in /etc/ftpusers file are +(counterintuitively) +To allow login access only for certain users, you can use an +pam.conf entry like this: + + +# +# permit login to users listed in /etc/loginusers +# +login auth required pam_listfile.so \ + onerr=fail item=user sense=allow file=/etc/loginusers + + + +

+For this example to work, all users who are allowed to use the login +service should be listed in the file /etc/loginusers. 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 +/etc/loginusers, or by listing a user who is able to + + 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 @@ + + +The mail module + +Synopsis + +

+ + +Module Name: +Author: +Andrew G. Morgan <morgan@linux.kernel.org> + +Maintainer: +Author + +Management groups provided: +Authentication (credential) +Session (open) + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +Default mail directory /var/spool/mail/ + +Network aware: + + + +Overview of module + +

+This module looks at the user's mail directory and indicates +whether the user has any mail in it. + +Session component + +

+ + +Recognized arguments: +Description: + +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 +The behavior of this module can be modified with one of the following +flags: + +

+ +/var/spool/mail. Note, if the supplied /var/spool/mail/u/s/user. + + + +Examples/suggested usage: + +This module can be used to indicate that the user has new mail when +they /etc/pam.conf file: + + +# +# do we have any mail? +# +login session optional pam_mail.so + + + +

+Note, some applications may perform this function themselves. In such +cases, this module is not necessary. + + + +Authentication compent + +

+Then authentication companent works the same as the session component, +expect that everything is done during the pam_setcred() phase. + + 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 @@ + + +Create home directories on initial login + +Synopsis + +

+ + +Module Name: +Author: +Jason Gunthorpe <jgg@ualberta.ca> + +Maintainer: +Ben Collins <bcollins@debian.org> + +Management groups provided: +Session + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: + +Network aware: + + + +Overview of module + +

+Creates home directories on the fly for authenticated users. + +Session component + +

+ + +Recognized arguments: +Description: +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. + +

+The behavior of this module can be modified with one of the following +flags: + +

+ + + + +Examples/suggested usage: + +session required pam_mkhomedir.so skel=/etc/skel/ umask=0022 + + + + 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 @@ + + +Output the motd file + +Synopsis + +

+ + +Module Name: +Author: +Ben Collins <bcollins@debian.org> + +Maintainer: +Author + +Management groups provided: +Session (open) + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: + +Network aware: + + + +Overview of module + +

+This module outputs the motd file (/etc/motd by default) upon succesful +login. + +Session component + +

+ + +Recognized arguments: +Description: +This module allows you to have arbitrary motd's (message of the day) +output after a succesful login. By default this file is /etc/motd, +but is configurable to any file. + +

+The behavior of this module can be modified with one of the following +flags: + +

+ + + + +Examples/suggested usage: + +login session pam_motd.so motd=/etc/motd + + + + 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 @@ + + +The no-login module + +Synopsis + +

+ + +Module Name: +Author: +Written by Michael K. Johnson <johnsonm@redhat.com> +(based on code taken from a module written by Andrew G. Morgan +<morgan@parc.power.net>). + +Maintainer: +Michael K. Johnson <johnsonm@redhat.com> + +Management groups provided: +authentication + +Cryptographically sensitive: + +Security rating: + +Clean code base: +1 warning about dropping const + +System dependencies: + +Network aware: + + + +Overview of module + +

+Provides standard Unix Authentication component + +

+ + +Recognized arguments: + +Description: + +Provides standard Unix /etc/nologin 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 /etc/nologin. + +

+If the file /etc/nologin does not exist, this module succeeds +silently. + +Examples/suggested usage: + +In order to make this module effective, all login methods should +be secured by it. It should be used as a required +method listed before any sufficient methods in order to +get standard Unix nologin semantics. + + + + 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 @@ + + +The promiscuous module + +Synopsis + +

+ + +Module Name: +pam_permit + +Author: +Andrew G. Morgan, <morgan@parc.power.net> + +Maintainer: +Linux-PAM maintainer. + +Management groups provided: +account; authentication; password; session + +Cryptographically sensitive: + +Security rating: +VERY LOW. Use with extreme caution. + +Clean code base: +Clean. + +System dependencies: + +Network aware: + + + +Overview of module + +

+This module is very dangerous. It should be used with extreme +caution. Its action is always to permit access. It does nothing else. + +Account+Authentication+Password+Session components + +

+ + +Recognized arguments: + +Description: + +No matter what management group, the action of this module is to +simply return +In the case of authentication, the user's name will be acquired. Many +applications become confused if this name is unknown. + +Examples/suggested usage: + +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: + + +# +# 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 + + + + + + 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 @@ + + +The Password-Database module + +Synopsis + +

+ + +Module Name: +pam_pwdb + +Author: +Cristian Gafton <gafton@redhat.com> +and Andrew G. Morgan <morgan@linux.kernel.org> + +Maintainer: +Authors. + +Management groups provided: +account; authentication; password; session + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +Requires properly configured Network aware: + + + +Overview of module + +

+This module is a pluggable replacement for the . + +Account component + +

+ + +Recognized arguments: +Description: + +The Examples/suggested usage: + +In its accounting mode, this module can be inserted as follows: + + +# +# Ensure users account and password are still active +# +login account required pam_pwdb.so + + + + + +Authentication component + +

+ + +Recognized arguments: +Description: + +The +The default action of this module is to not permit the user access to +a service if their +When given the argument +The argument, nodelay, 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. + +

+Remaining arguments, supported by the other functions of this module, +are silently ignored. Other arguments are logged as errors through + +A helper binary, pwdb_chkpwd, 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 xlock to work without being setuid-root. + +

+The likeauth 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. + +Examples/suggested usage: + +The correct functionality of this module is dictated by having an +appropriate /etc/pwdb.conf file, the user +databases specified there dictate the source of the authenticated +user's record. + + + +Password component + +

+ + +Recognized arguments: +Description: + +This part of the +In the case of conventional unix databases (which store the password +encrypted) the +The +The argument +The +The Examples/suggested usage: + +An example of the stacking of this module with respect to the +pluggable password checking module, + +Session component + +

+ + +Recognized arguments: + +Description: + +No arguments are recognized by this module component. Its action is +simply to log the username and the service-type to +Examples/suggested usage: + +The use of the session modules is straightforward: + + +# +# pwdb - unix like session opening and closing +# +login session required pam_pwdb.so + + + + + + 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 @@ + + +The RADIUS session module + +Synopsis + +

+ + +Module Name: +Author: +Cristian Gafton <gafton@redhat.com> + +Maintainer: +Author. + +Management groups provided: +session + +Cryptographically sensitive: +This module does not deal with passwords + +Security rating: + +Clean code base: +gcc reports 1 warning when compiling /usr/include/rpc/clnt.h. +Hey, is not my fault ! + +System dependencies: + +Network aware: + +yes; this is a network module (independent of application). + + + +Overview of module + +

+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. + +Session component + +

+ + +Recognized arguments: + +Description: + +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 +(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.) + +

+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 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. . + +Examples/suggested usage: + +For the services that need this module (/etc/pam.conf as the last line for that +service (usually after the pam_unix session line): + + +login session required pam_radius.so + + +Replace +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). + + + + + 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 @@ + + +The rhosts module + +Synopsis + +

+ + +Module Name: +Author: +Al Longyear <longyear@netcom.com> + +Maintainer: + +Management groups provided: +authentication + +Cryptographically sensitive: + +Security rating: + +Clean code base: +Clean. + +System dependencies: + +Network aware: +Standard + +Overview of module + +

+This module performs the standard network authentication for services, +as used by traditional implementations of Authentication component + +

+ + +Recognized arguments: +Description: + +The authentication mechanism of this module is based on the contents +of two files; /etc/hosts.equiv (or #include <netdb.h>) and ~/.rhosts. 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 "/etc/hosts.equiv and their remote account +is identical to their local one, or if their remote account has an +entry in their personal configuration file. + +

+Some restrictions are applied to the attributes of the user's personal +configuration file: it must be a regular file (as defined by + +The module authenticates a remote user (internally specified by the +item +In the case of /etc/host.equiv file is +hosts_equiv_rootok option +should be used. Instead, the superuser must have a correctly configured +personal configuration file. + +

+The behavior of the module is modified by flags: + + + + +/etc/hosts.equiv file. + + +/etc/hosts.equiv for superuser. Without this +option /etc/hosts.equiv is not consulted for the superuser account. +This option has no effect if the no_hosts_equiv option is used. + + +~/.rhosts. + + +~/.rhosts 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. + + + + +Examples/suggested usage: + +To allow users to login from trusted remote machines, you should try +adding the following line to your /etc/pam.conf file + + +# +# No passwords required for users from hosts listed above. +# +login auth sufficient pam_rhosts_auth.so no_rhosts + + +Note, in this example, the system administrator has turned off all +/etc/host.equiv file, by replacing + + 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 @@ + + +The root access module + +Synopsis + +

+ + +Module Name: +pam_rootok + +Author: +Andrew G. Morgan <morgan@parc.power.net> + +Maintainer: +Linux-PAM maintainer + +Management groups provided: +authentication + +Cryptographically sensitive: + +Security rating: + +Clean code base: +Clean. + +System dependencies: + +Network aware: + + + +Overview of module + +

+This module is for use in situations where the superuser wishes +to gain access to a service without having to enter a password. + +Authentication component + +

+ + +Recognized arguments: +Description: + +This module authenticates the user if their Examples/suggested usage: + +In the case of the + +# +# su authentication. Root is granted access by default. +# +su auth sufficient pam_rootok.so +su auth required pam_unix_auth.so + + + +

+Note. For programs that are run by the superuser (or started when the +system boots) this module should not be used to authenticate users. + + + + 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 @@ + + +The securetty module + +Synopsis + +

+ + +Module Name: +Author[s]: +Elliot Lee <sopwith@cuc.edu> + +Maintainer: +Red Hat Software: + +(if unavailable, contact Elliot Lee <sopwith@cuc.edu>). + +Management groups provided: +authentication + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +/etc/securetty file + +Network aware: + +Requires the application to fill in the PAM_TTY item +correctly in order to act meaningfully. + + + +Overview of module + +

+Provides standard Unix securetty checking. + +Authentication component + +

+ + +Recognized arguments: + +Description: + +Provides standard Unix securetty checking, which causes authentication +for root to fail unless PAM_TTY is set to a string listed in +the /etc/securetty file. For all other users, it succeeds. + +Examples/suggested usage: + +For canonical usage, should be listed as a required +authentication method before any sufficient authentication +methods. + + + + 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 @@ + + +Time control + +Synopsis + +

+ + +Module Name: +Author: +Andrew G. Morgan <morgan@parc.power.net> + +Maintainer: +Author + +Management groups provided: +account + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +Requires a configuration file /etc/security/time.conf + +Network aware: +Through the + +Overview of module + +

+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. + +Account component + +

+ + +Recognized arguments: + +Description: + +This module bases its actions on the rules listed in its configuration +file: /etc/security/pam.conf. Each rule has the following +form, + + +In words, each rule occupies a line, terminated with a newline or the +beginning of a comment; a ` + + +By a logic list we mean a sequence of tokens (associated with the +appropriate !morgan&!root, indicating that this rule +does not apply to the user morgan nor to root; and +tty*&!ttyp*, which indicates that the rule applies only +to console terminals but not pseudoterminals. + + + +Mo Tu We Th Fr Sa Su Wk Wd Al + + +The last two of these being +The time range part is a pair of 24-hour times, + +

+Note, that the given time restriction is only applied when the first +three fields are satisfied by a user's application for service. + +

+For convenience and readability a rule can be extended beyond a single +line with a `\Examples/suggested usage: + +The use of this module is initiated with an entry in the + + +# +# apply pam_time accounting to login requests +# +login account required pam_time.so + + +where, here we are applying the module to the +Some examples of rules that can be placed in the +/etc/security/time.conf configuration file are the following: + + +login ; tty* & ; !ttyp* ; !root ; !Al0000-2400 +all users except for games ; * ; !waster ; Wd0000-2400 | Wk1800-0800 +games (configured to use Linux-PAM) are only to be accessed out of +working hours. This rule does not apply to the user + +

+Note, currently there is no daemon enforcing the end of a session. +This needs to be remedied. + +

+Poorly formatted rules are logged as errors using + + 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 @@ + + +The Unix Password module + +Synopsis + +

+ + +Module Name: +pam_unix + +Author: + +Maintainer: +Authors. + +Management groups provided: +account; authentication; password; session + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: + +Network aware: + + + +Overview of module + +

+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. + +Account component + +

+ + +Recognized arguments: +Description: + +The Examples/suggested usage: + +In its accounting mode, this module can be inserted as follows: + + +# +# Ensure users account and password are still active +# +login account required pam_unix.so + + + + + +Authentication component + +

+ + +Recognized arguments: +Description: + +The +The default action of this module is to not permit the user access to +a service if their +When given the argument +The argument, nodelay, 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. + +

+Remaining arguments, supported by the other functions of this module, +are silently ignored. Other arguments are logged as errors through + +A helper binary, unix_chkpwd, 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 xlock to work without being setuid-root. + +Examples/suggested usage: + +The correct functionality of this module is dictated by having an +appropriate /etc/nsswitch.conf file, the user +databases specified there dictate the source of the authenticated +user's record. +

+In its authentication mode, this module can be inserted as follows: + + +# +# Authenticate the user +# +login auth required pam_unix.so + + + + + +Password component + +

+ + +Recognized arguments: +Description: + +This part of the +In the case of conventional unix databases (which store the password +encrypted) the +The +The argument +The +The +With the +The /etc/security/opasswd in order to force password change history +and keep the user from alternating between the same password too frequently. + +Examples/suggested usage: + +Standard usage: + + +# +# Change the users password +# +passwd password required pam_unix.so + + + +

+An example of the stacking of this module with respect to the +pluggable password checking module, + +# +# 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 + + + + + +Session component + +

+ + +Recognized arguments: + +Description: + +No arguments are recognized by this module component. Its action is +simply to log the username and the service-type to +Examples/suggested usage: + +The use of the session modules is straightforward: + + +# +# session opening and closing +# +login session required pam_unix.so + + + + + + 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 @@ + + +The userdb module + +Synopsis + +

+ + +Module Name: +Author: +Cristian Gafton <gafton@redhat.com> + +Maintainer: +Author. + +Management groups provided: +authentication + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +Requires Berkeley DB. + +Network aware: + + + +Overview of module + +

+Look up users in a .db database and verify their password against +what is contained in that database. + +Authentication component + +

+ + +Recognized arguments: +Description: + +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 +The action of the module may be modified from this default by one or +more of the following flags in the /etc/pam.d/<service> file. + + + + + +/etc/foodata +instead of /etc/foodata.db. + + + +Examples/suggested usage: + +This is a normal ftp configuration file (usually placed as /etc/pam.d/ftp +on most systems) that will accept for login users whose username/password pairs are +provided in the /tmp/dbtest.db file: + + + +#%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 + + + + + + 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 @@ + + +Warning logger module + +Synopsis + +

+ + +Module Name: +Author: +Andrew G. Morgan <morgan@parc.power.net> + +Maintainer: +Author. + +Management groups provided: +authentication; password + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: + +Network aware: +logs information about the remote user and host (if pam-items are known) + + + +Overview of module + +

+This module is principally for logging information about a +proposed authentication or application to update a password. + +Authentication+Password component + +

+ + +Recognized arguments: + +Description: + +Log the service, terminal, user, remote user and remote host to +Examples/suggested usage: + +an example is provided in the configuration file section . + + + + 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 @@ + + +The wheel module + +Synopsis + +

+ + +Module Name: +Author: +Cristian Gafton <gafton@redhat.com> + +Maintainer: +Author. + +Management groups provided: +authentication + +Cryptographically sensitive: + +Security rating: + +Clean code base: + +System dependencies: +Requires libpwdb. + +Network aware: + + + +Overview of module + +

+Only permit root access to members of the wheel (Authentication component + +

+ + +Recognized arguments: +Description: + +This module is used to enforce the so-called +The action of the module may be modified from this default by one or +more of the following flags in the /etc/pam.conf file. + + + + + + + + +Examples/suggested usage: + +To restrict access to superuser status to the members of the + + +# +# 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 + + + + + + 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 @@ + + + + +

+ +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><security/pam_appl.h></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/$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><NUL></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><security/pam_appl.h></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><security/pam_appl.h></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><security/pam_appl.h></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/<unset>/ += 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><security/pam_misc.h></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 +``..\a.Time is running out...\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 ``..\a.Sorry, your time is +up!\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><morgan@transmeta.com></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,&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><security/pam_modules.h></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><security/pam_modules.h></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><security/pam_modules.h></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><security/pam_modules.h></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><security/pam_modules.h></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><morgan@transmeta.com></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/#/' and extend to the +next end-of-line; also, module specification lines may be extended +with a `<tt/\/' 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_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><morgan@linux.kernel.org></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 = "e; /* 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 && ("e != var->defval) && + ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) { + return retval; + } + if (var->override && ("e != var->override) && + ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) { + return retval; + } + + /* Now its easy */ + + if (var->override && *(var->override) && "e != 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 ("e == 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 && ("e != var->defval)) { + free(var->defval); + } + if (var->override && ("e != 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 **)¤t); + 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 **)¤t); + 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----- -- cgit v1.2.3