diff options
Diffstat (limited to 'Linux-PAM')
315 files changed, 62732 insertions, 0 deletions
diff --git a/Linux-PAM/CHANGELOG b/Linux-PAM/CHANGELOG new file mode 100644 index 00000000..3a886546 --- /dev/null +++ b/Linux-PAM/CHANGELOG @@ -0,0 +1,1719 @@ + +$Id: CHANGELOG,v 1.184 2005/03/31 14:50:39 kukuk Exp $ + +----------------------------- + +TODO: + + - 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..." + - add in the pam_cap and pam_netid modules + +==================================================================== +Note, as of release 0.73, all checkins should be accompanied with a +Bug ID. The bug IDs relate to sourceforge IDs.. (Of course, nothing is +ever that simple. It turns out that at some point in Sourceforge's +history all of the bug ids got bumped by 100000, so pretty much if you +see a bug ID below that begins with a '1' and your attempted query +fails, try adding 100000 to the number and trying again. I believe +this only affects bugs before release 0.76.) + +You can query the related bug description with the following URL: + + http://sourceforge.net/tracker/index.php?func=detail&aid=XXXXXX&group_id=6663&atid=106663 + +Where you should replace XXXXXX with a bug-id. + +For general documentation completion work, I'm doing it all with +respect to specific tasks. Open tasks are listed here: + + http://sourceforge.net/pm/task.php?group_id=6663&group_project_id=2741&func=browse&set=open + +If you have found a bug in Linux-PAM (including a documentation bug, +or a new feature request and/or patch), please consider filing such a +bug report - outstanding bugs are listed here: + + http://sourceforge.net/tracker/?atid=106663&group_id=6663&func=browse + +(to file another bug see the 'submit bug' button on that page). + + +There is now a second bug tracking system for Linux-PAM on BerliOS. +You can find the list of outstanding bugs on BerliOS here: + +http://developer.berlios.de/bugs/?func=browse&group_id=2134&set=open + +BerliOS Bugs are marked with (BerliOS #XXXX). + +==================================================================== + +0.79: Thu Mar 31 16:48:45 CEST 2005 +* pam_tally: added audit option (toady) +* pam_unix: don't log user unknown failure when he can be properly + authenticated by another module (t8m) +* configure: don't abort if no cracklib dictinaries were found, but + warn user that pam_cracklib will not be built (kukuk) +* modules/pam_unix/support.c: Fix return value if user aborts while + changes the password (Bug 872945 - kukuk) +* modules/pam_unix/support.c: Fix return value for an unknown user + (Bug 872943 - kukuk) +* pam_limits: support for new Linux kernel 2.6 limits (from toby cabot + - t8m) +* pam_tally: major rewrite of the module (t8m) +* libpam: don't return PAM_IGNORE for OK or JUMP actions if using + cached chain (Bug 629251 - t8m) +* pam_nologin: don't overwrite return value with return from + pam_get_item (t8m) +* libpam: Add more checks for broken PAM configuration files to + avoid seg.faults (kukuk) +* pam_shells: correct README +* libpam: Fix debug code (kukuk) +* pam_limits: Fix order of LIMITS_DEF_* priorities (kukuk) +* pam_xauth: preserve DISPLAY variable (Novell #66885 - kukuk) +* libpam: Add prelude ids (http://www.prelude-ids.org) support, + as experimental. (toady) +* configure: Add the directory where new versions of cracklib is + installed (from Jim Gifford - toady) + +0.78: Do Nov 18 14:48:36 CET 2004 + +* pam_unix: change the order of trying password changes - local first, + NIS second (t8m) +* pam_wheel: add option only_root to make it affect authentication + to root account only +* pam_unix: test return values on renaming files and report error to + syslog and to user +* pam_unix: forced password change shouldn't trump account expiration +* pam_unix: remove the use of openlog (from debian - toady) +* pam_unix: NIS cleanup (patch from Philippe Troin) +* pam_access: you can now authenticate an explicit user on an explicit + tty (from debian - toady) +* pam_limits, pam_rhosts, pam_unix: fixed hurd portability issues + (patch from Igor Khavkine) +* pam_env: added comments in the configuration file to avoid errors + (from debian - toady) +* pam_mail: check PAM_NO_ENV to know if we can delete the environment + variable (from debian - toady) +* pam_filter: s/termio/termios/g (from debian - toady) +* pam_mkhomedir: no maxpathlen required (from debian - toady) +* pam_limits: applied patch to allow explicit limits for root + and remove limits on su. (from debian - toady) +* pam_unix: severe denial of service possible with this module since + it locked too aggressively. Bug report and testing help from Sascha + Loetz. (Bug 664290 - agmorgan) +* getlogin was spoofable: "/tmp/" and "/dev/" have the same number of + characters, so 'ln /dev/tty /tmp/tty1 ; bash < /tmp/tty1 ; logname' + attacks could potentially spoof pam_wheel with the 'trust' module + argument into granting access to a luser. Also, pam_unix gave + odd error messages in such a situation (logname != uid). This + problem was found by David Endler of iDefense.com (Bug 667584 - + agmorgan). +* added my new DSA public key to the pgp.keys.asc file. Also included + a signed copy of my new public key (1024D/D41A6DF2) made with my old + key (1024/2A398175). +* added "include" directive to config file syntax. + The whole idea is to create few "systemwide" pam configs and include + parts of them in application pam configs. + (patch by "Dmitry V. Levin" <ldv@altlinux.org>) (Bug 812567 - baggins). +* doc/modules/pam_mkhomedir.sgml: Remove wrong debug options + (Bug 591605 - kukuk) +* pam_unix: Call password checking helper whenever the password field + contains only one character (Bug 1027903 - kukuk) +* libpam/pam_start.c: All service names should be files below /etc/pam.d + and nothing else. Forbid paths. (Bug 1027912 - kukuk) +* pam_cracklib: Fix error in distance algorithm in the 0.9 pam_cracklib + module (Bug 1010142 - toady) +* pam_userdb: applied patch from Paul Walmsley <paul@booyaka.com> + it now indicates whether encrypted or plaintext passwords are stored + in the database needed for pam_userdb (BerliOS - toady) +* pam_group: The module should also ignore PAM_REINITIALIZE_CRED to + avoid spurious errors (from Linux distributors - kukuk) +* pam_cracklib: Clear the entire options structure (from Linux + distributors - kukuk) +* pam_issue: We write a NUL to prompt_tmp[tot_size] later, so make sure + that the destination is part of the allocated block, make do_prompt + static (from Linux distributors - kukuk) +* ldconfig: Only run full ldconfig, if we don't install into a FAKEROOT + environment, else let ldconfig only create the symlinks correct + (from Linux distributors - kukuk) +* pam_unix/pam_pwdb: Use SIG_DFL instead of SIG_IGN for SIGCHLD + (from Linux distributors - kukuk) +* Add most of Steve Grubb's resource leak and other fixes (from + Linux distributors - kukuk) +* doc/Makefile: Don't include .cvsignore files in tar ball (kukuk) +* libpam_misc/misc_conv.c: Differentiate between Ctrl-D and + <Return> (Bug 1032604 - kukuk) +* Make.Rules.in: Add targets for installing man pages for modules + (from Linux distributors - kukuk) +* Add pam_xauth module (Bug 436440 - kukuk) +* Add pam_localuser module (Bug 436444 - kukuk) +* Add pam_succeed_if module (from Linux distributors - kukuk) +* configure.in: Fix check for libcrypt (Bug 417704 - kukuk) +* Add the "broken_shadow" argument to pam_unix, for ignoring errors + reading shadow information (from Linux distributors - kukuk) +* Add patches to make PAM modules reentrant (Bug 440107 - kukuk) +* Merge patches from Red Hat (Bug 477000 and other - kukuk) +* Fix pam_rhosts option parsing (Bug 922648 - kukuk) +* Add $ISA support in config files (from Red Hat - kukuk) + +0.77: Mon Sep 23 10:25:42 PDT 2002 + +* documentation support for pdf files was not quite right - + installation was messed up. +* pam_wheel was too aggressive to grant access (in the case of the + 'deny' option you want to pay attention to 'trust'). Fix from + Nalin (Bugs 476951, 476953 - agmorgan) +* account management support for: pam_shells, pam_listfile, pam_wheel + and pam_securetty (+ static module fix for pam_nologin). Patch from + redhat through Harald Welte (Bug 436435 - agmorgan). +* pam_wheel feature from Nalin - can use the module to provide wheel + access to non-root accounts. Also from Nalin, a bugfix related to + the primary group of the applicant is the 'wheel' group. (Bugs + 476980, 476941 - agmorgan) +* pam_unix and pam_pwdb: by default turn off the SIGCHLD handler while + running the helper binary (patch from Nalin) added the "noreap" + module argument to both of these modules to turn off this new + default. Bugfix found by Silvan Minghetti for former module and + 521314 checkin. (Bugs 476963, 521314 - agmorgan). +* updated CHANGELOG and configure.in for 0.77 work. + +0.76: Mon Jul 8 21:44:59 PDT 2002 + +* pam_unix: fix for legacy crypt() support when the password entered + was long. (Bug 521314 - agmorgan). +* pam_access no longer include gethostname() prototype complaint from + David Lee (Bug 415423 - agmorgan). +* make pam_nologin more secure by default, added two new module + arguments etc. - acting on suggestion from Nico (Bug 419307 - + agmorgan) +* link in libpam to libpam_misc - since the latter uses functions in + the former it makes some sort of sense to do this (although, in the + static library case, I remain to be convinced). (Bug 565470 - + agmorgan). +* absorbed some of the proposed darwin (OS X) changes from Luke Howard + (of PADL software) - hopefully will get the rest (see Rob Braun's + 534205) by 0.77 (Bug 491466 - agmorgan). +* README fix for pam_unix from Nalin (Bug 476971 - agmorgan). +* add support for building pdf files from the documentation - request + from 'lolive' (Bug 471377 - agmorgan). +* documented the equivalent '[..]' expressions for "required" + etc. Request from Ross Patterson (Bug 529078 - agmorgan). +* '[...]' parsing: document it and also fix it to support '\]' escape + sequence. Feature request from Russell Kliese (Bug 517064 - + agmorgan). +* pam_rootok: compilation warning noted by Tony den Haan wrt no + prototype for strcmp() (Bug 557322 - agmorgan). +* documentation: (a few of mine in passing) and app documentation + suggestions regarding PAM environment variables and module + documentation changes regarding the conversation function from Jenn + Vesperman (Bug 527821, 527965 - agmorgan) +* documentation: pam_time.sgml typo fixed, pam_motd exists now, + correct Red Hat comment about config files (Bugs 554274, 554261, + 554182 - agmorgan) +* pam_limits: added '%' domain for maxlogins limiting, now '*' and @group + have the old meaning (every) and '%' the new one (all) + (Bug 533664 - baggins) +* pam_limits: put not so interesting log messages under debug arg + (Bug 533668 - baggins) +* pam_access: added the 'fieldsep=' argument (Bug 547051 - agmorgan), + made a PAM_RHOST of "" equivalent to NULL (Bug 547521 - agmorgan). +* pam_limits: keep well know behaviour of maxlogins default ('*') limit + (Bug 533664 - baggins) +* pam_unix: more from Nalin log password changes (Bug 517743 - agmorgan) +* pam_limits: make it use the priority value specified in config + (bug 530428 - baggins) +* pam_unix: removed broken code in password update code. Report from + Len Lattanzi (Bug 507379 - agmorgan) +* pam_mkhomedir: recurse directories. Patch from Nalin (Bug 476981 - + agmorgan) +* pam_limits can handle negative priority limits now (which can apply + to the superuser too) - based on patch from Nalin. Also cleanup the + error handling that was very sloppy before. Also, courtesy of Berend + De Schouwe get the math right on login counting (Bug 476990, 476987, + 493294 - agmorgan) +* documentation: random typo fixes from Nalin and more stuff from me + (Bug 476949, Tasks 43507, 17426 - agmorgan) +* A Tru64 fix (given other stuff has already resolved this, it + actually just a comment actually) from 'Eddie'. (Bug 418450 - + agmorgan) +* pam_handlers: BSD fix from Dag-Erling Smørgrav and Anton Berezin + (Bug 486063 - agmorgan) +* added the dynamic/* directory to the distribution. If you go in + there after building the rest of the tree, you'll make a pam.so + object that can be used by something like a java runtime with + dlopen. Its not very well tested - caveat emptor. (Bug 232194 - + agmorgan) +* somehow pam_unix has started forcing the user prompt to be "login: ". + This is entirely inapropriate as it overrides PAM_USER_PROMPT. (Bug + 486361 - agmorgan). +* added a static module helper library object includes a few changes + to examples/xsh.c for testing purposes (added a simple shell wrapper + for running xsh with the sandbox libraries), and also modified the + pam_rhosts_auth module to use this new library. (Bug 490938, 409852 + - agmorgan). +* pam_unix: fix 'likeauth' to kill off the memory leak once and for all. + (Bug 483959 - vorlon) +* pam_unix: restore handling of 'likeauth' argument to a known working + state; prettify AUTH_RETURN macro; remove redundant argv checks in + pam_sm_setcred() (Bugs 483959, 113596 - vorlon) +* pam_cracklib: another try at implementing similar() from Harald + Welte and Nalin (Bugs 436053, 476957 - agmorgan) +* pam_access: default access.conf file contained a type (console + instead of LOCAL) fix from Nalin (Bug 476934 - agmorgan) +* pam_unix: fixed bizarre memory leak pointed out by Fernando Trias + (Bug 483959 - agmorgan) +* misc string comparison length checking changes from Nalin. Modules + touched, pam_cracklib, pam_listfile, pam_unix, pam_wheel (Bug 476947 - + agmorgan) +* pam_userdb: require that all of typed password matches that in + database report and fix from Vladimir Pastukhov. (Bug 484252 - agmorgan) +* pam_malloc: revived malloc debugging code, now tied to + --enable-memory-debug and added strdup() support (Bug 485454 - agmorgan) +* pam_tally: Nalin's fix for lastlog corruption (Bug 476985 - agmorgan) +* pam_rhosts: Nalin adds support for '+hostname', and zdd fix + compilation warning. (Bug 476986 - agmorgan) +* pam_motd: Nalin fixed compiler warning. (Bug 476938 - agmorgan) +* pam_pwdb: Solar Designer pointed out that there was a problem with + the compatibility support for md5 password hashing. (Bug 460717, + 476961 - agmorgan) +* pam_issue: Nalin found segfaulting problems if the PAM_USER_PROMPT + is unset, found some similar problems with assumptions about + realloc. (Bug 476983 - agmorgan) +* pam_env: 'weichangyang of hotmail' pointed out a wild string with no + valid '\0' was leading to problems with sshd and suggested fix (Bug + 473034 - agmorgan) +* MANDIR cleanup. It defaults to /usr/share/man, but can be overridden + using the --enable-mandir ./configure option, similarly for DOCDIR + from Nalin (Bug 476940 - agmorgan) +* pam_filter cleanup (including moving the filter directory) Nalin + and Harald Welte (Bugs 436057, 476970 - agmorgan) +* db3 is now recognized as a libdb candidate (Bug 435764 - agmorgan) +* more changes (extracted from redhat version) courtesy of + Harald Welte (Bugs pam_limits=436061, pam_lastlog=436060, + pam_mkhomedir/pam_env=435991 - agmorgan) +* fix for legacy behavior of pam_setcred and pam_close_session in + the case that pam_authenticate and pam_open_session hadn't been + called - bug report from Seongwan Park. (Bug 468724 - agmorgan) +* some BSD updates and fixes from Mark Murray - including a slightly + more robust conversation function and some minimization of gcc + warnings. (Bugs 449203,463984 - agmorgan) +* verified that the setcred stack didn't suffer from the bug I was + nervous about, add a new module pam_debug to help me test this. + fixed a libpam/pam_dispatch.c instrumentation line that I tripped + over when testing. Also restructured pam_warn to help here (Bug + 424315 - agmorgan). +* pam_unix/support.c: sample use of reentrant NSS function. Not yet active, + because modules do not include _pam_aconf_h! (Bug 440107 - vorlon) +* doc/Makefile changes - use $(mandir) [courtesy Harald Welte] (Bug + 435760) and add some rules to make/delete the draft rfc I've been + working on (Task 17426 - agmorgan) +* pam_modules.sgml: sourceforge has changed its CVS viewing software + (Bug 460491 - agmorgan) +* pam_unix_passwd: got rid of an annoying warning (Bug 461089 - agmorgan) +* configure.in, _pam_aconf.h.in: set the stage for fully reentrant PAM + modules, with some infrastructure to detect getxxbyxx_r() functions + (Bug 440107 - vorlon) +* pam_unix: removed superfluous use of static variables in md5 and bigcrypt + routines, bringing us a step closer to thread-safeness. Eliminated + some variable indirection along the way. (Bug 440107 - vorlon) +* pam_tally: remove #include of stdlib.h, which isn't needed by anything + found in this module. Can be readded if we find a real need for it at + a later date. (Bug 436432 - vorlon) +* pam_tally: added an #include (was it really needed?) and made the + pam_tally app install (with more pretty printing and a corrected + Makefile dependency) motivated by a (red hat diff) courtesy of Harald + Welte (Bug 436432 - agmorgan) +* configure.in changes to help support non-Linux environments courtesy + of Scott T. Emery (Bug 422563 - agmorgan) +* made a pam_cracklib enhancement to interpret -ve limits in a + sensible fashion contributed by Werner Puschitz (Bug 413162 - + agmorgan) +* another fix for the latest number of rlimits available to pam_limits + (Bug 424060 - agmorgan) +* removed stale link from pam_pwdb documentation (Bug 433460 - agmorgan) +* pam_appl.sgml change - more discussion of choosing a service name + (Bug 417512 - agmorgan) +* more specific linking requirements for -lndbm for pam_userdb - from + David Lee (Bug 417339 - agmorgan) +* a large number of small changes to make AIX support better (Bug + 416229 - agmorgan) +* $(MAKE) instead of 'make' - from Scott T. Emery (Bug 422144 - + agmorgan) +* c++ header fixes for pam_misc.h and pam_client.h - from Alexandre + Sagala (Bug 420270 - agmorgan) +* pam_access fixes - looks out for trailing '.' - from Carlo Marcelo + Arenas Belon (Bug 419631 - agmorgan) +* don't zero out password strings during pam_unix's password changing + function (Bug 419803 - vorlon) +* propagate some definitions to the _pam_aconf.h file - from David Lee + (Bug 415419 - agmorgan) +* solaris GCC OS_CFLAGS change from David Lee (Bug 415412 - agmorgan) +* added a comment to this CHANGELOG to explain why most of the bugids + used below appear not to be known to sourceforge [try adding 100000 + to the bugid number.] (Bug 414943 - agmorgan) +* bumped version numbers and also added support for SONAME defines + that appear not to have survived the great autoconf experiment (Bug + 414669 - agmorgan). + +0.75: Sat Apr 7 23:10:50 PDT 2001 + + ** WARNING ** + +This release contains backwardly incompatible changes to +libpam. Prior versions were buggy - see bugfix for Bug 129775. + + ** WARNING ** + +* made 0.75 release (Bug 414665 - agmorgan) +* pam_pwdb has been removed from the suggested pam.conf template. I've + replaced it with pam_unix. (Bug 227565 - agmorgan) +* pam_limits - Richard M. Yumul reported that "<domain> -" didn't + work, first fix suggested by Werner Puschitz (Bug 404953 - agmorgan) +* Nicolay Pelov suggested a simple fix for freebsd support (Bug 407282 + - agmorgan) +* Michel D'HOOGE submitted documentation fixes (Bug 408961 - agmorgan) +* fix for module linking directions (Bug 133545 - agmorgan) +* fix for glibc-2.2.2 compilation of pam_issue (Bug 133542 - agmorgan) +* fix pam_userdb to make and link both .o files it needs - converse() + wasn't being linked! (Bug 132880 - agmorgan) +* added some sys-admin documentation for the pam_tally module (Bug + 126210 - agmorgan). +* added a link to module examples from the module writers doc (Bug + 131192 - agmorgan). +* fixed a small security hole (more of a user confusion issue) with + the unix and pwdb password helper binaries. The beef is described in + the bug report, but no uid change was possible so no-one should + think they need to issue a security bulletin over this one! (Bug + 112540 - agmorgan) +* pam_lastlog needs to be linked with -lutil, also removed ambiguity + from sysadmin guide regarding this module being a 'session' module + (Bug 131549 - agmorgan). +* pam_cracklib needs to be linked with -lcrypt (old password checking) + (Bug 131601 - agmorgan). +* fixes for static library builds and also the examples when linked + with the debugging build of the libraries. (Bug 131783 - agmorgan) +* fixed URL for original RFC to a cached kernel.org file. (Bug 131503 + - agmorgan) +* quoted the $CRACKLIB_DICTPATH test in configure.in (Bug 130130 - + agmorgan). +* improved handling of the setcred/close_session and update chauthtok + stack. *Warning* This is a backwardly incompatable change, but 'more + sane' than before. (Bug 129775 - agmorgan) +* bumped the version number, and added some code to assist in making + documentation releases (Bug 129644 - agmorgan). + +0.74: Sun Jan 21 22:36:08 PST 2001 + +* made 0.74 release (Bug 129642 - agmorgan) +* libpam - cleaned up a few non-static functions to be static and added + support for libpam to enforce things like pam_[gs]et_data() and + AUTHTOK rules for using the API. Also documented pam_[gs]et_item() + a little better including return codes (Bugs 129027, 128576 - + agmorgan). +* pam_access - fixed the non-default config file option (Bug 127561 - + agmorgan) +* pam.8 manual page clarified with respect to the default location for + finding modules, also added some text describing the [...] control + syntax. (Bug 127625 - agmorgan) +* md5.h ia64 fixes for pam_unix and pam_pwdb (Bug 127700 - agmorgan) +* removed requirement for c++ from the configure{.in,} files (Bug + 128298 - agmorgan) +* removed subdirectories from man page redirections (124396 - baggins) +* per David Lee, fixed non-POSIX shell command in modules/pam_filter/Makefile + (Bug 126440 - vorlon) +* modify format of pam_unix log messages to include service name + (Bug 126423 - vorlon) +* prevent pam_unix from logging unknown usernames (Bug 126431 - vorlon) +* changed format of pam_unix 'authentication failure' log messages to make + them clearer and more consistent (Bug 126036 - vorlon) +* improved portability of pam_unix by eliminating Linux-specific utmp + defines in PAM_getlogin() (Bug 125704 - vorlon) +* removed static variables from pam_tally (Bug 117434 - agmorgan) +* added copyright message to pam_access module from original logdaemon + sources (Bug 125022 - agmorgan) +* configure.in - removed the GCC -Wtraditional flag (Bug 124923 - agmorgan) +* pam_mail - use PAM_PATH_MAILDIR as the location of mail spool + (Bug 124397 - baggins) +* _pam_aconf.h.in, configure.in - added PAM_PATH_MAILDIR set via + --with-mailspool=dir option (default is _PAM_MAILDIR if defined + in paths.h otherwise /var/spool/mail (Bug 124397 - baggins) +* removed unnecessary CVS Log tags from all over the source + (Bug 124391 - baggins) +* pam_tally - check for PAM_TTY if PAM_RHOST is not set when writing + to faillog (Bug 124394 - baggins) +* use O_NOFOLLOW if available when opening debug log (Bug 124385 - baggins) +* pam_cracklib - removed comments about pam_unix not working with + pam_cracklib, added information about use_authtok parameter + (Bug 124388 - baggins) +* pam_userdb - fixed wrong definition of struct pam_module (was pam_wheel) + (Bug 124386 - baggins) +* fixed example/Makefile include path (Bug 124187, 127563(?) - agmorgan) +* pam_userdb compiles on RH5x. Also removed circular dependency on + configure.in. Also bumped revision number to 0.74. (Bug 124136 - + agmorgan) + +0.73: Sat Dec 2 00:04:04 PST 2000 + +* updated documentaion revisions and added 'make release' support + to the top level Makefile (Bug 124132 - agmorgan). +* documented Qmail support in pam_mail (Bug 109219 - baggins) +* add change_uid option to pam_limits, and set real uid only if + this option is present (Bug 124062 - baggins) +* pam_limits - set real uid to the user for who we set limits. + (Bug 123972 - baggins) +* removed static variables from pam_limits (thread safe now). (Bug + 117450 - agmorgan). +* removed static variable from pam_wheel (module should be thread safe + now). (Bug 112906 - agmorgan) +* added support for '/' symbols in pam_time and pam_group config files + (support for modern terminal devices). Fixed infinite loop problem + with '\\[^\n]' in these files. (Bug 116076 - agmorgan) +* avoid potential SIGPIPE when writing to helper binaries with (Bug + 123399 - agmorgan) +* replaced bogus logic in the pam_cracklib module for determining if + the replacement is too similar to the old password (Bug 115055 - + agmorgan) +* added accessconf=<filename> feature to pam_access - request from + Aldrin Martoq and Meelis Roos (Bugs 111927,117240 - agmorgan) +* fix for pam_limit module not dealing with all limits Adam J. Richter + (Bug 119554 - agmorgan) +* comment fix describing fail_delay callback in _pam_types.h (Bug + 112646 - agmorgan) +* "likeauth" fix for pam_unix and pam_pwdb which (Bug 113596 - agmorgan) +* fix for pam_unix (support.c) to avoid segfault with NULL password + (Bug 113238 - vorlon) +* fix to pam_unix_passwd: try repeatedly to get a lock on the password + file, instead of failing immediately (Bug 108845 - fix vorlon) +* fix to pam_shells: logged information was not formatted correctly + (extra comma) (Bug 111491 - fix vorlon) +* fix for C++ application support (Bug 111645 - fix agmorgan) +* fix for typo in pam_client.h (Bug 111648 - fix agmorgan) +* removal of -lpam from pam_mkhomedir Makefile (Bug 116380 - fix agmorgan) +* autoconf support [Task ID 15788, Bug ID 108297 - agmorgan with help!] + - bugfix for libpamc.h include file [Bug ID 117476 - agmorgan] + - bugfix for pam_filter.h inclusion [Bug ID 117474 - agmorgan] + +0.72: Mon Dec 13 22:41:11 PST 1999 + +* patches from Debian (Ben Collins): pam_ftp supports event driven + conversations now; pwdb_chkpwd cleanup; pam_warn static compile fix; + user_db compiler warnings removed; debian defs file; pam_mail can + now be used as a session module +* ndbm compilation option for user_db module (fix explained by Richard Khoo) +* pam_cracklib bug fix +* packaging fixes & build from scratch stuff (Konst Bulatnikov & Frodo + Looijaard) +* -ldl appended to the libpam.so compilation make rule. (Charles Seeger) +* Red Hat security patch for pam_pwdb forwarded by Debian! (Ben + Collins. Fix provided by Andrey as it caught the problem earlier in the + code.) +* heuristic to prevent leaking filedescriptors to an agent. [This needs + to be better supported perhaps by an additional libpamc API function?] +* pam_userdb segfault fix from (Ben Collins) +* PAM draft spec extras added at request of 'sen_ml' + +0.71: Sun Nov 7 20:21:19 PST 1999 + +* added -lc to linker pass for pam_nologin module (glibc is weird). +* various header changes to lower the number of warnings on glibc + systems (Dan Yefimov) +* merged a bunch of Debian fixes/patches/documentation (Ben Collins) + things touched: libpam (minor); doc/modules/pam_unix.sgml; pam_env + (plus docs); pam_mkhomedir (new module for new home directories on + the fly...); pam_motd (new module); pam_limits (adjust to match + docs); pam_issue (new module + doc) [Some of these were also + submitted by Thorsten Kukuk] +* small hack to lower the number of warnings that pam_client.h was + generating. +* debian and SuSE apparently can use the pam_ftp module, so + removed the obsolete comment about this from the docs. (Thorsten + Kukuk) + +0.70: Fri Oct 8 22:05:30 PDT 1999 + +* bug fix for parsing of value=action tokens in libpam/pam_misc.c was + segfaulting (Jan Rekorajski and independently Matthew Melvin) +* numerous fixes from Thorsten Kukuk (icluding much needed fixes for + bitrot in modules and some documentation) that got included in SuSE 6.2. +* reentrancy issues in pam_unix and pam_cracklib resolved (Jan Rekorajski) +* added hosts_equiv_rootok module option to pam_rhosts module (Tim Berger) +* added comment about 'expose_account' module argument to admin and + module writers' docs (request from Michael K Johnson). +* myriad of bug fixes for libpamc - library now built by default and + works with the biomouse fingerprint scanner agent/module + (distributed separately). + +0.69: Sun Aug 1 20:25:37 PDT 1999 + +* c++ header #ifdef'ing for pam_appl.h (Tuomo Pyhala) +* added pam_userdb module (Cristian Gafton) +* minor documentation changes +* added in revised pam_client library (libpamc). Not installed by + default yet, since the example agent/module combo is not very secure. +* glibc fixes (Thorsten Kukuk, Adam J. Richter) + +0.68: Sun Jul 4 23:04:13 PDT 1999 + +* completely new pam_unix module from Jan Rekorajski and Stephen Langasek +* Jan Rekorajski pam_mail - support for Maildir format mailboxes +* Jan Rekorajski pam_cracklib - support for old password comparison +* Jan Rekorajski bug fix for pam_pwdb setcred reusing auth retval +* Andrey's pam_tally patch (lstat -> fstat) +* Robert Milkowski's additional pam_tally patches to **change format of + /var/log/faillog** to one from shadow-utils, add new option "per_user" + for pam_tally module, failure time logging, support for fail_line + field, and support for fail_locktime field with new option + no_lock_time. +* pam_tally: clean up the tally application too. +* Marcin Korzonek added process priority settings to pam_limits (bonus + points for adding to documentation!) +* Andrey's pam_pwdb patch (cleanup + md5 endian fubar fix) +* more binary prompt preparations (make misc conv more compatible with spec) +* modified callback hook for fail delay to be more useful with event + driven applications (changed function prototype - suspect no one + will notice). Documented this in app developer guide. +* documentation for pam_access from Tim Berger +* syntax fixes for the documentation - a long time since I've built it :*( + added some more names to the CREDITS file. + +0.67: Sat Jun 19 14:01:24 PDT 1999 + +* [dropped libpam_client - libpamc will be in the next release and + conforms to the developing spec in doc/specs/draft-morgan-pam.raw. + Sorry if you are keeping a PAM tree in CVS. CVS is a pain for + directories, but this directory was actually not referenced by + anything so the disruption should be light.] +* updates to pam_tally from Tim +* multiple updates from Stephen Langasek to pam_unix +* pam_filter had some trouble compiling (bug report from Sridhar) +* pam_wheel now attempts to identify the wheel group for the local + system instead of blindly assuming it is gid=0. In the case that + there is no "wheel" group, we default to assuming gid=0 is what was + meant - former behavior. (courtesy of Sridhar) +* NIS+ changes to pam_unix module from Dmitry O Panov +* hopefully, a fix for redefinition of LOG_AUTHPRIV (bug report Luke + Kenneth Casson Leighton) +* fix for minor typo in pam_wheel documentation (Jacek Kopecky) +* slightly more explanation of the [x=y] pam.conf syntax in the sys + admin guide. + +0.66: Mon Dec 28 20:22:23 PST 1998 <morgan@linux.kernel.org> + +* Started using cvs to keep track of changes to Linux-PAM. This will + likely break some of the automated building stuff (RPMs etc..). +* security bug fix to pam_unix and pam_tally from Andrey. +* modules make file is now more automatic. It should be possible to + unpack an external module in the modules directory and have it automatically + added to the build process. Also added a modules/download-all script + that will make such downloading easier. I'm happy to receive patches to + this file, informing the distribution of places from which to enrich itself. +* removed pam_system_log stuff. Thought about it long and hard: a + bad idea. If libc cannot guarantee a thread safe syslog, it needs + to be fixed and compatibility with other PAM libraries was + unnecessarily strained. +* SAG documentation changes: Seth Chaiklin +* rhosts: problems with NIS lookup failures with the root-uid check. + As a work-around, I've partially eliminated the need for the lookup + by supplying two new arguments: no_uid_check, superuser=<username>. + As a general rule this is more pluggable, since this module might be + used as an authentication scheme for a network service that does not + need root privilege... +* authenticate retval -> setcred for pam_pwdb (likeauth arg). +* pam_pwdb event driven support +* non openlog pam_listfile logging +* BUGFIX: close filedescriptor in pam_group and pam_time (Emmanuel Galanos) +* Chris Adams' mailhash change for pam_mail module +* fixed malloc failure check in pam_handlers.c (follow up to comment + by Brad M. Garcia). +* update to _pam_compat.h (Brad M. Garcia) +* support static modules in libpam again (Brad M. Garcia) +* libpam/pam_misc.c for egcs to grok the code (Brad M. Garcia) +* added a solaris-2.5.1 defs file (revived by Derrick J Brashear) +* pam_listfile logs failed attempts +* added a comment (Michael K Johnson pointed it out) about sgml2latex + having a new syntax. I'll make it the change real when I upgrade... +* a little more text to the RFC, spelling fix from William J Buffam. +* minor changes to pam_securetty to accommodate event driven support. + +0.65: Sun Apr 5 22:29:09 PDT 1998 <morgan@linux.kernel.org> + +* added event driven programming extensions to libpam + - added PAM_INCOMPLETE handling to libpam/pam_dispatch.c + - added PAM_CONV_AGAIN which is a new conversation response that + should be mapped to PAM_INCOMPLETE by the module. + - ensured that the pam_get_user() function can resume + - changes to pam_strerror to accommodate above return codes + - clean up _pam_former_state at pam_end() + - ensured that former state is correctly initialized + - added resumption tests to pam_authenticate(), pam_chauthtok() + - added PAM_FAIL_DELAY item for pausing on failure + +* improved _pam_macros.h so that macros can be used as single commands + (Andrey) + +* reimplemented logging to avoid bad interactions with libc. Added + new functions, pam_[,v]system_log() to libpam's API. A programmer + can check for this function's availablility by checking if + HAVE_PAM_SYSTEM_LOG is #defined. + +* removed the reduce conflict from pam_conv1 creation -- I can sleep + again now. :^] + +* made building of static and dynamic libpam separate. This is + towards making it possible to build both under Solaris (for Derrick) + +* made USE_CRACKLIB a condition in unix module (Luke Kenneth Casson Leighton) + +* automated (quiet) config installation (Andrey) + +0.64: Thu Feb 19 23:30:24 PST 1998 Andrew Morgan <morgan@linux.kernel.org> + +* miscellaneous patches for building under Solaris (Derrick J Brashear) + +* removed STATIC support from a number of module Makefiles. Notably, + these modules are those that use libpwdb and caused difficulties + satisfying the build process. (Please submit patches to fix this...;) + +* reomved the union for binary packet conversations from + (_pam_types.h). This is now completely implemented in libpam_client. + +* Andrey's patch for working environment variable handling in + sh_secret module. + +* made the libpam_misc conversation function a bit more flexible with + respect to binary conversations. + +* added top level define (DEBUG_REL) for compiling in the form of + a debugging release. I use this on a Red Hat 4.2 system with little + chance of crashing the system as a whole. (Andrey has another + implementation of this -- with a spec file to match..) + +0.63: Wed Jan 28 22:55:30 PST 1998 Andrew Morgan <morgan@linux.kernel.org> + +* added libpam_client "convention" library. This makes explicit the + use of PAM_BINARY_PROMPT. It is a first cut, so don't take it too + seriously yet. Comments/suggestions for improvements are very + welcome. Note, this library does not compile by default. It will + be enabled when it is judged stable. The library comes with two + module/agent pairs and can be used with ssh using a patch available + from my pre-release directory [where you got this file.] + +* backward compatibility patch for libpam/pam_handlers.c (PAM_IGNORE + was working with neither "requistie" nor "required") and a DEBUG'ing + compile time bug with pam_dispatch.c (Savochkin Andrey Vladimirovich) + +* minor Makefile change from (Savochkin Andrey Vladimirovich) + +* added pam_afsauth, pam_afspass, pam_restrict, and pam_syslog hooks + (Derrick J Brashear) + +* pam_access use of uname(2) problematic (security problem + highlighted by Olaf Kirch). + +* pam_listfile went a bit crazy reading group membersips (problem + highlighted by Olaf Kirch and patched independently by Cristian + Gafton and Savochkin Andrey Vladimirovich) + +* compatibility hooks for solaris and hpux (Derrick J Brashear) + +* 64 bit Linux/alpha bug fixed in pam_rhosts (Andrew D. Isaacson) + +0.62: Wed Jan 14 14:10:55 PST 1998 Andrew Morgan <morgan@linux.kernel.org> + +* Derrick J Brashear's patches: adds the HP stuff missed in the first + patch; adds SunOS support; adds support for the Solaris native ld + instead of requiring gnu ld. + +* last line of .rhosts file need not contain a newline. (Bug reported by + Thompson Freeman.) + +0.61: Thu Jan 8 22:57:44 PST 1998 Andrew Morgan <morgan@linux.kernel.org> + +* complete rewrite of the "control flag" logic. Formerly, we were + limited to four flags: requisite, required, sufficient, optional. + We can now use these keywords _and_ a great deal more besides. + The extra logic was inspired by Vipin Samar, a preliminary patch was + written by Andy Berkheimer, but I "had some ideas of my own" and + that's what I've actually included. The basic idea is to allow the + admin to custom build a control flag with a series of token=value + pairs inside square brackets. Eg., '[default=die success=ok]' which + is pretty close to a synonym for 'requisite'. I'll try to document it + better in the sys-admin guide but I'm pretty sure it is a change for + the better.... If what is in the sys-admin guide is not good enough + for you, just take a look at the source for libpam ;^) + +0.59: Thu Jan 8 22:27:22 PST 1998 Andrew Morgan <morgan@linux.kernel.org> + +* better handling of empty lines in .rhosts file. (Formerly, we asked + the nameserver about them!) Fix from Hugh Daschbach. + +* _broke_some_binary_compatibility_ with previous versions to become + compliant with X/Open's XSSO spec. Specifically, this has been + by changing the prototype for pam_strerror(). + +* altered the convention for the conversation mechanism to agree + with that of Sun. (number of responses 'now=' number of messages + with help from Cristian for finding a bug.. Cristian also found a + nasty speradic segfault bug -- Thanks!) + +* added NIS+ support to pam_unix_* + +* fixed a "regular file checking" problem with the ~/.rhosts sanity + check. Added "privategroup" option to permit group write permission + on the ~/.rhosts file in the case that the group owner has the same + name as the authenticating user. :*) "promiscuous" and "suppress" + were not usable! + +* added glibc compatibility to pam_rhosts_auth (protected __USE_MISC + with #ifndef since my libc already defines it!). + +* Security fix from Savochkin Andrey Vladimirovich with suggested + modification from Olaf Seibert. + +* preC contains mostly code clean-ups and a number of changes to + _pam_macros. + +0.58: whenever + +* pam_getenvlist() has a more robust definition (XSSO) than was previously + thought. It would seem that we no longer need pam_misc_copy_env() + which was there to provide the robustness that pam_getenvlist() + lacked before... + + Accordingly, I have REMOVED the prototype from libpam_misc. (The + function, however, will remain in the library as a wrapper for + legacy apps, but will likely be removed from libpam_misc-1.0.) PLEASE + FIX YOUR APPS *BEFORE* WE GET THERE! + +* Alexy Nogin reported garbage output from pam_env in the case of + a non-existent environment variable. + +* 'fixed' pwdb compilation for pam_wheel. Not very cleanly + done.. Mmmm. Should really clean up the entire source tree... + +* added prototypes for mapping functions + + <**WARNING**> + + various constants have had there names changed. Numerical values have + been retained but be aware some source old modules/applications will + need to be fixed before recompilation. + + </**WARNING**> + +* appended documentation to README for pam_rhosts module (Nicolai + Langfeldt). + +* verified X/Open compatibility of header files - note, where we differ + it is at the level of compilation warnings and the use of 'const char *' + instead of 'char *'. Previously, Sun(X/open) have revised their spec + to be more 'const'-ervative in the light of comments from Linux-PAM + development. + +* Ooops! PAM_AUTHTOKEN_REQD should have been PAM_NEW_AUTHTOK_REQD. + + changed: pam_pwdb(pam_unix_acct) (also bug fix for + _shadow_acct_mgmt_exp() return value), pam_stress, + libpam/pam_dispatch, blank, xsh. + +* New: PAM_AUTHTOK_EXPIRED - password has expired. + +* Ooops! PAM_CRED_ESTABLISH (etc.) should have been PAM_ESTABLISH_CRED + etc... (changed - this may break some people's modules - PLEASE TAKE + NOTE!) + changed: pam_group, pam_mail, blank, xsh; module and appl + docs, pam_setcred manual page. + +* renamed internal _pam_handle structure to be pam_handle as per XSSO. + +* added PAM_RADIO_TYPE (for multiple choice input method). Also + added PAM_BINARY_{MSG,PROMPT} (for interaction out of sight of user + - this could be used for RSA type authentication but is currently + just there for experimental purposes). The _BINARY_ types are now + usable with hooks in the libpam_misc conversation function. Still + have to add PAM_RADIO_TYPE. + +* added pam_access module (Alexei Nogin) + +* added documentation for pam_lastlog. Also modified the module to + not (by default) print "welcome to your new account" when it cannot + find a utmp entry for the user (you can turn this on with the + "never" argument). + +* small correction to the pam_fail_delay manual page. Either the appl or + the modules header file will prototype this function. + +* added "bigcrypt" (DEC's C2) algorithm(0) to pam_pwdb. (Andy Phillips) + +* *BSD tweaking for various #include's etc. (pam_lastlog, pam_rhosts, + pam_wheel, libpam/pam_handlers). (Michael Smith) + +* added configuration directory $SCONFIGED for module specific + configuration files. + +* added two new "linked" man pages (pam.conf(8) and pam.d(8)) + +* included a reasonable default for /etc/pam.conf (which can be + translated to /etc/pam.d/* files with the pam_conv1 binary) + +* fixed the names of the new configuration files in + conf/pam_conv1/pam_conv.y + +* fixed make check. + +* pam_lastlog fixed to handle UID in virgin part of /var/log/lastlog + (bug report from Ronald Wahl). + +* grammar fix in pam_cracklib + +* segfault avoided in pam_pwdb (getting user). Updating of passwords + that are directed to a "new" database are more robust now (bug noted + by Michael K. Johnson). Added "unix" module argument for migrating + passwords from another database to /etc/passwd. (documentation + updated). Removed "bad username []" warning for empty passwords - + on again if you supply the 'debug' module argument. + +* ctrl-D respected in conversation function (libpam_misc) + +* Removed -DPAM_FAIL_DELAY_ON from top-level Makefile. Nothing in + the distribution uses it. I guess this change happened a while + back, basically I'm trying to make the module parts of the + distribution "source compatible" with the RFC definition of PAM. + This implementation of PAM is a superset of that definition. I have + added the following symbols to the Linux-PAM header files: + + PAM_DATA_SILENT (see _pam_types.h) + HAVE_PAM_FAIL_DELAY (see _pam_types.h) + PAM_DATA_REPLACE (see _pam_modules.h) + + Any module (or application) that wants to utilize these features, + should check (#ifdef) for these tokens before using the associated + functionality. (Credit to Michael K. Johnson for pointing out my + earlier omission: not documenting this change :*) + +* first stab at making modules more independent of full library + source. Modules converted: + pam_deny + pam_permit + pam_lastlog + pam_pwdb + +* pam_env.c: #include <errno.h> added to ease GNU libc use. (Michael + K. Johnson) + +* pam_unix_passwd fixes to shadow aging code (Eliot Frank) + +* added README for pam_tally + +0.57: Fri Apr 4 23:00:45 PST 1997 Andrew Morgan <morgan@parc.power.net> + +* added "nodelay" argument to pam_pwdb. This can be used to turn off + the call to pam_fail_delay that takes effect when the user fails to + authenticate themself. + +* added "suppress" argument to pam_rhosts_auth module. This will stop + printing the "rlogin failure message" when the user does not have a + .rhosts file. + +* Extra fixes for FAKEROOT in Makefiles (Savochkin Andrey + Vladimirovich) + +* pam_tally added to tree courtesy of Tim Baverstock + +* pam_rhosts_auth was failing to read NFS mounted .rhosts + files. (Fixed by Peter Allgeyer). Refixed and further enhanced + (netgroups) by Nicolai Langfeldt. [Credit also to G.Wilford for some + changes that were not actually included..] + +* optional (#ifdef PAM_READ_BOTH_CONFS) support for parsing of pam.d/ + AND pam.conf files (Elliot Lee). + +* Added (and signed) Cristian's PGP key. (I've never met him, but I am + convinced the key belongs to the guy that is making the PAM rpms and + also producing libpwdb. Please note, I will not be signing anyone + else's key without a personal introduction..) + +* fixed erroneous syslog warning in pam_listfile (Savochkin Andrey + Vladimirovich, whole file reformatted by Cristian) + +* modified pam_securetty to return PAM_IGNORE in the case that the user's + name is not known to the system (was previously, PAM_USER_UNKNOWN). The + Rationale is that pam_securetty's sole purpose is to prevent superuser + login anywhere other than at the console. It is not its concern that the + user is unknown - only that they are _not_ root. Returning + PAM_IGNORE, however, insures that the pam_securetty can never be used to + "authenticate" a non-existent user. (Cristian Gafton with bug report from + Roger Hu) + +* Modified pam_nologin to display the no-login message when the user + is not known. The return value in this case is still PAM_USER_UNKNOWN. + (Bug report from Cristian Gafton) + +* Added NEED_LCKPWD for pam_unix/ This is used to define the locking + functions and should only be turned on if you don't have them in + your libc. + +* tidied up pam_lastlog and pam_pwdb: removed function that was never used. + +* Note for package maintainers: I have added $(FAKEROOT) to the list of + environment variables. This should help greatly when you build PAM + in a subdirectory. I've gone through the tree and tried to make + everything compatible with it. + +* added pam_env (courtesy of Dave Kinchlea) + +* removed pam_passwd+ from the tree. It has not been maintained in a + long time and running a shell script was basically insecure. I've + indicated where you can pick up the source if you want it. + +* #define HAVE_PAM_FAIL_DELAY . Applications can conditionally compile + with this if they want to see if the facility is available. It is + now always available. (corresponding compilation cleanups..) + +* _pam_sanitize() added to pam_misc. It purges the PAM_AUTHTOK and + PAM_OLDAUTHTOK items. (calls replaced in pam_auth and pam_password) + +* pam_rhosts now knows about the '+' entry. Since I think this is a + dangerous thing, I have required that the sysadmin supply the + "promiscuous" flag for it in the corresponding configuration file + before it will work. + +* FULL_LINUX_PAM_SOURCE_TREE exported from the top level make file. + If you want to build a module, you can test for this to determine if + it should take its directions from above or supply default locations + for installation. Etc. + +0.56: Sat Feb 15 12:21:01 PST 1997 <morgan@parc.power.net> + +* pam_handlers.c can now interpret the pam.d/ service config tree: + - if /etc/pam.d/ exists /etc/pam.conf is IGNORED + (otherwise /etc/pam.conf is treated as before) + - given /etc/pam.d/ + . config files are named (in lower case) by service-name + . config files have same syntax as /etc/pam.conf except + that the "service-name" field is not present. (there + are thus three manditory fields (and arguments are + optional): + + module-type control-flag module-path optional-args... + + ) + +* included conf/pam_conv1 for converting pam.conf to a pam.d/ version + 1.0 directory tree. This program reads a pam.conf file on the + standard input stream and creates ./pam.d/ (in the local directory) + and fills it with ./pam.d/"service-name" files. + + *> Note: It will fail if ./pam.d/ already exists. + + PLEASE REPORT ANY BUGS WITH THIS CONVERSION PROGRAM... It currently + cannot retain comments from the old conf file, so take care to do this + by hand. Also, please email me with the fix that makes the + shift/reduce conflict go away... + +* Added default module path to libpam for modules (see pam_handlers.c) + it makes use of Makfile defined symbol: DEFAULT_MODULE_PATH which is + inhereted from the defs/* variable $(SECUREDIR). Removed module + paths from the sample pam.conf file as they are no longer needed. + +* pam_pwdb can now verify read protected passwords when it is not run + by root. This is via a helper binary that is setuid root. + +* pam_permit now prompts for a username if it is not already determined + +* pam_rhosts now honors "debug" and no longer hardwire's "root" as the + superuser's name. + +* pam_securetty now honors the "debug" flag + +* trouble parsing extra spaces fixed in pam_time and pam_group + +* added Michael K. Johnson's PGP key to the pgp.keys.asc list + +* pam_end->env not being free()'d: fixed + +* manuals relocated to section 3 + +* fixed bug in pam_mail.c, and enhanced to recognize '~' as a prefix + to indicate the $HOME of the user (courtesy David + Kinchlea). *Changed* from a "session" module to an "auth" + module. It cannot be used to authenticate a user, but it can be used + in setting credentials. + +* fixed a stupid bug in pam_warn.. Only PAM_SERVICE was being read :*( + +* pam_radius rewritten to exclusively make use of libpwdb. (minor fix + to Makefile for cleaning up - AGM) + +* pam_limits extended to limit the total number of logins on a system + at any given time. + +* libpam and libpam_misc use $(MAJOR_REL) and $(MINOR_REL) to set their + version numbers [defined in top level makefile] + +* bugfix in sed command in defs/redhat.defs (AGM's fault) + +* The following was related to a possibility of buffer overruns in + the syslogging code: removed fixed length array from syslogging + function in the following modules [capitalized the log identifier + so the sysadmin can "know" these are fixed on the local system], + + pam_ftp, pam_stress, pam_rootok, pam_securetty, + pam_listfile, pam_shells, pam_warn, pam_lastlog + and + pam_unix_passwd (where it was definitely _not_ exploitable) + +0.55: Sat Jan 4 14:43:02 PST 1997, Andrew Morgan <morgan@parc.power.net> + +* added "requisite" control_flag to /etc/pam.conf syntax. [See + Sys. Admin. Guide for explanation] changes to pam_handlers.c + +* completely new handling of garbled pam.conf lines. The modus + operandi now is to assume that any errors in the line are minor. + Errors of this sort should *most definitely* lead to the module + failing, however, just ignoring the line (as was the case + previously) can lead to gaping security holes(! Not foreseen by the + RFC). The "motivation" for the RFC's comments about ignoring garbled + lines is present in spirit in the new code: basically a garbled line + is treated like an instance of the pam_deny.so module. + changes to pam_handlers.c and pam_dispatch.c . + +* patched libpam, to (a) call _pam_init_handlers from pam_start() and + (b) to log a text error if there are no modules defined for a given + service when a call to a module is requested. [pam_start() and + pam_dispatch() were changed]. + +* patched pam_securetty to deal with "/dev/" prefix on PAM_TTY item. + +* reorganized the modules/Makefile to include *ALL* modules. It is now + the responsibility of the modules themselves to test whether they can + be compiled locally or not. + +* modified pam_group to add to the getgroups() list rather than overwrite + it. [In the case of "HAVE_LIBPWDB" we use the pwdb_..() calls to + translate the group names.]. Module now pays attention to + PAM_CRED_.. flag(!) + +* identified and removed bugs in field reading code of pam_time and + (thus) pam_group. + +* Cristian's patches to pam_listfile module, corresponding change to + documentation. + +* I've discovered &ero; for sgml! + Added pam_time documentation to the admin guide. + +* added manual pages: pam.8, pam_start.2(=pam_end.2), + pam_authenticate.2, pam_setcred.2, pam_strerror.2, + pam_open_session.2(=pam_close_session.2) and pam_chauthtok.2 . + +* added new modules: + + - pam_mail (tells the user if they have any new mail + and sets their MAIL env variable) + - pam_lastlog (reports on the last time this user called + this module) + +* new module hooks provided. + +* added a timeout feature to the conversation function in + libpam_misc. Documented it in the application developers' guide. + +* fixed bug in pam_misc_paste_env() function.. + +* slight modifications to wheel and rhosts writeup. + +* more security issues added to module and application guides. + +-- +Things present but not mentioned in previous release (sorry) + +* pam_pwdb module now resets the "last_change" entry before updating a + password. +-- + +Sat Nov 30 19:30:20 PST 1996, Andrew Morgan <morgan@parc.power.net> + +* added environment handling to libpam. involved change to _pam_types.h + also added supplementary functions to libpam_misc + +* added pam_radius - Cristian + +* slight speed up for pam_rhosts + +* significantly enhanced sys-admin documentation (8 p -> 41 p in + PostScript). Added to other documentation too. Mostly the changes + in the other docs concern the new PAM-environment support, there is + also some coverage of libpam_misc in the App. Developers' guide. + +* Cristian's patches to pam_limits and pam_pwdb. Fixing bugs. (MORE added) + +* adopted Cristian's _pam_macros.h file to help with common macros and + debugging stuff, gone through tree tidying up debugging lines to use + this [not complete]. + + - for consistency replaced DROP() with _pam_drop() + +* commented memory debugging in top level makefile + +* added the following modules + + - pam_warn log information to syslog(3) about service application + - pam_ftp if user is 'ftp' then set PAM_RUSER/PAM_RHOST with password + (comment about nologin added to last release's notes) + +* modified the pam_listfile module. It now declares a meaningful static + structure name. + +Sun Nov 10 13:26:39 PST 1996, Andrew Morgan <morgan@parc.power.net> + + **PLEASE *RE*AMEND YOUR PERSONAL LINKS** + + -------> http://parc.power.net/morgan/Linux-PAM/index.html <------- + + **PLEASE *RE*AMEND YOUR PERSONAL LINKS** + +A brief summary of what has changed: + +* many modules have been modified to accomodate fixing the pam_get_user() + change. Please take note if you have a module in this distribution. + +* pam_unix is now the pam_unix that Red Hat has been using and which + should be fairly well debugged. + + - I've added some #ifdef's to make it compile for me, and also + updated it with respect to the libpam-0.53, so have a look at the + .../modules/pam_unix/Makefile to enable cracklib and shadow features + + ** BECAUSE OF THIS, I cannot guarantee this code works as it ** + ** did for Red Hat. Please test and report any problems. ** + +* the pam_unix of .52 (renamed to pam_pwdb) has been enhanced and made + more flexible with by implementing it with respect to the new + "Password Database Library" see + + http://parc.power.net/morgan/libpwdb/index.html + + modules included in this release that require this library to + function are the following: + + - pam_pwdb (ne pam_unix-0.52 + some enhancements) + - pam_wheel + - pam_limits + - pam_nologin + +* Added some optional code for memory debugging. In order to support + this you have to enable MEMORY_DEBUG in the top level makefile and + also #define MEMORY_DEBUG in your applications when they are compiled. + The extra code resides in libpam (compiled if MEMORY_DEBUG is defined) + and the macros for malloc etc. are to be found at the end of + _pam_types.h + +* used above code to locate two memory leaks in pam_unix module and two + in libpam (pam_handlers.h) + +* pam_get_user() now sets the PAM_USER item. After reading the Sun + manual page again, it was clear that it should do this. Various + modules have been assuming this and now I have modified most of them + to account for this change. Additionally, pam_get_user() is now + located in the module include file; modules are supposed to be the + ones that use it(!) [Note, this is explicitly contrary to the Sun + manual page, but in the spirit of the Linux distribution to date.] + +* replaced -D"LINUX" with -D"LINUX_PAM" as this is more explicit and less + likely to be confused with -D"linux". + Also, modified the libpam #include files to behave more like the Sun + ones #ifndef LINUX_PAM. + +* removed <bf/ .. / from documentation titles. This was not giving + politically correct html.. + +----- My vvvvvvvvvvvvvvvvvvv was a long time ago ;*] ----- + +Wed Sep 4 23:57:19 PDT 1996 (Andrew Morgan <morgan@physics.ucla.edu> + +0. Before I begin, Linux-PAM has a new primary distribution site (kindly +donated by Power Net Inc., Los Angeles) + + **PLEASE AMMEND YOUR PERSONAL LINKS** + + -------> http://www.power.net/morgan/Linux-PAM <------- + + **PLEASE AMMEND YOUR PERSONAL LINKS** + +1. I'm hoping to make the next release a bug-fix release... So please find + all the bugs(! ;^) + +2. here are the changes for .52: + +* minor changes to module documentation [Incidently, it is now + available on-line from the WWW page above]. More changes to follow in + the next two releases. PLEASE EMAIL me or the list if there is + anything that isn't clear! + +* completely changed the unix module. Now a single module for all four + management groups (this meant that I could define all functions as + static that were not part of the pam_sm_... scheme. AGM) + + - Shadow support added +PASSWD - Elliot's account management included, and enhanced by Cristian Gafton. + - MD5 password support added by Cristian Gafton. + - maxtries for authentication now enforced. + - Password changing function in pam_unix now works! + Although obviously, I'm not going to *guarantee* it ;^) . + - stole Marek's locking code from the Red Hat unix module. + [ If you like you can #ifdef it in or out ... ] + + You can configure the module more from its Makefile in + 0.52/modules/pam_unix/ + + If you are nervous that it will destroy your /etc/passwd or shadow + files then EDIT the 0.52/modules/pam_unix/pam_unix_pass.-c file. + Here is the warning comment from this file... + +-------------8<----------------- +/* <WARNING> + * + * Uncomment the following #define if you are paranoid, and do not + * want to risk losing your /etc/passwd or shadow files. + * It works for me (AGM) but there are no guarantees. + * + * </WARNING> + */ +/* #define TMP__FILE */ +------------->8----------------- + + *** If anyone has any trouble, please *say*. Your problem will be + fixed in the next release. Also please feel free to scour the + code for race conditions etc... + +[* The above change requires that you purge your /usr/lib/security + directory of the old pam_unix_XXX.so modules: they will NOT be deleted + with a 'make remove'.] + +* the prototype for the cleanup function supplied to pam_set_data used + to return "int". According to Sun it should be "void". CHANGED. + +* added some definitions for the 'error_status' mask values that are + passed to the cleanup function associated with each + module-data-item. These numbers were needed to keep up with changing + a data item (see for example the code in pam_unix/support.-c that + manages the maximum number of retries so far). Will see what Sun says + (current indications are positive); this may be undone before 1.0 is + released. Here are the definitions (from pam_modules.h). + +#define PAM_DATA_SILENT 0x40000000 /* used to suppress messages... */ +#define PAM_DATA_REPLACE 0x20000000 /* used when replacing a data item */ + +* Changed the .../conf/pam.conf file. It now points to the new + pam_unix module for 'su' and 'passwd' [can get these as SimpleApps -- + I use them for testing. A more extensive selection of applications is + available from Red Hat...] + +* corrected a bug in pam_dispatch. Basically, the problem was that if + all the modules were "sufficient" then the return value for this + function was never set. The net effect was that _pam_dispatch_aux + returned success when all the sufficient modules failed. :^( I think + this is the correct fix to a problem that the Red Hat folks had + found... + +sopwith* Removed advisory locking from libpam (thanks for the POSIX patch + goes to Josh Wilmes's, my apologies for not using it in the + end.). Advisory locking did not seem sufficiently secure for libpam. + Thanks to Werner Almesberger for identifying the corresponding "denial + of service attack". :*( + +* related to fix, have introduced a lock file /var/lock/subsys/PAM + that can be used to indicate the system should pay attention to + advisory locking on /etc/pam.conf file. To implement this you need to + define PAM_LOCKING though. (see .52/libpam) + +* modified pam_fail_delay() function. Couldn't find the "not working" + problem indicated by Michael, but modified it to do pseudo-random + delays based on the values indicated by pam_fail_delay() -- the + function "that may eventually go away"... Although Sun is warming to + the idea. + +* new modules include: + + pam_shells - authentication for users with a shell listed in + /etc/shells. Erik Troan <ewt@redhat.com> + + pam_listfile - authentication based on the contents of files. + Set to be more general than the above in the + future. UNTESTED. Elliot Lee <@redhat.com> + [Note, this module compiles with a non-trivial + warning: AGM] + +Thu Aug 8 22:32:15 PDT 1996 (Andrew Morgan <morgan@physics.ucla.edu>) + +* modified makefiles to take more of their installation instructions + from the top level makefile. Desired for integration into the Debian + distribution, and generally a good idea. + +* fixed memory arithmetic in pam_handlers + -- still need to track down why failure to load modules can lead to + authentication succeding.. + +* added tags for new modules (smartcards from Alex -- just a promise + at this stage) and a new module from Elliot Lee; pam_securetty + +* I have not had time to smooth out the wrinkles with it, but Alex's + pam_unix modifications are provided in pam_unix-alex (in the modules + directory) they will not be compiled by 'make all' and I can't even + say if they do compile... I will try to look at them for .52 but, in + the mean time please feel free to study/fix/discuss what is there. + +* pam_rhosts module. Removed code for manually setting the ruser + etc. This was not very secure. + +* [remade .ps docs to be in letter format -- my printer complains + about a4] + +Sunday July, 7 12:45:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>) + +* No longer accompanying the Linux-PAM release with apps installed. + [Will provide what was here in a separate package.. (soon) +lib Also see http://www.redhat.com/pam for some more (in .rpm form...)] + +* renamed libmisc to libpam_misc. It is currently configured to only compile + the static library. For some strange reason (perhaps someone can + investigate) my Linux 2.0.0 kernel with RedHat 3.0.3 system + segfaults when I compile it to be a dynamic library. The segfault + seems to be inside the call to the ** dl_XXX ** function...!? + + There is a simple flag in the libpam_misc/Makefile to turn on dynamic + compiles. + +* Added a little unofficial code for delay support in libpam (will probably + disappear later..) There is some documentation for it in the pam_modules + doc now. That will obviously go too. + +* rewritten pam_time to use *logic* to specify the stringing together of + users/times/terminals etc.. (what was there before was superficially + logical but basically un-predictable!) + +* added pam_group. Its syntax is almost identical to pam_time but it + has another field added; a list of groups to make the user a member + of if they pass the previous tests. It seems to not co-exist too well + with the groups in the /etc/group but I hope to have that fixed by + the next release... + +* minor re-formatting of pam_modules documentation + +* removed ...// since it wasn't being used and didn't look like it + would be! + +GCCSunday 23 22:35:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>) + +* The major change is the addition of a new module: pam_time for + restricting access on terminals at given times for indicated users + it comes with its own configuration file /etc/security/time.conf + and the sample file simply restricts 'you' from satisfying the blank + application if they try to use blank from any tty* + +* Small changes include +- altered pam.conf to demonstrate above new module (try typing username: you) +- very minor changes to the docs (pam_appl and pam_modules) + +Saturday June 2 01:40:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>) + +*** PLEASE READ THE README, it has changed *** + +* NOTE, 'su' exhibits a "system error", when static linking is + used. This is because the pam_unix_... module currently only has + partial static linking support. This is likely to change on Monday + June 3, when Alex makes his latest version availible. I will include + the updated module in next release. + +changes for .42: + +* modified the way in which libpam/pam_modules.h defines prototypes for + the pam_sm_ functions. Now the module must declare which functions it + is to provide *before* the #include <security/pam_modules.h> line. + (for contrasting examples, see the pam_deny and pam_rootok modules) + This removed the ugly hack of defining functions that are never called + to overcome warnings... This seems much tidier. +insterted* updated the TODO list. (changed mailing list address) +* updated README in .../modules to reflect modifications to static + compliation protocol +* modified the pam_modules documentation to describe this. +* corrected last argument of pam_get_item( ... ) in + pam_appl/modules.sgml, to "const void **". +* altered GNU GPL's in the documentation, and various other parts of + the distribution. *Please check* that any code you are responsible for + is corrected. +* Added ./Copyright (please check that it is acceptable) +* updated ./README to make current and indicate the new mailing list + address +* have completely rewritten pam_filter. It now runs modular filter + executables (stored in /usr/sbin/pam_filter/) This should make it + trivial for others to write their own filters.. If you want yours + included in the distribution please email the list/me. +* changes to libpam; there was a silly bug with multiple arguments on a + pam.conf line that was broken with a '\<LF>'. +* 'su' rearranged code (to make better use of PAM) + *Also* now uses POSIX signals--this should help the Alpha port. +* 'passwd' now uses getlogin() to determine who's passwords to change. + +Sunday May 26 9:00:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>) + +* fixed module makefiles to create needed dynamic/static subdirectories + +Saturday May 25 20:30:27.8 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>) + +* LOTS has changed regarding how the modules/libpam are built. +* Michael's mostly complete changes for static support--see below + (Andrew got a little carried away and automated the static linking + of modules---bugs are likely mine ;( ) +* Thanks mostly to Michael, libpam now compiles without a single warning :^] +* made static modules/library optional. +CFLAGS* added 'make sterile' to top level makefile. This does extraclean and remove +* added Michael and Joseph to documentation credits (and a subsection for + future documentation of static module support in pam_modules.sgml) +* libpam; many changes to makefiles and also automated the inclusion of + static module objects in pam_static.c +* modified modules for automated static/dynamic support. Added static & + dynamic subdirectories, as instructed by Michael +* removed an annoying syslog message from pam_filter: "parent exited.." +* updated todo list (anyone know anything about svgalib/X? we probably should + have some support for these...) + +Friday May 24 16:30:15 EDT 1996 (Michael K. Johnson <johnsonm@redhat.com>) + +* Added first (incomplete) cut at static support. + This includes: + . changes in libpam, including a new file, pam_static.c + . changes to modules including exporting struct of function pointers + . static and dynamic linking can be combined + . right now, the only working combinations are just dynamic + linking and dynamic libpam.so with static modules linked + into libpam.so. That's on the list of things to fix... + . modules are built differently depending on whether they + are static or dynamic. Therefore, there are two directories + under each module directory, one for static, and one for + dynamic modules. +* Fixed random brokenness in the Makefiles. [ foo -nt bar ] is + rather redundant in a makefile, for instance. Also, passing + on the command line is broken because it cannot be + overridden in any way (even adding important parts) in lower-level + makefiles. +* Unfortunately, fixing some of the brokenness meant that I used + GNU-specific stuff. However, I *think* that there was GNU-specific + stuff already. And I think that we should just use the GNU + extensions, because any platform that GNU make doesn't port to + easily will be hard to port to anyway. It also won't be likely +passwd to handle autoconf, which was Ted's suggestion for getting + around limitations in standard make... + For now, I suggest that we just use some simple GNU-specific + extensions. + +Monday May 20 22:00:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>) + +* added some text to pam_modules.sgml +* corrected Marek's name in all documentation +* made pam_stress conform to chauthtok conventions -- ie can now request + old password before proceeding. +* included Alex's latest unix module +* included Al's + password strength checking module +* included pam_rootok module +* fixed too many bugs in libpam.. all subtly related to the argument lists + or use of syslog. Added more debugging lines here too. +* fixed the pam.conf file +* deleted pam_test module. It is pretty old and basically superceeded + by pam_stress + +Friday May 9 1:00:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>) + +* updated documentaion, added Al Longyear to credits and corrected the + spelling of Jeff's name(!). Most changes to pam.sgml (even added a figure!) +* new module pam_rhosts_auth (from Al Longyear) +* new apps rlogind and ftpd (a patch) from Al. +* modified 'passwd' to not call pam_authenticate (note, none of the + modules respect this convention yet!) +* fixed bug in libpam that caused trouble if the last line of a + pam.conf file ends with a module name and no newline character +* also made more compatable with documentation, in that bad lines in + pam.conf are now ignored rather than causing libpam to return an + error to the app. +* libpam now overwrites the AUTHTOKs when returning from + pam_authenticate and pam_chauthtok calls (as per Sun/RFC too) +* libpam is now installed as libpam.so.XXX in a way that ldconfig can + handle! + + +Wednesday May 1 22:00:00 PST 1996 (Andrew Morgan <morgan@physics.ucla.edu>) + +* removed .../test directory, use .../examples from now on. +* added .../apps directory for fully functional applications + - the apps directory contains directories that actually contain the apps. + the idea is to make application compilation conditional on the presence + of the directory. Note, there are entries in the Makefile for + 'login' and 'ftpd' that are ready for installation... Email me if + you want to reserve a directory name for an application you are + working on... +* similar changes to .../modules makefile [entries for pam_skey and + pam_kerberos created---awaiting the directories.] Email me if you + want to register another module... +* minor changes to docs.. Not really worth reprinting them quite yet! + [save the trees] +* added misc_conv to libmisc. it is a generic conversation function + for text based applications. [would be nice to see someone create + an Xlib and/or svgalib version] +* fixed ctrl-z/c bug with pam_filter module [try xsh with the default + pam.conf file] +* added 'required' argument to 'pam_stress' module. +* added a TODO list... other suggestions to the list please. + +Saturday April 7 00:00:00 PST 1996 ( Andrew Morgan <morgan@physics.ucla.edu> ) + +* Alex and Marek please note I have altered _pam_auth_unix a little, to + make it get the passwords with the "proper method" (and also fixed it + to not have as many compiler warnings) +* updated the conf/pam.conf file +* added new example application examples/xsh.c (like blank but invokes + /bin/sh) +* Marc's patches for examples/blank.c (and AGM's too) +* fixed stacking of modules in libpam/pam_handlers.c +* fixed RESETing in libpam/pam_item.c +* added new module modules/pam_filter/ to demonstrate the possibility + of inserting an arbitrary filter between the terminal and the + application that could do customized logging etc... (see use of + bin/xsh as defined in conf/pam.conf) + + +Saturday March 16 19:00:00 PST 1996 ( Andrew Morgan <morgan@physics.ucla.edu> ) + +These notes are for 0.3 I don't think I've left anything important +out, but I will use emacs 'C-x v a' next time! (Thanks Jeff) + + * not much has changed with the functionality of the Linux-PAM lib + .../libpam + - pam_password calls module twice with different arguments + - added const to some of the function arguments + - added PAM_MAX_MES_ to <security/_pam_types.h> + - was a lot over zealous about purging old passwords... + I have removed much of this from source to make it + more compatible with SUN. + - moved some PAM_... tokens to pam_modules.h from _pam_types.h + (no-one should notice) + + * added three modules: pam_permit pam_deny pam_stress + no prizes for guessing what the first two do. The third is + a reasonably complete (functional) module. Is intended for testing + applications with. + + * fixed a few pieces of examples/blank.c so that it works (with + pam_stress) + + * ammended the documentation. Looking better, but suggestions/comments + very welcome! + +Sunday March 10 10:50:00 PST 1996 ( Andrew Morgan <morgan@physics.ucla.edu> ) + +These notes are for Linux-PAM release 0.21. They cover what's changed +since I relased 0.2. + + * am now using RCS + * substantially changed ./README + * fixed bug reading \\\n in pam.conf file + * small changes to documentation + * added `blank' application to ./examples (could be viewed as + a `Linux-PAM aware' application template.) + * oops. now including pam_passwd.o and pam_session.o in pamlib.so + * compute md5 checksums for all the source when making a release + - added `make check' and `make RCScheck' to compute md5 checksums + * create a second tar file with all the RCS files in. + * removed the .html and .txt docs, supplying sgml sources instead. + - see README for info on where to get .ps files + +Thursday March 6 0:44:?? PST 1996 ( Andrew Morgan <morgan@physics.ucla.edu> ) + +These notes are for Linux-PAM release 0.2. They cover what's changed +since Marc Ewing relased 0.1. + +**** Please note. All of the directories in this release have been modified +**** slightly to conform to the new pamlib. A couple of new directories have +**** been added. As well as some documentation. If some of your code +**** was in the previous release. Feel free to update it, but please +**** try to conform to the new headers and Makefiles. + +* Andrew Morgan (morgan@physics.ucla.edu) is making this release + availible, Marc has been busy...! + +* Marc's pam-0.1/lib has been (quietly) enhanced and integrated into + Alex Yurie's collected tree of library and module code + (linux-pam.prop.1.tar.gz). Most of the changes are to do with error + checking. Some more robustness in the reading of the pam.conf file + and the addition of the pam_get_user() function. + +* The pam_*.h files have been reorganized to logically enforce the + separation of modules from applications. [Don't panic! Apart from + changing references of the form + + #include "pam_appl.h" + + to + + #include <security/pam_appl.h> + + The reorganization should be backwardly compatable (ie. a module + written for SUN will be as compatable as it was before with the + previous version ;)~ ] + + (All of the source in this tree now conforms to this scheme...) + + The new reorganization means that modules can be compiled with a + single header, <security/pam_modules.h>, and applications with + <security/pam_appl.h>. + +* I have tried to remove all the compiler warnings from the updated + "pamlib/*.c" files. On my system, (with a slightly modified <dlfcn.h> + email me if it interests you..) there are only two warnings that + remain: they are that ansi does not permit void --> fn ptr + assignment. K&Rv2 doesn't mention this....? As a matter of principle, + if anyone knows how to get rid of that warning... please + tell. Thanks! "-pedantic" + +* you can "make all" as a plain user, but + +* to "make install" you must be root. The include files are placed in + /usr/include/security. The libpam.so library is installed in /usr/lib + and the modules in /usr/lib/security. The two test binaries + are installed in the Linux-PAM-0.2/bin directory and a chance is given to + replace your /etc/pam.conf file with the one in Linux-PAM-0.2/conf. + +* I have included some documentation (pretty preliminary at the +moment) which I have been working on in .../doc . + +I have had a little trouble with the modules, but atleast there are no +segfaults! Please try it out and discuss your results... I actually +hope it all works for you. But, Email any bugs/suggestions to the +Linux-PAM list: linux-pam@mit.edu ..... + +Regards, + +Andrew Morgan +(morgan@physics.ucla.edu) + + +Sat Feb 17 17:30:24 EST 1996 (Alexander O. Yuriev alex@bach.cis.temple.edu) + + * conf directory created with example of pam_conf + * stable code from pam_unix is added to modules/pam_unix + * test/test.c now requests username and password and attempts + to perform authentication + diff --git a/Linux-PAM/Copyright b/Linux-PAM/Copyright new file mode 100644 index 00000000..2f27a2ee --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/Make.Rules.in b/Linux-PAM/Make.Rules.in new file mode 100644 index 00000000..86a80524 --- /dev/null +++ b/Linux-PAM/Make.Rules.in @@ -0,0 +1,124 @@ +## +## $Id: Make.Rules.in,v 1.18 2005/03/29 20:41:19 toady Exp $ +## +## @configure_input@ +## + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +includedir = @includedir@ +mandir = @mandir@ + +absolute_srcdir = @LOCALSRCDIR@ +absolute_objdir = @LOCALOBJDIR@ + +# major and minor numbers of this release +MAJOR_REL=@LIBPAM_VERSION_MAJOR@ +MINOR_REL=@LIBPAM_VERSION_MINOR@ + +# The following is the generic set of compiler options for compiling +# Linux-PAM. True, they are a little anal. Pay attention to the comments +# they generate. + +HEADER_DIRS=-I./include -I$(absolute_srcdir)/libpam/include \ + -I$(absolute_objdir) -I$(absolute_srcdir)/libpamc/include +WARNINGS=@WARNINGS@ +OS_CFLAGS=@OS_CFLAGS@ +PIC=@PIC@ + +# Mode to install shared libraries with +SHLIBMODE=@SHLIBMODE@ +# Mode to install man pages with +MANMODE=@MANMODE@ + +NEED_LINK_LIB_C=@PAM_NEEDS_LIBC@ +HAVE_LCKPWDF=@HAVE_LCKPWDF@ +HAVE_LIBCRACK=@HAVE_LIBCRACK@ +HAVE_LIBCAP=@HAVE_LIBCAP@ +HAVE_LIBCRYPT=@HAVE_LIBCRYPT@ +HAVE_LIBUTIL=@HAVE_LIBUTIL@ +HAVE_NDBM_H=@HAVE_NDBM_H@ +HAVE_LIBNDBM=@HAVE_LIBNDBM@ +HAVE_LIBDB=@HAVE_LIBDB@ +HAVE_LIBFL=@HAVE_LIBFL@ +HAVE_LIBNSL=@HAVE_LIBNSL@ +HAVE_LIBPWDB=@HAVE_LIBPWDB@ + +ifeq (@HAVE_LIBFLEX@,yes) +LINK_LIBLEX=-lfl +else +ifeq (@HAVE_LIBLEX@,yes) +LINK_LIBLEX=-ll +endif +endif + +# documentation support +HAVE_SGML2TXT=@HAVE_SGML2TXT@ +HAVE_SGML2HTML=@HAVE_SGML2HTML@ +HAVE_PS2PDF=@HAVE_PS2PDF@ +PSER=@PSER@ +DOCDIR=@DOCDIR@ +MANDIR=@MANDIR@ + +# configuration settings +WITH_DEBUG=@WITH_DEBUG@ +WITH_MEMORY_DEBUG=@WITH_MEMORY_DEBUG@ +WITH_LIBDEBUG=@WITH_LIBDEBUG@ +WITH_PRELUDE=@WITH_PRELUDE@ +WITH_PAMLOCKING=@WITH_PAMLOCKING@ +WITH_LCKPWDF=@WITH_LCKPWDF@ +STATIC_LIBPAM=@STATIC_LIBPAM@ +DYNAMIC_LIBPAM=@DYNAMIC_LIBPAM@ +STATIC=@STATIC@ +DYNAMIC=@DYNAMIC@ + +# Location of libraries when installed on the system +FAKEROOT=@FAKEROOT@ +SECUREDIR=@SECUREDIR@ +SCONFIGD=@SCONFIGDIR@ +SUPLEMENTED=@SUPLEMENTED@ +INCLUDED=@INCLUDEDIR@/security +CRACKLIB_DICTPATH=@CRACKLIB_DICTPATH@ + +# generic build setup +OS=@OS@ +CC=@CC@ +CFLAGS=$(WARNINGS) -D$(OS) @CFLAGS@ @CPPFLAGS@ $(OS_CFLAGS) $(HEADER_DIRS) @CONF_CFLAGS@ +LDFLAGS=@LDFLAGS@ +LD=@LD@ +LD_D=@LD_D@ +LD_L=@LD_L@ +MV=@MV@ +LDCONFIG=@LDCONFIG@ +DYNTYPE=@DYNTYPE@ +USESONAME=@USESONAME@ +NEEDSONAME=@NEEDSONAME@ +SOSWITCH=@SOSWITCH@ +LIBDL=@LIBDL@ +MKDIR=@MKDIR@ +INSTALL=@INSTALL@ +RANLIB=@RANLIB@ +STRIP=@STRIP@ +CC_STATIC=@CC_STATIC@ + +LINKLIBS = $(NEED_LINK_LIB_C) $(LIBDL) + +USESONAME=@USESONAME@ +SOSWITCH=@SOSWITCH@ + +ifdef DYNAMIC +CFLAGS += $(PIC) +endif diff --git a/Linux-PAM/Makefile b/Linux-PAM/Makefile new file mode 100644 index 00000000..398ae2df --- /dev/null +++ b/Linux-PAM/Makefile @@ -0,0 +1,76 @@ +## +## $Id: Makefile,v 1.12 2004/10/14 14:47:53 kukuk Exp $ +## + +## Note, ideally I would prefer it if this top level makefile did +## not get created by autoconf. As I find typing 'make' and relying +## on it to take care of all dependencies much more friendly than +## the multi-stage autoconf+make and also worry about updates to +## configure.in not getting propagated down the tree. (AGM) [I realise +## that this may not prove possible, but at least I tried.. Sigh.] + +DISTNAME=Linux-PAM + +-include Make.Rules + +THINGSTOMAKE = libpam libpamc libpam_misc modules doc examples + +all: $(THINGSTOMAKE) + + # Let's get a dynamic libpam.so first + bootstrap-libpam: _pam_aconf.h prep + $(MAKE) -C libpam bootstrap-libpam + +prep: + rm -f security + ln -sf . security + +clean: + if [ ! -f Make.Rules ]; then touch Make.Rules ; fi + for i in $(THINGSTOMAKE) ; do $(MAKE) -C $$i clean ; done + rm -f security *~ *.orig *.rej #*# + +distclean: clean + rm -f Make.Rules _pam_aconf.h + rm -f config.status config.cache config.log core + rm -rf autom4te.cache/ + +maintainer-clean: distclean + @echo files should be ok for packaging now. + +# NB _pam_aconf.h.in changes will remake this too +Make.Rules: configure Make.Rules.in _pam_aconf.h.in + ./config.status --recheck + ./config.status + +_pam_aconf.h: Make.Rules + +configure: configure.in + @echo + @echo You do not appear to have an up-to-date ./configure file. + @echo Please run autoconf, and then ./configure [..options..] + @echo + @rm -f configure + @exit 1 + +$(THINGSTOMAKE): _pam_aconf.h prep bootstrap-libpam + $(MAKE) -C $@ all + +install: _pam_aconf.h prep + for x in $(THINGSTOMAKE) ; do $(MAKE) -C $$x install ; done + +remove: + rm -f $(FAKEROOT)$(INCLUDED)/_pam_aconf.h + for x in $(THINGSTOMAKE) ; do $(MAKE) -C $$x remove ; done + +release: + @if [ ! -f Make.Rules ]; then echo $(MAKE) Make.Rules first ;exit 1 ;fi + @if [ ! -L ../$(DISTNAME)-$(MAJOR_REL).$(MINOR_REL) ]; then \ + echo generating ../$(DISTNAME)-$(MAJOR_REL).$(MINOR_REL) link ; \ + ln -sf $(DISTNAME) ../$(DISTNAME)-$(MAJOR_REL).$(MINOR_REL) ; \ + echo to ../$(DISTNAME) . ; fi + @diff ../$(DISTNAME)-$(MAJOR_REL).$(MINOR_REL)/Make.Rules Make.Rules + $(MAKE) distclean + cd .. ; tar zvfc $(DISTNAME)-$(MAJOR_REL).$(MINOR_REL).tar.gz \ + --exclude CVS --exclude .cvsignore --exclude '.#*' \ + $(DISTNAME)-$(MAJOR_REL).$(MINOR_REL)/* diff --git a/Linux-PAM/README b/Linux-PAM/README new file mode 100644 index 00000000..1e769a5d --- /dev/null +++ b/Linux-PAM/README @@ -0,0 +1,28 @@ +# +# $Id: README,v 1.3 2000/11/20 00:01:49 agmorgan Exp $ +# + +Hello! + +Thanks for downloading Linux-PAM. + +NOTES: + +How to use it is as follows: + + ./configure --help | less + ./configure <your-options> + make + +Note, if you are worried - don't even think about doing the next line +(most Linux distributions already support PAM out of the box, so if +something goes wrong with installing the code from this version your +box may stop working..) + + make install + +That said, please report problems to me. + +Andrew Morgan +<morgan@kernel.org> +<agmorgan@users.sourceforge.net> diff --git a/Linux-PAM/_pam_aconf.h.in b/Linux-PAM/_pam_aconf.h.in new file mode 100644 index 00000000..17adbb34 --- /dev/null +++ b/Linux-PAM/_pam_aconf.h.in @@ -0,0 +1,104 @@ +/* + * $Id: _pam_aconf.h.in,v 1.11 2004/10/06 13:42:36 kukuk Exp $ + * + * + */ + +#ifndef PAM_ACONF_H +#define PAM_ACONF_H + +/* lots of stuff gets written to /tmp/pam-debug.log */ +#undef DEBUG + +/* build libraries with different names (suffixed with 'd') */ +#undef WITH_LIBDEBUG + +/* provide a global locking facility within libpam */ +#undef PAM_LOCKING + +/* GNU systems as a class, all have the feature.h file */ +#undef HAVE_FEATURES_H +#ifdef HAVE_FEATURES_H +# define _SVID_SOURCE +# define _BSD_SOURCE +# define _GNU_SOURCE +# include <features.h> +#endif /* HAVE_FEATURES_H */ + +/* we have libcrack available */ +#undef HAVE_LIBCRACK + +/* we have libcrypt - its not part of libc (do we need both definitions?) */ +#undef HAVE_LIBCRYPT +#undef HAVE_CRYPT_H + +/* we have libndbm and/or libdb */ +#undef HAVE_DB_H +#undef HAVE_NDBM_H + +/* have libfl (Flex) */ +#undef HAVE_LIBFL + +/* have libnsl - instead of libc support */ +#undef HAVE_LIBNSL + +/* have libpwdb - don't expect this to be important for much longer */ +#undef HAVE_LIBPWDB + +/* have gethostname() declared */ +#undef HAVE_GETHOSTNAME + +#undef HAVE_GETTIMEOFDAY +#undef HAVE_MKDIR +#undef HAVE_SELECT +#undef HAVE_STRCSPN +#undef HAVE_STRDUP +#undef HAVE_STRERROR +#undef HAVE_STRSPN +#undef HAVE_STRSTR +#undef HAVE_STRTOL +#undef HAVE_UNAME + +/* Define if reentrant declarations of standard nss functions are available */ +#undef HAVE_GETPWNAM_R +#undef HAVE_GETPWUID_R +#undef HAVE_GETSPNAM_R +#undef HAVE_GETGRNAM_R +#undef HAVE_GETGRGID_R +#undef HAVE_GETGROUPLIST + +/* ugly hack to partially support old pam_strerror syntax */ +#undef UGLY_HACK_FOR_PRIOR_BEHAVIOR_SUPPORT + +/* read both confs - read /etc/pam.d and /etc/pam.conf in serial */ +#undef PAM_READ_BOTH_CONFS + +#undef HAVE_PATHS_H +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif +/* location of the mail spool directory */ +#undef PAM_PATH_MAILDIR + +/* where should we include setfsuid's prototype from? If this is not + defined, we get it from unistd.h */ +#undef HAVE_SYS_FSUID_H + +/* track all memory allocations and liberations */ +#undef MEMORY_DEBUG +#ifdef MEMORY_DEBUG +/* + * this is basically a hack - we need to include a semiarbitrary + * number of headers to ensure that we don't get silly prototype/macro + * confusion. + */ +# include <string.h> +# include <stdlib.h> +# include <security/pam_malloc.h> +#endif /* MEMORY_DEBUG */ + +/* the path, relative to SECUREDIR, where PAMs specific to this architecture + * can be found */ +#undef _PAM_ISA + +#endif /* PAM_ACONF_H */ diff --git a/Linux-PAM/bin/README b/Linux-PAM/bin/README new file mode 100644 index 00000000..2d2fba52 --- /dev/null +++ b/Linux-PAM/bin/README @@ -0,0 +1,30 @@ +## +# $Id: README,v 1.2 2000/12/04 19:02:33 baggins Exp $ +## + +(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/Linux-PAM/conf/Makefile b/Linux-PAM/conf/Makefile new file mode 100644 index 00000000..67523c59 --- /dev/null +++ b/Linux-PAM/conf/Makefile @@ -0,0 +1,34 @@ +# +# $Id: Makefile,v 1.1.1.1 2000/06/20 22:10:44 agmorgan Exp $ +# +# + +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/Linux-PAM/conf/install b/Linux-PAM/conf/install new file mode 100755 index 00000000..2eae3671 --- /dev/null +++ b/Linux-PAM/conf/install @@ -0,0 +1,178 @@ +#!/bin/sh +# +# [This file was lifted from an X distribution. There was no explicit +# copyright in the file, but the following text was associated with it. +# should anyone from the X Consortium wish to alter the following +# text. Please email <morgan@parc.power.net> Thanks. ] +# +# -------------------------- +# The X Consortium maintains and distributes the X Window System and +# related software and documentation in coordinated releases. A release +# consists of two distinct parts: +# +# 1) Specifications and Sample implementations of X Consortium +# standards, and +# +# 2) software and documentation contributed by the general X Consortium +# community. +# +# The timing and contents of a release are determined by the Consortium +# staff based on the needs and desires of the Members and the advice of +# the Advisory Board, tempered by the resource constraints of the +# Consortium. +# +# Members have access to all X Consortium produced software and +# documentation prior to release to the public. Each Member can receive +# pre-releases and public releases at no charge. In addition, Members +# have access to software and documentation while it is under +# development, and can periodically request snapshots of the development +# system at no charge. +# +# The X Consortium also maintains an electronic mail system for +# reporting problems with X Consortium produced software and +# documentation. Members have access to all bug reports, as well as all +# software patches as they are incrementally developed by the Consortium +# staff between releases. +# +# In general, all materials included in X Consortium releases are +# copyrighted and contain permission notices granting unrestricted use, +# sales and redistribution rights provided that the copyrights and the +# permission notices are left intact. All materials are provided "as +# is," without express or implied warranty. +# -------------------------- +# +# This accepts bsd-style install arguments and makes the appropriate calls +# to the System V install. +# + +flags="" +dst="" +src="" +dostrip="" +owner="" +mode="" + +while [ x$1 != x ]; do + case $1 in + -c) shift + continue;; + + -m) flags="$flags $1 $2 " + mode="$2" + shift + shift + continue;; + + -o) flags="$flags -u $2 " + owner="$2" + shift + shift + continue;; + + -g) flags="$flags $1 $2 " + shift + shift + continue;; + + -s) dostrip="strip" + shift + continue;; + + *) if [ x$src = x ] + then + src=$1 + else + dst=$1 + fi + shift + continue;; + esac +done + +case "$mode" in +"") + ;; +*) + case "$owner" in + "") + flags="$flags -u root" + ;; + esac + ;; +esac + +if [ x$src = x ] +then + echo "$0: no input file specified" + exit 1 +fi + +if [ x$dst = x ] +then + echo "$0: no destination specified" + exit 1 +fi + + +# set up some variable to be used later + +rmcmd="" +srcdir="." + +# if the destination isn't a directory we'll need to copy it first + +if [ ! -d $dst ] +then + dstbase=`basename $dst` + cp $src /tmp/$dstbase + rmcmd="rm -f /tmp/$dstbase" + src=$dstbase + srcdir=/tmp + dst="`echo $dst | sed 's,^\(.*\)/.*$,\1,'`" + if [ x$dst = x ] + then + dst="." + fi +fi + + +# If the src file has a directory, copy it to /tmp to make install happy + +srcbase=`basename $src` + +if [ "$src" != "$srcbase" -a "$src" != "./$srcbase" ] +then + cp $src /tmp/$srcbase + src=$srcbase + srcdir=/tmp + rmcmd="rm -f /tmp/$srcbase" +fi + +# do the actual install + +if [ -f /usr/sbin/install ] +then + installcmd=/usr/sbin/install +elif [ -f /etc/install ] +then + installcmd=/etc/install +else + installcmd=install +fi + +# This rm is commented out because some people want to be able to +# install through symbolic links. Uncomment it if it offends you. +rm -f $dst/$srcbase +(cd $srcdir ; $installcmd -f $dst $flags $src) + +if [ x$dostrip = xstrip ] +then + strip $dst/$srcbase +fi + +# and clean up + +$rmcmd + +exit + diff --git a/Linux-PAM/conf/install_conf b/Linux-PAM/conf/install_conf new file mode 100755 index 00000000..7a2acd98 --- /dev/null +++ b/Linux-PAM/conf/install_conf @@ -0,0 +1,36 @@ +#!/bin/sh + +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/Linux-PAM/conf/md5itall b/Linux-PAM/conf/md5itall new file mode 100755 index 00000000..0f2656fe --- /dev/null +++ b/Linux-PAM/conf/md5itall @@ -0,0 +1,43 @@ +#!/bin/bash +# +# $Id: md5itall,v 1.2 2000/12/04 19:02:33 baggins Exp $ +# +# 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/Linux-PAM/conf/mkdirp b/Linux-PAM/conf/mkdirp new file mode 100755 index 00000000..b0e04b05 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/conf/pam.conf b/Linux-PAM/conf/pam.conf new file mode 100644 index 00000000..8e78e547 --- /dev/null +++ b/Linux-PAM/conf/pam.conf @@ -0,0 +1,126 @@ +# ---------------------------------------------------------------------------# +# /etc/pam.conf # +# # +# Last modified by Andrew G. Morgan <morgan@kernel.org> # +# ---------------------------------------------------------------------------# +# $Id: pam.conf,v 1.2 2001/04/08 06:02:33 agmorgan Exp $ +# ---------------------------------------------------------------------------# +# serv. module ctrl module [path] ...[args..] # +# name type flag # +# ---------------------------------------------------------------------------# +# +# The PAM configuration file for the `chfn' service +# +chfn auth required pam_unix.so +chfn account required pam_unix.so +chfn password required pam_cracklib.so retry=3 +chfn password required pam_unix.so shadow md5 use_authtok +# +# The PAM configuration file for the `chsh' service +# +chsh auth required pam_unix.so +chsh account required pam_unix.so +chsh password required pam_cracklib.so retry=3 +chsh password required pam_unix.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_unix.so +ftp account required pam_unix.so +# +# The PAM configuration file for the `imap' service +# +imap auth required pam_unix.so +imap account required pam_unix.so +# +# The PAM configuration file for the `login' service +# +login auth requisite pam_securetty.so +login auth required pam_unix.so +login auth optional pam_group.so +login account requisite pam_time.so +login account required pam_unix.so +login password required pam_cracklib.so retry=3 +login password required pam_unix.so shadow md5 use_authtok +login session required pam_unix.so +# +# The PAM configuration file for the `netatalk' service +# +netatalk auth required pam_unix.so +netatalk account required pam_unix.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_unix.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_unix.so +rexec account required pam_unix.so +rexec session required pam_unix.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_unix.so +rlogin password required pam_cracklib.so retry=3 +rlogin password required pam_unix.so shadow md5 use_authtok +rlogin session required pam_unix.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_unix.so +rsh account required pam_unix.so +rsh session required pam_unix.so +rsh session required pam_limits.so +# +# The PAM configuration file for the `samba' service +# +samba auth required pam_unix.so +samba account required pam_unix.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_unix.so +su account required pam_unix.so +su session required pam_unix.so +# +# The PAM configuration file for the `vlock' service +# +vlock auth required pam_unix.so +# +# The PAM configuration file for the `xdm' service +# +xdm auth required pam_unix.so +xdm account required pam_unix.so +# +# The PAM configuration file for the `xlock' service +# +xlock auth required pam_unix.so + diff --git a/Linux-PAM/conf/pam_conv1/Makefile b/Linux-PAM/conf/pam_conv1/Makefile new file mode 100644 index 00000000..daf2e6cf --- /dev/null +++ b/Linux-PAM/conf/pam_conv1/Makefile @@ -0,0 +1,46 @@ +# +# $Id: Makefile,v 1.3 2004/09/23 15:52:56 kukuk Exp $ +# + +include ../../Make.Rules + +# +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 $(CFLAGS) pam_conv.tab.c $(LINK_LIBLEX) + +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/Linux-PAM/conf/pam_conv1/README b/Linux-PAM/conf/pam_conv1/README new file mode 100644 index 00000000..7a09df38 --- /dev/null +++ b/Linux-PAM/conf/pam_conv1/README @@ -0,0 +1,10 @@ +$Id: README,v 1.1.1.1 2000/06/20 22:10:45 agmorgan Exp $ + +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/Linux-PAM/conf/pam_conv1/pam_conv.lex b/Linux-PAM/conf/pam_conv1/pam_conv.lex new file mode 100644 index 00000000..ef8cb41a --- /dev/null +++ b/Linux-PAM/conf/pam_conv1/pam_conv.lex @@ -0,0 +1,42 @@ + +%{ +/* + * $Id: pam_conv.lex,v 1.1.1.1 2000/06/20 22:10:45 agmorgan Exp $ + * + * Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net> + * + * This file is covered by the Linux-PAM License (which should be + * distributed with this file.) + */ + + const static char lexid[]= + "$Id: pam_conv.lex,v 1.1.1.1 2000/06/20 22:10:45 agmorgan Exp $\n" + "Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net>\n"; + + extern int current_line; +%} + +%% + +"#"[^\n]* ; /* skip comments (sorry) */ + +"\\\n" { + ++current_line; +} + +([^\n\t ]|[\\][^\n])+ { + return TOK; +} + +[ \t]+ ; /* Ignore */ + +<<EOF>> { + return EOFILE; +} + +[\n] { + ++current_line; + return NL; +} + +%% diff --git a/Linux-PAM/conf/pam_conv1/pam_conv.y b/Linux-PAM/conf/pam_conv1/pam_conv.y new file mode 100644 index 00000000..ef712e78 --- /dev/null +++ b/Linux-PAM/conf/pam_conv1/pam_conv.y @@ -0,0 +1,204 @@ +%{ + +/* + * $Id: pam_conv.y,v 1.2 2002/05/29 06:35:46 agmorgan Exp $ + * + * Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net> + * + * This file is covered by the Linux-PAM License (which should be + * distributed with this file.) + */ + + const static char bisonid[]= + "$Id: pam_conv.y,v 1.2 2002/05/29 06:35:46 agmorgan Exp $\n" + "Copyright (c) Andrew G. Morgan 1997-8 <morgan@linux.kernel.org>\n"; + +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + + int current_line=1; + extern char *yytext; + +/* XXX - later we'll change this to be the specific conf file(s) */ +#define newpamf stderr + +#define PAM_D "./pam.d" +#define PAM_D_MODE 0755 +#define PAM_D_MAGIC_HEADER \ + "#%PAM-1.0\n" \ + "#[For version 1.0 syntax, the above header is optional]\n" + +#define PAM_D_FILE_FMT PAM_D "/%s" + + const char *old_to_new_ctrl_flag(const char *old); + void yyerror(const char *format, ...); +%} + +%union { + int def; + char *string; +} + +%token NL EOFILE TOK + +%type <string> tok path tokenls + +%start complete + +%% + +complete +: +| complete NL +| complete line +| complete EOFILE { + return 0; +} +; + +line +: tok tok tok path tokenls NL { + char *filename; + FILE *conf; + int i; + + /* make sure we have lower case */ + for (i=0; $1[i]; ++i) { + $1[i] = tolower($1[i]); + } + + /* $1 = service-name */ + yyerror("Appending to " PAM_D "/%s", $1); + + filename = malloc(strlen($1) + sizeof(PAM_D) + 6); + sprintf(filename, PAM_D_FILE_FMT, $1); + conf = fopen(filename, "r"); + if (conf == NULL) { + /* new file */ + conf = fopen(filename, "w"); + if (conf != NULL) { + fprintf(conf, PAM_D_MAGIC_HEADER); + fprintf(conf, + "#\n" + "# The PAM configuration file for the `%s' service\n" + "#\n", $1); + } + } else { + fclose(conf); + conf = fopen(filename, "a"); + } + if (conf == NULL) { + yyerror("trouble opening %s - aborting", filename); + exit(1); + } + free(filename); + + /* $2 = module-type */ + fprintf(conf, "%-10s", $2); + free($2); + + /* $3 = required etc. */ + { + const char *trans; + + trans = old_to_new_ctrl_flag($3); + free($3); + fprintf(conf, " %-10s", trans); + } + + /* $4 = module-path */ + fprintf(conf, " %s", $4); + free($4); + + /* $5 = arguments */ + if ($5 != NULL) { + fprintf(conf, " \\\n\t\t%s", $5); + free($5); + } + + /* end line */ + fprintf(conf, "\n"); + + fclose(conf); +} +| error NL { + yyerror("malformed line"); +} +; + +tokenls +: { + $$=NULL; +} +| tokenls tok { + int len; + + if ($1) { + len = strlen($1) + strlen($2) + 2; + $$ = malloc(len); + sprintf($$,"%s %s",$1,$2); + free($1); + free($2); + } else { + $$ = $2; + } +} +; + +path +: TOK { + /* XXX - this could be used to check if file present */ + $$ = strdup(yytext); +} + +tok +: TOK { + $$ = strdup(yytext); +} + +%% + +#include "lex.yy.c" + +const char *old_to_new_ctrl_flag(const char *old) +{ + static const char *clist[] = { + "requisite", + "required", + "sufficient", + "optional", + NULL, + }; + int i; + + for (i=0; clist[i]; ++i) { + if (strcasecmp(clist[i], old) == 0) { + break; + } + } + + return clist[i]; +} + +void yyerror(const char *format, ...) +{ + va_list args; + + fprintf(stderr, "line %d: ", current_line); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} + +int main(int argc, char *argv[]) +{ + if (mkdir(PAM_D, PAM_D_MODE) != 0) { + yyerror(PAM_D " already exists.. aborting"); + exit(1); + } + yyparse(); + exit(0); +} diff --git a/Linux-PAM/configure b/Linux-PAM/configure new file mode 100755 index 00000000..1d2c8591 --- /dev/null +++ b/Linux-PAM/configure @@ -0,0 +1,8860 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59. +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="conf/pam_conv1/pam_conv.y" +ac_default_prefix= +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS LIBPAM_VERSION_MAJOR LIBPAM_VERSION_MINOR LOCALSRCDIR LOCALOBJDIR OS CONF_CFLAGS MKDIR SHLIBMODE MANMODE USESONAME SOSWITCH NEEDSONAME LDCONFIG CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT YACC LEX LEXLIB LEX_OUTPUT_ROOT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S SET_MAKE WITH_DEBUG WITH_MEMORY_DEBUG WITH_LIBDEBUG WITH_PRELUDE FAKEROOT SECUREDIR SCONFIGDIR SUPLEMENTED INCLUDEDIR DOCDIR MANDIR WITH_PAMLOCKING PAM_READ_BOTH_CONFS STATIC_LIBPAM DYNAMIC_LIBPAM DYNAMIC STATIC WITH_LCKPWDF CPP EGREP PAM_NEEDS_LIBC HAVE_LCKPWDF LIBDL HAVE_LIBCRACK HAVE_LIBCRYPT HAVE_LIBUTIL HAVE_LIBNDBM HAVE_LIBDB HAVE_LIBFL HAVE_LIBNSL HAVE_LIBPWDB HAVE_LIBFLEX HAVE_LIBLEX HAVE_NDBM_H CRACKLIB_DICTPATH DYNTYPE OS_CFLAGS WARNINGS PIC LD LD_D LD_L RANLIB STRIP CC_STATIC LIBOBJS HAVE_SGML2TXT HAVE_SGML2HTML HAVE_SGML2LATEX HAVE_PS2PDF HAVE_SGML2PS PSER PS2PDF LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-debug specify you are building with debugging on + --enable-memory-debug specify you want every malloc etc. call tracked + --enable-libdebug specify you are building debugging libraries + --enable-prelude build prelude ids support + --enable-fakeroot=<path to packaging directory> + --enable-securedir=<path to location of PAMs> default \$libdir/security + --enable-isadir=<path to arch-specific module files> default ../../\`basename \$libdir\`/security + --enable-sconfigdir=<path to module conf files> default \$sysconfdir/security + --enable-suplementedir=<path to module helper binaries> default \$sbindir + --enable-includedir=<path to include location> - where to put <security> + --enable-docdir=<path to store documentation in - /usr/share/doc/pam> + --enable-mandir=<path to store manuals in - /usr/share/man> + --enable-pamlocking configure libpam to observe a global authentication lock + --enable-uglyhack configure libpam to try to honor old pam_strerror syntax + --enable-read-both-confs read both /etc/pam.d and /etc/pam.conf files + --enable-static-libpam build a libpam.a library + --disable-dynamic-libpam do not build a shared libpam library + --enable-static-modules do not make the modules dynamically loadable + --disable-lckpwdf do not use the lckpwdf function + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-mailspool path to mail spool directory + default _PATH_MAILDIR if defined in paths.h, otherwise /var/spool/mail + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have + headers in a nonstandard directory <include dir> + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + ac_config_headers="$ac_config_headers _pam_aconf.h" + + + +LIBPAM_VERSION_MAJOR=0 +LIBPAM_VERSION_MINOR=79 + + + +cat >>confdefs.h <<\_ACEOF +#define LIBPAM_VERSION_MAJOR 1 +_ACEOF + +cat >>confdefs.h <<\_ACEOF +#define LIBPAM_VERSION_MINOR 1 +_ACEOF + + + + + +LOCALSRCDIR=`/bin/pwd` ; +LOCALOBJDIR=`/bin/pwd` ; +OS=`uname|sed -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` + + + +CONF_CFLAGS= ; +MKDIR="mkdir -p" ; + +SHLIBMODE=755 ; +MANMODE=644 ; + +USESONAME=yes ; +SOSWITCH="-Wl,-soname -Wl," ; +NEEDSONAME=yes ; +LDCONFIG=/sbin/ldconfig ; + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 + (eval $ac_compiler --version </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5 + (eval $ac_compiler -v </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5 + (eval $ac_compiler -V </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include <stdlib.h> +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +for ac_prog in 'bison -y' byacc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_YACC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$YACC"; then + ac_cv_prog_YACC="$YACC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_YACC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +YACC=$ac_cv_prog_YACC +if test -n "$YACC"; then + echo "$as_me:$LINENO: result: $YACC" >&5 +echo "${ECHO_T}$YACC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$YACC" && break +done +test -n "$YACC" || YACC="yacc" + + +for ac_prog in flex lex +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_LEX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$LEX"; then + ac_cv_prog_LEX="$LEX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LEX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +LEX=$ac_cv_prog_LEX +if test -n "$LEX"; then + echo "$as_me:$LINENO: result: $LEX" >&5 +echo "${ECHO_T}$LEX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$LEX" && break +done +test -n "$LEX" || LEX=":" + +if test -z "$LEXLIB" +then + echo "$as_me:$LINENO: checking for yywrap in -lfl" >&5 +echo $ECHO_N "checking for yywrap in -lfl... $ECHO_C" >&6 +if test "${ac_cv_lib_fl_yywrap+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lfl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char yywrap (); +int +main () +{ +yywrap (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_fl_yywrap=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_fl_yywrap=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_fl_yywrap" >&5 +echo "${ECHO_T}$ac_cv_lib_fl_yywrap" >&6 +if test $ac_cv_lib_fl_yywrap = yes; then + LEXLIB="-lfl" +else + echo "$as_me:$LINENO: checking for yywrap in -ll" >&5 +echo $ECHO_N "checking for yywrap in -ll... $ECHO_C" >&6 +if test "${ac_cv_lib_l_yywrap+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ll $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char yywrap (); +int +main () +{ +yywrap (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_l_yywrap=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_l_yywrap=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_l_yywrap" >&5 +echo "${ECHO_T}$ac_cv_lib_l_yywrap" >&6 +if test $ac_cv_lib_l_yywrap = yes; then + LEXLIB="-ll" +fi + +fi + +fi + +if test "x$LEX" != "x:"; then + echo "$as_me:$LINENO: checking lex output file root" >&5 +echo $ECHO_N "checking lex output file root... $ECHO_C" >&6 +if test "${ac_cv_prog_lex_root+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # The minimal lex program is just a single line: %%. But some broken lexes +# (Solaris, I think it was) want two %% lines, so accommodate them. +cat >conftest.l <<_ACEOF +%% +%% +_ACEOF +{ (eval echo "$as_me:$LINENO: \"$LEX conftest.l\"") >&5 + (eval $LEX conftest.l) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +if test -f lex.yy.c; then + ac_cv_prog_lex_root=lex.yy +elif test -f lexyy.c; then + ac_cv_prog_lex_root=lexyy +else + { { echo "$as_me:$LINENO: error: cannot find output from $LEX; giving up" >&5 +echo "$as_me: error: cannot find output from $LEX; giving up" >&2;} + { (exit 1); exit 1; }; } +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_lex_root" >&5 +echo "${ECHO_T}$ac_cv_prog_lex_root" >&6 +rm -f conftest.l +LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root + +echo "$as_me:$LINENO: checking whether yytext is a pointer" >&5 +echo $ECHO_N "checking whether yytext is a pointer... $ECHO_C" >&6 +if test "${ac_cv_prog_lex_yytext_pointer+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # POSIX says lex can declare yytext either as a pointer or an array; the +# default is implementation-dependent. Figure out which it is, since +# not all implementations provide the %pointer and %array declarations. +ac_cv_prog_lex_yytext_pointer=no +echo 'extern char *yytext;' >>$LEX_OUTPUT_ROOT.c +ac_save_LIBS=$LIBS +LIBS="$LIBS $LEXLIB" +cat >conftest.$ac_ext <<_ACEOF +`cat $LEX_OUTPUT_ROOT.c` +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_lex_yytext_pointer=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_save_LIBS +rm -f "${LEX_OUTPUT_ROOT}.c" + +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_lex_yytext_pointer" >&5 +echo "${ECHO_T}$ac_cv_prog_lex_yytext_pointer" >&6 +if test $ac_cv_prog_lex_yytext_pointer = yes; then + +cat >>confdefs.h <<\_ACEOF +#define YYTEXT_POINTER 1 +_ACEOF + +fi + +fi +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether ln -s works" >&5 +echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6 +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no, using $LN_S" >&5 +echo "${ECHO_T}no, using $LN_S" >&6 +fi + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + + +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + WITH_DEBUG=yes ; cat >>confdefs.h <<\_ACEOF +#define DEBUG 1 +_ACEOF + +else + WITH_DEBUG=no +fi; + + +# Check whether --enable-memory-debug or --disable-memory-debug was given. +if test "${enable_memory_debug+set}" = set; then + enableval="$enable_memory_debug" + WITH_MEMORY_DEBUG=yes ; cat >>confdefs.h <<\_ACEOF +#define MEMORY_DEBUG 1 +_ACEOF + +else + WITH_MEMORY_DEBUG=no +fi; + + +# Check whether --enable-libdebug or --disable-libdebug was given. +if test "${enable_libdebug+set}" = set; then + enableval="$enable_libdebug" + WITH_LIBDEBUG=yes ; cat >>confdefs.h <<\_ACEOF +#define WITH_LIBDEBUG 1 +_ACEOF + +else + WITH_LIBDEBUG=no +fi; + + +# Check whether --enable-prelude or --disable-prelude was given. +if test "${enable_prelude+set}" = set; then + enableval="$enable_prelude" + WITH_PRELUDE=yes ; cat >>confdefs.h <<\_ACEOF +#define WITH_PRELUDE 1 +_ACEOF + +else + WITH_PRELUDE=no +fi; + + +# Check whether --enable-fakeroot or --disable-fakeroot was given. +if test "${enable_fakeroot+set}" = set; then + enableval="$enable_fakeroot" + FAKEROOT=$enableval +fi; + + +# Check whether --enable-securedir or --disable-securedir was given. +if test "${enable_securedir+set}" = set; then + enableval="$enable_securedir" + SECUREDIR=$enableval +else + SECUREDIR=$libdir/security +fi; + + +# Check whether --enable-isadir or --disable-isadir was given. +if test "${enable_isadir+set}" = set; then + enableval="$enable_isadir" + ISA=$enableval +else + ISA=../../`basename $libdir`/security +fi; +unset mylibdirbase + +cat >>confdefs.h <<_ACEOF +#define _PAM_ISA "$ISA" +_ACEOF + +echo "$as_me:$LINENO: result: Defining \$ISA to \\"$ISA\\"." >&5 +echo "${ECHO_T}Defining \$ISA to \\"$ISA\\"." >&6 + +# Check whether --enable-sconfigdir or --disable-sconfigdir was given. +if test "${enable_sconfigdir+set}" = set; then + enableval="$enable_sconfigdir" + SCONFIGDIR=$enableval +else + SCONFIGDIR=$sysconfdir/security +fi; + + +# Check whether --enable-suplementedir or --disable-suplementedir was given. +if test "${enable_suplementedir+set}" = set; then + enableval="$enable_suplementedir" + SUPLEMENTED=$enableval +else + SUPLEMENTED=$sbindir +fi; + + +# Check whether --enable-includedir or --disable-includedir was given. +if test "${enable_includedir+set}" = set; then + enableval="$enable_includedir" + INCLUDEDIR=$enableval +else + INCLUDEDIR=/usr/include +fi; + + +# Check whether --enable-docdir or --disable-docdir was given. +if test "${enable_docdir+set}" = set; then + enableval="$enable_docdir" + DOCDIR=$enableval +else + DOCDIR=/usr/share/doc/pam +fi; + + +# Check whether --enable-mandir or --disable-mandir was given. +if test "${enable_mandir+set}" = set; then + enableval="$enable_mandir" + MANDIR=$enableval +else + MANDIR=/usr/share/man +fi; + + +# Check whether --enable-pamlocking or --disable-pamlocking was given. +if test "${enable_pamlocking+set}" = set; then + enableval="$enable_pamlocking" + WITH_PAMLOCKING=yes ; cat >>confdefs.h <<\_ACEOF +#define PAM_LOCKING 1 +_ACEOF + +else + WITH_PAMLOCKING=no +fi; + + +# Check whether --enable-uglyhack or --disable-uglyhack was given. +if test "${enable_uglyhack+set}" = set; then + enableval="$enable_uglyhack" + cat >>confdefs.h <<\_ACEOF +#define UGLY_HACK_FOR_PRIOR_BEHAVIOR_SUPPORT 1 +_ACEOF + +fi; + +# Check whether --enable-read-both-confs or --disable-read-both-confs was given. +if test "${enable_read_both_confs+set}" = set; then + enableval="$enable_read_both_confs" + cat >>confdefs.h <<\_ACEOF +#define PAM_READ_BOTH_CONFS 1 +_ACEOF + +fi; + + +# Check whether --enable-static-libpam or --disable-static-libpam was given. +if test "${enable_static_libpam+set}" = set; then + enableval="$enable_static_libpam" + STATIC_LIBPAM=yes +else + STATIC_LIBPAM=no +fi; + + +# Check whether --enable-dynamic-libpam or --disable-dynamic-libpam was given. +if test "${enable_dynamic_libpam+set}" = set; then + enableval="$enable_dynamic_libpam" + DYNAMIC_LIBPAM=no +else + DYNAMIC_LIBPAM=yes +fi; + + +DYNAMIC=-DPAM_DYNAMIC + + +# Check whether --enable-static-modules or --disable-static-modules was given. +if test "${enable_static_modules+set}" = set; then + enableval="$enable_static_modules" + STATIC=-DPAM_STATIC +fi; + + +# Check whether --enable-lckpwdf or --disable-lckpwdf was given. +if test "${enable_lckpwdf+set}" = set; then + enableval="$enable_lckpwdf" + WITH_LCKPWDF=no +else + WITH_LCKPWDF=yes +fi; + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_header in paths.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Check whether --with-mailspool or --without-mailspool was given. +if test "${with_mailspool+set}" = set; then + withval="$with_mailspool" + with_mailspool=${withval} +fi; +if test x$with_mailspool != x ; then + pam_mail_spool="\"$with_mailspool\"" +else + if test "$cross_compiling" = yes; then + pam_mail_spool="\"/var/spool/mail\"" +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include <paths.h> +int main() { +#ifdef _PATH_MAILDIR +exit(0); +#else +exit(1); +#endif +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + pam_mail_spool="_PATH_MAILDIR" +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +pam_mail_spool="\"/var/spool/mail\"" +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +cat >>confdefs.h <<_ACEOF +#define PAM_PATH_MAILDIR $pam_mail_spool +_ACEOF + + +echo "$as_me:$LINENO: checking for __libc_sched_setscheduler in -lc" >&5 +echo $ECHO_N "checking for __libc_sched_setscheduler in -lc... $ECHO_C" >&6 +if test "${ac_cv_lib_c___libc_sched_setscheduler+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lc $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char __libc_sched_setscheduler (); +int +main () +{ +__libc_sched_setscheduler (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_c___libc_sched_setscheduler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_c___libc_sched_setscheduler=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_c___libc_sched_setscheduler" >&5 +echo "${ECHO_T}$ac_cv_lib_c___libc_sched_setscheduler" >&6 +if test $ac_cv_lib_c___libc_sched_setscheduler = yes; then + PAM_NEEDS_LIBC= +else + PAM_NEEDS_LIBC=-lc +fi + + + +echo "$as_me:$LINENO: checking for lckpwdf in -lc" >&5 +echo $ECHO_N "checking for lckpwdf in -lc... $ECHO_C" >&6 +if test "${ac_cv_lib_c_lckpwdf+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lc $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char lckpwdf (); +int +main () +{ +lckpwdf (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_c_lckpwdf=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_c_lckpwdf=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_c_lckpwdf" >&5 +echo "${ECHO_T}$ac_cv_lib_c_lckpwdf" >&6 +if test $ac_cv_lib_c_lckpwdf = yes; then + HAVE_LCKPWDF=yes +else + HAVE_LCKPWDF=no +fi + + + +echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + LIBDL=-ldl +fi + + + +echo "$as_me:$LINENO: checking for FascistCheck in -lcrack" >&5 +echo $ECHO_N "checking for FascistCheck in -lcrack... $ECHO_C" >&6 +if test "${ac_cv_lib_crack_FascistCheck+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcrack $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char FascistCheck (); +int +main () +{ +FascistCheck (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_crack_FascistCheck=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_crack_FascistCheck=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_crack_FascistCheck" >&5 +echo "${ECHO_T}$ac_cv_lib_crack_FascistCheck" >&6 +if test $ac_cv_lib_crack_FascistCheck = yes; then + HAVE_LIBCRACK=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBCRACK 1 +_ACEOF + +else + HAVE_LIBCRACK=no +fi + + + +echo "$as_me:$LINENO: checking for crypt in -lcrypt" >&5 +echo $ECHO_N "checking for crypt in -lcrypt... $ECHO_C" >&6 +if test "${ac_cv_lib_crypt_crypt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcrypt $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char crypt (); +int +main () +{ +crypt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_crypt_crypt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_crypt_crypt=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_crypt_crypt" >&5 +echo "${ECHO_T}$ac_cv_lib_crypt_crypt" >&6 +if test $ac_cv_lib_crypt_crypt = yes; then + HAVE_LIBCRYPT=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBCRYPT 1 +_ACEOF + +else + HAVE_LIBCRYPT=no +fi + + +echo "$as_me:$LINENO: checking for logwtmp in -lutil" >&5 +echo $ECHO_N "checking for logwtmp in -lutil... $ECHO_C" >&6 +if test "${ac_cv_lib_util_logwtmp+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lutil $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char logwtmp (); +int +main () +{ +logwtmp (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_util_logwtmp=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_util_logwtmp=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_util_logwtmp" >&5 +echo "${ECHO_T}$ac_cv_lib_util_logwtmp" >&6 +if test $ac_cv_lib_util_logwtmp = yes; then + HAVE_LIBUTIL=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBUTIL 1 +_ACEOF + +else + HAVE_LIBUTIL=no +fi + + +echo "$as_me:$LINENO: checking for dbm_store in -lndbm" >&5 +echo $ECHO_N "checking for dbm_store in -lndbm... $ECHO_C" >&6 +if test "${ac_cv_lib_ndbm_dbm_store+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lndbm $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dbm_store (); +int +main () +{ +dbm_store (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ndbm_dbm_store=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_ndbm_dbm_store=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_ndbm_dbm_store" >&5 +echo "${ECHO_T}$ac_cv_lib_ndbm_dbm_store" >&6 +if test $ac_cv_lib_ndbm_dbm_store = yes; then + HAVE_LIBNDBM=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBNDBM 1 +_ACEOF + +else + HAVE_LIBNDBM=no +fi + + +echo "$as_me:$LINENO: checking for dbm_store in -ldb" >&5 +echo $ECHO_N "checking for dbm_store in -ldb... $ECHO_C" >&6 +if test "${ac_cv_lib_db_dbm_store+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldb $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dbm_store (); +int +main () +{ +dbm_store (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_db_dbm_store=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_db_dbm_store=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_db_dbm_store" >&5 +echo "${ECHO_T}$ac_cv_lib_db_dbm_store" >&6 +if test $ac_cv_lib_db_dbm_store = yes; then + HAVE_LIBDB=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBDB 1 +_ACEOF + +else + HAVE_LIBDB=no +fi + +if test x$HAVE_LIBDB != xyes ; then + echo "$as_me:$LINENO: checking for db_create in -ldb" >&5 +echo $ECHO_N "checking for db_create in -ldb... $ECHO_C" >&6 +if test "${ac_cv_lib_db_db_create+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldb $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char db_create (); +int +main () +{ +db_create (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_db_db_create=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_db_db_create=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_db_db_create" >&5 +echo "${ECHO_T}$ac_cv_lib_db_db_create" >&6 +if test $ac_cv_lib_db_db_create = yes; then + HAVE_LIBDB=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBDB 1 +_ACEOF + +else + HAVE_LIBDB=no +fi + +fi + +echo "$as_me:$LINENO: checking for yylex in -lfl" >&5 +echo $ECHO_N "checking for yylex in -lfl... $ECHO_C" >&6 +if test "${ac_cv_lib_fl_yylex+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lfl HAVE_LIBFL=no $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char yylex (); +int +main () +{ +yylex (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_fl_yylex=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_fl_yylex=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_fl_yylex" >&5 +echo "${ECHO_T}$ac_cv_lib_fl_yylex" >&6 +if test $ac_cv_lib_fl_yylex = yes; then + yyterminate +else + HAVE_LIBFL=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBFL 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for yp_maplist in -lnsl" >&5 +echo $ECHO_N "checking for yp_maplist in -lnsl... $ECHO_C" >&6 +if test "${ac_cv_lib_nsl_yp_maplist+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char yp_maplist (); +int +main () +{ +yp_maplist (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_nsl_yp_maplist=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_nsl_yp_maplist=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_yp_maplist" >&5 +echo "${ECHO_T}$ac_cv_lib_nsl_yp_maplist" >&6 +if test $ac_cv_lib_nsl_yp_maplist = yes; then + HAVE_LIBNSL=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBNSL 1 +_ACEOF + +else + HAVE_LIBNSL=no +fi + + + +if test $HAVE_LIBNSL = yes ; then + pwdblibs="$pwdblibs -lnsl" +fi +echo "$as_me:$LINENO: checking for pwdb_db_name in -lpwdb" >&5 +echo $ECHO_N "checking for pwdb_db_name in -lpwdb... $ECHO_C" >&6 +if test "${ac_cv_lib_pwdb_pwdb_db_name+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpwdb $pwdblibs $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pwdb_db_name (); +int +main () +{ +pwdb_db_name (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pwdb_pwdb_db_name=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pwdb_pwdb_db_name=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pwdb_pwdb_db_name" >&5 +echo "${ECHO_T}$ac_cv_lib_pwdb_pwdb_db_name" >&6 +if test $ac_cv_lib_pwdb_pwdb_db_name = yes; then + HAVE_LIBPWDB=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBPWDB 1 +_ACEOF + +else + HAVE_LIBPWDB=no +fi + + +unset pwdblibs + +echo "$as_me:$LINENO: checking for yywrap in -lfl" >&5 +echo $ECHO_N "checking for yywrap in -lfl... $ECHO_C" >&6 +if test "${ac_cv_lib_fl_yywrap+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lfl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char yywrap (); +int +main () +{ +yywrap (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_fl_yywrap=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_fl_yywrap=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_fl_yywrap" >&5 +echo "${ECHO_T}$ac_cv_lib_fl_yywrap" >&6 +if test $ac_cv_lib_fl_yywrap = yes; then + HAVE_LIBFLEX=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBFLEX 1 +_ACEOF + +else + HAVE_LIBFLEX=no +fi + + +echo "$as_me:$LINENO: checking for yywrap in -ll" >&5 +echo $ECHO_N "checking for yywrap in -ll... $ECHO_C" >&6 +if test "${ac_cv_lib_l_yywrap+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ll $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char yywrap (); +int +main () +{ +yywrap (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_l_yywrap=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_l_yywrap=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_l_yywrap" >&5 +echo "${ECHO_T}$ac_cv_lib_l_yywrap" >&6 +if test $ac_cv_lib_l_yywrap = yes; then + HAVE_LIBLEX=yes ; cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBLEX 1 +_ACEOF + +else + HAVE_LIBLEX=no +fi + + + + + + + + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do + as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5 +echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <$ac_hdr> + +int +main () +{ +if ((DIR *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 +_ACEOF + +ac_header_dirent=$ac_hdr; break +fi + +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then + echo "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 +if test "${ac_cv_search_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_opendir=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_opendir" = no; then + for ac_lib in dir; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6 +if test "$ac_cv_search_opendir" != no; then + test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" + +fi + +else + echo "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 +if test "${ac_cv_search_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_opendir=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_opendir" = no; then + for ac_lib in x; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6 +if test "$ac_cv_search_opendir" != no; then + test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" + +fi + +fi + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 +if test "${ac_cv_header_sys_wait_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/wait.h> +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_sys_wait_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_sys_wait_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + + + + + + + + + + + +for ac_header in fcntl.h limits.h malloc.h sys/file.h sys/ioctl.h sys/time.h syslog.h termio.h unistd.h sys/fsuid.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_header in features.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_header in crypt.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + +for ac_header in ndbm.h db.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +HAVE_NDBM_H=$ac_cv_header_ndbm_h + + + + + +for ac_header in lastlog.h utmp.h utmpx.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +echo "$as_me:$LINENO: checking path to cracklib dictionary" >&5 +echo $ECHO_N "checking path to cracklib dictionary... $ECHO_C" >&6 +DICT_DIR_CANDIDATES="/usr/lib /usr/share/dict /usr/share/lib \ + /usr/local/lib /usr/local/share/lib /usr/share/cracklib" +DICT_FILE_CANDIDATES="pw_dict cracklib_dict" +CRACKLIB_DICTPATH="" +for d in $DICT_DIR_CANDIDATES ; do + for f in $DICT_FILE_CANDIDATES ; do + if test -r $d/$f.hwm ; then + CRACKLIB_DICTPATH=$d/$f + break 2 + elif test -r $d/dict/$f.hwm ; then + CRACKLIB_DICTPATH=$d/dict/$f + break 2 + fi + done +done +if test -z "$CRACKLIB_DICTPATH" ; then + { echo "$as_me:$LINENO: WARNING: none found - pam_cracklib will not be built" >&5 +echo "$as_me: WARNING: none found - pam_cracklib will not be built" >&2;} +else + echo "$as_me:$LINENO: result: $CRACKLIB_DICTPATH" >&5 +echo "${ECHO_T}$CRACKLIB_DICTPATH" >&6 +fi + + + +GCC_WARNINGS="-Wall -Wwrite-strings \ + -Wpointer-arith -Wcast-qual -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes \ + -Wnested-externs -Winline -Wshadow" + +if test "$GCC" = yes; then + CC=gcc ; +### May need per-OS attention +### Example: -D_POSIX_SOURCE: needed on Linux but harms Solaris. + case $OS in + linux) + OS_CFLAGS= + LD_D="$CC -shared $LDFLAGS" + WARNINGS="$GCC_WARNINGS" + PIC="-fPIC" + DYNTYPE=so + LD=gcc + LD_L="$CC -shared $LDFLAGS" + RANLIB=: + STRIP=strip + CC_STATIC="-Xlinker -export-dynamic" + ;; + sunos) + OS_CFLAGS="-ansi -pedantic" + LD_D="gcc -shared -Xlinker -x" + WARNINGS="$GCC_WARNINGS" + PIC="-fPIC" + DYNTYPE=so + LD=ld + LD_L="$LD -x -shared" + RANLIB=ranlib + STRIP=strip + CC_STATIC="-Xlinker -export-dynamic" + ;; + aix) + OS_CFLAGS="" + DYNTYPE=lo + LD=ld + LD_L=ld -bexpall -bM:SRE -bnoentry + LD_D="$LD_L" + RANLIB=ranlib + STRIP=strip + ;; + *) + OS_CFLAGS="" + ;; + esac +else +### +### Non-gcc needs attention on per-OS basis +### + case "$OS" in + darwin) +# add some stuff here (see sourceforge bug 534205) +# DOCDIR=/System/Documentation/Administration/Libraries/PAM +# MANDIR=/usr/share/man + ;; + solaris) + ### Support for Solaris-C + OS_CFLAGS="" + WARNINGS="" + PIC="-K pic" + LD=ld + LD_D="cc -z text -G -R." + LD_L="$LD_D" + RANLIB=ranlib + STRIP=strip + CC_STATIC= + ;; + irix*) + OSRELEASE=`uname -r` + if test "$OSRELEASE" = 6.5; then + OS_CFLAGS="" + WARNINGS="-fullwarn" + PIC= #PIC code is default for IRIX + LD="cc -shared" # modules friendly approach + LD_D="cc -shared" + LD_L="ld -G -z redlocsym" + RANLIB=echo + STRIP=strip + CC_STATIC= + else + echo "IRIX prior to 6.5 not allowed for" + exit + fi + ;; + *) echo "Native compiler on $OS is not yet supported" + exit + ;; + esac +fi + + + + + + + + + + + + +echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/param.h> + +int +main () +{ +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/param.h> + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_bigendian=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +# It does not; compile a test program. +if test "$cross_compiling" = yes; then + # try to guess the endianness by grepping values into an object file + ac_cv_c_bigendian=unknown + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; +short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; +void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } +short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; +short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; +void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } +int +main () +{ + _ascii (); _ebcdic (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then + ac_cv_c_bigendian=yes +fi +if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi +fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +main () +{ + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_bigendian=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6 +case $ac_cv_c_bigendian in + yes) + +cat >>confdefs.h <<\_ACEOF +#define WORDS_BIGENDIAN 1 +_ACEOF + ;; + no) + ;; + *) + { { echo "$as_me:$LINENO: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&5 +echo "$as_me: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} + { (exit 1); exit 1; }; } ;; +esac + +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 +echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6 +if test "${ac_cv_type_uid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5 +echo "${ECHO_T}$ac_cv_type_uid_t" >&6 +if test $ac_cv_type_uid_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uid_t int +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define gid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for off_t" >&5 +echo $ECHO_N "checking for off_t... $ECHO_C" >&6 +if test "${ac_cv_type_off_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((off_t *) 0) + return 0; +if (sizeof (off_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_off_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_off_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5 +echo "${ECHO_T}$ac_cv_type_off_t" >&6 +if test $ac_cv_type_off_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define off_t long +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pid_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/time.h> +#include <time.h> + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <time.h> + +int +main () +{ +struct tm *tp; tp->tm_sec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_struct_tm=sys/time.h +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6 +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking type of array argument to getgroups" >&5 +echo $ECHO_N "checking type of array argument to getgroups... $ECHO_C" >&6 +if test "${ac_cv_type_getgroups+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_type_getgroups=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Mike Rendell for this test. */ +#include <sys/types.h> +#define NGID 256 +#undef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +int +main () +{ + gid_t gidset[NGID]; + int i, n; + union { gid_t gval; long lval; } val; + + val.lval = -1; + for (i = 0; i < NGID; i++) + gidset[i] = val.gval; + n = getgroups (sizeof (gidset) / MAX (sizeof (int), sizeof (gid_t)) - 1, + gidset); + /* Exit non-zero if getgroups seems to require an array of ints. This + happens when gid_t is short but getgroups modifies an array of ints. */ + exit ((n > 0 && gidset[n] != val.gval) ? 1 : 0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_getgroups=gid_t +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_type_getgroups=int +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +if test $ac_cv_type_getgroups = cross; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <unistd.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "getgroups.*int.*gid_t" >/dev/null 2>&1; then + ac_cv_type_getgroups=gid_t +else + ac_cv_type_getgroups=int +fi +rm -f conftest* + +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_type_getgroups" >&5 +echo "${ECHO_T}$ac_cv_type_getgroups" >&6 + +cat >>confdefs.h <<_ACEOF +#define GETGROUPS_T $ac_cv_type_getgroups +_ACEOF + + +if test $ac_cv_c_compiler_gnu = yes; then + echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sgtty.h> +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <termio.h> +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +echo "$as_me:$LINENO: checking for working memcmp" >&5 +echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6 +if test "${ac_cv_func_memcmp_working+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = 0x40, c1 = 0x80, c2 = 0x81; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + exit (1); + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + exit (1); + } + exit (0); + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_memcmp_working=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 +echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6 +test $ac_cv_func_memcmp_working = no && case $LIBOBJS in + "memcmp.$ac_objext" | \ + *" memcmp.$ac_objext" | \ + "memcmp.$ac_objext "* | \ + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" ;; +esac + + + +for ac_func in vprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +echo "$as_me:$LINENO: checking for _doprnt" >&5 +echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6 +if test "${ac_cv_func__doprnt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef _doprnt + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +char (*f) () = _doprnt; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != _doprnt; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func__doprnt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func__doprnt=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 +echo "${ECHO_T}$ac_cv_func__doprnt" >&6 +if test $ac_cv_func__doprnt = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DOPRNT 1 +_ACEOF + +fi + +fi +done + + + + + + + + + + + + + +for ac_func in gethostname gettimeofday mkdir select strcspn strdup strerror strspn strstr strtol uname +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + + + + + + +for ac_func in getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r getgrouplist +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# Extract the first word of "sgml2txt", so it can be a program name with args. +set dummy sgml2txt; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_HAVE_SGML2TXT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$HAVE_SGML2TXT"; then + ac_cv_prog_HAVE_SGML2TXT="$HAVE_SGML2TXT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_HAVE_SGML2TXT="yes" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_HAVE_SGML2TXT" && ac_cv_prog_HAVE_SGML2TXT="no" +fi +fi +HAVE_SGML2TXT=$ac_cv_prog_HAVE_SGML2TXT +if test -n "$HAVE_SGML2TXT"; then + echo "$as_me:$LINENO: result: $HAVE_SGML2TXT" >&5 +echo "${ECHO_T}$HAVE_SGML2TXT" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +# Extract the first word of "sgml2html", so it can be a program name with args. +set dummy sgml2html; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_HAVE_SGML2HTML+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$HAVE_SGML2HTML"; then + ac_cv_prog_HAVE_SGML2HTML="$HAVE_SGML2HTML" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_HAVE_SGML2HTML="yes" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_HAVE_SGML2HTML" && ac_cv_prog_HAVE_SGML2HTML="no" +fi +fi +HAVE_SGML2HTML=$ac_cv_prog_HAVE_SGML2HTML +if test -n "$HAVE_SGML2HTML"; then + echo "$as_me:$LINENO: result: $HAVE_SGML2HTML" >&5 +echo "${ECHO_T}$HAVE_SGML2HTML" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +# Extract the first word of "sgml2latex", so it can be a program name with args. +set dummy sgml2latex; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_HAVE_SGML2LATEX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$HAVE_SGML2LATEX"; then + ac_cv_prog_HAVE_SGML2LATEX="$HAVE_SGML2LATEX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_HAVE_SGML2LATEX="yes" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_HAVE_SGML2LATEX" && ac_cv_prog_HAVE_SGML2LATEX="no" +fi +fi +HAVE_SGML2LATEX=$ac_cv_prog_HAVE_SGML2LATEX +if test -n "$HAVE_SGML2LATEX"; then + echo "$as_me:$LINENO: result: $HAVE_SGML2LATEX" >&5 +echo "${ECHO_T}$HAVE_SGML2LATEX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +if test $HAVE_SGML2LATEX = "yes" ; then + if sgml2latex -h | grep -e --paper | grep ' -p ' > /dev/null ; then + PSER="sgml2latex -o ps" + else + PSER="sgml2latex -p" + fi + # Extract the first word of "ps2pdf", so it can be a program name with args. +set dummy ps2pdf; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_HAVE_PS2PDF+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$HAVE_PS2PDF"; then + ac_cv_prog_HAVE_PS2PDF="$HAVE_PS2PDF" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_HAVE_PS2PDF="yes" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_HAVE_PS2PDF" && ac_cv_prog_HAVE_PS2PDF="no" +fi +fi +HAVE_PS2PDF=$ac_cv_prog_HAVE_PS2PDF +if test -n "$HAVE_PS2PDF"; then + echo "$as_me:$LINENO: result: $HAVE_PS2PDF" >&5 +echo "${ECHO_T}$HAVE_PS2PDF" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +else + # Extract the first word of "sgml2ps", so it can be a program name with args. +set dummy sgml2ps; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_HAVE_SGML2PS+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$HAVE_SGML2PS"; then + ac_cv_prog_HAVE_SGML2PS="$HAVE_SGML2PS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_HAVE_SGML2PS="yes" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_HAVE_SGML2PS" && ac_cv_prog_HAVE_SGML2PS="no" +fi +fi +HAVE_SGML2PS=$ac_cv_prog_HAVE_SGML2PS +if test -n "$HAVE_SGML2PS"; then + echo "$as_me:$LINENO: result: $HAVE_SGML2PS" >&5 +echo "${ECHO_T}$HAVE_SGML2PS" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + if test $HAVE_SGML2PS = yes ; then + PSER="sgml2ps" + fi +fi + + + + ac_config_files="$ac_config_files Make.Rules" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to <bug-autoconf@gnu.org>." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Make.Rules" ) CONFIG_FILES="$CONFIG_FILES Make.Rules" ;; + "_pam_aconf.h" ) CONFIG_HEADERS="$CONFIG_HEADERS _pam_aconf.h" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@LIBPAM_VERSION_MAJOR@,$LIBPAM_VERSION_MAJOR,;t t +s,@LIBPAM_VERSION_MINOR@,$LIBPAM_VERSION_MINOR,;t t +s,@LOCALSRCDIR@,$LOCALSRCDIR,;t t +s,@LOCALOBJDIR@,$LOCALOBJDIR,;t t +s,@OS@,$OS,;t t +s,@CONF_CFLAGS@,$CONF_CFLAGS,;t t +s,@MKDIR@,$MKDIR,;t t +s,@SHLIBMODE@,$SHLIBMODE,;t t +s,@MANMODE@,$MANMODE,;t t +s,@USESONAME@,$USESONAME,;t t +s,@SOSWITCH@,$SOSWITCH,;t t +s,@NEEDSONAME@,$NEEDSONAME,;t t +s,@LDCONFIG@,$LDCONFIG,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@YACC@,$YACC,;t t +s,@LEX@,$LEX,;t t +s,@LEXLIB@,$LEXLIB,;t t +s,@LEX_OUTPUT_ROOT@,$LEX_OUTPUT_ROOT,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@LN_S@,$LN_S,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@WITH_DEBUG@,$WITH_DEBUG,;t t +s,@WITH_MEMORY_DEBUG@,$WITH_MEMORY_DEBUG,;t t +s,@WITH_LIBDEBUG@,$WITH_LIBDEBUG,;t t +s,@WITH_PRELUDE@,$WITH_PRELUDE,;t t +s,@FAKEROOT@,$FAKEROOT,;t t +s,@SECUREDIR@,$SECUREDIR,;t t +s,@SCONFIGDIR@,$SCONFIGDIR,;t t +s,@SUPLEMENTED@,$SUPLEMENTED,;t t +s,@INCLUDEDIR@,$INCLUDEDIR,;t t +s,@DOCDIR@,$DOCDIR,;t t +s,@MANDIR@,$MANDIR,;t t +s,@WITH_PAMLOCKING@,$WITH_PAMLOCKING,;t t +s,@PAM_READ_BOTH_CONFS@,$PAM_READ_BOTH_CONFS,;t t +s,@STATIC_LIBPAM@,$STATIC_LIBPAM,;t t +s,@DYNAMIC_LIBPAM@,$DYNAMIC_LIBPAM,;t t +s,@DYNAMIC@,$DYNAMIC,;t t +s,@STATIC@,$STATIC,;t t +s,@WITH_LCKPWDF@,$WITH_LCKPWDF,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@PAM_NEEDS_LIBC@,$PAM_NEEDS_LIBC,;t t +s,@HAVE_LCKPWDF@,$HAVE_LCKPWDF,;t t +s,@LIBDL@,$LIBDL,;t t +s,@HAVE_LIBCRACK@,$HAVE_LIBCRACK,;t t +s,@HAVE_LIBCRYPT@,$HAVE_LIBCRYPT,;t t +s,@HAVE_LIBUTIL@,$HAVE_LIBUTIL,;t t +s,@HAVE_LIBNDBM@,$HAVE_LIBNDBM,;t t +s,@HAVE_LIBDB@,$HAVE_LIBDB,;t t +s,@HAVE_LIBFL@,$HAVE_LIBFL,;t t +s,@HAVE_LIBNSL@,$HAVE_LIBNSL,;t t +s,@HAVE_LIBPWDB@,$HAVE_LIBPWDB,;t t +s,@HAVE_LIBFLEX@,$HAVE_LIBFLEX,;t t +s,@HAVE_LIBLEX@,$HAVE_LIBLEX,;t t +s,@HAVE_NDBM_H@,$HAVE_NDBM_H,;t t +s,@CRACKLIB_DICTPATH@,$CRACKLIB_DICTPATH,;t t +s,@DYNTYPE@,$DYNTYPE,;t t +s,@OS_CFLAGS@,$OS_CFLAGS,;t t +s,@WARNINGS@,$WARNINGS,;t t +s,@PIC@,$PIC,;t t +s,@LD@,$LD,;t t +s,@LD_D@,$LD_D,;t t +s,@LD_L@,$LD_L,;t t +s,@RANLIB@,$RANLIB,;t t +s,@STRIP@,$STRIP,;t t +s,@CC_STATIC@,$CC_STATIC,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@HAVE_SGML2TXT@,$HAVE_SGML2TXT,;t t +s,@HAVE_SGML2HTML@,$HAVE_SGML2HTML,;t t +s,@HAVE_SGML2LATEX@,$HAVE_SGML2LATEX,;t t +s,@HAVE_PS2PDF@,$HAVE_PS2PDF,;t t +s,@HAVE_SGML2PS@,$HAVE_SGML2PS,;t t +s,@PSER@,$PSER,;t t +s,@PS2PDF@,$PS2PDF,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/Linux-PAM/configure.in b/Linux-PAM/configure.in new file mode 100644 index 00000000..f090a542 --- /dev/null +++ b/Linux-PAM/configure.in @@ -0,0 +1,448 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(conf/pam_conv1/pam_conv.y) + +dnl The configuration header file +AC_CONFIG_HEADER(_pam_aconf.h) + +dnl +dnl Release specific +dnl + +LIBPAM_VERSION_MAJOR=0 +LIBPAM_VERSION_MINOR=79 + +AC_SUBST(LIBPAM_VERSION_MAJOR) +AC_SUBST(LIBPAM_VERSION_MINOR) +AC_DEFINE(LIBPAM_VERSION_MAJOR) +AC_DEFINE(LIBPAM_VERSION_MINOR) + +dnl +dnl By default, everything under PAM is installed under the root fs. +dnl + +AC_PREFIX_DEFAULT() + +dnl +dnl Useful info (believed to be portable) - in the future +dnl the LOCALSRCDIR and LOCALOBJDIRs may be different +dnl +LOCALSRCDIR=`/bin/pwd` ; AC_SUBST(LOCALSRCDIR) +LOCALOBJDIR=`/bin/pwd` ; AC_SUBST(LOCALOBJDIR) +OS=`uname|sed -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` +AC_SUBST(OS) + +dnl +dnl Rules needed for the following (hardcoded Linux defaults for now) +dnl + +CONF_CFLAGS= ; AC_SUBST(CONF_CFLAGS) +MKDIR="mkdir -p" ; AC_SUBST(MKDIR) + +SHLIBMODE=755 ; AC_SUBST(SHLIBMODE) +MANMODE=644 ; AC_SUBST(MANMODE) + +dnl These are most likely platform specific - I think HPUX differs +USESONAME=yes ; AC_SUBST(USESONAME) +SOSWITCH="-Wl,-soname -Wl," ; AC_SUBST(SOSWITCH) +NEEDSONAME=yes ; AC_SUBST(NEEDSONAME) +LDCONFIG=/sbin/ldconfig ; AC_SUBST(LDCONFIG) + +dnl Checks for programs. +AC_PROG_CC +dnl ### AC_PROG_CXX +AC_PROG_YACC +AC_PROG_LEX +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET + +dnl +dnl options and defaults +dnl + +dnl lots of debugging information goes to /tmp/pam-debug.log +AC_ARG_ENABLE(debug, +[ --enable-debug specify you are building with debugging on], + WITH_DEBUG=yes ; AC_DEFINE(DEBUG) , WITH_DEBUG=no) +AC_SUBST(WITH_DEBUG) + +AC_ARG_ENABLE(memory-debug, +[ --enable-memory-debug specify you want every malloc etc. call tracked], + WITH_MEMORY_DEBUG=yes ; AC_DEFINE(MEMORY_DEBUG) , WITH_MEMORY_DEBUG=no) +AC_SUBST(WITH_MEMORY_DEBUG) + +dnl build specially named libraries (for debugging purposes) +AC_ARG_ENABLE(libdebug, +[ --enable-libdebug specify you are building debugging libraries], + WITH_LIBDEBUG=yes ; AC_DEFINE(WITH_LIBDEBUG) , WITH_LIBDEBUG=no) +AC_SUBST(WITH_LIBDEBUG) + +dnl have prelude support +AC_ARG_ENABLE(prelude, +[ --enable-prelude build prelude ids support], + WITH_PRELUDE=yes ; AC_DEFINE(WITH_PRELUDE), WITH_PRELUDE=no) +AC_SUBST(WITH_PRELUDE) + +dnl packaging convenience +AC_ARG_ENABLE(fakeroot, +[ --enable-fakeroot=<path to packaging directory>], FAKEROOT=$enableval) +AC_SUBST(FAKEROOT) + +AC_ARG_ENABLE(securedir, +[ --enable-securedir=<path to location of PAMs> [default \$libdir/security]], + SECUREDIR=$enableval, SECUREDIR=$libdir/security) +AC_SUBST(SECUREDIR) + +AC_ARG_ENABLE(isadir, +[ --enable-isadir=<path to arch-specific module files> [default ../../\`basename \$libdir\`/security]], +ISA=$enableval, +ISA=../../`basename $libdir`/security) +unset mylibdirbase +AC_DEFINE_UNQUOTED(_PAM_ISA,"$ISA",[Define to the path, relative to SECUREDIR, where PAMs specific to this architecture can be found.]) +AC_MSG_RESULT([Defining \$ISA to \"$ISA\".]) + +AC_ARG_ENABLE(sconfigdir, +[ --enable-sconfigdir=<path to module conf files> [default \$sysconfdir/security]], + SCONFIGDIR=$enableval, SCONFIGDIR=$sysconfdir/security) +AC_SUBST(SCONFIGDIR) + +AC_ARG_ENABLE(suplementedir, +[ --enable-suplementedir=<path to module helper binaries> [default \$sbindir]], + SUPLEMENTED=$enableval, SUPLEMENTED=$sbindir) +AC_SUBST(SUPLEMENTED) + +AC_ARG_ENABLE(includedir, +[ --enable-includedir=<path to include location> - where to put <security>], + INCLUDEDIR=$enableval, INCLUDEDIR=/usr/include) +AC_SUBST(INCLUDEDIR) + +AC_ARG_ENABLE(docdir, +[ --enable-docdir=<path to store documentation in - /usr/share/doc/pam>], + DOCDIR=$enableval, DOCDIR=/usr/share/doc/pam) +AC_SUBST(DOCDIR) + +AC_ARG_ENABLE(mandir, +[ --enable-mandir=<path to store manuals in - /usr/share/man>], + MANDIR=$enableval, MANDIR=/usr/share/man) +AC_SUBST(MANDIR) + +AC_ARG_ENABLE(pamlocking, +[ --enable-pamlocking configure libpam to observe a global authentication lock], + WITH_PAMLOCKING=yes ; AC_DEFINE(PAM_LOCKING) , WITH_PAMLOCKING=no) +AC_SUBST(WITH_PAMLOCKING) + +AC_ARG_ENABLE(uglyhack, +[ --enable-uglyhack configure libpam to try to honor old pam_strerror syntax], + AC_DEFINE(UGLY_HACK_FOR_PRIOR_BEHAVIOR_SUPPORT)) + +AC_ARG_ENABLE(read-both-confs, +[ --enable-read-both-confs read both /etc/pam.d and /etc/pam.conf files], + AC_DEFINE(PAM_READ_BOTH_CONFS)) +AC_SUBST(PAM_READ_BOTH_CONFS) + +AC_ARG_ENABLE(static-libpam, [ --enable-static-libpam build a libpam.a library], + STATIC_LIBPAM=yes , STATIC_LIBPAM=no) +AC_SUBST(STATIC_LIBPAM) + +AC_ARG_ENABLE(dynamic-libpam, +[ --disable-dynamic-libpam do not build a shared libpam library], + DYNAMIC_LIBPAM=no, DYNAMIC_LIBPAM=yes) +AC_SUBST(DYNAMIC_LIBPAM) + +DYNAMIC=-DPAM_DYNAMIC +AC_SUBST(DYNAMIC) + +AC_ARG_ENABLE(static-modules, +[ --enable-static-modules do not make the modules dynamically loadable], + STATIC=-DPAM_STATIC) +AC_SUBST(STATIC) + +AC_ARG_ENABLE(lckpwdf, +[ --disable-lckpwdf do not use the lckpwdf function], + WITH_LCKPWDF=no, WITH_LCKPWDF=yes) +AC_SUBST(WITH_LCKPWDF) + +AC_CHECK_HEADERS(paths.h) +AC_ARG_WITH(mailspool, +[ --with-mailspool path to mail spool directory + [default _PATH_MAILDIR if defined in paths.h, otherwise /var/spool/mail]], +with_mailspool=${withval}) +if test x$with_mailspool != x ; then + pam_mail_spool="\"$with_mailspool\"" +else + AC_TRY_RUN([ +#include <paths.h> +int main() { +#ifdef _PATH_MAILDIR +exit(0); +#else +exit(1); +#endif +}], pam_mail_spool="_PATH_MAILDIR", +pam_mail_spool="\"/var/spool/mail\"", +pam_mail_spool="\"/var/spool/mail\"") +fi +AC_DEFINE_UNQUOTED(PAM_PATH_MAILDIR, $pam_mail_spool) + +dnl Checks for libraries. +AC_CHECK_LIB(c, __libc_sched_setscheduler, PAM_NEEDS_LIBC=, PAM_NEEDS_LIBC=-lc) +AC_SUBST(PAM_NEEDS_LIBC) + +dnl Checks for the existence of lckpwdf in libc +AC_CHECK_LIB(c, lckpwdf, HAVE_LCKPWDF=yes, HAVE_LCKPWDF=no) +AC_SUBST(HAVE_LCKPWDF) + +dnl Checks for the existence of libdl - on BSD and Tru64 its part of libc +AC_CHECK_LIB(dl, dlopen, LIBDL=-ldl) +AC_SUBST(LIBDL) + +dnl +dnl At least on Solaris, the existing libcrack must be dynamic. +dnl Ought to introduce a check for this. +dnl +AC_CHECK_LIB(crack, FascistCheck, HAVE_LIBCRACK=yes ; AC_DEFINE(HAVE_LIBCRACK), + HAVE_LIBCRACK=no) +AC_SUBST(HAVE_LIBCRACK) + +AC_CHECK_LIB(crypt, crypt, HAVE_LIBCRYPT=yes ; AC_DEFINE(HAVE_LIBCRYPT), + HAVE_LIBCRYPT=no) +AC_SUBST(HAVE_LIBCRYPT) +AC_CHECK_LIB(util, logwtmp, HAVE_LIBUTIL=yes ; AC_DEFINE(HAVE_LIBUTIL), + HAVE_LIBUTIL=no) +AC_SUBST(HAVE_LIBUTIL) +AC_CHECK_LIB(ndbm, dbm_store, HAVE_LIBNDBM=yes ; AC_DEFINE(HAVE_LIBNDBM), + HAVE_LIBNDBM=no) +AC_SUBST(HAVE_LIBNDBM) +AC_CHECK_LIB(db, dbm_store, HAVE_LIBDB=yes ; AC_DEFINE(HAVE_LIBDB), + HAVE_LIBDB=no) +if test x$HAVE_LIBDB != xyes ; then + AC_CHECK_LIB(db, db_create, HAVE_LIBDB=yes ; AC_DEFINE(HAVE_LIBDB), + HAVE_LIBDB=no) +fi +AC_SUBST(HAVE_LIBDB) +AC_CHECK_LIB(fl, yylex, yyterminate, HAVE_LIBFL=yes ; AC_DEFINE(HAVE_LIBFL), + HAVE_LIBFL=no) +AC_SUBST(HAVE_LIBFL) +AC_CHECK_LIB(nsl, yp_maplist, HAVE_LIBNSL=yes ; AC_DEFINE(HAVE_LIBNSL), + HAVE_LIBNSL=no) +AC_SUBST(HAVE_LIBNSL) + +if test $HAVE_LIBNSL = yes ; then + pwdblibs="$pwdblibs -lnsl" +fi +AC_CHECK_LIB(pwdb, pwdb_db_name, HAVE_LIBPWDB=yes ; AC_DEFINE(HAVE_LIBPWDB), + HAVE_LIBPWDB=no,$pwdblibs) +AC_SUBST(HAVE_LIBPWDB) +unset pwdblibs + +AC_CHECK_LIB(fl, yywrap, HAVE_LIBFLEX=yes ; AC_DEFINE(HAVE_LIBFLEX), + HAVE_LIBFLEX=no) +AC_SUBST(HAVE_LIBFLEX) +AC_CHECK_LIB(l, yywrap, HAVE_LIBLEX=yes ; AC_DEFINE(HAVE_LIBLEX), + HAVE_LIBLEX=no) +AC_SUBST(HAVE_LIBLEX) + +dnl Checks for header files. +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(fcntl.h limits.h malloc.h sys/file.h sys/ioctl.h sys/time.h syslog.h termio.h unistd.h sys/fsuid.h) + +dnl Linux wants features.h in some of the source files. +AC_CHECK_HEADERS(features.h) + +dnl For module/pam_cracklib +AC_CHECK_HEADERS(crypt.h) + +dnl For module/pam_userdb +AC_CHECK_HEADERS(ndbm.h db.h) +dnl I suspect the following two lines are a hack. +HAVE_NDBM_H=$ac_cv_header_ndbm_h +AC_SUBST(HAVE_NDBM_H) + +dnl For module/pam_lastlog +AC_CHECK_HEADERS(lastlog.h utmp.h utmpx.h) + +dnl This following rule should be made conditional upon HAVE_LIBCRYPT +dnl being found. + +dnl Look for cracklib dictionary +AC_MSG_CHECKING(path to cracklib dictionary) +DICT_DIR_CANDIDATES="/usr/lib /usr/share/dict /usr/share/lib \ + /usr/local/lib /usr/local/share/lib /usr/share/cracklib" +DICT_FILE_CANDIDATES="pw_dict cracklib_dict" +CRACKLIB_DICTPATH="" +for d in $DICT_DIR_CANDIDATES ; do + for f in $DICT_FILE_CANDIDATES ; do + if test -r $d/$f.hwm ; then + CRACKLIB_DICTPATH=$d/$f + break 2 + elif test -r $d/dict/$f.hwm ; then + CRACKLIB_DICTPATH=$d/dict/$f + break 2 + fi + done +done +if test -z "$CRACKLIB_DICTPATH" ; then + AC_MSG_WARN([none found - pam_cracklib will not be built]) +else + AC_MSG_RESULT($CRACKLIB_DICTPATH) +fi +AC_SUBST(CRACKLIB_DICTPATH) + +dnl Set FLAGS, linker options etc. depending on C compiler. +dnl gcc is tested and much preferred; others less so, if at all +dnl +dnl If compiling with gcc, linking is also supposed to be done with gcc +dnl +dnl For the moment, gcc is enforced above at "CC=gcc". +dnl +dnl There is an issue over _POSIX_SOURCE _BSD_SOURCE and _GNU_SOURCE . +dnl The original "Linux-PAM" had blanket inclusion. But portability +dnl requires their default absence: if particular OSes require them, +dnl this should be done selectively. + +GCC_WARNINGS="-Wall -Wwrite-strings \ + -Wpointer-arith -Wcast-qual -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes \ + -Wnested-externs -Winline -Wshadow" + +if test "$GCC" = yes; then + CC=gcc ; AC_SUBST(CC) +### May need per-OS attention +### Example: -D_POSIX_SOURCE: needed on Linux but harms Solaris. + case $OS in + linux) + OS_CFLAGS= + LD_D="$CC -shared $LDFLAGS" + WARNINGS="$GCC_WARNINGS" + PIC="-fPIC" + DYNTYPE=so + LD=gcc + LD_L="$CC -shared $LDFLAGS" + RANLIB=: + STRIP=strip + CC_STATIC="-Xlinker -export-dynamic" + ;; + sunos) + OS_CFLAGS="-ansi -pedantic" + LD_D="gcc -shared -Xlinker -x" + WARNINGS="$GCC_WARNINGS" + PIC="-fPIC" + DYNTYPE=so + LD=ld + LD_L="$LD -x -shared" + RANLIB=ranlib + STRIP=strip + CC_STATIC="-Xlinker -export-dynamic" + ;; + aix) + OS_CFLAGS="" + DYNTYPE=lo + LD=ld + LD_L=ld -bexpall -bM:SRE -bnoentry + LD_D="$LD_L" + RANLIB=ranlib + STRIP=strip + ;; + *) + OS_CFLAGS="" + ;; + esac +else +### +### Non-gcc needs attention on per-OS basis +### + case "$OS" in + darwin) +# add some stuff here (see sourceforge bug 534205) +# DOCDIR=/System/Documentation/Administration/Libraries/PAM +# MANDIR=/usr/share/man + ;; + solaris) + ### Support for Solaris-C + OS_CFLAGS="" + WARNINGS="" + PIC="-K pic" + LD=ld + LD_D="cc -z text -G -R." + LD_L="$LD_D" + RANLIB=ranlib + STRIP=strip + CC_STATIC= + ;; + irix*) + OSRELEASE=`uname -r` + if test "$OSRELEASE" = 6.5; then + OS_CFLAGS="" + WARNINGS="-fullwarn" + PIC= #PIC code is default for IRIX + LD="cc -shared" # modules friendly approach + LD_D="cc -shared" + LD_L="ld -G -z redlocsym" + RANLIB=echo + STRIP=strip + CC_STATIC= + else + echo "IRIX prior to 6.5 not allowed for" + exit + fi + ;; + *) echo "Native compiler on $OS is not yet supported" + exit + ;; + esac +fi + +AC_SUBST(DYNTYPE) +AC_SUBST(OS_CFLAGS) +AC_SUBST(WARNINGS) +AC_SUBST(PIC) +AC_SUBST(LD) +AC_SUBST(LD_D) +AC_SUBST(LD_L) +AC_SUBST(RANLIB) +AC_SUBST(STRIP) +AC_SUBST(CC_STATIC) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_BIGENDIAN +AC_C_CONST +AC_TYPE_UID_T +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM + +dnl Checks for library functions. +AC_TYPE_GETGROUPS +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_FUNC_VPRINTF +AC_CHECK_FUNCS(gethostname gettimeofday mkdir select strcspn strdup strerror strspn strstr strtol uname) + +AC_CHECK_FUNCS(getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r getgrouplist) + +dnl Checks for programs/utilities +AC_CHECK_PROG(HAVE_SGML2TXT, sgml2txt, yes, no) +AC_CHECK_PROG(HAVE_SGML2HTML, sgml2html, yes, no) +AC_CHECK_PROG(HAVE_SGML2LATEX, sgml2latex, yes, no) +if test $HAVE_SGML2LATEX = "yes" ; then + if sgml2latex -h | grep -e --paper | grep ' -p ' > /dev/null ; then + PSER="sgml2latex -o ps" + else + PSER="sgml2latex -p" + fi + AC_CHECK_PROG(HAVE_PS2PDF, ps2pdf, yes, no) +else + AC_CHECK_PROG(HAVE_SGML2PS, sgml2ps, yes, no) + if test $HAVE_SGML2PS = yes ; then + PSER="sgml2ps" + fi +fi +AC_SUBST(PSER) +AC_SUBST(PS2PDF) + +dnl Files to be created from when we run configure +AC_OUTPUT(Make.Rules) diff --git a/Linux-PAM/defs/debian.defs b/Linux-PAM/defs/debian.defs new file mode 100644 index 00000000..19ba4663 --- /dev/null +++ b/Linux-PAM/defs/debian.defs @@ -0,0 +1,40 @@ +## +# defs for Debian +# Ben Collins <bcollins@debian.org> +## +# this file indicates the compiler and the various hardware/OS dependent +# flags for installation. It also defines the various destinations of +# installed files on the system. +## + +CFLAGS := -O2 -I${shell pwd}/include # -D__NO_STRING_INLINES +ifneq (,$(findstring $(DEB_BUILD_OPTIONS),debug DEBUG Debug)) + CFLAGS += -g +endif + +OS := $(shell dpkg-architecture -qDEB_BUILD_GNU_SYSTEM) +ARCH := $(shell dpkg-architecture -qDEB_BUILD_GNU_CPU) +CC := gcc +INSTALL := install +MKDIR := mkdir -p +ULIBS := +LD := ld +LD_D := gcc -shared -Xlinker -x +LD_L := $(LD) -x -shared +AR := ar -cr +RANLIB := ranlib +PREFIX := +LIBDIR := $(PREFIX)/lib +USESONAME := yes +SOSWITCH := -soname +LINKLIBS := -lc -L${shell pwd}/libpam -L${shell pwd}/libpam_misc +NEEDSONAME := no +LDCONFIG := /sbin/ldconfig +FAKEROOT := +SUPLEMENTED := $(PREFIX)/sbin +SECUREDIR := $(LIBDIR)/security +INCLUDED := /usr/include/security +CONFIGED := /etc +SCONFIGED := /etc/security +EXTRALS := -lnsl -lcrypt +WARNINGS := -Wall diff --git a/Linux-PAM/defs/hpux.defs b/Linux-PAM/defs/hpux.defs new file mode 100644 index 00000000..d8341983 --- /dev/null +++ b/Linux-PAM/defs/hpux.defs @@ -0,0 +1,36 @@ +## +# HPUX defs contributed by Derrick J Brashear <shadow@dementia.org> +## +# this file indicates the compiler and the various hardware/OS dependent +# flags for installation. It also defines the various destinations of +# installed files on the system. +# +# This file is the default version. Please look in .../defs/ for your +# preferred OS/vendor. + +OS=hpux9 +ARCH=hpux +CC=gcc +INSTALL=install +MKDIR=mkdir -p +CFLAGS=-g -DPAM_SHL -DHAVE_UTMP_H +ULIBS= +LD=ld +LD_D=$(LD) -b +LD_L=$(LD) -b +USESONAME=no +NEEDSONAME=no +LDCONFIG=: +AR=ar -cr +RANLIB=ranlib +FAKEROOT= +PREFIX=/usr +SUPLEMENTED=$(PREFIX)/sbin +LIBDIR=$(PREFIX)/lib +SECUREDIR=$(LIBDIR)/security +INCLUDED=/usr/include/security +CONFIGED=/etc +SCONFIGED=/etc/security +DYNLOAD="dld" +DYNTYPE="sl" +SHLIBMODE=755 diff --git a/Linux-PAM/defs/linux.defs b/Linux-PAM/defs/linux.defs new file mode 100644 index 00000000..0e274320 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/defs/morgan.defs b/Linux-PAM/defs/morgan.defs new file mode 100644 index 00000000..2b0cf289 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/defs/redhat.defs b/Linux-PAM/defs/redhat.defs new file mode 100644 index 00000000..a6ed36da --- /dev/null +++ b/Linux-PAM/defs/redhat.defs @@ -0,0 +1,36 @@ +## +# defs for Red Hat Linux +# Michael K. Johnson <johnsonm@redhat.com> +## +# this file indicates the compiler and the various hardware/OS dependent +# flags for installation. It also defines the various destinations of +# installed files on the system. +# +# This file is the version used for Red Hat Linux. + +OS=linux +ARCH=$(shell rpm --showrc | grep '^build arch' | sed 's/^.*: //g') +CC=gcc +INSTALL=install +MKDIR=mkdir -p +CFLAGS=$(RPM_OPT_FLAGS) -pipe -g +ULIBS=#-lefence +LD=ld +LD_D=gcc -shared -Xlinker -x +LD_L=$(LD) -x -shared +USESONAME=yes +SOSWITCH=-soname +LINKLIBS=-lc +NEEDSONAME=no +LDCONFIG=/sbin/ldconfig +AR=ar -cr +RANLIB=ranlib +FAKEROOT=$(RPM_BUILD_ROOT) +PREFIX= +SUPLEMENTED=$(PREFIX)/sbin +LIBDIR=$(PREFIX)/lib +SECUREDIR=$(LIBDIR)/security +INCLUDED=/usr/include/security +CONFIGED=/etc +SCONFIGED=/etc/security +EXTRALS=-lcrypt diff --git a/Linux-PAM/defs/redhat4.defs b/Linux-PAM/defs/redhat4.defs new file mode 100644 index 00000000..219abeb6 --- /dev/null +++ b/Linux-PAM/defs/redhat4.defs @@ -0,0 +1,35 @@ +## +# defs for Red Hat Linux +# Michael K. Johnson <johnsonm@redhat.com> +## +# this file indicates the compiler and the various hardware/OS dependent +# flags for installation. It also defines the various destinations of +# installed files on the system. +# +# This file is the version used for Red Hat Linux. + +OS=linux +ARCH=$(shell rpm --showrc | grep '^build arch' | sed 's/^.*: //g') +CC=gcc +INSTALL=install +MKDIR=mkdir -p +CFLAGS=$(RPM_OPT_FLAGS) -pipe -g +ULIBS=#-lefence +LD=ld +LD_D=gcc -shared -Xlinker -x +LD_L=$(LD) -x -shared +USESONAME=yes +SOSWITCH=-soname +LINKLIBS=-lc +NEEDSONAME=no +LDCONFIG=/sbin/ldconfig +AR=ar -cr +RANLIB=ranlib +FAKEROOT=$(RPM_BUILD_ROOT) +PREFIX= +SUPLEMENTED=$(PREFIX)/sbin +LIBDIR=$(PREFIX)/lib +SECUREDIR=$(LIBDIR)/security +INCLUDED=/usr/include/security +CONFIGED=/etc +SCONFIGED=/etc/security diff --git a/Linux-PAM/defs/solaris-2.1.5.defs b/Linux-PAM/defs/solaris-2.1.5.defs new file mode 100644 index 00000000..4624b604 --- /dev/null +++ b/Linux-PAM/defs/solaris-2.1.5.defs @@ -0,0 +1,45 @@ +## +# Solaris defs contributed by Josh Wilmes <josh@makita.jpl.nasa.gov> +## +# this file indicates the compiler and the various hardware/OS dependent +# flags for installation. It also defines the various destinations of +# installed files on the system. +# +# This file is the default version. Please look in .../defs/ for your +# preferred OS/vendor. + +# Please note that the linker used must be the GNU ld, not the native Sun +# linker. It is fairly common for the gnu linker (/usr/ccs/bin/ld) to be +# configured as the default linker for gcc. To tell gcc to use the +# gnu linker, you need to set the GCC_EXEC_PREFIX environment variable +# to point at the directory where the gnu linker is installed. Here's +# what I do: +# $ mkdir /tmp/foo +# $ ln -s /path/to/gnu/ld /tmp/foo/ld +# $ export GCC_EXEC_PREFIX=/tmp/foo/ +# $ export PATH=/tmp/foo:$PATH + +OS=solaris +ARCH=sun +CC=gcc +INSTALL=install +MKDIR=mkdir -p +CFLAGS=-O7 -pipe -g -D__EXTENSIONS__ -Dsolaris +ULIBS= +LD_D=gcc -shared -Xlinker -x +LD=ld +LD_L=$(LD) -G +USESONAME=yes +SOSWITCH=-h +NEEDSONAME=no +LDCONFIG=/sbin/echo +AR=ar -cr +RANLIB=ranlib +FAKEROOT= +PREFIX=/usr +SUPLEMENTED=$(PREFIX)/sbin +LIBDIR=$(PREFIX)/lib +SECUREDIR=$(LIBDIR)/security +INCLUDED=/usr/include/security +CONFIGED=/etc +SCONFIGED=/etc/security diff --git a/Linux-PAM/defs/solaris.defs b/Linux-PAM/defs/solaris.defs new file mode 100644 index 00000000..f9f26529 --- /dev/null +++ b/Linux-PAM/defs/solaris.defs @@ -0,0 +1,48 @@ +## +# Solaris defs contributed by Josh Wilmes <josh@makita.jpl.nasa.gov> +## +# this file indicates the compiler and the various hardware/OS dependent +# flags for installation. It also defines the various destinations of +# installed files on the system. +# +# This file is the default version. Please look in .../defs/ for your +# preferred OS/vendor. + +# Please note that the linker used must be the GNU ld, not the native Sun +# linker. It is fairly common for the gnu linker (/usr/ccs/bin/ld) to be +# configured as the default linker for gcc. To tell gcc to use the +# gnu linker, you need to set the GCC_EXEC_PREFIX environment variable +# to point at the directory where the gnu linker is installed. Here's +# what I do: +# $ mkdir /tmp/foo +# $ ln -s /path/to/gnu/ld /tmp/foo/ld +# $ export GCC_EXEC_PREFIX=/tmp/foo/ +# $ export PATH=/tmp/foo:$PATH + +OS=solaris +ARCH=sun +CC=cc +INSTALL=install +MKDIR=mkdir -p +WARNINGS = -D_POSIX_SOURCE +PIC=-KPIC +CFLAGS=-g -D__EXTENSIONS__ -Dsolaris +ULIBS= +LD=ld +LD_L=$(LD) -G +LD_D=$(LD_L) +RDYNAMIC= +USESONAME=yes +SOSWITCH=-h +NEEDSONAME=no +LDCONFIG=echo +AR=ar -cr +RANLIB=ranlib +FAKEROOT= +PREFIX=/usr +SUPLEMENTED=$(PREFIX)/sbin +LIBDIR=$(PREFIX)/lib +SECUREDIR=$(LIBDIR)/security +INCLUDED=/usr/include/security +CONFIGED=/etc +SCONFIGED=/etc/security diff --git a/Linux-PAM/defs/sunos.defs b/Linux-PAM/defs/sunos.defs new file mode 100644 index 00000000..158accc5 --- /dev/null +++ b/Linux-PAM/defs/sunos.defs @@ -0,0 +1,37 @@ +## +# SunOS defs contributed by Derrick J Brashear <shadow@dementia.org> +## +# this file indicates the compiler and the various hardware/OS dependent +# flags for installation. It also defines the various destinations of +# installed files on the system. +# +# This file is the SunOS version. Please look in .../defs/ for your +# preferred OS/vendor. + +OS=sunos +ARCH=sun +CC=gcc +INSTALL=install +MKDIR=mkdir -p +CFLAGS=-O2 -pipe -g -D__EXTENSIONS__ +ULIBS= +LD_D=gcc -shared -Xlinker -x +LD=ld +LD_L=$(LD) +USESONAME=no +NEEDSONAME=yes +LDCONFIG=/usr/etc/ldconfig +AR=ar cr +RANLIB=ranlib +FAKEROOT= +PREFIX=/usr +SUPLEMENTED=$(PREFIX)/sbin +LIBDIR=$(PREFIX)/lib +SECUREDIR=$(LIBDIR)/security +INCLUDED=/usr/include/security +CONFIGED=/etc +SCONFIGED=/etc/security +WARNINGS= -ansi -Wall -Wwrite-strings \ + -Wpointer-arith -Wcast-qual -Wcast-align \ + -Wtraditional -Wstrict-prototypes -Wmissing-prototypes \ + -Wnested-externs -Winline -Wshadow diff --git a/Linux-PAM/defs/suse.defs b/Linux-PAM/defs/suse.defs new file mode 100644 index 00000000..1fc6b741 --- /dev/null +++ b/Linux-PAM/defs/suse.defs @@ -0,0 +1,36 @@ +## +# defs for SuSE Linux +# Thorsten Kukuk <kukuk@suse.de> +## +# this file indicates the compiler and the various hardware/OS dependent +# flags for installation. It also defines the various destinations of +# installed files on the system. +# +# This file is the version used for SuSE Linux. + +OS=linux +ARCH=$(shell rpm --showrc | grep 'build arch' | grep -v "compatible" | sed 's/^.*: //g') +CC=gcc +INSTALL=install +MKDIR=mkdir -p +CFLAGS=$(RPM_OPT_FLAGS) -pipe -D_REENTRANT +ULIBS=#-lefence +LD=ld +LD_D=gcc -shared -Xlinker -x +LD_L=$(LD) -x -shared +USESONAME=yes +SOSWITCH=-soname +LINKLIBS=-lc +NEEDSONAME=yes +LDCONFIG=/sbin/ldconfig +AR=ar -cr +RANLIB=ranlib +FAKEROOT=$(RPM_BUILD_ROOT) +PREFIX= +SUPLEMENTED=$(PREFIX)/sbin +LIBDIR=$(PREFIX)/lib +SECUREDIR=$(LIBDIR)/security +INCLUDED=/usr/include/security +CONFIGED=/etc +SCONFIGED=/etc/security +EXTRALS=-lcrypt diff --git a/Linux-PAM/doc/CREDITS b/Linux-PAM/doc/CREDITS new file mode 100644 index 00000000..528032bb --- /dev/null +++ b/Linux-PAM/doc/CREDITS @@ -0,0 +1,49 @@ +<!-- + an sgml list of people to credit for their contributions to Linux-PAM + $Id: CREDITS,v 1.2 2001/03/19 01:46:41 agmorgan Exp $ + --> +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, +Michel D'Hooge, +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/Linux-PAM/doc/Makefile b/Linux-PAM/doc/Makefile new file mode 100644 index 00000000..1c2ba510 --- /dev/null +++ b/Linux-PAM/doc/Makefile @@ -0,0 +1,169 @@ + +### $Id: Makefile,v 1.10 2004/09/22 09:37:47 kukuk Exp $ + +include ../Make.Rules + +####################################################### + +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 +PDFFILES=pdf/pam.pdf ps/pam_appl.pdf ps/pam_modules.pdf + +MODULES=$(shell ls modules/*.sgml) + +####################################################### + +dummy: + @echo "Making the documentation..." + @$(MAKE) all + +# note, at this time we don't include pdf files by default, but you +# can type make pdf in this directory and see what happens in the pdf +# subdirectory. + +all: htmls texts postscript pdf + +htmls: $(HTMLS) + +$(HTMLS) : $(FSRCS) +ifeq ($(HAVE_SGML2HTML),yes) + @for i in $(FILES) ; do \ + if [ ! -f "html/$$i.html" ] || [ "$$i.sgml" -nt "html/$$i.html" ]; \ + then \ + cd html ; sgml2html ../$$i ; \ + if [ $$? -ne 0 ]; then exit 1 ; fi ; \ + cd .. ; \ + fi ; \ + done +else + @echo XXX - you do not have the sgml2html binary installed +endif + +texts: $(TEXTS) + +$(TEXTS) : $(FSRCS) +ifeq ($(HAVE_SGML2TXT),yes) + @for i in $(FILES) ; do \ + if [ ! -f "txts/$$i.txt" ] \ + || [ "$$i.sgml" -nt "txts/$$i.txt" ]; then \ + cd txts ; sgml2txt ../$$i ; cd .. ; \ + fi ; \ + done +else + @echo XXX - you do not have the sgml2txt binary installed +endif + +postscript: $(PSFILES) + +$(PSFILES): $(FSRCS) +ifneq ($(PSER),) + @for i in $(FILES) ; do \ + if [ ! -f "ps/$$i.ps" ] || [ "$$i.sgml" -nt "ps/$$i.ps" ]; then \ + cd ps ; $(PSER) ../$$i ; cd .. ; \ + fi ; \ + done +else + @echo XXX - neither sgml2ps nor sgml2latex binaries are installed +endif + +pdf: $(PDFFILES) + +$(PDFFILES) : $(PSFILES) +ifeq ($(HAVE_PS2PDF),yes) + @for i in $(FILES) ; do \ + if [ ! -f "pdf/$$i.pdf" ] || [ "ps/$$i.ps" -nt "ps/$$i.pdf" ]; then \ + ps2pdf ps/$$i.ps pdf/$$i.pdf ; \ + fi ; \ + done +else + @echo XXX - ps2pdf is not installed +endif + +pam.sgml: pam_source.sgml MODULES-SGML CREDITS + @sed -e '/^<!\-\- insert\-file MODULES\-SGML \-\->/r MODULES-SGML' pam_source.sgml | sed -e '/^<!\-\- insert\-file CREDITS \-\->/r CREDITS' > pam.sgml + +MODULES-SGML: $(MODULES) + @echo 'Building module text from files in modules/*.sgml' + @rm -f MODULES-SGML + @echo '<!-- modules included:' > MODULES-SGML + @ls modules/*.sgml >> MODULES-SGML + @echo ' and that is all -->' >> MODULES-SGML + @cat modules/*.sgml >> MODULES-SGML + +extraclean: clean + +remove: + cd man && for file in *.3 ; do \ + rm -f $(FAKEROOT)$(MANDIR)/man3/$$file ; \ + done + cd man && for file in *.8 ; do \ + rm -f $(FAKEROOT)$(MANDIR)/man8/$$file ; \ + done + cd txts && for file in *.txt; do \ + rm -f $(FAKEROOT)$(DOCDIR)/text/$$file ; \ + done + cd ps && for file in *.ps; do \ + rm -f $(FAKEROOT)$(DOCDIR)/ps/$$file ; \ + done + cd html && for file in *.html; do \ + rm -f $(FAKEROOT)$(DOCDIR)/html/$$file ; \ + done + +install: all +ifeq ($(HAVE_SGML2TXT),yes) + mkdir -p $(FAKEROOT)$(DOCDIR)/text + for file in txts/*.txt; do \ + install -m 644 $$file $(FAKEROOT)$(DOCDIR)/text ; \ + done +endif +ifneq ($(PSER),) + mkdir -p $(FAKEROOT)$(DOCDIR)/ps + for file in ps/*.ps; do \ + install -m 644 $$file $(FAKEROOT)$(DOCDIR)/ps ; \ + done +ifeq ($(HAVE_PS2PDF),yes) + mkdir -p $(FAKEROOT)$(DOCDIR)/pdf + for file in pdf/*.pdf; do \ + install -m 644 $$file $(FAKEROOT)$(DOCDIR)/pdf ; \ + done +endif +endif +ifeq ($(HAVE_SGML2HTML),yes) + mkdir -p $(FAKEROOT)$(DOCDIR)/html + for file in html/*.html; do \ + install -m 644 $$file $(FAKEROOT)$(DOCDIR)/html ; \ + done +endif + mkdir -p $(FAKEROOT)$(MANDIR)/man3 + mkdir -p $(FAKEROOT)$(MANDIR)/man8 + for file in man/*.3 ; do \ + install -m 644 $$file $(FAKEROOT)$(MANDIR)/man3 ; \ + done + for file in man/*.8 ; do \ + install -m 644 $$file $(FAKEROOT)$(MANDIR)/man8 ; \ + done + +spec: specs/draft-morgan-pam.raw + cd specs/formatter && $(MAKE) + specs/formatter/padout < specs/draft-morgan-pam.raw > specs/draft-morgan-pam-current.txt + +releasedocs: all spec + tar zvfc Linux-PAM-$(MAJOR_REL).$(MINOR_REL)-docs.tar.gz \ + --exclude CVS --exclude .cvsignore --exclude '.#*' \ + html ps txts specs/draft-morgan-pam-current.txt + +clean: + rm -f *~ *.bak + rm -f html/pam*.html + rm -f man/*~ + rm -f $(TEXTS) + rm -f $(PSFILES) ps/missfont.log + rm -f pdf/*.pdf + rm -f MODULES-SGML pam.sgml + rm -f specs/draft-morgan-pam-current.txt + $(MAKE) -C specs/formatter clean + diff --git a/Linux-PAM/doc/NOTES b/Linux-PAM/doc/NOTES new file mode 100644 index 00000000..b0f40d47 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/doc/figs/pam_orient.txt b/Linux-PAM/doc/figs/pam_orient.txt new file mode 100644 index 00000000..a8b745a1 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/doc/html/index.html b/Linux-PAM/doc/html/index.html new file mode 100644 index 00000000..5cb1e0f0 --- /dev/null +++ b/Linux-PAM/doc/html/index.html @@ -0,0 +1,21 @@ + +<HTML> +<HEAD> +<TITLE>Linux-PAM - Pluggable Authentication Modules for Linux</TITLE> +</HEAD> +<BODY> + +<p> +Here is the documentation for Linux-PAM. As you will see it is +currently not complete. However, in order of decreasing length: + +<ul> +<li> <a href="pam.html">The System Administrators' Guide</a> +<li> <a href="pam_modules.html">The Module Writers' Manual</a> +<li> <a href="pam_appl.html">The Application developers' Manual</a> +</ul> + +<hr> +<p> +REVISION: <tt>$Id: index.html,v 1.1.1.1 2000/06/20 22:10:56 agmorgan Exp $</tt> +</BODY> diff --git a/Linux-PAM/doc/man/pam.8 b/Linux-PAM/doc/man/pam.8 new file mode 100644 index 00000000..81f9eb35 --- /dev/null +++ b/Linux-PAM/doc/man/pam.8 @@ -0,0 +1,375 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" $Id: pam.8,v 1.3 2003/09/25 17:49:29 baggins Exp $ +.\" Copyright (c) Andrew G. Morgan 1996-7,2001 <morgan@kernel.org> +.TH PAM 8 "2001 Jan 20" "Linux-PAM 0.74" "Linux-PAM Manual" +.SH NAME + +Linux-PAM \- Pluggable Authentication Modules for Linux + +.SH SYNOPSIS +.B /etc/pam.conf +.sp 2 +.SH DESCRIPTION + +This manual is intended to offer a quick introduction to +.BR Linux-PAM ". " +For more information the reader is directed to the +.BR "Linux-PAM system administrators' guide". + +.sp +.BR Linux-PAM +Is a system of libraries that handle the authentication tasks of +applications (services) on the system. The library provides a stable +general interface (Application Programming Interface - API) that +privilege granting programs (such as +.BR login "(1) " +and +.BR su "(1)) " +defer to to perform standard authentication tasks. + +.sp +The principal feature of the PAM approach is that the nature of the +authentication is dynamically configurable. In other words, the +system administrator is free to choose how individual +service-providing applications will authenticate users. This dynamic +configuration is set by the contents of the single +.BR Linux-PAM +configuration file +.BR /etc/pam.conf "." +Alternatively, the configuration can be set by individual +configuration files located in the +.B /etc/pam.d/ +directory. +.IB "The presence of this directory will cause " Linux-PAM " to ignore" +.BI /etc/pam.conf "." + +.sp +From the point of view of the system administrator, for whom this +manual is provided, it is not of primary importance to understand the +internal behavior of the +.BR Linux-PAM +library. The important point to recognize is that the configuration +file(s) +.I define +the connection between applications +.BR "" "(" services ")" +and the pluggable authentication modules +.BR "" "(" PAM "s)" +that perform the actual authentication tasks. + +.sp +.BR Linux-PAM +separates the tasks of +.I authentication +into four independent management groups: +.BR "account" " management; " +.BR "auth" "entication management; " +.BR "password" " management; " +and +.BR "session" " management." +(We highlight the abbreviations used for these groups in the +configuration file.) + +.sp +Simply put, these groups take care of different aspects of a typical +user's request for a restricted service: + +.sp +.BR account " - " +provide account verification types of service: has the user's password +expired?; is this user permitted access to the requested service? + +.br +.BR auth "entication - " +establish the user is who they claim to be. Typically this is via some +challenge-response request that the user must satisfy: if you are who +you claim to be please enter your password. Not all authentications +are of this type, there exist hardware based authentication schemes +(such as the use of smart-cards and biometric devices), with suitable +modules, these may be substituted seamlessly for more standard +approaches to authentication - such is the flexibility of +.BR Linux-PAM "." + +.br +.BR password " - " +this group's responsibility is the task of updating authentication +mechanisms. Typically, such services are strongly coupled to those of +the +.BR auth +group. Some authentication mechanisms lend themselves well to being +updated with such a function. Standard UN*X password-based access is +the obvious example: please enter a replacement password. + +.br +.BR session " - " +this group of tasks cover things that should be done prior to a +service being given and after it is withdrawn. Such tasks include the +maintenance of audit trails and the mounting of the user's home +directory. The +.BR session +management group is important as it provides both an opening and +closing hook for modules to affect the services available to a user. + +.SH The configuration file(s) + +When a +.BR Linux-PAM +aware privilege granting application is started, it activates its +attachment to the PAM-API. This activation performs a number of +tasks, the most important being the reading of the configuration file(s): +.BR /etc/pam.conf "." +Alternatively, this may be the contents of the +.BR /etc/pam.d/ +directory. + +These files list the +.BR PAM "s" +that will do the authentication tasks required by this service, and +the appropriate behavior of the PAM-API in the event that individual +.BR PAM "s " +fail. + +.sp +The syntax of the +.B /etc/pam.conf +configuration file is as follows. The file is made +up of a list of rules, each rule is typically placed on a single line, +but may be extended with an escaped end of line: `\\<LF>'. Comments +are preceded with `#' marks and extend to the next end of line. + +.sp +The format of each rule is a space separated collection of tokens, the +first three being case-insensitive: + +.sp +.br +.BR " service type control module-path module-arguments" + +.sp +The syntax of files contained in the +.B /etc/pam.d/ +directory, are identical except for the absence of any +.I service +field. In this case, the +.I service +is the name of the file in the +.B /etc/pam.d/ +directory. This filename must be in lower case. + +.sp +An important feature of +.BR Linux-PAM ", " +is that a number of rules may be +.I stacked +to combine the services of a number of PAMs for a given authentication +task. + +.sp +The +.BR service +is typically the familiar name of the corresponding application: +.BR login +and +.BR su +are good examples. The +.BR service "-name, " other ", " +is reserved for giving +.I default +rules. Only lines that mention the current service (or in the absence +of such, the +.BR other +entries) will be associated with the given service-application. + +.sp +The +.BR type +is the management group that the rule corresponds to. It is used to +specify which of the management groups the subsequent module is to +be associated with. Valid entries are: +.BR account "; " +.BR auth "; " +.BR password "; " +and +.BR session "." +The meaning of each of these tokens was explained above. + +.sp +The third field, +.BR control ", " +indicates the behavior of the PAM-API should the module fail to +succeed in its authentication task. There are two types of syntax for +this control field: the simple one has a single simple keyword; the +more complicated one involves a square-bracketed selection of +.B value=action +pairs. + +.sp +For the simple (historical) syntax 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 +New control directive first introduced in ALT Linux is +.BR include +- include all lines of given type from the configuration +file specified as an argument to this control. + +.sp +For the more complicated syntax valid +.B control +values have the following form: +.sp +.RB [value1=action1 value2=action2 ...] +.sp +Where +.B valueN +corresponds to the return code from the function invoked in the module +for which the line is defined. It is selected from one of these: +.BR success ; +.BR open_err ; +.BR symbol_err ; +.BR service_err ; +.BR system_err ; +.BR buf_err ; +.BR perm_denied ; +.BR auth_err ; +.BR cred_insufficient ; +.BR authinfo_unavail ; +.BR user_unknown ; +.BR maxtries ; +.BR new_authtok_reqd ; +.BR acct_expired ; +.BR session_err ; +.BR cred_unavail ; +.BR cred_expired ; +.BR cred_err ; +.BR no_module_data ; +.BR conv_err ; +.BR authtok_err ; +.BR authtok_recover_err ; +.BR authtok_lock_busy ; +.BR authtok_disable_aging ; +.BR try_again ; +.BR ignore ; +.BR abort ; +.BR authtok_expired ; +.BR module_unknown ; +.BR bad_item "; and" +.BR default . +The last of these, +.BR default , +implies 'all +.BR valueN 's +not mentioned explicitly. Note, the full list of PAM errors is +available in /usr/include/security/_pam_types.h . The +.B actionN +can be: an unsigned integer, +.BR J , +signifying an action of 'jump over the next J modules in the stack'; +or take one of the following forms: +.br +.B ignore +- when used with a stack of modules, the module's return status will +not contribute to the return code the application obtains; +.br +.B 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. +.br +.B die +- equivalent to bad with the side effect of terminating the module +stack and PAM immediately returning to the application. +.br +.B ok +- this tells 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 +.BR 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. +.br +.B done +- equivalent to ok with the side effect of terminating the module +stack and PAM immediately returning to the application. +.br +.B reset +- clear all memory of the state of the module stack and start again +with the next stacked module. + +.sp +.BR module-path +- this is either the full filename of the PAM to be used by the +application (it begins with a '/'), or a relative pathname from the +default module location: +.BR /lib/security/ . + +.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. Generally, if this directory is present, the +.B /etc/pam.conf +file is ignored. +.br +.BR /lib/libpam.so.X " - the dynamic library" +.br +.BR /lib/security/*.so " - the PAMs + +.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, but remains backwardly compatible with +this RFC. + +.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/Linux-PAM/doc/man/pam.conf.8 b/Linux-PAM/doc/man/pam.conf.8 new file mode 100644 index 00000000..d067b559 --- /dev/null +++ b/Linux-PAM/doc/man/pam.conf.8 @@ -0,0 +1 @@ +.so pam.8 diff --git a/Linux-PAM/doc/man/pam.d.8 b/Linux-PAM/doc/man/pam.d.8 new file mode 100644 index 00000000..d067b559 --- /dev/null +++ b/Linux-PAM/doc/man/pam.d.8 @@ -0,0 +1 @@ +.so pam.8 diff --git a/Linux-PAM/doc/man/pam_authenticate.3 b/Linux-PAM/doc/man/pam_authenticate.3 new file mode 100644 index 00000000..7383f5f0 --- /dev/null +++ b/Linux-PAM/doc/man/pam_authenticate.3 @@ -0,0 +1,91 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" $Id: pam_authenticate.3,v 1.1.1.1 2000/06/20 22:10:57 agmorgan Exp $ +.\" Copyright (c) Andrew G. Morgan 1996-7 <morgan@parc.power.net> +.TH PAM_AUTHENTICATE 3 "1996 Dec 9" "Linux-PAM 0.55" "App. Programmers' Manual" +.SH NAME + +pam_authenticate \- authenticate a user + +.SH SYNOPSIS +.B #include <security/pam_appl.h> +.sp +.BI "int pam_authenticate(pam_handle_t " *pamh ", int " flags ");" +.sp 2 +.SH DESCRIPTION +.B pam_authenticate + +.br +Use this function to authenticate an applicant user. It is linked +.I dynamically +to the authentication modules by +.BR Linux-PAM ". " +It is the task of these module to perform such an authentication. The +specific nature of the authentication is not the concern of the +application. + +.br +Following successful completion, the +.BR name +of the authenticated user will be present in the +.BR Linux-PAM +item +.BR PAM_USER ". " +This item may be recovered with a call to +.BR pam_get_item "(3)." + +.br +The application developer should note that the modules may request +that the user enter their username via the conversation mechanism (see +.BR pam_start "(3))." +Should this be the case, the user-prompt string can be set via +the +.BR PAM_USER_PROMPT +item (see +.BR pam_set_item "(3))." + +.SH "RETURN VALUE" +On success +.BR PAM_SUCCESS +is returned. All other returns should be considered +authentication failures and will be +.I delayed +by an amount specified with prior calls to +.BR pam_fail_delay "(3). " +Specific failures that demand special attention are the following: +.TP +.B PAM_ABORT +the application should exit immediately. Of course, +.BR pam_end "(3)" +should be called first. + +.TP +.B PAM_MAXTRIES +the application has tried too many times to authenticate the +user, authentication should not be attempted again. + +.SH ERRORS +May be translated to text with +.BR pam_strerror "(3). " + +.SH "CONFORMING TO" +DCE-RFC 86.0, October 1995. + +.SH BUGS +.sp 2 +none known. + +.SH "SEE ALSO" + +.BR pam_start "(3), " +.BR pam_get_item "(3) " +.BR pam_fail_delay "(3) " +and +.BR pam_strerror "(3). " + +Also, see the three +.BR Linux-PAM +Guides, for +.BR "System administrators" ", " +.BR "module developers" ", " +and +.BR "application developers" ". " diff --git a/Linux-PAM/doc/man/pam_chauthtok.3 b/Linux-PAM/doc/man/pam_chauthtok.3 new file mode 100644 index 00000000..a0466f0f --- /dev/null +++ b/Linux-PAM/doc/man/pam_chauthtok.3 @@ -0,0 +1,101 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" $Id: pam_chauthtok.3,v 1.1.1.1 2000/06/20 22:10:57 agmorgan Exp $ +.\" Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net> +.TH PAM_CHAUTHTOK 3 "1997 Jan 4" "Linux-PAM 0.55" "App. Programmers' Manual" +.SH NAME + +pam_chauthtok \- updating authentication tokens + +.SH SYNOPSIS +.B #include <security/pam_appl.h> +.sp +.BI "int pam_chauthtok(pam_handle_t " *pamh ", int " flags ");" +.sp 2 +.SH DESCRIPTION +.B pam_chauthtok + +.br +Use this function to rejuvenate the authentication tokens (passwords +etc.) of an applicant user. + +.br +Note, the application should not pre-authenticate the user, as this is +performed (if required) by the +.BR Linux-PAM +framework. + +.br +The +.I flags +argument can +.I optionally +take the value, +.BR PAM_CHANGE_EXPIRED_AUTHTOK "." +In such cases the framework is only required to update those +authentication tokens that have expired. Without this argument, the +framework will attempt to obtain new tokens for all configured +authentication mechanisms. The details of the types and number of such +schemes should not concern the calling application. + +.SH RETURN VALUE +A successful return from this function will be indicated with +.BR PAM_SUCCESS "." + +.br +Specific errors of special interest when calling this function are + +.br +.BR PAM_AUTHTOK_ERROR +- a valid new token was not obtained + +.br +.BR PAM_AUTHTOK_RECOVERY_ERR +- old authentication token was not available + +.br +.BR PAM_AUTHTOK_LOCK_BUSY +- a resource needed to update the token was locked (try again later) + +.br +.BR PAM_AUTHTOK_DISABLE_AGING +- one or more of the authentication modules does not honor +authentication token aging + +.br +.BR PAM_TRY_AGAIN +- one or more authentication mechanism is not prepared to update a +token at this time + +.br +In general other return values may be returned. They should be treated +as indicating failure. + +.SH ERRORS +May be translated to text with +.BR pam_strerror "(3). " + +.SH "CONFORMING TO" +DCE-RFC 86.0, October 1995. + +.SH BUGS +.sp 2 +none known. + +.SH "SEE ALSO" + +.BR pam_start "(3), " +.BR pam_authenticate "(3), " +.BR pam_setcred "(3), " +.BR pam_get_item "(3), " +.BR pam_strerror "(3) " +and +.BR pam "(8)." + +.br +Also, see the three +.BR Linux-PAM +Guides, for +.BR "System administrators" ", " +.BR "module developers" ", " +and +.BR "application developers" ". " diff --git a/Linux-PAM/doc/man/pam_close_session.3 b/Linux-PAM/doc/man/pam_close_session.3 new file mode 100644 index 00000000..d851700c --- /dev/null +++ b/Linux-PAM/doc/man/pam_close_session.3 @@ -0,0 +1 @@ +.so pam_open_session.3 diff --git a/Linux-PAM/doc/man/pam_end.3 b/Linux-PAM/doc/man/pam_end.3 new file mode 100644 index 00000000..de999f24 --- /dev/null +++ b/Linux-PAM/doc/man/pam_end.3 @@ -0,0 +1 @@ +.so pam_start.3 diff --git a/Linux-PAM/doc/man/pam_fail_delay.3 b/Linux-PAM/doc/man/pam_fail_delay.3 new file mode 100644 index 00000000..3b72f3d9 --- /dev/null +++ b/Linux-PAM/doc/man/pam_fail_delay.3 @@ -0,0 +1,130 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" $Id: pam_fail_delay.3,v 1.1.1.1 2000/06/20 22:10:58 agmorgan Exp $ +.\" Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net> +.TH PAM_FAIL_DELAY 3 "1997 Jan 12" "Linux-PAM 0.56" "Programmers' Manual" +.SH NAME + +pam_fail_delay \- request a delay on failure + +.SH SYNOPSIS +.B #include <security/pam_appl.h> +.br +or, +.br +.B #include <security/pam_modules.h> +.sp +.BI "int pam_fail_delay(pam_handle_t " "*pamh" ", unsigned int " "usec" ");" +.sp 2 +.SH DESCRIPTION +.br +It is often possible to attack an authentication scheme by exploiting +the time it takes the scheme to deny access to an applicant user. In +cases of +.I short +timeouts, it may prove possible to attempt a +.I brute force +dictionary attack -- with an automated process, the attacker tries all +possible passwords to gain access to the system. In other cases, +where individual failures can take measurable amounts of time +(indicating the nature of the failure), an attacker can obtain useful +information about the authentication process. These latter attacks +make use of procedural delays that constitute a +.I covert channel +of useful information. + +.br +To minimize the effectiveness of such attacks, it is desirable to +introduce a random delay in a failed authentication process. +.B Linux-PAM +provides such a facility. The delay occurs upon failure of the +.BR pam_authenticate "(3) " +and +.BR pam_chauthtok "(3) " +functions. It occurs +.I after +all authentication modules have been called, but +.I before +control is returned to the service application. + +.br +The function, +.BR pam_fail_delay "(3)," +is used to specify a required minimum for the length of the +failure-delay; the +.I usec +argument. This function can be called by the service application +and/or the authentication modules, both may have an interest in +delaying a reapplication for service by the user. The length of the +delay is computed at the time it is required. Its length is +pseudo-gausianly distributed about the +.I maximum +requested value; the resultant delay will differ by as much as 25% of +this maximum requested value (both up and down). + +.br +On return from +.BR pam_authenticate "(3) or " pam_chauthtok "(3)," +independent of success or failure, the new requested delay is reset to +its default value: zero. + +.SH EXAMPLE +.br +For example, a +.B login +application may require a failure delay of roughly 3 seconds. It will +contain the following code: +.sp +.br +.B " pam_fail_delay(pamh, 3000000 /* micro-seconds */ );" +.br +.B " pam_authenticate(pamh, 0);" +.sp +.br +if the modules do not request a delay, the failure delay will be +between 2.25 and 3.75 seconds. + +.br +However, the modules, invoked in the authentication process, may +also request delays: +.sp +.br +.RB " (module #1) " "pam_fail_delay(pamh, 2000000);" +.sp +.br +.RB " (module #2) " "pam_fail_delay(pamh, 4000000);" +.sp +.br +in this case, it is the largest requested value that is used to +compute the actual failed delay: here between 3 and 5 seconds. + +.SH "RETURN VALUE" +Following a successful call to +.BR pam_fail_delay "(3), " PAM_SUCCESS +is returned. All other returns should be considered serious failures. + +.SH ERRORS +May be translated to text with +.BR pam_strerror "(3). " + +.SH "CONFORMING TO" +Under consideration by the X/Open group for future inclusion in the +PAM RFC. 1996/1/10 + +.SH BUGS +.sp 2 +none known. + +.SH "SEE ALSO" + +.BR pam_start "(3), " +.BR pam_get_item "(3) " +and +.BR pam_strerror "(3). " + +Also, see the three +.BR Linux-PAM +Guides, for +.BR "System administrators" ", " +.BR "module developers" ", " +and +.BR "application developers" ". " diff --git a/Linux-PAM/doc/man/pam_get_item.3 b/Linux-PAM/doc/man/pam_get_item.3 new file mode 100644 index 00000000..f4f0d462 --- /dev/null +++ b/Linux-PAM/doc/man/pam_get_item.3 @@ -0,0 +1 @@ +.so pam_set_item.3 diff --git a/Linux-PAM/doc/man/pam_open_session.3 b/Linux-PAM/doc/man/pam_open_session.3 new file mode 100644 index 00000000..53f6adf1 --- /dev/null +++ b/Linux-PAM/doc/man/pam_open_session.3 @@ -0,0 +1,99 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" $Id: pam_open_session.3,v 1.1.1.1 2000/06/20 22:10:58 agmorgan Exp $ +.\" Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net> +.TH PAM_OPEN_SESSION 3 "1997 Jan 4" "Linux-PAM 0.55" "App. Programmers' Manual" +.SH NAME + +pam_open/close_session \- PAM session management + +.SH SYNOPSIS +.B #include <security/pam_appl.h> +.sp +.BI "int pam_open_session(pam_handle_t " *pamh ", int " flags ");" +.sp +.BI "int pam_close_session(pam_handle_t " *pamh ", int " flags ");" +.sp 2 +.SH DESCRIPTION + +PAM provides management-hooks for the initialization and termination +of a session. + +.TP +.B pam_open_session +.br +Use this function to signal that an authenticated user session has +begun. It should be called only after the user is properly identified +and (where necessary) has been granted their credentials with +.BR pam_authenticate "(3)" +and +.BR pam_setcred "(3)" +respectively. + +.br +Some types of functions associated with session +initialization are logging for the purposes of system-audit and +mounting directories (the user's home directory for example). These +should not concern the application. It should be noted that the +.I effective +uid, +.BR geteuid "(2)," +of the application should be of sufficient privilege to perform such +tasks. + +.TP +.B pam_close_session +.br +Use this function to signal that a user session has +terminated. In general this function may not need to be located in the +same application as the initialization function, +.BR pam_open_session "." + +.br +Typically, this function will undo the actions of +.BR pam_open_session "." +That is, log audit information concerning the end of the user session +or unmount the user's home directory. Apart from having sufficient +privilege the details of the session termination should not concern +the calling application. It is good programming practice, however, to +cease acting on behalf of the user on returning from this call. + +.SH RETURN VALUE +A successful return from the session management functions will be +indicated with +.BR PAM_SUCCESS "." + +.br +The specific error indicating a failure to open or close a session is +.BR PAM_SESSION_ERR "." +In general other return values may be returned. They should be treated +as indicating failure. + +.SH ERRORS +May be translated to text with +.BR pam_strerror "(3). " + +.SH "CONFORMING TO" +OSF-RFC 86.0, October 1995. + +.SH BUGS +.sp 2 +none known. + +.SH "SEE ALSO" + +.BR pam_start "(3), " +.BR pam_authenticate "(3), " +.BR pam_setcred "(3), " +.BR pam_get_item "(3), " +.BR pam_strerror "(3) " +and +.BR pam "(3)." + +.br +Also, see the three +.BR Linux-PAM +Guides, for +.BR "System administrators" ", " +.BR "module developers" ", " +and +.BR "application developers" ". " diff --git a/Linux-PAM/doc/man/pam_set_item.3 b/Linux-PAM/doc/man/pam_set_item.3 new file mode 100644 index 00000000..ddd081fe --- /dev/null +++ b/Linux-PAM/doc/man/pam_set_item.3 @@ -0,0 +1,55 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" $Id: pam_set_item.3,v 1.1 2001/12/08 19:02:48 agmorgan Exp $ +.\" Copyright (c) Andrew G. Morgan 1996,1997 <morgan@kernel.org> +.TH PAM_SET_ITEM 3 "2001 Jan 21" "Linux-PAM" "App. Programmers' Manual" +.SH NAME + +pam_set_item, pam_get_item \- item manipulation under PAM + +.SH SYNOPSIS +.B #include <security/pam_appl.h> +.br +or +.br +.B #include <secruity/pam_modules.h> +.sp +.BI "int pam_set_item(pam_handle_t " *pamh ", int " item_type ", void " *item ");" +.sp +.BI "int pam_get_item(const pam_handle_t " *pamh ", int " item_type ", const void " **item_p ");" +.sp 2 +.SH DESCRIPTION +.B pam_set_item +.sp +.B pam_set_item + +These functions are currently undocumented in a man page, but see the +end of this man page for more information (the PAM guides). + +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 (8) +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/Linux-PAM/doc/man/pam_setcred.3 b/Linux-PAM/doc/man/pam_setcred.3 new file mode 100644 index 00000000..ea251405 --- /dev/null +++ b/Linux-PAM/doc/man/pam_setcred.3 @@ -0,0 +1,79 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" $Id: pam_setcred.3,v 1.1.1.1 2000/06/20 22:10:58 agmorgan Exp $ +.\" Copyright (c) Andrew G. Morgan 1996,1997 <morgan@parc.power.net> +.TH PAM_SETCRED 3 "1997 July 6" "Linux-PAM 0.58" "App. Programmers' Manual" +.SH NAME + +pam_setcred \- set the credentials for the user + +.SH SYNOPSIS +.B #include <security/pam_appl.h> +.sp +.BI "int pam_setcred(pam_handle_t " *pamh ", int " flags ");" +.sp 2 +.SH DESCRIPTION +.B pam_setcred + +This function is used to establish, maintain and delete the +credentials of a user. It should be called after a user has been +authenticated and before a session is opened for the user (with +.BR pam_open_session "(3))." + +It should be noted that credentials come in many forms. Examples +include: group memberships; ticket-files; and Linux-PAM environment +variables. For this reason, it is important that the basic identity +of the user is established, by the application, prior to a call to +this function. For example, the default +.BR Linux-PAM +environment variables should be set and also +.BR initgroups "(2) " +(or equivalent) should have been performed. + +.SH "VALID FLAGS" +.TP +.BR PAM_ESTABLISH_CRED +initialize the credentials for the user. + +.TP +.BR PAM_DELETE_CRED +delete the user's credentials. + +.TP +.BR PAM_REINITIALIZE_CRED +delete and then initialize the user's credentials. + +.TP +.BR PAM_REFRESH_CRED +extend the lifetime of the existing credentials. + +.SH "RETURN VALUE" + +On success +.BR PAM_SUCCESS +is returned, all other return values should be treated as errors. + +.SH ERRORS +May be translated to text with +.BR pam_strerror "(3). " + +.SH "CONFORMING TO" +DCE-RFC 86.0, October 1995. + +.SH BUGS +.sp 2 +none known. + +.SH "SEE ALSO" + +.BR pam_authenticate "(3), " +.BR pam_strerror "(3)" +and +.BR pam_open_session "(3). " + +Also, see the three +.BR Linux-PAM +Guides, for +.BR "System administrators" ", " +.BR "module developers" ", " +and +.BR "application developers" ". " diff --git a/Linux-PAM/doc/man/pam_start.3 b/Linux-PAM/doc/man/pam_start.3 new file mode 100644 index 00000000..a912cc75 --- /dev/null +++ b/Linux-PAM/doc/man/pam_start.3 @@ -0,0 +1,98 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" $Id: pam_start.3,v 1.1.1.1 2000/06/20 22:10:58 agmorgan Exp $ +.\" Copyright (c) Andrew G. Morgan 1996-7 <morgan@parc.power.net> +.TH PAM_START 3 "1997 Feb 15" "Linux-PAM 0.56" "Application Programmers' Manual" +.SH NAME + +pam_start, pam_end \- activating Linux-PAM + +.SH SYNOPSIS +.B #include <security/pam_appl.h> +.sp +.BI "int pam_start(const char " *service ", const char " *user ", const struct pam_conv " *conv ", pam_handle_t " **pamh_p ");" +.sp +.BI "int pam_end(pam_handle_t " *pamh ", int " pam_status ");" +.sp 2 +.SH DESCRIPTION +.TP +.B pam_start +Initialize the +.I Linux-PAM +library. Identifying the application with a particular +.IR service +name. The +.IR user "name" +can take the value +.IR NULL ", " +if not known at the time the interface is initialized. The +conversation structure is passed to the library via the +.IR conv +argument. (For a complete description of this and other structures +the reader is directed to the more verbose +.IR Linux-PAM +application developers' guide). Upon successful initialization, an +opaque pointer-handle for future access to the library is returned +through the contents of the +.IR pamh_p +pointer. + +.TP +.B pam_end +Terminate the +.B Linux-PAM +library. The service application associated with the +.IR pamh +handle, is terminated. The argument, +.IR pam_status ", " +passes the value most recently returned to the application from the +library; it indicates the manner in which the library should be +shutdown. Besides carrying a return value, this argument may be +logically OR'd with +.IR PAM_DATA_SILENT +to indicate that the module should not treat the call too +seriously. It is generally used to indicate that the current closing +of the library is in a +.IR fork "(2)ed" +process, and that the parent will take care of cleaning up things that +exist outside of the current process space (files etc.). + +.SH "RETURN VALUE" +.TP +.B pam_start +.TP +.B pam_end +On success, +.BR PAM_SUCCESS +is returned + +.SH ERRORS +May be translated to text with +.BR pam_strerror "(3). " + +.SH "CONFORMING TO" +DCE-RFC 86.0, October 1995. +.sp +Note, the +.BR PAM_DATA_SILENT +flag is pending acceptance with the DCE (as of 1996/12/4). + +.SH BUGS +.sp 2 +None known. + +.SH "SEE ALSO" + +.BR fork "(2), " +.BR pam_authenticate "(3), " +.BR pam_acct_mgmt "(3), " +.BR pam_open_session "(3), " +and +.BR pam_chauthtok "(3)." + +Also, see the three +.BR Linux-PAM +Guides, for +.BR "System administrators" ", " +.BR "module developers" ", " +and +.BR "application developers" ". " diff --git a/Linux-PAM/doc/man/pam_strerror.3 b/Linux-PAM/doc/man/pam_strerror.3 new file mode 100644 index 00000000..b2318f28 --- /dev/null +++ b/Linux-PAM/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: pam_strerror.3,v 1.1.1.1 2000/06/20 22:10:58 agmorgan Exp $ +.\" Copyright (c) Andrew G. Morgan 1996-7 <morgan@linux.kernel.org> +.TH PAM_STRERROR 3 "1999 Oct 4" "Linux-PAM 0.70" "Programmers' Manual" +.SH NAME + +pam_strerror \- return a textual description of a Linux-PAM error + +.SH SYNOPSIS +.B #include <security/pam_appl.h> +.br +or, +.br +.B #include <security/pam_modules.h> +.sp +.BI "const char * pam_strerror( pam_handle_t " "*pamh" ", int " pam_error ");" +.sp 2 +.SH DESCRIPTION +.B pam_strerror + +This function returns some text describing the +.BR Linux-PAM +error associated with the +.B pam_error +argument. + +.SH "RETURN VALUE" + +On success this function returns a description of the indicated +error. Should the function not recognize the error, ``Unknown +Linux-PAM error'' is returned. + +.SH "CONFORMING TO" +DCE-RFC 86.0, October 1995. + +.SH BUGS +.sp 2 +This function should be internationalized. + +.SH "SEE ALSO" + +.BR pam "(8). " + +Also, see the three +.BR Linux-PAM +Guides, for +.BR "System administrators" ", " +.BR "module developers" ", " +and +.BR "application developers" ". " diff --git a/Linux-PAM/doc/man/template-man b/Linux-PAM/doc/man/template-man new file mode 100644 index 00000000..11e7a061 --- /dev/null +++ b/Linux-PAM/doc/man/template-man @@ -0,0 +1,52 @@ +.\" Hey Emacs! This file is -*- nroff -*- source. +.\" $Id: template-man,v 1.1.1.1 2000/06/20 22:10:58 agmorgan Exp $ +.\" Copyright (c) Andrew G. Morgan 1997 <morgan@parc.power.net> +.TH PAM_???? 2 "1997 Jan 4" "Linux-PAM 0.55" "Application Programmers' Manual" +.SH NAME + +function names \- brief summary of function + +.SH SYNOPSIS +.B #include <security/pam_????.h> +.sp +.BI "int pam_???(pam_handle_t " pamh ", int " flags); +.sp 2 +.SH DESCRIPTION +.TP +.B pam_??? +Here goes the +.I explanation +it may be quite +.IR long . +.TP +.SH "RETURN VALUE" +.B pam_??? +On success... +.BR PAM_SUCCESS +is returned +.TP +.SH ERRORS +May be translated to text with +.BR pam_strerror "(2). " + +.SH "CONFORMING TO" +.B pam_??? +DCE-RFC 86.0, October 1995. + +.SH BUGS +.sp 2 +none known. + +.SH "SEE ALSO" + +.BR pam_??? "(2), " +and +.BR pam_??? "(2). " + +Also, see the three +.BR Linux-PAM +Guides, for +.BR "System administrators" ", " +.BR "module developers" ", " +and +.BR "application developers" ". " diff --git a/Linux-PAM/doc/modules/README b/Linux-PAM/doc/modules/README new file mode 100644 index 00000000..653448f3 --- /dev/null +++ b/Linux-PAM/doc/modules/README @@ -0,0 +1,13 @@ +$Id: README,v 1.2 2001/12/08 18:56:47 agmorgan Exp $ + +This directory contains a number of sgml sub-files. One for each +documented module. They contain a description of each module and give +some indication of its reliability. + +Additionally, there is a 'module.sgml-template' file which should be +used as a blank form for new module descriptions. + +Please feel free to submit amendments/comments etc. regarding these +files to: + + Andrew G. Morgan <morgan@kernel.org> diff --git a/Linux-PAM/doc/modules/module.sgml-template b/Linux-PAM/doc/modules/module.sgml-template new file mode 100644 index 00000000..3fffc754 --- /dev/null +++ b/Linux-PAM/doc/modules/module.sgml-template @@ -0,0 +1,170 @@ +<!-- + + $Id: module.sgml-template,v 1.2 2001/02/11 07:52:56 agmorgan Exp $ + + This template file was written by Andrew G. Morgan + <morgan@kernel.org> + +[ + Text that should be deleted/replaced, is enclosed within + '[' .. ']' + marks. For example, this text should be deleted! +] + +--> + +<sect1> [*Familiar full name of module*, eg. The "allow all" module.] + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +[ + insert the name of the module + + Blank is not permitted. +] + +<tag><bf>Author[s]:</bf></tag> + +[ + Insert author names here + + Blank is not permitted. If in doubt, put "unknown" if the + author wishes to remain anonymous, put "anonymous". +] + +<tag><bf>Maintainer:</bf></tag> + +[ + Insert names and date-begun of most recent maintainer. +] + +<tag><bf>Management groups provided:</bf></tag> + +[ + list the subset of four management groups supported by the + module. Choose from: account; authentication; password; + session. + + Blank entries are not permitted. Explicitly list all of the + management groups. In the future more may be added to libpam! +] + +<tag><bf>Cryptographically sensitive:</bf></tag> + +[ + Indicate whether this module contains code that can perform + reversible (strong) encryption. This field is primarily to + ensure that people redistributing it are not unwittingly + breaking laws... + + Modules may also require the presence of some local library + that performs the necessary encryption via some standard API. + In this case "uses API" can be included in this field. The + library in question should be added to the system requirements + below. + + Blank = no cryptography is used by module. +] + +<tag><bf>Security rating:</bf></tag> + +[ + Initially, this field should be left blank. If someone takes + it upon themselves to test the strength of the module, it can + later be filled. + + Blank = unknown. +] + +<tag><bf>Clean code base:</bf></tag> + +[ + This will probably be filled by the libpam maintainer. + It can be considered to be a public humiliation list. :*) + + I am of the opinion that "gcc -with_all_those_flags" is + trying to tell us something about whether the program + works as intended. Since there is currently no Security + evaluation procedure for modules IMHO this is not a + completely unreasonable indication (a lower bound anyway) + of the reliability of a module. + + This field would indicate the number and flavor of + warnings that gcc barfs up when trying to compile the + module as part of the tree. Is this too tyrannical? + + Blank = Linux-PAM maintainer has not tested it :) +] + +<tag><bf>System dependencies:</bf></tag> + +[ + here we list config files, dynamic libraries needed, system + resources, kernel options.. etc. + + Blank = nothing more than libc required. +] + +<tag><bf>Network aware:</bf></tag> + +[ + Does the module base its behavior on probing a network + connection? Does it expect to be protected by the + application? + + Blank = Ignorance of network. +] + +</descrip> + +<sect2>Overview of module + +[ + some text describing the intended actions of the module + general comments mainly (specifics in sections + below). +] + +[ + + [ now we have a <sect2> level subsection for each of the + management groups. Include as many as there are groups + listed above in the synopsis ] + +<sect2>[ Account | Authentication | Password | Session ] component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +[ + List the supported arguments (leave their description for the + description below. + + Blank = no arguments are read and nothing is logged to syslog + about any arguments that are passed. Note, this + behavior is contrary to the RFC! +] + +<tag><bf>Description:</bf></tag> + +[ + This component of the module performs the task of ... +] + +<tag><bf>Examples/suggested usage:</bf></tag> + +[ + Here we list some doos and don'ts for this module. +] + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_access.sgml b/Linux-PAM/doc/modules/pam_access.sgml new file mode 100644 index 00000000..8a910d13 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_access.sgml @@ -0,0 +1,117 @@ +<!-- + + pam_access module docs added by Tim Berger <timb@transmeta.com> + +--> + +<sect1> The access module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> + +<tt>pam_access</tt> + + +<tag><bf>Author[s]:</bf></tag> + +Alexei Nogin <alexei@nogin.dnttm.ru> + +<tag><bf>Maintainer:</bf></tag> + +<tag><bf>Management groups provided:</bf></tag> + +account + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +Requires a configuration file. By default +<tt>/etc/security/access.conf</tt> is used but this can be overridden. + +<tag><bf>Network aware:</bf></tag> + +Through <tt/PAM_TTY/ if set, otherwise attempts getting tty name of +the stdin file descriptor with <tt/ttyname()/. Standard +gethostname(), <tt/yp_get_default_domain()/, <tt/gethostbyname()/ +calls. <bf/NIS/ is used for netgroup support. + +</descrip> + +<sect2>Overview of module + +<p> +Provides logdaemon style login access control. + +<sect2> Account component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tt>accessfile=<it>/path/to/file.conf</it></tt>; +<tt>fieldsep=<it>separators</it></tt> + +<tag><bf>Description:</bf></tag> + +This module provides logdaemon style login access control based on +login names and on host (or domain) names, internet addresses (or +network numbers), or on terminal line names in case of non-networked +logins. Diagnostics are reported through <tt/syslog(3)/. Wietse +Venema's <tt/login_access.c/ from <em/logdaemon-5.6/ is used with +several changes by A. Nogin. + +<p> +The behavior of this module can be modified with the following +arguments: +<itemize> + +<item><tt>accessfile=/path/to/file.conf</tt> - +indicate an alternative <em/access/ configuration file to override +the default. This can be useful when different services need different +access lists. + +<item><tt>fieldsep=<it>separators</it></tt> - +this option modifies the field separator character that +<tt/pam_access/ will recognize when parsing the access configuration +file. For example: <tt>fieldsep=|</tt> will cause the default `:' +character to be treated as part of a field value and `|' becomes the +field separator. Doing this is useful in conjuction with a system that +wants to use pam_access with X based applications, since the +<tt/PAM_TTY/ item is likely to be of the form "hostname:0" which +includes a `:' character in its value. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +Use of module is recommended, for example, on administrative machines +such as <bf/NIS/ servers and mail servers where you need several accounts +active but don't want them all to have login capability. + +For <tt>/etc/pam.d</tt> style configurations where your modules live +in <tt>/lib/security</tt>, start by adding the following line to +<tt>/etc/pam.d/login</tt>, <tt>/etc/pam.d/rlogin</tt>, +<tt>/etc/pam.d/rsh</tt> and <tt>/etc/pam.d/ftp</tt>: + +<tscreen> +<verb> +account required /lib/security/pam_access.so +</verb> +</tscreen> + +Note that use of this module is not effective unless your system ignores +<tt>.rhosts</tt> files. See the the pam_rhosts_auth documentation. + +A sample <tt>access.conf</tt> configuration file is included with the +distribution. + +</descrip> diff --git a/Linux-PAM/doc/modules/pam_chroot.sgml b/Linux-PAM/doc/modules/pam_chroot.sgml new file mode 100644 index 00000000..2366880e --- /dev/null +++ b/Linux-PAM/doc/modules/pam_chroot.sgml @@ -0,0 +1,86 @@ +<!-- + $Id: pam_chroot.sgml,v 1.1.1.1 2000/06/20 22:10:59 agmorgan Exp $ + + This file was written by Bruce Campbell <brucec@humbug.org.au> +--> + +<sect1>Chroot + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_chroot/ + +<tag><bf>Author:</bf></tag> +Bruce Campbell <brucec@humbug.org.au> + +<tag><bf>Maintainer:</bf></tag> +Author; proposed on 20/11/96 - email for status + +<tag><bf>Management groups provided:</bf></tag> +account; session; authentication + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> +Unwritten. + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> +Expects localhost. + +</descrip> + +<sect2>Overview of module + +<p> +This module is intended to provide a transparent wrapper around the +average user, one that puts them in a fake file-system (eg, their +'<tt>/</tt>' is really <tt>/some/where/else</tt>). + +<p> +Useful if you have several classes of users, and are slightly paranoid +about security. Can be used to limit who else users can see on the +system, and to limit the selection of programs they can run. + +<sect2>Account component: + +<p> +<em/Need more info here./ + +<sect2>Authentication component: + +<p> +<em/Need more info here./ + +<sect2>Session component: + +<p> +<em/Need more info here./ + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +Arguments and logging levels for the PAM version are being worked on. + +<tag><bf>Description:</bf></tag> + +<tag><bf>Examples/suggested usage:</bf></tag> +Do provide a reasonable list of programs - just tossing 'cat', 'ls', 'rm', +'cp' and 'ed' in there is a bit... +<p> +Don't take it to extremes (eg, you can set up a separate environment for +each user, but its a big waste of your disk space.) + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_cracklib.sgml b/Linux-PAM/doc/modules/pam_cracklib.sgml new file mode 100644 index 00000000..d6fc0c56 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_cracklib.sgml @@ -0,0 +1,304 @@ +<!-- + $Id: pam_cracklib.sgml,v 1.5 2001/12/08 18:56:47 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> + long password amendments are from Philip W. Dalrymple III <pwd@mdtsoft.com> +--> + +<sect1>Cracklib pluggable password strength-checker + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> + +pam_cracklib + +<tag><bf>Author:</bf></tag> + +Cristian Gafton <gafton@redhat.com> + +<tag><bf>Maintainer:</bf></tag> + +Author. + +<tag><bf>Management groups provided:</bf></tag> + +password + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> + +Requires the system library <tt/libcrack/ and a system dictionary: +<tt>/usr/lib/cracklib_dict</tt>. + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module can be plugged into the <tt/password/ stack of a given +application to provide some plug-in strength-checking for passwords. + +<p> +This module works in the following manner: it first calls the +<em>Cracklib</em> routine to check the strength of the password; if +crack likes the password, the module does an additional set of +strength checks. These checks are: +<itemize> + +<item> <bf/Palindrome/ - + +Is the new password a palindrome of the old one? + +<item> <bf/Case Change Only/ - + +Is the new password the the old one with only a change of case? + +<item> <bf/Similar/ - + +Is the new password too much like the old one? This is primarily +controlled by one argument, <tt/difok/ which is a number of characters +that if different between the old and new are enough to accept the new +password, this defaults to 10 or 1/2 the size of the new password +whichever is smaller. + +To avoid the lockup associated with trying to change a long and +complicated password, <tt/difignore/ is available. This argument can +be used to specify the minimum length a new password needs to be +before the <tt/difok/ value is ignored. The default value for +<tt/difignore/ is 23. + + +<item> <bf/Simple/ - + +Is the new password too small? This is controlled by 5 arguments +<tt/minlen/, <tt/dcredit/, <tt/ucredit/, <tt/lcredit/, and +<tt/ocredit/. See the section on the arguments for the details of how +these work and there defaults. + +<item> <bf/Rotated/ - + +Is the new password a rotated version of the old password? + +<item> <bf/Already used/ - + +Was the password used in the past? Previously used passwords are to +be found in /etc/security/opasswd. + +</itemize> + +<p> +This module with no arguments will work well for standard unix +password encryption. With md5 encryption, passwords can be longer +than 8 characters and the default settings for this module can make it +hard for the user to choose a satisfactory new password. Notably, the +requirement that the new password contain no more than 1/2 of the +characters in the old password becomes a non-trivial constraint. For +example, an old password of the form "the quick brown fox jumped over +the lazy dogs" would be difficult to change... In addition, the +default action is to allow passwords as small as 5 characters in +length. For a md5 systems it can be a good idea to increase the +required minimum size of a password. One can then allow more credit +for different kinds of characters but accept that the new password may +share most of these characters with the old password. + +<sect2>Password component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tt/debug/; <tt/type=XXX/; <tt/retry=N/; <tt/difok=N/; <tt/minlen=N/; +<tt/dcredit=N/; <tt/ucredit=N/; <tt/lcredit=N/; <tt/ocredit=N/; +<tt/use_authtok/; + +<tag><bf>Description:</bf></tag> + +The action of this module is to prompt the user for a password and +check its strength against a system dictionary and a set of rules for +identifying poor choices. + +<p> +The default action is to prompt for a single password, check its +strength and then, if it is considered strong, prompt for the password +a second time (to verify that it was typed correctly on the first +occasion). All being well, the password is passed on to subsequent +modules to be installed as the new authentication token. + +<p> +The default action may be modified in a number of ways using the +arguments recognized by the module: +<itemize> + +<item> <tt/debug/ - + +this option makes the module write information to syslog(3) indicating +the behavior of the module (this option does <bf/not/ write password +information to the log file). + +<item> <tt/type=XXX/ - + +the default action is for the module to use the following prompts when +requesting passwords: ``New UNIX password: '' and ``Retype UNIX +password: ''. Using this option you can replace the word UNIX with +<tt/XXX/. + +<item> <tt/retry=N/ - + +the default number of times this module will request a new password +(for strength-checking) from the user is 1. Using this argument this +can be increased to <tt/N/. + +<item> <tt/difok=N/ - + +This argument will change the default of 10 for the number of +characters in the new password that must not be present in the old +password. In addition, if 1/2 of the characters in the new password +are different then the new password will be accepted anyway. + +<item> <tt/minlen=N/ - + +The minimum acceptable size for the new password (plus one if credits +are not disabled which is the default). +In addition to the number of characters in the new password, credit (of ++1 in length) is given for each different kind of character (<em>other, +upper, lower</em> and <em/digit/). The default for this parameter is +9 which is good for a old style UNIX password all of the same type of +character but may be too low to exploit the added security of a md5 +system. Note that there is a pair of length limits in +<em>Cracklib</em> itself, a "way too short" limit of 4 which is hard +coded in and a defined limit (6) that will be checked without +reference to <tt>minlen</tt>. If you want to allow passwords as short +as 5 characters you should either not use this module or recompile +the crack library and then recompile this module. + +<item> <tt/dcredit=N/ - + +(N >= 0) This is the maximum credit for having digits in the new password. If +you have less than or <tt/N/ digits, each digit will count +1 towards +meeting the current <tt/minlen/ value. The default for <tt/dcredit/ +is 1 which is the recommended value for <tt/minlen/ less than 10. +(N < 0) This is the minimum number of digits that must be met for a new +password. + +<item> <tt/ucredit=N/ - + +(N >= 0) This is the maximum credit for having upper case letters in the new +password. If you have less than or <tt/N/ upper case letters each +letter will count +1 towards meeting the current <tt/minlen/ value. +The default for <tt/ucredit/ is 1 which is the recommended value for +<tt/minlen/ less than 10. (N < 0) This is the minimum number of upper +case letters that must be met for a new password. + +<item> <tt/lcredit=N/ - + +(N >= 0) This is the maximum credit for having lower case letters in the new +password. If you have less than or <tt/N/ lower case letters, each +letter will count +1 towards meeting the current <tt/minlen/ value. +The default for <tt/lcredit/ is 1 which is the recommended value for +<tt/minlen/ less than 10. (N < 0) This is the minimum number of lower +case letters that must be met for a new password. + +<item> <tt/ocredit=N/ - + +(N >= 0) This is the maximum credit for having other characters in the new +password. If you have less than or <tt/N/ other characters, each +character will count +1 towards meeting the current <tt/minlen/ value. +The default for <tt/ocredit/ is 1 which is the recommended value for +<tt/minlen/ less than 10. (N < 0) This is the minimum number of other +characters that must be met for a new password. + +<item> <tt/use_authtok/ - + +This argument is used to <em/force/ the module to not prompt the user +for a new password but use the one provided by the previously stacked +<tt/password/ module. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +<p> +For an example of the use of this module, we show how it may be +stacked with the password component of <tt/pam_pwdb/: +<tscreen> +<verb> +# +# These lines stack two password type modules. In this example the +# user is given 3 opportunities to enter a strong password. The +# "use_authtok" argument ensures that the pam_pwdb module does not +# prompt for a password, but instead uses the one provided by +# pam_cracklib. +# +passwd password required pam_cracklib.so retry=3 +passwd password required pam_pwdb.so use_authtok +</verb> +</tscreen> + +<p> +Another example (in the <tt>/etc/pam.d/passwd</tt> format) is for the +case that you want to use md5 password encryption: +<tscreen> +<verb> +#%PAM-1.0 +# +# These lines allow a md5 systems to support passwords of at least 14 +# bytes with extra credit of 2 for digits and 2 for others the new +# password must have at least three bytes that are not present in the +# old password +# +password required pam_cracklib.so \ + difok=3 minlen=15 dcredit= 2 ocredit=2 +password required pam_pwdb.so use_authtok nullok md5 +</verb> +</tscreen> + +<p> +And here is another example in case you don't want to use credits: +<tscreen> +<verb> +#%PAM-1.0 +# +# These lines require the user to select a password with a minimum +# length of 8 and with at least 1 digit number, 1 upper case letter, +# and 1 other character +# +password required pam_cracklib.so \ + dcredit=-1 ucredit=-1 ocredit=-1 lcredit=0 minlen=8 +password required pam_pwdb.so use_authtok nullok md5 +</verb> +</tscreen> + +<p> +In this example we simply say that the password must have a minimum +length of 8: +<tscreen> +<verb> +#%PAM-1.0 +# +# These lines require the user to select a password with a mimimum +# length of 8. He gets no credits and he is not forced to use +# digit numbers, upper case letters etc. +# +password required pam_cracklib.so \ + dcredit=0 ucredit=0 ocredit=0 lcredit=0 minlen=8 +password required pam_pwdb.so use_authtok nullok md5 +</verb> +</tscreen> + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_deny.sgml b/Linux-PAM/doc/modules/pam_deny.sgml new file mode 100644 index 00000000..bf9dfd2b --- /dev/null +++ b/Linux-PAM/doc/modules/pam_deny.sgml @@ -0,0 +1,177 @@ +<!-- + $Id: pam_deny.sgml,v 1.3 2002/05/10 04:03:02 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>The locking-out module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +pam_deny + +<tag><bf>Author:</bf></tag> +Andrew G. Morgan <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> +current <bf/Linux-PAM/ maintainer + +<tag><bf>Management groups provided:</bf></tag> +account; authentication; password; session + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> +clean. + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module can be used to deny access. It always indicates a failure +to the application through the PAM framework. As is commented in the +overview section <ref id="overview-section" name="above">, this module +might be suitable for using for default (the <tt/OTHER/) entries. + +<sect2>Account component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +This component does nothing other than return a failure. The +failure type is <tt/PAM_ACCT_EXPIRED/. + +<tag><bf>Examples/suggested usage:</bf></tag> + +Stacking this module with type <tt/account/ will prevent the user from +gaining access to the system via applications that refer to +<bf/Linux-PAM/'s account management function <tt/pam_acct_mgmt()/. + +<p> +The following example would make it impossible to login: +<tscreen> +<verb> +# +# add this line to your other login entries to disable all accounts +# +login account required pam_deny.so +</verb> +</tscreen> + +</descrip> + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +This component does nothing other than return a failure. The failure +type is <tt/PAM_AUTH_ERR/ in the case that <tt/pam_authenticate()/ is +called (when the application tries to authenticate the user), and is +<tt/PAM_CRED_UNAVAIL/ when the application calls <tt/pam_setcred()/ +(to establish and set the credentials of the user -- it is unlikely +that this function will ever be called in practice). + +<tag><bf>Examples/suggested usage:</bf></tag> + +To deny access to default applications with this component of the +<tt/pam_deny/ module, you might include the following line in your +<bf/Linux-PAM/ configuration file: +<tscreen> +<verb> +# +# add this line to your existing OTHER entries to prevent +# authentication succeeding with default applications. +# +OTHER auth required pam_deny.so +</verb> +</tscreen> + +</descrip> + +<sect2>Password component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +This component of the module denies the user the opportunity to change +their password. It always responds with <tt/PAM_AUTHTOK_ERR/ when +invoked. + +<tag><bf>Examples/suggested usage:</bf></tag> + +This module should be used to prevent an application from updating the +applicant user's password. For example, to prevent <tt/login/ from +automatically prompting for a new password when the old one has +expired you should include the following line in your configuration +file: +<tscreen> +<verb> +# +# add this line to your other login entries to prevent the login +# application from being able to change the user's password. +# +login password required pam_deny.so +</verb> +</tscreen> + +</descrip> + +<sect2>Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +This aspect of the module prevents an application from starting a +session on the host computer. + +<tag><bf>Examples/suggested usage:</bf></tag> + +Together with another session module, that displays a message of the +day perhaps (<tt/pam_motd/ for example), this module can be used to +block a user from starting a shell. We might use the following entries +in the configuration file to inform the user it is system time: +<tscreen> +<verb> +# +# An example to see how to configure login to refuse the user a +# session (politely) +# +login session required pam_motd.so \ + motd=/etc/system_time +login session required pam_deny.so +</verb> +</tscreen> + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_env.sgml b/Linux-PAM/doc/modules/pam_env.sgml new file mode 100644 index 00000000..a6361cac --- /dev/null +++ b/Linux-PAM/doc/modules/pam_env.sgml @@ -0,0 +1,141 @@ +<!-- + $Id: pam_env.sgml,v 1.2 2001/03/19 01:46:41 agmorgan Exp $ + + This file was written by Dave Kinchlea <kinch@kinch.ark.com> + Ed. AGM +--> + +<sect1>Set/unset environment variables + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_env/ + +<tag><bf>Author:</bf></tag> +Dave Kinchlea <kinch@kinch.ark.com> + +<tag><bf>Maintainer:</bf></tag> +Author + +<tag><bf>Management groups provided:</bf></tag> +Authentication (setcred) + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +<tt>/etc/security/pam_env.conf</tt> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module allows the (un)setting of environment variables. Supported +is the use of previously set environment variables as well as +<em>PAM_ITEM</em>s such as <tt>PAM_RHOST</tt>. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; <tt/conffile=/<em/configuration-file-name/; +<tt/envfile/=<em/env-file-name/; <tt/readenv/=<em/0|1/ + +<tag><bf>Description:</bf></tag> +This module allows you to (un)set arbitrary environment variables +using fixed strings, the value of previously set environment variables +and/or <em/PAM_ITEM/s. + +<p> +All is controlled via a configuration file (by default, +<tt>/etc/security/pam_env.conf</tt> but can be overriden with +<tt>conffile</tt> argument). Each line starts with the variable name, +there are then two possible options for each variable <bf>DEFAULT</bf> +and <bf>OVERRIDE</bf>. <bf>DEFAULT</bf> allows an administrator to +set the value of the variable to some default value, if none is +supplied then the empty string is assumed. The <bf>OVERRIDE</bf> +option tells pam_env that it should enter in its value (overriding the +default value) if there is one to use. <bf>OVERRIDE</bf> is not used, +<tt>""</tt> is assumed and no override will be done. + +<p> +<tscreen> +<verb> +VARIABLE [DEFAULT=[value]] [OVERRIDE=[value]] +</verb> +</tscreen> + +<p> +(Possibly non-existent) environment variables may be used in values +using the <tt>${string}</tt> syntax and (possibly +non-existent) <em/PAM_ITEM/s may be used in values using the +<tt>@{string}</tt> syntax. Both the <tt>$</tt> +and <tt>@</tt> characters can be backslash-escaped to be used +as literal values (as in <tt>\$</tt>. Double quotes may +be used in values (but not environment variable names) when white +space is needed <bf>the full value must be delimited by the quotes and +embedded or escaped quotes are not supported</bf>. + +<p> +This module can also parse a file with simple <tt>KEY=VAL</tt> pairs +on seperate lines (<tt>/etc/environment</tt> by default). You can +change the default file to parse, with the <em/envfile/ flag and turn +it on or off by setting the <em/readenv/ flag to 1 or 0 respectively. + +<p> +The behavior of this module can be modified with one of the following +flags: + +<p> +<itemize> + +<item><tt/debug/ +- write more information to <tt/syslog(3)/. + +<item><tt/conffile=/<em/filename/ +- by default the file <tt>/etc/security/pam_env.conf</tt> is used as +the configuration file. This option overrides the default. You must +supply a complete path + file name. + +<item><tt/envfile=/<em/filename/ +- by default the file <tt>/etc/environment</tt> is used to load KEY=VAL +pairs directly into the env. This option overrides the default. You must +supply a complete path + file name. + +<item><tt/readenv=/<em/0|1/ +- turns on or off the reading of the file specified by envfile (0 is off, +1 is on). By default this option is on. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +See sample <tt>pam_env.conf</tt> for more information and examples. + +</descrip> + +<!-- +End of sgml insert for this module. +--> + + + + + + + + + + diff --git a/Linux-PAM/doc/modules/pam_filter.sgml b/Linux-PAM/doc/modules/pam_filter.sgml new file mode 100644 index 00000000..e22ad9b6 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_filter.sgml @@ -0,0 +1,150 @@ +<!-- + $Id: pam_filter.sgml,v 1.3 2001/12/08 18:56:47 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>The filter module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> + +pam_filter + +<tag><bf>Author:</bf></tag> + +Andrew G. Morgan <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> + +Author. + +<tag><bf>Management groups provided:</bf></tag> + +account; authentication; password; session + +<tag><bf>Cryptographically sensitive:</bf></tag> + +Not yet. + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +This module compiles cleanly on Linux based systems. + +<tag><bf>System dependencies:</bf></tag> + +To function it requires <em/filters/ to be installed on the system. + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module was written to offer a plug-in alternative to programs +like ttysnoop (XXX - need a reference). Since writing a filter that +performs this function has not occurred, it is currently only a toy. +The single filter provided with the module simply transposes upper and +lower case letters in the input and output streams. (This can be very +annoying and is not kind to termcap based editors). + +<sect2>Account+Authentication+Password+Session components + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tt/debug/; <tt/new_term/; <tt/non_term/; <tt/runX/ + +<tag><bf>Description:</bf></tag> + +Each component of the module has the potential to invoke the desired +filter. The filter is always <tt/execv(2)/d with the privilege of the +calling application and <bf/not/ that of the user. For this reason it +cannot usually be killed by the user without closing their session. + +<p> +The behavior of the module can be significantly altered by the +arguments passed to it in the <bf/Linux-PAM/ configuration file: +<itemize> +<item><tt/debug/ - + +this option increases the amount of information logged to +<tt/syslog(3)/ as the module is executed. + +<item><tt/new_term/ - + +the default action of the filter is to set the <tt/PAM_TTY/ item to +indicate the terminal that the user is using to connect to the +application. This argument indicates that the filter should set +<tt/PAM_TTY/ to the filtered pseudo-terminal. + +<item><tt/non_term/ - +don't try to set the <tt/PAM_TTY/ item. + +<item><tt/runX/ - + +in order that the module can invoke a filter it should know when to +invoke it. This argument is required to tell the filter when to do +this. The arguments that follow this one are respectively the full +pathname of the filter to be run and any command line arguments that +the filter might expect. + +<p> +Permitted values for <tt/X/ are <tt/1/ and <tt/2/. These indicate the +precise time that the filter is to be run. To understand this concept +it will be useful to have read the Linux-PAM Module developer's +guide. Basically, for each management group there are up to two ways +of calling the module's functions. + +In the case of the <em/authentication/ and <em/session/ components +there are actually two separate functions. For the case of +authentication, these functions are <tt/_authenticate/ and +<tt/_setcred/ -- here <tt/run1/ means run the filter from the +<tt/_authenticate/ function and <tt/run2/ means run the filter from +<tt/_setcred/. In the case of the session modules, <tt/run1/ implies +that the filter is invoked at the <tt/_open_session/ stage, and +<tt/run2/ for <tt/_close_session/. + +<p> +For the case of the account component. Either <tt/run1/ or <tt/run2/ +may be used. + +<p> +For the case of the password component, <tt/run1/ is used to indicate +that the filter is run on the first occasion <tt/_chauthtok/ is run +(the <tt/PAM_PRELIM_CHECK/ phase) and <tt/run2/ is used to indicate +that the filter is run on the second occasion (the +<tt/PAM_UPDATE_AUTHTOK/ phase). + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +At the time of writing there is little real use to be made of this +module. For fun you might try adding the following line to your +login's configuration entries +<tscreen> +<verb> +# +# An example to see how to configure login to transpose upper and +# lower case letters once the user has logged in(!) +# +login session required pam_filter.so \ + run1 /usr/sbin/pam_filter/upperLOWER +</verb> +</tscreen> + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_ftp.sgml b/Linux-PAM/doc/modules/pam_ftp.sgml new file mode 100644 index 00000000..cb4c4f33 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_ftp.sgml @@ -0,0 +1,93 @@ +<!-- + $Id: pam_ftp.sgml,v 1.3 2001/12/08 18:56:47 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>Anonymous access module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_ftp.so/ + +<tag><bf>Author:</bf></tag> +Andrew G. Morgan <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> +Author. + +<tag><bf>Management groups provided:</bf></tag> +authentication + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> +prompts for email address of user; easily spoofed (XXX - needs work) + +</descrip> + +<sect2>Overview of module + +<p> +The purpose of this module is to provide a pluggable anonymous ftp +mode of access. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; +<tt/users=XXX,YYY,.../; +<tt/ignore/ + +<tag><bf>Description:</bf></tag> + +This module intercepts the user's name and password. If the name is +``<tt/ftp/'' or ``<tt/anonymous/'', the user's password is broken up +at the `<tt/@/' delimiter into a <tt/PAM_RUSER/ and a <tt/PAM_RHOST/ +part; these pam-items being set accordingly. The username +(<tt/PAM_USER/) is set to ``<tt/ftp/''. In this case the module +succeeds. Alternatively, the module sets the <tt/PAM_AUTHTOK/ item +with the entered password and fails. + +<p> +The behavior of the module can be modified with the following flags: +<itemize> +<item><tt/debug/ - +log more information to with <tt/syslog(3)/. + +<item><tt/users=XXX,YYY,.../ - +instead of ``<tt/ftp/'' or ``<tt/anonymous/'', provide anonymous login +to the comma separated list of users; ``<tt/XXX,YYY,.../''. Should the +applicant enter one of these usernames the returned username is set to +the first in the list; ``<tt/XXX/''. + +<item><tt/ignore/ - +pay no attention to the email address of the user (if supplied). + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +An example of the use of this module is provided in the configuration +file section <ref id="configuration" name="above">. With care, this +module could be used to provide new/temporary account anonymous +login. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_group.sgml b/Linux-PAM/doc/modules/pam_group.sgml new file mode 100644 index 00000000..2d767275 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_group.sgml @@ -0,0 +1,108 @@ +<!-- + $Id: pam_group.sgml,v 1.2 2001/12/08 18:56:47 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>The group access module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_group/ + +<tag><bf>Author:</bf></tag> +Andrew G. Morgan <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> +Author. + +<tag><bf>Management groups provided:</bf></tag> +authentication + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> +Sensitive to <em/setgid/ status of file-systems accessible to users. + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +Requires an <tt>/etc/security/group.conf</tt> file. Can be compiled +with or without <tt/libpwdb/. + +<tag><bf>Network aware:</bf></tag> +Only through correctly set <tt/PAM_TTY/ item. + +</descrip> + +<sect2>Overview of module + +<p> +This module provides group-settings based on the user's name and the +terminal they are requesting a given service from. It takes note of +the time of day. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +This module does not authenticate the user, but instead it grants +group memberships (in the credential setting phase of the +authentication module) to the user. Such memberships are based on the +service they are applying for. The group memberships are listed in +text form in the <tt>/etc/security/group.conf</tt> file. + +<tag><bf>Examples/suggested usage:</bf></tag> + +For this module to function correctly there must be a correctly +formatted <tt>/etc/security/groups.conf</tt> file present. The format +of this file is as follows. Group memberships are given based on the +service application satisfying any combination of lines in the +configuration file. Each line (barring comments which are preceded by +`<tt/#/' marks) has the following +syntax: +<tscreen> +<verb> +services ; ttys ; users ; times ; groups +</verb> +</tscreen> +Here the first four fields share the syntax of the <tt>pam_time</tt> +configuration file; <tt>/etc/security/pam_time.conf</tt>, and the last +field, the <tt/groups/ field, is a comma (or space) separated list of +the text-names of a selection of groups. If the users application for +service satisfies the first four fields, the user is granted membership +of the listed groups. + +<p> +As stated in above this module's usefulness relies on the file-systems +accessible to the user. The point being that once granted the +membership of a group, the user may attempt to create a <em/setgid/ +binary with a restricted group ownership. Later, when the user is not +given membership to this group, they can recover group membership with +the precompiled binary. The reason that the file-systems that the user +has access to are so significant, is the fact that when a system is +mounted <em/nosuid/ the user is unable to create or execute such a +binary file. For this module to provide any level of security, all +file-systems that the user has write access to should be mounted +<em/nosuid/. + +<p> +The <tt>pam_group</tt> module fuctions in parallel with the +<tt>/etc/group</tt> file. If the user is granted any groups based on +the behavior of this module, they are granted <em>in addition</em> to +those entries <tt>/etc/group</tt> (or equivalent). + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_issue.sgml b/Linux-PAM/doc/modules/pam_issue.sgml new file mode 100644 index 00000000..1f617e3b --- /dev/null +++ b/Linux-PAM/doc/modules/pam_issue.sgml @@ -0,0 +1,120 @@ +<!-- + +Ben Collins <bcollins@debian.org> + +--> + +<sect1>Add issue file to user prompt + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_issue/ + +<tag><bf>Author:</bf></tag> +Ben Collins <bcollins@debian.org> + +<tag><bf>Maintainer:</bf></tag> +Author + +<tag><bf>Management groups provided:</bf></tag> +Authentication (pam_sm_authenticate) + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module prepends the issue file (<em>/etc/issue</em> by default) when +prompting for a username. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/issue=issue-file-name/; <tt/noesc/; + +<tag><bf>Description:</bf></tag> +This module allows you to prepend an issue file to the username prompt. It +also by default parses escape codes in the issue file similar to some +common getty's (using \x format). +<p> +Recognized escapes: +<itemize> + +<item><tt/d/ +- current date + +<item><tt/s/ +- operating system name + +<item><tt/l/ +- name of this tty + +<item><tt/m/ +- architecture of this system (i686, sparc, powerpc, ...) + +<item><tt/n/ +- hostname of this system + +<item><tt/o/ +- domainname of this system + +<item><tt/r/ +- release number of the operation system (eg. 2.2.12) + +<item><tt/t/ +- current time + +<item><tt/u/ +- number of users currently logged in + +<item><tt/U/ +- same as <tt/u/, except it is suffixed with "user" or "users" (eg. "1 +user" or "10 users" + +<item><tt/v/ +- version/build-date of the operating system (eg. "#3 Mon Aug 23 14:38:16 +EDT 1999" on Linux). + +</itemize> + +<p> +The behavior of this module can be modified with one of the following +flags: + +<p> +<itemize> + +<item><tt/issue/ +- the file to output if not using the default + +<item><tt/noesc/ +- turns off escape code parsing + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +login auth pam_issue.so issue=/etc/issue + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_krb4.sgml b/Linux-PAM/doc/modules/pam_krb4.sgml new file mode 100644 index 00000000..51a46522 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_krb4.sgml @@ -0,0 +1,126 @@ +<!-- + $Id: pam_krb4.sgml,v 1.1.1.1 2000/06/20 22:11:01 agmorgan Exp $ + + This file was written by Derrick J. Brashear <shadow@DEMENTIA.ORG> +--> + +<sect1>The Kerberos 4 module. + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_krb4/ + +<tag><bf>Author:</bf></tag> +Derrick J. Brashear <shadow@dementia.org> + +<tag><bf>Maintainer:</bf></tag> +Author. + +<tag><bf>Management groups provided:</bf></tag> +authentication; password; session + +<tag><bf>Cryptographically sensitive:</bf></tag> +uses API + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +libraries - <tt/libkrb/, <tt/libdes/, <tt/libcom_err/, <tt/libkadm/; +and a set of Kerberos include files. + +<tag><bf>Network aware:</bf></tag> +Gets Kerberos ticket granting ticket via a Kerberos key distribution +center reached via the network. + +</descrip> + +<sect2>Overview of module + +<p> +This module provides an interface for doing Kerberos verification of a +user's password, getting the user a Kerberos ticket granting ticket +for use with the Kerberos ticket granting service, destroying the +user's tickets at logout time, and changing a Kerberos password. + +<sect2> Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +This component of the module currently sets the user's <tt/KRBTKFILE/ +environment variable (although there is currently no way to export +this), as well as deleting the user's ticket file upon logout (until +<tt/PAM_CRED_DELETE/ is supported by <em/login/). + +<tag><bf>Examples/suggested usage:</bf></tag> + +This part of the module won't be terribly useful until we can change +the environment from within a <tt/Linux-PAM/ module. + +</descrip> + +<sect2> Password component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/use_first_pass/; <tt/try_first_pass/ + +<tag><bf>Description:</bf></tag> + +This component of the module changes a user's Kerberos password +by first getting and using the user's old password to get +a session key for the password changing service, then sending +a new password to that service. + +<tag><bf>Examples/suggested usage:</bf></tag> + +This should only be used with a real Kerberos v4 <tt/kadmind/. It +cannot be used with an AFS kaserver unless special provisions are +made. Contact the module author for more information. + +</descrip> + +<sect2> Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/use_first_pass/; <tt/try_first_pass/ + +<tag><bf>Description:</bf></tag> + +This component of the module verifies a user's Kerberos password +by requesting a ticket granting ticket from the Kerberos server +and optionally using it to attempt to retrieve the local computer's +host key and verifying using the key file on the local machine if +one exists. + +It also writes out a ticket file for the user to use later, and +deletes the ticket file upon logout (not until <tt/PAM_CRED_DELETE/ +is called from <em/login/). + +<tag><bf>Examples/suggested usage:</bf></tag> + +This module can be used with a real Kerberos server using MIT +v4 Kerberos keys. The module or the system Kerberos libraries +may be modified to support AFS style Kerberos keys. Currently +this is not supported to avoid cryptography constraints. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_lastlog.sgml b/Linux-PAM/doc/modules/pam_lastlog.sgml new file mode 100644 index 00000000..451bfaa2 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_lastlog.sgml @@ -0,0 +1,119 @@ +<!-- + $Id: pam_lastlog.sgml,v 1.2 2001/02/17 01:55:38 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>The last login module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_lastlog/ + +<tag><bf>Author:</bf></tag> +Andrew G. Morgan <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> +Author + +<tag><bf>Management groups provided:</bf></tag> +auth + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +uses information contained in the <tt>/var/log/lastlog</tt> file. + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This session module maintains the <tt>/var/log/lastlog</tt> file. Adding +an open entry when called via the <tt>pam_open_seesion()</tt> function +and completing it when <tt>pam_close_session()</tt> is called. This +module can also display a line of information about the last login of +the user. If an application already performs these tasks, it is not +necessary to use this module. + +<sect2>Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; <tt/nodate/; <tt/noterm/; <tt/nohost/; <tt/silent/; +<tt/never/ + +<tag><bf>Description:</bf></tag> + +<p> +This module can be used to provide a ``Last login on ...'' +message. when the user logs into the system from what ever application +uses the PAM libraries. In addition, the module maintains the +<tt>/var/log/lastlog</tt> file. + +<p> +The behavior of this module can be modified with one of the following +flags: + +<p> +<itemize> +<item><tt/debug/ +- write more information to <tt/syslog(3)/. + +<item><tt/nodate/ +- neglect to give the date of the last login when displaying +information about the last login on the system. + +<item><tt/noterm/ +- neglect to diplay the terminal name on which the last login was +attempt. + +<item><tt/nohost/ +- neglect to indicate from which host the last login was attempted. + +<item><tt/silent/ +- neglect to inform the user about any previous login: just update +the <tt>/var/log/lastlog</tt> file. + +<item><tt/never/ +- if the <tt>/var/log/lastlog</tt> file does not contain any old entries +for the user, indicate that the user has never previously logged in +with a ``welcome..." message. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +This module can be used to indicate that the user has new mail when +they <em/login/ to the system. Here is a sample entry for your +<tt>/etc/pam.d/XXX</tt> file: +<tscreen> +<verb> +# +# When were we last here? +# +session optional pam_lastlog.so +</verb> +</tscreen> + +<p> +Note, some applications may perform this function themselves. In such +cases, this module is not necessary. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_limits.sgml b/Linux-PAM/doc/modules/pam_limits.sgml new file mode 100644 index 00000000..22674d42 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_limits.sgml @@ -0,0 +1,247 @@ +<!-- + $Id: pam_limits.sgml,v 1.7 2002/05/09 12:00:35 baggins Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> + from information compiled by Cristian Gafton (author of module) +--> + +<sect1>The resource limits module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_limits/ + +<tag><bf>Authors:</bf></tag> +Cristian Gafton <gafton@redhat.com> <newline> +Thanks are also due to Elliot Lee <sopwith@redhat.com> +for his comments on improving this module. + +<tag><bf>Maintainer:</bf></tag> +Cristian Gafton - 1996/11/20 + +<tag><bf>Management groups provided:</bf></tag> +session + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +requires an <tt>/etc/security/limits.conf</tt> file and kernel support +for resource limits. Also uses the library, <tt/libpwdb/. + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module, through the <bf/Linux-PAM/ <em/open/-session hook, sets +limits on the system resources that can be obtained in a +user-session. Its actions are dictated more explicitly through the +configuration file discussed below. + +<sect2>Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; <tt>conf=/path/to/file.conf</tt>; <tt>change_uid</tt>; +<tt>utmp_early</tt> + +<tag><bf>Description:</bf></tag> + +Through the contents of the configuration file, +<tt>/etc/security/limits.conf</tt>, resource limits are placed on +users' sessions. Users of <tt/uid=0/ are not affected by this +restriction. + +<p> +The behavior of this module can be modified with the following +arguments: +<itemize> + +<item><tt/debug/ - +verbose logging to <tt/syslog(3)/. + +<item><tt>conf=/path/to/file.conf</tt> - +indicate an alternative <em/limits/ configuration file to the default. + +<item><tt/change_uid/ - +change real uid to the user for who the limits are set up. Use this +option if you have problems like login not forking a shell for user +who has no processes. Be warned that something else may break when +you do this. + +<item><tt/utmp_early/ - +some broken applications actually allocate a utmp entry for the user +before the user is admitted to the system. If some of the services you +are configuring PAM for do this, you can selectively use this module +argument to compensate for this behavior and at the same time maintain +system-wide consistency with a single limits.conf file. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +In order to use this module the system administrator must first create +a <em/root-only-readable/ file (default is +<tt>/etc/security/limits.conf</tt>). This file describes the resource +limits the superuser wishes to impose on users and groups. No limits +are imposed on <tt/uid=0/ accounts. + +<p> +Each line of the configuration file describes a limit for a user in +the form: +<tscreen> +<verb> +<domain> <type> <item> <value> +</verb> +</tscreen> + +<p> +The fields listed above should be filled as follows...<newline> +<tt><domain></tt> can be: +<itemize> +<item> a username +<item> a groupname, with <tt>@group</tt> syntax +<item> the wild-card <tt/*/, for default entry +<item> the wild-card <tt/%/, for maxlogins limit only, +can also be used with <tt>%group</tt> syntax +</itemize> + +<p> +<tt><type></tt> can have the three values: +<itemize> + +<item> <tt/hard/ for enforcing <em/hard/ resource limits. These limits +are set by the superuser and enforced by the Linux Kernel. The user +cannot raise his requirement of system resources above such values. + +<item> <tt/soft/ for enforcing <em/soft/ resource limits. These limits +are ones that the user can move up or down within the permitted range +by any pre-exisiting <em/hard/ limits. The values specified with this +token can be thought of as <em/default/ values, for normal system +usage. + +<item> <tt/-/ for enforcing both <em/soft/ and <em/hard/ limits +together. + +</itemize> + +<p> +<tt><item></tt> can be one of the following: +<itemize> +<item><tt/core/ - limits the core file size (KB) +<item><tt/data/ - max data size (KB) +<item><tt/fsize/ - maximum filesize (KB) +<item><tt/memlock/ - max locked-in-memory address space (KB) +<item><tt/nofile/ - max number of open files +<item><tt/rss/ - max resident set size (KB) +<item><tt/stack/ - max stack size (KB) +<item><tt/cpu/ - max CPU time (MIN) +<item><tt/nproc/ - max number of processes +<item><tt/as/ - address space limit +<item><tt/maxlogins/ - max number of logins for this user +<item><tt/maxsyslogins/ - max number of logins on system +<item><tt/priority/ - the priority to run user process with (negative +values boost process priority) +<item><tt/locks/ - max locked files (Linux 2.4 and higher) +</itemize> + +<p> +Note, if you specify a type of ``-'' but neglect to supply the +<tt/item/ and <tt/value/ fields then the module will never enforce any +limits on the corresponding user/group-members etc. . Note, the first +entry of the form which applies to the authenticating user will +override all other entries in the limits configuration file. In such +cases, the <tt/pam_limits/ module will always return <tt/PAM_SUCCESS/. + +<p> +In general, individual limits have priority over group limits, so if +you impose no limits for <tt/admin/ group, but one of the members in +this group have a limits line, the user will have its limits set +according to this line. + +<p> +Also, please note that all limit settings are set <em/per login/. +They are not global, nor are they permanent; existing only for the +duration of the session. + +<p> +In the <em/limits/ configuration file, the ``<tt/#/'' character +introduces a comment - after which the rest of the line is ignored. + +<p> +The <tt/pam_limits/ module does its best to report configuration +problems found in its configuration file via <tt/syslog(3)/. + +<p> +The following is an example configuration file: +<tscreen> +<verb> +# EXAMPLE /etc/security/limits.conf file: +# ======================================= +# <domain> <type> <item> <value> +* soft core 0 +* hard rss 10000 +@student hard nproc 20 +@faculty soft nproc 20 +@faculty hard nproc 50 +ftp hard nproc 0 +@student - maxlogins 4 +</verb> +</tscreen> +Note, the use of <tt/soft/ and <tt/hard/ limits for the same resource +(see <tt/@faculty/) -- this establishes the <em/default/ and permitted +<em/extreme/ level of resources that the user can obtain in a given +service-session. + +<p> +Note, that wild-cards <tt/*/ and <tt/%/ have the following meaning when +used for maxlogins limit +<itemize> +<item> <tt/*/ every user +<item> <tt/%/ all users, or entire group when <tt>%group</tt> is specified +</itemize> +See the following examples: +<tscreen> +<verb> +# EXAMPLE /etc/security/limits.conf file: +# <domain> <type> <item> <value> +* - maxlogins 2 +@faculty - maxlogins 4 +% - maxlogins 30 +%student - maxlogins 10 +</verb> +</tscreen> +Explanation: every user can login 2 times, members of the <tt/faculty/ +group can login 4 times, there can be only 30 logins, only 10 from +<tt/students/ group. + +<p> +For the services that need resources limits (login for example) put +the following line in <tt>/etc/pam.conf</tt> as the last line for that +service (usually after the pam_unix session line: +<tscreen> +<verb> +# +# Resource limits imposed on login sessions via pam_limits +# +login session required pam_limits.so +</verb> +</tscreen> + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_listfile.sgml b/Linux-PAM/doc/modules/pam_listfile.sgml new file mode 100644 index 00000000..1284d1b6 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_listfile.sgml @@ -0,0 +1,138 @@ +<!-- + $Id: pam_listfile.sgml,v 1.2 2001/03/19 01:46:41 agmorgan Exp $ + + This file was written by Michael K. Johnson <johnsonm@redhat.com> +--> + +<sect1>The list-file module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_listfile/ + +<tag><bf>Author:</bf></tag> +Elliot Lee <tt><sopwith@cuc.edu></tt> + +<tag><bf>Maintainer:</bf></tag> +Red Hat Software:<newline> +Michael K. Johnson <johnsonm@redhat.com> 1996/11/18<newline> +(if unavailable, contact Elliot Lee <sopwith@cuc.edu>). + +<tag><bf>Management groups provided:</bf></tag> +authentication + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> +clean + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +The list-file module provides a way to deny or allow services based on +an arbitrary file. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tt>onerr=succeed|fail</tt>; +<tt>sense=allow|deny</tt>; +<tt>file=</tt><it>filename</it>; +<tt>item=user|tty|rhost|ruser|group|shell</tt> +<tt>apply=user|@group</tt> + +<tag><bf>Description:</bf></tag> + +The module gets the item of the type specified -- <tt>user</tt> specifies +the username, <tt>PAM_USER</tt>; tty specifies the name of the terminal +over which the request has been made, <tt>PAM_TTY</tt>; rhost specifies +the name of the remote host (if any) from which the request was made, +<tt>PAM_RHOST</tt>; and ruser specifies the name of the remote user +(if available) who made the request, <tt>PAM_RUSER</tt> -- and looks for +an instance of that item in the file <it>filename</it>. <it>filename</it> +contains one line per item listed. If the item is found, then if +<tt>sense=allow</tt>, <tt>PAM_SUCCESS</tt> is returned, causing the +authorization request to succeed; else if <tt>sense=deny</tt>, +<tt>PAM_AUTH_ERR</tt> is returned, causing the authorization +request to fail. + +<p> +If an error is encountered (for instance, if <it>filename</it> +does not exist, or a poorly-constructed argument is encountered), +then if <tt>onerr=succeed</tt>, <tt>PAM_SUCCESS</tt> is returned, +otherwise if <tt>onerr=fail</tt>, <tt>PAM_AUTH_ERR</tt> or +<tt>PAM_SERVICE_ERR</tt> (as appropriate) will be returned. + +<p> +An additional argument, <tt>apply=</tt>, can be used to restrict the +application of the above to a specific user +(<tt>apply=</tt><em>username</em>) or a given group +(<tt>apply=@</tt><em>groupname</em>). This added restriction is only +meaningful when used with the <tt/tty/, <tt/rhost/ and <tt/shell/ +<em/items/. + +<p> +Besides this last one, all arguments should be specified; do not count +on any default behavior, as it is subject to change. + +<p> +No credentials are awarded by this module. + +<tag><bf>Examples/suggested usage:</bf></tag> + +Classic ``ftpusers'' authentication can be implemented with this entry +in <tt>/etc/pam.conf</tt>: +<tscreen> +<verb> +# +# deny ftp-access to users listed in the /etc/ftpusers file +# +ftp auth required pam_listfile.so \ + onerr=succeed item=user sense=deny file=/etc/ftpusers +</verb> +</tscreen> +Note, users listed in <tt>/etc/ftpusers</tt> file are +(counterintuitively) <bf/not/ allowed access to the ftp service. + +<p> +To allow login access only for certain users, you can use a +<tt/pam.conf/ entry like this: +<tscreen> +<verb> +# +# permit login to users listed in /etc/loginusers +# +login auth required pam_listfile.so \ + onerr=fail item=user sense=allow file=/etc/loginusers +</verb> +</tscreen> + +<p> +For this example to work, all users who are allowed to use the login +service should be listed in the file <tt>/etc/loginusers</tt>. Unless +you are explicitly trying to lock out root, make sure that when you do +this, you leave a way for root to log in, either by listing root in +<tt>/etc/loginusers</tt>, or by listing a user who is able to <em/su/ +to the root account. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_mail.sgml b/Linux-PAM/doc/modules/pam_mail.sgml new file mode 100644 index 00000000..c157659a --- /dev/null +++ b/Linux-PAM/doc/modules/pam_mail.sgml @@ -0,0 +1,142 @@ +<!-- + $Id: pam_mail.sgml,v 1.4 2001/12/08 18:56:47 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>The mail module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_mail/ + +<tag><bf>Author:</bf></tag> +Andrew G. Morgan <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> +Author + +<tag><bf>Management groups provided:</bf></tag> +Authentication (credential) +Session (open) + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +Default mail directory <tt>/var/spool/mail/</tt> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module looks at the user's mail directory and indicates +whether the user has any mail in it. + +<sect2>Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; <tt/dir=/<em/directory-name/; <tt/nopen/; <tt/close/; +<tt/noenv/; <tt/empty/; <tt/hash=/<em/hashcount/; <tt/standard/; +<tt/quiet/; + +<tag><bf>Description:</bf></tag> + +This module provides the ``you have new mail'' service to the user. It +can be plugged into any application that has credential hooks. It gives a +single message indicating the <em/newness/ of any mail it finds in the +user's mail folder. This module also sets the <bf/Linux-PAM/ +environment variable, <tt/MAIL/, to the user's mail directory. + +<p> +The behavior of this module can be modified with one of the following +flags: + +<p> +<itemize> +<item><tt/debug/ +- write more information to <tt/syslog(3)/. + +<item><tt/dir=/<em/pathname/ +- look for the users' mail in an alternative directory given by +<em/pathname/. The default location for mail is +<tt>/var/spool/mail</tt>. Note, if the supplied <em/pathname/ is +prefixed by a `<tt/˜/', the directory is interpreted as +indicating a file in the user's home directory. + +<item><tt/nopen/ +- instruct the module to <em/not/ print any mail information when the +user's credentials are acquired. This flag is useful to get the <tt/MAIL/ +environment variable set, but to not display any information about it. + +<item><tt/close/ +- instruct the module to indicate if the user has any mail at the as +the user's credentials are revoked. + +<item><tt/noenv/ +- do not set the <tt/MAIL/ environment variable. + +<item><tt/empty/ +- indicate that the user's mail directory is empty if this is found to +be the case. + +<item><tt/hash=/<em/hashcount/ +- mail directory hash depth. For example, a <em/hashcount/ of 2 would +make the mailfile be <tt>/var/spool/mail/u/s/user</tt>. + +<item><tt/standard/ +- old style "You have..." format which doesn't show the mail spool being used. + this also implies "empty" + +<item><tt/quiet/ +- only report when there is new mail. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +This module can be used to indicate that the user has new mail when +they <em/login/ to the system. Here is a sample entry for your +<tt>/etc/pam.conf</tt> file: +<tscreen> +<verb> +# +# do we have any mail? +# +login session optional pam_mail.so +</verb> +</tscreen> + +<p> +Note, if the mail spool file (be it <tt>/var/spool/mail/$USER</tt> or +a pathname given with the <tt>dir=</tt> parameter) is a directory then +<tt>pam_mail</tt> assumes it is in the <it>Qmail Maildir</it> format. + +<p> +Note, some applications may perform this function themselves. In such +cases, this module is not necessary. + +</descrip> + +<sect2>Authentication component + +<p> +Then authentication companent works the same as the session component, +except that everything is done during the <tt>pam_setcred()</tt> phase. + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_mkhomedir.sgml b/Linux-PAM/doc/modules/pam_mkhomedir.sgml new file mode 100644 index 00000000..8428565d --- /dev/null +++ b/Linux-PAM/doc/modules/pam_mkhomedir.sgml @@ -0,0 +1,83 @@ +<!-- + +Ben Collins <bcollins@debian.org> + +--> + +<sect1>Create home directories on initial login + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_mkhomedir/ + +<tag><bf>Author:</bf></tag> +Jason Gunthorpe <jgg@ualberta.ca> + +<tag><bf>Maintainer:</bf></tag> +Ben Collins <bcollins@debian.org> + +<tag><bf>Management groups provided:</bf></tag> +Session + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +Creates home directories on the fly for authenticated users. + +<sect2>Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/skel=skeleton-dir/; <tt/umask=octal-umask/; + +<tag><bf>Description:</bf></tag> +This module is useful for distributed systems where the user account is +managed in a central database (such as NIS, NIS+, or LDAP) and accessed +through miltiple systems. It frees the administrator from having to create +a default home directory on each of the systems by creating it upon the +first succesfully authenticated login of that user. The skeleton directory +(usually /etc/skel/) is used to copy default files and also set's a umask +for the creation. + +<p> +The behavior of this module can be modified with one of the following +flags: + +<p> +<itemize> + +<item><tt/skel/ +- The skeleton directory for default files to copy to the new home directory. + +<item><tt/umask/ +- An octal for of the same format as you would pass to the shells umask command. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +session required pam_mkhomedir.so skel=/etc/skel/ umask=0022 + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_motd.sgml b/Linux-PAM/doc/modules/pam_motd.sgml new file mode 100644 index 00000000..8ddc6392 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_motd.sgml @@ -0,0 +1,77 @@ +<!-- + +Ben Collins <bcollins@debian.org> + +--> + +<sect1>Output the motd file + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_motd/ + +<tag><bf>Author:</bf></tag> +Ben Collins <bcollins@debian.org> + +<tag><bf>Maintainer:</bf></tag> +Author + +<tag><bf>Management groups provided:</bf></tag> +Session (open) + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module outputs the motd file (<em>/etc/motd</em> by default) upon +successful login. + +<sect2>Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; <tt/motd=motd-file-name/; + +<tag><bf>Description:</bf></tag> +This module allows you to have arbitrary motd's (message of the day) +output after a succesful login. By default this file is <em>/etc/motd</em>, +but is configurable to any file. + +<p> +The behavior of this module can be modified with one of the following +flags: + +<p> +<itemize> + +<item><tt/motd/ +- the file to output if not using the default. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +login session pam_motd.so motd=/etc/motd + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_nologin.sgml b/Linux-PAM/doc/modules/pam_nologin.sgml new file mode 100644 index 00000000..241c24f0 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_nologin.sgml @@ -0,0 +1,81 @@ +<!-- + $Id: pam_nologin.sgml,v 1.3 2002/06/27 05:43:28 agmorgan Exp $ + + This file was written by Michael K. Johnson <johnsonm@redhat.com> +--> + +<sect1>The no-login module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_nologin/ + +<tag><bf>Author:</bf></tag> +Written by Michael K. Johnson <johnsonm@redhat.com><newline> + +<tag><bf>Maintainer:</bf></tag> + +<tag><bf>Management groups provided:</bf></tag> +account; authentication + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +Provides standard Unix <em/nologin/ authentication. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +successok, file=<<em/filename/> + +<tag><bf>Description:</bf></tag> + +Provides standard Unix <em/nologin/ authentication. If the file +<tt>/etc/nologin</tt> exists, only root is allowed to log in; other +users are turned away with an error message (and the module returns +<tt/PAM_AUTH_ERR/ or <tt/PAM_USER_UNKNOWN/). All users (root or +otherwise) are shown the contents of <tt>/etc/nologin</tt>. + +<p> +If the file <tt>/etc/nologin</tt> does not exist, this module defaults +to returning <tt/PAM_IGNORE/, but the <tt/successok/ module argument +causes it to return <tt/PAM_SUCCESS/ in this case. + +<p> +The administrator can override the default nologin file with the +<tt/file=/<em/pathname/ module argument. + +<tag><bf>Examples/suggested usage:</bf></tag> + +In order to make this module effective, all login methods should be +secured by it. It should be used as a <tt>required</tt> method listed +before any <tt>sufficient</tt> methods in order to get standard Unix +nologin semantics. Note, the use of <tt/successok/ module argument +causes the module to return <tt/PAM_SUCCESS/ and as such would break +such a configuration - failing <tt/sufficient/ modules would lead to a +successful login because the nologin module <em/succeeded/. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_permit.sgml b/Linux-PAM/doc/modules/pam_permit.sgml new file mode 100644 index 00000000..1d6bbce4 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_permit.sgml @@ -0,0 +1,83 @@ +<!-- + $Id: pam_permit.sgml,v 1.2 2001/12/08 18:56:47 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>The promiscuous module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +pam_permit + +<tag><bf>Author:</bf></tag> +Andrew G. Morgan, <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> +Linux-PAM maintainer. + +<tag><bf>Management groups provided:</bf></tag> +account; authentication; password; session + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> +VERY LOW. Use with extreme caution. + +<tag><bf>Clean code base:</bf></tag> +Clean. + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module is very dangerous. It should be used with extreme +caution. Its action is always to permit access. It does nothing else. + +<sect2>Account+Authentication+Password+Session components + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +No matter what management group, the action of this module is to +simply return <tt/PAM_SUCCESS/ -- operation successful. + +<p> +In the case of authentication, the user's name will be acquired. Many +applications become confused if this name is unknown. + +<tag><bf>Examples/suggested usage:</bf></tag> + +It is seldom a good idea to use this module. However, it does have +some legitimate uses. For example, if the system-administrator wishes +to turn off the account management on a workstation, and at the same +time continue to allow logins, then she might use the following +configuration file entry for login: +<tscreen> +<verb> +# +# add this line to your other login entries to disable account +# management, but continue to permit users to log in... +# +login account required pam_permit.so +</verb> +</tscreen> + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_pwdb.sgml b/Linux-PAM/doc/modules/pam_pwdb.sgml new file mode 100644 index 00000000..7b237d2e --- /dev/null +++ b/Linux-PAM/doc/modules/pam_pwdb.sgml @@ -0,0 +1,257 @@ +<!-- + $Id: pam_pwdb.sgml,v 1.4 2002/07/11 05:43:50 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>The Password-Database module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +pam_pwdb + +<tag><bf>Author:</bf></tag> +Cristian Gafton <gafton@redhat.com> <newline> +and Andrew G. Morgan <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> +Red Hat. + +<tag><bf>Management groups provided:</bf></tag> +account; authentication; password; session + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +Requires properly configured <tt/libpwdb/ + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module is a pluggable replacement for the <tt/pam_unix_../ +modules. It uses the generic interface of the <em/Password Database/ +library <tt>libpwdb</tt>. + +<sect2>Account component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/ + +<tag><bf>Description:</bf></tag> + +The <tt/debug/ argument makes the accounting functions of this module +<tt/syslog(3)/ more information on its actions. (Remaining arguments +supported by the other functions of this module are silently ignored, +but others are logged as errors through <tt/syslog(3)/). + +Based on the following <tt/pwdb_element/s: +<tt/expire/; +<tt/last_change/; +<tt/max_change/; +<tt/defer_change/; +<tt/warn_change/, +this module performs the task of establishing the status of the user's +account and password. In the case of the latter, it may offer advice +to the user on changing their password or, through the +<tt/PAM_AUTHTOKEN_REQD/ return, delay giving service to the user until +they have established a new password. The entries listed above are +documented in the <em/Password Database Library Guide/ (see pointer +above). Should the user's record not contain one or more of these +entries, the corresponding <em/shadow/ check is not performed. + +<tag><bf>Examples/suggested usage:</bf></tag> + +In its accounting mode, this module can be inserted as follows: +<tscreen> +<verb> +# +# Ensure users account and password are still active +# +login account required pam_pwdb.so +</verb> +</tscreen> + +</descrip> + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; +<tt/use_first_pass/; +<tt/try_first_pass/; +<tt/nullok/; +<tt/nodelay/; +<tt/likeauth/; +<tt/noreap/ + +<tag><bf>Description:</bf></tag> + +The <tt/debug/ argument makes the authentication functions of this +module <tt/syslog(3)/ more information on its actions. + +<p> +The default action of this module is to not permit the user access to +a service if their <em/official/ password is blank. The <tt/nullok/ +argument overrides this default. + +<p> +When given the argument <tt/try_first_pass/, before prompting the user +for their password, the module first tries the previous stacked +<tt/auth/-module's password in case that satisfies this module as +well. The argument <tt/use_first_pass/ forces the module to use such a +recalled password and will never prompt the user - if no password is +available or the password is not appropriate, the user will be denied +access. + +<p> +The argument, <tt>nodelay</tt>, can be used to discourage the +authentication component from requesting a delay should the +authentication as a whole fail. The default action is for the module +to request a delay-on-failure of the order of one second. + +<p> +Remaining arguments, supported by the other functions of this module, +are silently ignored. Other arguments are logged as errors through +<tt/syslog(3)/. + +<p> +A helper binary, <tt>pwdb_chkpwd</tt>, is provided to check the user's +password when it is stored in a read protected database. This binary +is very simple and will only check the password of the user invoking +it. It is called transparently on behalf of the user by the +authenticating component of this module. In this way it is possible +for applications like <em>xlock</em> to work without being +setuid-root. The module, by default, will temporarily turn off +<tt/SIGCHLD/ handling for the duration of execution of the helper +binary. This is generally the right thing to do, as many applications +are not prepared to handle this signal from a child they didn't know +was <tt/fork()/d. The <tt/noreap/ module argument can be used to +suppress this temporary shielding and may be needed for use with +certain applications. + +<p> +The <tt>likeauth</tt> argument makes the module return the same value +when called as a credential setting module and an authentication +module. This will help libpam take a sane path through the auth +component of your configuration file. + +<tag><bf>Examples/suggested usage:</bf></tag> + +The correct functionality of this module is dictated by having an +appropriate <tt>/etc/pwdb.conf</tt> file, the user +databases specified there dictate the source of the authenticated +user's record. + +</descrip> + +<sect2>Password component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; <tt/nullok/; <tt/not_set_pass/; <tt/use_authtok/; +<tt/try_first_pass/; <tt/use_first_pass/; <tt/md5/; <tt/bigcrypt/; +<tt/shadow/; <tt/radius/; <tt/unix/ + +<tag><bf>Description:</bf></tag> + +This part of the <tt/pam_pwdb/ module performs the task of updating +the user's password. Thanks to the flexibility of <tt/libpwdb/ this +module is able to move the user's password from one database to +another, perhaps securing the user's database entry in a dynamic +manner (<em/this is very ALPHA code at the moment!/) - this is the +purpose of the <tt/shadow/, <tt/radius/ and <tt/unix/ arguments. + +<p> +In the case of conventional unix databases (which store the password +encrypted) the <tt/md5/ argument is used to do the encryption with the +MD5 function as opposed to the <em/conventional/ <tt/crypt(3)/ call. +As an alternative to this, the <tt/bigcrypt/ argument can be used to +encrypt more than the first 8 characters of a password with DEC's +(Digital Equipment Cooperation) `C2' extension to the standard UNIX +<tt/crypt()/ algorithm. + +<p> +The <tt/nullok/ module is used to permit the changing of a password +<em/from/ an empty one. Without this argument, empty passwords are +treated as account-locking ones. + +<p> +The argument <tt/use_first_pass/ is used to lock the choice of old and +new passwords to that dictated by the previously stacked <tt/password/ +module. The <tt/try_first_pass/ argument is used to avoid the user +having to re-enter an old password when <tt/pam_pwdb/ follows a module +that possibly shared the user's old password - if this old password is +not correct the user will be prompted for the correct one. The +argument <tt/use_authtok/ is used to <em/force/ this module to set the +new password to the one provided by the previously stacked +<tt/password/ module (this is used in an example of the stacking of +the <em/Cracklib/ module documented above). + +<p> +The <tt/not_set_pass/ argument is used to inform the module that it is +not to pay attention to/make available the old or new passwords from/to +other (stacked) password modules. + +<p> +The <tt/debug/ argument makes the password functions of this module +<tt/syslog(3)/ more information on its actions. Other arguments may be +logged as erroneous to <tt/syslog(3)/. + +<tag><bf>Examples/suggested usage:</bf></tag> + +An example of the stacking of this module with respect to the +pluggable password checking module, <tt/pam_cracklib/, is given in +that modules section above. +</descrip> + +<sect2>Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +No arguments are recognized by this module component. Its action is +simply to log the username and the service-type to +<tt/syslog(3)/. Messages are logged at the beginning and end of the +user's session. + +<tag><bf>Examples/suggested usage:</bf></tag> + +The use of the session modules is straightforward: +<tscreen> +<verb> +# +# pwdb - unix like session opening and closing +# +login session required pam_pwdb.so +</verb> +</tscreen> + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_radius.sgml b/Linux-PAM/doc/modules/pam_radius.sgml new file mode 100644 index 00000000..8ebfa0a8 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_radius.sgml @@ -0,0 +1,117 @@ +<!-- + $Id: pam_radius.sgml,v 1.2 2001/03/19 01:46:41 agmorgan Exp $ + + This file was written by Cristian Gafton <gafton@redhat.com> +--> + +<sect1>The RADIUS session module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_radius/ + +<tag><bf>Author:</bf></tag> +Cristian Gafton <gafton@redhat.com> + +<tag><bf>Maintainer:</bf></tag> +Author. + +<tag><bf>Management groups provided:</bf></tag> +session + +<tag><bf>Cryptographically sensitive:</bf></tag> +This module does not deal with passwords + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> +gcc reports 1 warning when compiling <tt>/usr/include/rpc/clnt.h</tt>. +Hey, is not my fault ! + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +yes; this is a network module (independent of application). + +</descrip> + +<sect2>Overview of module + +<p> +This module is intended to provide the session service for users +authenticated with a RADIUS server. At the present stage, the only +option supported is the use of the RADIUS server as an accounting +server. + +<sect2>Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tt/debug/ - verbose logging to <tt/syslog(3)/. + +<tag><bf>Description:</bf></tag> + +This module is intended to provide the session service for users +authenticated with a RADIUS server. At the present stage, the only +option supported is the use of the RADIUS server as an <em/accounting/ +server. + +<p> +(There are few things which needs to be cleared out first in +the PAM project until one will be able to use this module and expect +it to magically start pppd in response to a RADIUS server command to +use PPP for this user, or to initiate a telnet connection to another +host, or to hang and call back the user using parameters provided in +the RADIUS server response. Most of these things are better suited for +the radius login application. I hope to make available Real Soon (tm) +patches for the login apps to make it work this way.) + +<p> +When opening a session, this module sends an ``Accounting-Start'' +message to the RADIUS server, which will log/update/whatever a +database for this user. On close, an ``Accounting-Stop'' message is +sent to the RADIUS server. + +<p> +This module has no other prerequisites for making it work. One can +install a RADIUS server just for fun and use it as a centralized +accounting server and forget about wtmp/last/sac etc. . + +<tag><bf>Examples/suggested usage:</bf></tag> + +For the services that need this module (<em/login/ for example) put +the following line in <tt>/etc/pam.conf</tt> as the last line for that +service (usually after the pam_unix session line): +<tscreen> +<verb> +login session required pam_radius.so +</verb> +</tscreen> +Replace <tt/login/ for each service you are using this module. + +<p> +This module make extensive use of the API provided in libpwdb +0.54preB or later. By default, it will read the radius server +configuration (hostname and secret) from <tt>/etc/raddb/server</tt>. +This is a default compiled into libpwdb, and curently there is no way to +modify this default without recompiling libpwdb. I am working on +extending the radius support from libpwdb to provide a possibility +to make this runtime-configurable. + +Also please note that libpwdb will require also the RADIUS +dictionary to be present (<tt>/etc/raddb/dictionary</tt>). + +</descrip> + +<!-- +End of sgml insert for this module. +--> + diff --git a/Linux-PAM/doc/modules/pam_rhosts.sgml b/Linux-PAM/doc/modules/pam_rhosts.sgml new file mode 100644 index 00000000..ded5697b --- /dev/null +++ b/Linux-PAM/doc/modules/pam_rhosts.sgml @@ -0,0 +1,164 @@ +<!-- + $Id: pam_rhosts.sgml,v 1.2 2001/12/08 18:56:47 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>The rhosts module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_rhosts_auth/ + +<tag><bf>Author:</bf></tag> +Al Longyear <longyear@netcom.com> + +<tag><bf>Maintainer:</bf></tag> + +<tag><bf>Management groups provided:</bf></tag> +authentication + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> +Clean. + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> +Standard <tt/inet_addr()/, <tt/gethostbyname()/ function calls. + +</descrip> + +<sect2>Overview of module + +<p> +This module performs the standard network authentication for services, +as used by traditional implementations of <em/rlogin/ and <em/rsh/ +etc. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/no_hosts_equiv/; <tt/no_rhosts/; <tt/debug/; <tt/no_warn/; +<tt/privategroup/; <tt/promiscuous/; <tt/suppress/ + +<tag><bf>Description:</bf></tag> + +The authentication mechanism of this module is based on the contents +of two files; <tt>/etc/hosts.equiv</tt> (or <tt/_PATH_HEQUIV/ in +<tt>#include <netdb.h></tt>) and <tt>~/.rhosts</tt>. Firstly, +hosts listed in the former file are treated as equivalent to the +localhost. Secondly, entries in the user's own copy of the latter file +is used to map "<tt/remote-host remote-user/" pairs to that user's +account on the current host. Access is granted to the user if their +host is present in <tt>/etc/hosts.equiv</tt> and their remote account +is identical to their local one, or if their remote account has an +entry in their personal configuration file. + +<p> +Some restrictions are applied to the attributes of the user's personal +configuration file: it must be a regular file (as defined by +<tt/S_ISREG(x)/ of POSIX.1); it must be owned by the <em/superuser/ or +the user; it must not be writable by any user besides its owner. + +<p> +The module authenticates a remote user (internally specified by the +item <tt/PAM_RUSER/) connecting from the remote host (internally +specified by the item <tt/PAM_RHOST/). Accordingly, for applications +to be compatible this authentication module they must set these items +prior to calling <tt/pam_authenticate()/. The module is not capable +of independently probing the network connection for such information. + +<p> +In the case of <tt/root/-access, the <tt>/etc/host.equiv</tt> file is +<em/ignored/ unless the <tt>hosts_equiv_rootok</tt> option +should be used. Instead, the superuser must have a correctly configured +personal configuration file. + +<p> +The behavior of the module is modified by flags: +<itemize> +<item> +<tt/debug/ - +log more information to <tt/syslog(3)/. (XXX - actually, this module +does not do any logging currently, please volunteer to fix this!) + +<item> +<tt/no_warn/ - +do not give verbal warnings to the user about failures etc. (XXX - +this module currently does not issue any warnings, please volunteer to +fix this!) + +<item> +<tt/no_hosts_equiv/ - +ignore the contents of the <tt>/etc/hosts.equiv</tt> file. + +<item> +<tt/hosts_equiv_rootok/ - +allow the use of <tt>/etc/hosts.equiv</tt> for superuser. Without this +option <tt>/etc/hosts.equiv</tt> is not consulted for the superuser account. +This option has no effect if the <tt>no_hosts_equiv</tt> option is used. + +<item> +<tt/no_rhosts/ - +ignore the contents of all user's personal configuration file +<tt>~/.rhosts</tt>. + +<item> +<tt/privategroup/ - +normally, the <tt>~/.rhosts</tt> file must not be writable by anyone +other than its owner. This option overlooks group write access in the +case that the group owner of this file has the same name as the +user being authenticated. To lessen the security problems associated +with this option, the module also checks that the user is the only +member of their private group. + +<item> +<tt/promiscuous/ - +A host entry of `+' will lead to all hosts being granted +access. Without this option, '+' entries will be ignored. Note, that +the <tt/debug/ option will syslog a warning in this latter case. + +<item> +<tt/suppress/ - +This will prevent the module from <tt/syslog(3)/ing a warning message +when this authentication fails. This option is mostly for keeping +logs free of meaningless errors, in particular when the module is used +with the <tt/sufficient/ control flag. + +</itemize> +<tag><bf>Examples/suggested usage:</bf></tag> + +To allow users to login from trusted remote machines, you should try +adding the following line to your <tt>/etc/pam.conf</tt> file +<em/before/ the line that would otherwise prompt the user for a +password: +<tscreen> +<verb> +# +# No passwords required for users from hosts listed above. +# +login auth sufficient pam_rhosts_auth.so no_rhosts +</verb> +</tscreen> +Note, in this example, the system administrator has turned off all +<em/personal/ <em/rhosts/ configuration files. Also note, that this module +can be used to <em/only/ allow remote login from hosts specified in +the <tt>/etc/host.equiv</tt> file, by replacing <tt/sufficient/ in the +above example with <tt/required/. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_rootok.sgml b/Linux-PAM/doc/modules/pam_rootok.sgml new file mode 100644 index 00000000..b5ae6921 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_rootok.sgml @@ -0,0 +1,85 @@ +<!-- + $Id: pam_rootok.sgml,v 1.2 2001/12/08 18:56:47 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>The root access module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +pam_rootok + +<tag><bf>Author:</bf></tag> +Andrew G. Morgan <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> +<bf>Linux-PAM</bf> maintainer + +<tag><bf>Management groups provided:</bf></tag> +authentication + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> +Clean. + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module is for use in situations where the superuser wishes +to gain access to a service without having to enter a password. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/ + +<tag><bf>Description:</bf></tag> + +This module authenticates the user if their <tt/uid/ is <tt/0/. +Applications that are created <em/setuid/-root generally retain the +<tt/uid/ of the user but run with the authority of an enhanced +<em/effective-/<tt/uid/. It is the real <tt/uid/ that is checked. + +<tag><bf>Examples/suggested usage:</bf></tag> + +In the case of the <tt/su/ application the historical usage is to +permit the superuser to adopt the identity of a lesser user without +the use of a password. To obtain this behavior under <tt/Linux-PAM/ +the following pair of lines are needed for the corresponding entry in +the configuration file: +<tscreen> +<verb> +# +# su authentication. Root is granted access by default. +# +su auth sufficient pam_rootok.so +su auth required pam_unix_auth.so +</verb> +</tscreen> + +<p> +Note. For programs that are run by the superuser (or started when the +system boots) this module should not be used to authenticate users. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_securetty.sgml b/Linux-PAM/doc/modules/pam_securetty.sgml new file mode 100644 index 00000000..fc89af23 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_securetty.sgml @@ -0,0 +1,72 @@ +<!-- + $Id: pam_securetty.sgml,v 1.1.1.1 2000/06/20 22:11:04 agmorgan Exp $ + + This file was written by Michael K. Johnson <johnsonm@redhat.com> +--> + +<sect1>The securetty module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_securetty/ + +<tag><bf>Author[s]:</bf></tag> +Elliot Lee <sopwith@cuc.edu> + +<tag><bf>Maintainer:</bf></tag> +Red Hat Software:<newline> +<em/currently/ Michael K. Johnson <johnsonm@redhat.com><newline> +(if unavailable, contact Elliot Lee <sopwith@cuc.edu>). + +<tag><bf>Management groups provided:</bf></tag> +authentication + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +<tt>/etc/securetty</tt> file + +<tag><bf>Network aware:</bf></tag> + +Requires the application to fill in the <tt>PAM_TTY</tt> item +correctly in order to act meaningfully. + +</descrip> + +<sect2>Overview of module + +<p> +Provides standard Unix securetty checking. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +Provides standard Unix securetty checking, which causes authentication +for root to fail unless <tt>PAM_TTY</tt> is set to a string listed in +the <tt>/etc/securetty</tt> file. For all other users, it succeeds. + +<tag><bf>Examples/suggested usage:</bf></tag> + +For canonical usage, should be listed as a <tt>required</tt> +authentication method before any <tt>sufficient</tt> authentication +methods. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_tally.sgml b/Linux-PAM/doc/modules/pam_tally.sgml new file mode 100644 index 00000000..ee6fad46 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_tally.sgml @@ -0,0 +1,203 @@ +<!-- + + $Id: pam_tally.sgml,v 1.3 2005/01/16 22:12:25 toady Exp $ + + This template file was written by Andrew G. Morgan <morgan@kernel.org> + adapted from text provided by Tim Baverstock. +--> + +<sect1>The login counter (tallying) module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +pam_tally + +<tag><bf>Author[s]:</bf></tag> +Tim Baverstock +Tomas Mraz + +<tag><bf>Maintainer:</bf></tag> + +<tag><bf>Management groups provided:</bf></tag> +auth; account + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +A faillog file (default location /var/log/faillog) + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This module maintains a count of attempted accesses, can reset count +on success, can deny access if too many attempts fail. + +<p> +pam_tally comes in two parts: <tt>pam_tally.so</tt> and +<tt>pam_tally</tt>. The former is the PAM module and the latter, a +stand-alone program. <tt>pam_tally</tt> 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. For example, one might find it +useful to clear all counts every midnight from a cron job. + +<p> +The counts file is organized as a binary-word array, indexed by +uid. You can probably make sense of it with <tt>od</tt>, if you don't +want to use the supplied appliction. + +<p> +Note, there are some outstanding issues with this module: +<tt>pam_tally</tt> is very dependant on <tt>getpw*()</tt> - a database +of usernames would be much more flexible + +<sect3>Generic options accepted by both components +<p> +<itemize> +<item> <tt>onerr=</tt>(<tt>succeed</tt>|<tt>fail</tt>): + if something weird happens, such as unable to open the file, how + should the module react? +<item> <tt>file=</tt><em>/where/to/keep/counts</em>: + specify the file location for the counts. + The default location is <tt>/var/log/faillog</tt>. +<item> <tt>audit</tt>: + display the username typed if the user is not found. It may be + useful for scripts, but you should know users often type their + password instead making your system weaker. Activate it only if you + know what you are doing. +</itemize> + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt>onerr=</tt>(<tt>succeed</tt>|<tt>fail</tt>); +<tt>file=</tt>/where/to/keep/counts; +<tt>deny=</tt><em>n</em>; +<tt>lock_time=</tt><em>n</em>; +<tt>unlock_time=</tt><em>n</em>; +<tt>magic_root</tt>; +<tt>even_deny_root_account</tt>; +<tt>per_user</tt>; +<tt>no_lock_time</tt> +<tt>no_reset</tt>; + +<tag><bf>Description:</bf></tag> + +<p> +The authentication component first checks if the user should be denied +access and if not it increments attempted login counter. +Then on call to <tt>pam_setcred</tt> it resets the attempts counter +if the user is NOT magic root. + +<p> +<tag><bf>Examples/suggested usage:</bf></tag> + +<p> +The <tt>deny=</tt><em>n</em> option is used to deny access if tally +for this user exceeds <em>n</em>. + +<p> +The <tt>lock_time=</tt><em>n</em> option is used to always deny access +for at least <em>n</em> seconds after a failed attempt. + +<p> +The <tt>unlock_time=</tt><em>n</em> option is used to allow access after +<em>n</em> seconds after the last failed attempt with exceeded tally. +If this option is used the user will be locked out only for the specified +amount of time after he exceeded his maximum allowed attempts. Otherwise +the lock is removed only by a manual intervention of the system administrator. + +<p> +The <tt>magic_root</tt> option is used to indicate that if +the module is invoked by a user with uid=0, then the counter is not +incremented. The sys-admin should use this for user launched services, +like <tt>su</tt>, otherwise this argument should be omitted. + +<p> +By way of more explanation, when a process already running as root +tries to access some service, the access is <em>magic</em>, and +bypasses <tt>pam_tally</tt>'s checks: this is handy for <tt>su</tt>ing +from root into an account otherwise blocked. However, for services +like <tt>telnet</tt> or <tt>login</tt>, which always effectively run +from the root account, root (ie everyone) shouldn't be granted this +magic status, and the flag `magic_root' should not be set in this +situation, as noted in the summary above. + +<p> +Normally, failed attempts to access root will <bf>NOT</bf> 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 +<tt>su</tt> or at the machine console (not +<tt>telnet</tt>/<tt>rsh</tt>, etc), this is safe. If you really want +root to be blocked for some given service, use +<tt>even_deny_root_account</tt>. + +<p> +If <tt>/var/log/faillog</tt> contains a non-zero <tt>.fail_max/.fail_locktime</tt> +field for this user then the <tt>per_user</tt> module argument will +ensure that the module uses this value and not the global +<tt>deny/lock_time=</tt><em>n</em> parameter. + +<p> +The <tt>no_lock_time</tt> option is for ensuring that the module does +not use the <tt>.fail_locktime</tt> field in /var/log/faillog for this +user. + +<p> +The <tt>no_reset</tt> option is used to instruct the module to not reset +the count on successful entry. + +</descrip> + +<sect2>Account component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt>onerr=</tt>(<tt>succeed</tt>|<tt>fail</tt>); +<tt>file=</tt>/where/to/keep/counts; +<tt>magic_root</tt>; +<tt>no_reset</tt>; + +<tag><bf>Description:</bf></tag> + +<p> +The account component resets attempts counter if the user is NOT +magic root. This phase can be used optionaly for services which don't call +pam_setcred correctly or if the reset should be done regardless +of the failure of the account phase of other modules. + +<tag><bf>Examples/suggested usage:</bf></tag> + +<p> +The <tt>magic_root</tt> option is used to indicate that if +the module is invoked by a user with uid=0, then the counter is not +decremented/reset. The sys-admin should use this for user launched services, +like <tt>su</tt>, otherwise this argument should be omitted. + +<p> +The <tt>no_reset</tt> option is used to instruct the module to not reset +the count on successful entry. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_time.sgml b/Linux-PAM/doc/modules/pam_time.sgml new file mode 100644 index 00000000..ef761223 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_time.sgml @@ -0,0 +1,166 @@ +<!-- + $Id: pam_time.sgml,v 1.4 2002/05/10 04:03:02 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>Time control + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_time/ + +<tag><bf>Author:</bf></tag> +Andrew G. Morgan <tt><morgan@kernel.org></tt> + +<tag><bf>Maintainer:</bf></tag> +Author + +<tag><bf>Management groups provided:</bf></tag> +account + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +Requires a configuration file <tt>/etc/security/time.conf</tt> + +<tag><bf>Network aware:</bf></tag> +Through the <tt/PAM_TTY/ item only + +</descrip> + +<sect2>Overview of module + +<p> +Running a well regulated system occasionally involves restricting +access to certain services in a selective manner. This module offers +some time control for access to services offered by a system. Its +actions are determined with a configuration file. This module can be +configured to deny access to (individual) users based on their name, +the time of day, the day of week, the service they are applying for +and their terminal from which they are making their request. + +<sect2>Account component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +This module bases its actions on the rules listed in its configuration +file: <tt>/etc/security/time.conf</tt>. Each rule has the following +form, +<tscreen> +<em/services/<tt/;/<em/ttys/<tt/;/<em/users/<tt/;/<em/times/ +</tscreen> +In words, each rule occupies a line, terminated with a newline or the +beginning of a comment; a `<tt/#/'. It contains four fields separated +with semicolons, `<tt/;/'. The fields are as follows: + +<p> +<itemize> +<item><em/services/ - +a logic list of service names that are affected by this rule. + +<item><em/ttys/ - +a logic list of terminal names indicating those terminals covered by +the rule. + +<item><em/user/ - +a logic list of usernames to which this rule applies + +<p> +By a logic list we mean a sequence of tokens (associated with the +appropriate <tt/PAM_/ item), containing no more than one wildcard +character; `<tt/*/', and optionally prefixed with a negation operator; +`<tt/!/'. Such a sequence is concatenated with one of two logical +operators: <tt/&/ (logical AND) and <tt/|/ (logical OR). Two +examples are: <tt>!morgan&!root</tt>, indicating that this rule +does not apply to the user <tt>morgan</tt> nor to <tt>root</tt>; and +<tt>tty*&!ttyp*</tt>, which indicates that the rule applies only +to console terminals but not pseudoterminals. + +<item><em/times/ - a logic list of times at which this rule +applies. The format of each element is a day/time-range. The days are +specified by a sequence of two character entries. For example, +<tt/MoTuSa/, indicates Monday Tuesday and Saturday. Note that +repeated days are <em/unset/; <tt/MoTuMo/ indicates Tuesday, and +<tt/MoWk/ means all weekdays bar Monday. The two character +combinations accepted are, +<tscreen> +<verb> +Mo Tu We Th Fr Sa Su Wk Wd Al +</verb> +</tscreen> +The last two of these being <em/weekend/ days and <em/all 7 days/ of +the week respectively. + +<p> +The time range part is a pair of 24-hour times, <em/HHMM/, separated +by a hyphen -- indicating the start and finish time for the rule. If +the finsish time is smaller than the start time, it is assumed to +apply on the following day. For an example, <tt/Mo1800-0300/ indicates +that the permitted times are Monday night from 6pm to 3am the +following morning. + +</itemize> + +<p> +Note, that the given time restriction is only applied when the first +three fields are satisfied by a user's application for service. + +<p> +For convenience and readability a rule can be extended beyond a single +line with a `<tt>\</tt><em/newline/'. + +<tag><bf>Examples/suggested usage:</bf></tag> + +The use of this module is initiated with an entry in the +<bf/Linux-PAM/ configuration file of the following type: +<tscreen> +<verb> +# +# apply pam_time accounting to login requests +# +login account required pam_time.so +</verb> +</tscreen> +where, here we are applying the module to the <em/login/ application. + +<p> +Some examples of rules that can be placed in the +<tt>/etc/security/time.conf</tt> configuration file are the following: +<descrip> + +<tag><tt>login ; tty* & !ttyp* ; !root ; !Al0000-2400</tt></tag> +all users except for <tt/root/ are denied access to console-login at +all times. + +<tag><tt>games ; * ; !waster ; Wd0000-2400 | Wk1800-0800</tt></tag> +games (configured to use Linux-PAM) are only to be accessed out of +working hours. This rule does not apply to the user <tt/waster/. + +</descrip> + +<p> +Note, currently there is no daemon enforcing the end of a session. +This needs to be remedied. + +<p> +Poorly formatted rules are logged as errors using <tt/syslog(3)/. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_unix.sgml b/Linux-PAM/doc/modules/pam_unix.sgml new file mode 100644 index 00000000..86c584a8 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_unix.sgml @@ -0,0 +1,296 @@ +<!-- + This file was written by Andrew G. Morgan <morgan@kernel.org> + + Converted from the pam_pwdb.sgml file for pam_unix by Ben Collins <bcollins@debian.org> +--> + +<sect1>The Unix Password module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +pam_unix + +<tag><bf>Author:</bf></tag> + +<tag><bf>Maintainer:</bf></tag> + +<tag><bf>Management groups provided:</bf></tag> +account; authentication; password; session + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +This is the standard Unix authentication module. It uses standard calls +from the system's libraries to retrieve and set account information as +well as authentication. Usually this is obtained from the /etc/passwd +and the /etc/shadow file as well if shadow is enabled. + +<sect2>Account component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; <tt/audit/ + +<tag><bf>Description:</bf></tag> + +The <tt/debug/ argument makes the accounting functions of this module +<tt/syslog(3)/ more information on its actions. (Remaining arguments +supported by the other functions of this module are silently ignored, +but others are logged as errors through <tt/syslog(3)/). The <tt/audit/ +argument causes even more logging. + +Based on the following <tt/shadow/ elements: +<tt/expire/; +<tt/last_change/; +<tt/max_change/; +<tt/min_change/; +<tt/warn_change/, +this module performs the task of establishing the status of the user's +account and password. In the case of the latter, it may offer advice +to the user on changing their password or, through the +<tt/PAM_AUTHTOKEN_REQD/ return, delay giving service to the user until +they have established a new password. The entries listed above are +documented in the <em/GNU Libc/ info documents. Should the user's record +not contain one or more of these entries, the corresponding <em/shadow/ +check is not performed. + +<tag><bf>Examples/suggested usage:</bf></tag> + +In its accounting mode, this module can be inserted as follows: +<tscreen> +<verb> +# +# Ensure users account and password are still active +# +login account required pam_unix.so +</verb> +</tscreen> + +</descrip> + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; +<tt/audit/; +<tt/use_first_pass/; +<tt/try_first_pass/; +<tt/nullok/; +<tt/nodelay/; +<tt/noreap/ + +<tag><bf>Description:</bf></tag> + +The <tt/debug/ argument makes the authentication functions of this +module <tt/syslog(3)/ more information on its actions. The <tt/audit/ +causes even more information to be logged. + +<p> +The default action of this module is to not permit the user access to +a service if their <em/official/ password is blank. The <tt/nullok/ +argument overrides this default. + +<p> +When given the argument <tt/try_first_pass/, before prompting the user +for their password, the module first tries the previous stacked +<tt/auth/-module's password in case that satisfies this module as +well. The argument <tt/use_first_pass/ forces the module to use such a +recalled password and will never prompt the user - if no password is +available or the password is not appropriate, the user will be denied +access. + +<p> +The argument, <tt>nodelay</tt>, can be used to discourage the +authentication component from requesting a delay should the +authentication as a whole fail. The default action is for the module +to request a delay-on-failure of the order of one second. + +<p> +A helper binary, <tt>unix_chkpwd</tt>, is provided to check the user's +password when it is stored in a read protected database. This binary +is very simple and will only check the password of the user invoking +it. It is called transparently on behalf of the user by the +authenticating component of this module. In this way it is possible +for applications like <em>xlock</em> to work without being +setuid-root. The module, by default, will temporarily turn off +<tt/SIGCHLD/ handling for the duration of execution of the helper +binary. This is generally the right thing to do, as many applications +are not prepared to handle this signal from a child they didn't know +was <tt/fork()/d. The <tt/noreap/ module argument can be used to +suppress this temporary shielding and may be needed for use with +certain applications. + +<p> +Remaining arguments, supported by the other functions of this module, +are silently ignored. Other arguments are logged as errors through +<tt/syslog(3)/. + +<tag><bf>Examples/suggested usage:</bf></tag> + +The correct functionality of this module is dictated by having an +appropriate <tt>/etc/nsswitch.conf</tt> file, the user +databases specified there dictate the source of the authenticated +user's record. +<p> +In its authentication mode, this module can be inserted as follows: +<tscreen> +<verb> +# +# Authenticate the user +# +login auth required pam_unix.so +</verb> +</tscreen> + +</descrip> + +<sect2>Password component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; +<tt/audit/; +<tt/nullok/; +<tt/not_set_pass/; +<tt/use_authtok/; +<tt/try_first_pass/; +<tt/use_first_pass/; +<tt/md5/; +<tt/bigcrypt/; +<tt/shadow/; +<tt/nis/; +<tt/remember/ + +<tag><bf>Description:</bf></tag> + +This part of the <tt/pam_unix/ module performs the task of updating +the user's password. + +<p> +In the case of conventional unix databases (which store the password +encrypted) the <tt/md5/ argument is used to do the encryption with the +MD5 function as opposed to the <em/conventional/ <tt/crypt(3)/ call. +As an alternative to this, the <tt/bigcrypt/ argument can be used to +encrypt more than the first 8 characters of a password with DEC's +(Digital Equipment Cooperation) `C2' extension to the standard UNIX +<tt/crypt()/ algorithm. + +<p> +The <tt/nullok/ argument is used to permit the changing of a password +<em/from/ an empty one. Without this argument, empty passwords are +treated as account-locking ones. + +<p> +The argument <tt/use_first_pass/ is used to lock the choice of old and +new passwords to that dictated by the previously stacked <tt/password/ +module. The <tt/try_first_pass/ argument is used to avoid the user +having to re-enter an old password when <tt/pam_unix/ follows a module +that possibly shared the user's old password - if this old password is +not correct the user will be prompted for the correct one. The +argument <tt/use_authtok/ is used to <em/force/ this module to set the +new password to the one provided by the previously stacked +<tt/password/ module (this is used in an example of the stacking of +the <em/Cracklib/ module documented above). + +<p> +The <tt/not_set_pass/ argument is used to inform the module that it is +not to pay attention to/make available the old or new passwords from/to +other (stacked) password modules. + +<p> +The <tt/debug/ argument makes the password functions of this module +<tt/syslog(3)/ more information on its actions. Other arguments may be +logged as erroneous to <tt/syslog(3)/. The <tt/audit/ argument causes +even more information to be logged. + +<p> +With the <tt/nis/ argument, <tt/pam_unix/ will attempt to use NIS RPC +for setting new passwords. + +<p> +The <tt/remember/ argument takes one value. This is the number of most +recent passwords to save for each user. These are saved in +<tt>/etc/security/opasswd</tt> in order to force password change history +and keep the user from alternating between the same password too frequently. + +<tag><bf>Examples/suggested usage:</bf></tag> + +Standard usage: +<tscreen> +<verb> +# +# Change the users password +# +passwd password required pam_unix.so +</verb> +</tscreen> + +<p> +An example of the stacking of this module with respect to the +pluggable password checking module, <tt/pam_cracklib/: +<tscreen> +<verb> +# +# Change the users password +# +passwd password required pam_cracklib.so retry=3 minlen=6 difok=3 +passwd password required pam_unix.so use_authtok nullok md5 +</verb> +</tscreen> + +</descrip> + +<sect2>Session component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +No arguments are recognized by this module component. Its action is +simply to log the username and the service-type to +<tt/syslog(3)/. Messages are logged at the beginning and end of the +user's session. + +<tag><bf>Examples/suggested usage:</bf></tag> + +The use of the session modules is straightforward: +<tscreen> +<verb> +# +# session opening and closing +# +login session required pam_unix.so +</verb> +</tscreen> + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_userdb.sgml b/Linux-PAM/doc/modules/pam_userdb.sgml new file mode 100644 index 00000000..155a2668 --- /dev/null +++ b/Linux-PAM/doc/modules/pam_userdb.sgml @@ -0,0 +1,126 @@ +<!-- + This file was written by Cristian Gafton <gafton@redhat.com> +--> + +<sect1>The userdb module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_userdb/ + +<tag><bf>Author:</bf></tag> +Cristian Gafton <gafton@redhat.com> + +<tag><bf>Maintainer:</bf></tag> +Author. + +<tag><bf>Management groups provided:</bf></tag> +authentication + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> +Requires Berkeley DB. + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +Look up users in a .db database and verify their password against +what is contained in that database. + +<sect2>Authentication component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; +<tt/icase/; +<tt/dump/; +<tt/db=XXXX/; +<tt/use_authtok/; +<tt/unknown_ok/; + +<tag><bf>Description:</bf></tag> + +This module is used to verify a username/password pair against values stored in +a Berkeley DB database. The database is indexed by the username, and the data +fields corresponding to the username keys are the passwords, in unencrypted form, +so caution must be exercised over the access rights to the DB database itself.. + +The module will read the password from the user using the conversation mechanism. If +you are using this module on top of another authentication module (like <tt/pam_pwdb/;) +then you should tell that module to read the entered password from the PAM_AUTHTOK field, which is set by this module. + +<p> +The action of the module may be modified from this default by one or +more of the following flags in the <tt>/etc/pam.d/<service></tt> file. +<itemize> +<item> +<tt/debug/ - +Supply more debugging information to <tt/syslog(3)/. + +<item> +<tt/icase/ - +Perform the password comparisons case insensitive. + +<item> +<tt/dump/ - +dump all the entries in the database to the log (eek, +don't do this by default!) + +<item> +<tt/db=XXXX/ - +use the database found on pathname XXXX. Note that Berkeley DB usually adds the +needed filename extension for you, so you should use something like <tt>/etc/foodata</tt> +instead of <tt>/etc/foodata.db</tt>. + +<item> <tt/use_authtok/ - +use the authentication token previously obtained by another module that did the +conversation with the application. If this token can not be obtained then +the module will try to converse again. This option can be used for stacking +different modules that need to deal with the authentication tokens. + +<item> +<tt/unknown_ok/ - +do not return error when checking for a user that is not in the database. +This can be used to stack more than one pam_userdb module that will check a +username/password pair in more than a database. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +This is a normal ftp configuration file (usually placed as <tt>/etc/pam.d/ftp</tt> +on most systems) that will accept for login users whose username/password pairs are +provided in the <tt>/tmp/dbtest.db</tt> file: + +<tscreen> +<verb> +#%PAM-1.0 +auth required pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed +auth sufficient pam_userdb.so icase db=/tmp/dbtest +auth required pam_pwdb.so shadow nullok try_first_pass +auth required pam_shells.so +account required pam_pwdb.so +session required pam_pwdb.so +</verb> +</tscreen> + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_warn.sgml b/Linux-PAM/doc/modules/pam_warn.sgml new file mode 100644 index 00000000..b015554d --- /dev/null +++ b/Linux-PAM/doc/modules/pam_warn.sgml @@ -0,0 +1,67 @@ +<!-- + $Id: pam_warn.sgml,v 1.2 2001/12/08 18:56:47 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> +--> + +<sect1>Warning logger module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_warn/ + +<tag><bf>Author:</bf></tag> +Andrew G. Morgan <morgan@kernel.org> + +<tag><bf>Maintainer:</bf></tag> +Author. + +<tag><bf>Management groups provided:</bf></tag> +authentication; password + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> +logs information about the remote user and host (if pam-items are known) + +</descrip> + +<sect2>Overview of module + +<p> +This module is principally for logging information about a +proposed authentication or application to update a password. + +<sect2>Authentication+Password component + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> + +<tag><bf>Description:</bf></tag> + +Log the service, terminal, user, remote user and remote host to +<tt/syslog(3)/. The items are not probed for, but instead obtained +from the standard pam-items. + +<tag><bf>Examples/suggested usage:</bf></tag> + +an example is provided in the configuration file section <ref +id="configuration" name="above">. + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/modules/pam_wheel.sgml b/Linux-PAM/doc/modules/pam_wheel.sgml new file mode 100644 index 00000000..e4dc501a --- /dev/null +++ b/Linux-PAM/doc/modules/pam_wheel.sgml @@ -0,0 +1,131 @@ +<!-- + $Id: pam_wheel.sgml,v 1.3 2002/07/13 05:48:19 agmorgan Exp $ + + This file was written by Andrew G. Morgan <morgan@kernel.org> + from notes provided by Cristian Gafton. +--> + +<sect1>The wheel module + +<sect2>Synopsis + +<p> +<descrip> + +<tag><bf>Module Name:</bf></tag> +<tt/pam_wheel/ + +<tag><bf>Author:</bf></tag> +Cristian Gafton <gafton@redhat.com> + +<tag><bf>Maintainer:</bf></tag> +Author. + +<tag><bf>Management groups provided:</bf></tag> +authentication; account + +<tag><bf>Cryptographically sensitive:</bf></tag> + +<tag><bf>Security rating:</bf></tag> + +<tag><bf>Clean code base:</bf></tag> + +<tag><bf>System dependencies:</bf></tag> + +<tag><bf>Network aware:</bf></tag> + +</descrip> + +<sect2>Overview of module + +<p> +Only permit root access to members of the wheel (<tt/gid=0/) group. + +<sect2>Authentication and Account components + +<p> +<descrip> + +<tag><bf>Recognized arguments:</bf></tag> +<tt/debug/; +<tt/use_uid/; +<tt/trust/; +<tt/deny/; +<tt/group=XXXX/ + +<tag><bf>Description:</bf></tag> + +This module is used to enforce the so-called <em/wheel/ group. By +default, it permits root access to the system if the applicant user is +a member of the <tt/wheel/ group (first, the module checks for the +existence of a '<tt/wheel/' group. Otherwise the module defines the +group with group-id <tt/0/ to be the <em/wheel/ group). + +<p> +The module can be used as either an '<tt/auth/' or an '<tt/account/' +module. + +<p> +The action of the module may be modified from this default by one or +more of the following flags in the <tt>/etc/pam.conf</tt> file. +<itemize> +<item> +<tt/debug/ - +Supply more debugging information to <tt/syslog(3)/. + +<item> +<tt/use_uid/ - +This option modifies the behavior of the module by using the current +<tt/uid/ of the process and not the <tt/getlogin(3)/ name of the user. +This option is useful for being able to jump from one account to +another, for example with 'su'. + +<item> +<tt/trust/ - +This option instructs the module to return <tt/PAM_SUCCESS/ should it +find the user applying for root privilege is a member of the wheel +group. The default action is to return <tt/PAM_IGNORE/ in this +situation. By using the <tt/trust/ option it is possible to arrange +for <tt/wheel/-group members to become root without typing a +password. <bf/USE WITH CARE/. + +<item> +<tt/deny/ - +This is used to reverse the logic of the module's behavior. If the +user is trying to get <tt/uid=0/ access and is a member of the wheel +group, deny access (for the wheel group, this is perhaps nonsense!): +it is intended for use in conjunction with the <tt/group=/ argument... +Conversely, if the user is not in the group, return <tt/PAM_IGNORE/ +(unless <tt/trust/ was also specified, in which case we return +<tt/PAM_SUCCESS/). + +<item> +<tt/group=XXXX/ - +Instead of checking the <tt/gid=0/ group, use the user's <tt/XXXX/ +group membership for the authentication. Here, <tt/XXXX/ is the name +of the group and <bf/not/ its numeric identifier. + +</itemize> + +<tag><bf>Examples/suggested usage:</bf></tag> + +To restrict access to superuser status to the members of the +<tt/wheel/ group, use the following entries in your configuration +file: +<tscreen> +<verb> +# +# root gains access by default (rootok), only wheel members can +# become root (wheel) but Unix authenticate non-root applicants. +# +su auth sufficient pam_rootok.so +su auth required pam_wheel.so +su auth required pam_unix.so +</verb> +</tscreen> + +</descrip> + +<!-- +End of sgml insert for this module. +--> diff --git a/Linux-PAM/doc/pam_appl.sgml b/Linux-PAM/doc/pam_appl.sgml new file mode 100644 index 00000000..33fdcba6 --- /dev/null +++ b/Linux-PAM/doc/pam_appl.sgml @@ -0,0 +1,1777 @@ +<!doctype linuxdoc system> + +<!-- + + $Id: pam_appl.sgml,v 1.10 2004/09/22 09:37:47 kukuk Exp $ + + Copyright (C) Andrew G. Morgan 1996-2001. All rights reserved. + +Redistribution and use in source (sgml) and binary (derived) forms, +with or without modification, are permitted provided that the +following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, and the entire permission notice in its entirety, + including the disclaimer of warranties. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +ALTERNATIVELY, this product may be distributed under the terms of the +GNU General Public License, in which case the provisions of the GNU +GPL are required INSTEAD OF the above restrictions. (This clause is +necessary due to a potential bad interaction between the GNU GPL and +the restrictions contained in a BSD-style copyright.) + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + + --> + +<article> + +<title>The Linux-PAM Application Developers' Guide +<author>Andrew G. Morgan, <tt>morgan@kernel.org</tt> +<date>DRAFT v0.76 2001/12/08 +<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 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 the PAM session is opened. 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 application for +service the appropriate <bf/Linux-PAM/ error-return value should be +used here. Note, <tt/pam_end()/ unconditionally shuts down the +authentication stack associated with the <tt/pamh/ handle. The value +taken by <tt/pam_status/ is used as an argument to the module specific +callback functions, <tt/cleanup()/ (see the <bf/Linux-PAM/ <htmlurl +url="pam_modules.html" name="Module Developers' Guide">). In this way, +the module can be given notification of the pass/fail nature of the +tear-down process, and perform any last minute tasks that are +appropriate to the module before it is unlinked. + +<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 (which identifies that PAM stack that + <tt/libpam/ will use to authenticate the program). + +<tag><tt/PAM_USER/</tag> + + The username of the entity under who's identity service will + be given. That is, following authentication, <tt/PAM_USER/ + identifies the local entity that gets to use the + service. Note, this value can be mapped from something (eg., + "<tt/anonymous/") to something else (eg. "<tt/guest119/") by + any module in the PAM stack. As such an application should + consult the value of <tt/PAM_USER/ after each call to a + <tt/pam_*()/ function. + +<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: ''. + +<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_RUSER/</tag> + + The requesting entity: user's username for a locally + requesting user or a remote requesting user - generally an + application or module will attempt to supply the value that is + most strongly authenticated (a local account before a remote + one. The level of trust in this value is embodied in the + actual authentication stack associated with the application, + so it is ultimately at the discretion of the system + administrator. It should generally match the current + <tt/PAM_RHOST/ value. That is, "<tt/PAM_RUSER@PAM_RHOST/" + should always identify the requesting user. In some cases, + <tt/PAM_RUSER/ may be NULL. In such situations, it is unclear + who the requesting entity is. + +<tag><tt/PAM_RHOST/</tag> + + The requesting hostname (the hostname of the machine from + which the <tt/PAM_RUSER/ entity is requesting service). That + is "<tt/PAM_RUSER@PAM_RHOST/" does identify the requesting + user. "<tt/luser@localhost/" or "<tt/evil@evilcom.com/" are + valid "<tt/PAM_RUSER@PAM_RHOST/" examples. In some + applications, <tt/PAM_RHOST/ may be NULL. In such situations, + it is unclear where the authentication request is originating + from. + +<tag><tt/PAM_CONV/</tag> + + The conversation structure (see section <ref + id="the-conversation-function" name="below">). + +<tag><tt/PAM_FAIL_DELAY/</tag> A function pointer to redirect + centrally managed failure delays (see section <ref + id="the-failure-delay-function" name="below">). + +</descrip> + +<p> +For all <tt/item_type/s, other than <tt/PAM_CONV/ and +<tt/PAM_FAIL_DELAY/, <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">). In the case of +<tt/PAM_FAIL_DELAY/, <tt/item/ is a function pointer: <tt/void +(*delay_fn)(int retval, unsigned usec_delay, void *appdata_ptr)/ (see +section <ref id="the-failure-delay-function" name="below">). + +<p> +A successful call to this function returns <tt/PAM_SUCCESS/. However, +the application should expect at least one the following errors: + +<p> +<descrip> +<tag><tt/PAM_SYSTEM_ERR/</tag> + The <tt/pam_handle_t/ passed as a first argument to this + function was invalid. +<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 or inaccessible + 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! + +<p> +A successful call is signaled by a return value of <tt/PAM_SUCCESS/. +However, the application should expect one of the following errors: + +<p> +<descrip> +<tag><tt/PAM_SYSTEM_ERR/</tag> + The <tt/pam_handle_t/ passed as a first argument to this + function was invalid. +<tag><tt/PAM_PERM_DENIED/</tag> + The value of <tt/item/ was <tt/NULL/. +<tag><tt/PAM_BAD_ITEM/</tag> + The application attempted to set an undefined or inaccessible + item. +</descrip> + +<p> +In the case of an error, the contents of <tt/item/ is set to <tt/NULL/. + +<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 +<label id="the-failure-delay-function"> + +<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 PAM_FAIL_DELAY + .... +#endif /* 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 delay 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 but before 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 to access 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 modules 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 modules 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> +This function simply calls each of the corresponding functions of the +loaded modules in the same order that they were invoked with +<tt/pam_open_session()/. 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> +The <tt/libpam/ library associates with each PAM-handle (<tt/pamh/), a +set of <it/PAM environment variables/. These variables are intended to +hold the session environment variables that the user will inherit when +the session is granted and the authenticated user obtains access to +the requested service. For example, when <tt/login/ has finally given +the user a shell, the environment (as viewed with the command +<tt/env/) will be what <tt/libpam/ was maintaining as the PAM +environment for that service application. Note, these variables are not +the environment variables of the <tt/login/ application. This is +principally for two reasons: <tt/login/ may want to have an +environment that cannot be seen or manipulated by a user; and +<tt/login/ (or whatever the serving application is) may be maintaining +a number of parallel sessions, via different <tt/pamh/ values, at the +same time and a single environment may not be appropriately shared +between each of these. The PAM environment may contain variables +seeded by the applicant user's client program, for example, and as +such it is not appropriate for one applicant to interfere with the +environment of another applicant. + +<p> +<tscreen> +<verb> +extern int pam_putenv(pam_handle_t *pamh, const char *name_value); +</verb> +</tscreen> + +<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 +corresponding 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> +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> +The PAM environment variables (see section <ref +id="pam-putenv-section" name="above">) are a complete set of enviroment +variables that are associated with a PAM-handle (<tt/pamh/). They +represent the contents of the <it/regular/ environment variables of +the authenticated user when service is granted. + +<p> +Th function, <tt>pam_getenvlist()</tt> returns a pointer to a complete, +<tt/malloc()/'d, copy of the PAM environment. It is a pointer to a +duplicated list of environment variables. It should be noted that +this memory will never be <tt/free()'d/ by <tt/libpam/. Once obtained +by a call to <tt/pam_getenvlist()/, <bf>it is the responsibility of +the calling application</bf> to <tt/free()/ this memory. + +<p> +The format of the memory is a <tt/malloc()/'d array of <tt/char */ +pointers, the last element of which is set to <tt/NULL/. Each of the +non-<tt/NULL/ entries in this array point to a <tt/NUL/ terminated and +<tt/malloc()/'d <tt/char/ string of the form: +<tt/"/<it/name/<tt/=/<it/value/<tt/"/. + +<p> +It is by design, and not a coincidence, that the format and contents +of the returned array matches that required for the third argument of +the <tt/execle(3)/ function call. + +<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> +In passing, it is worth noting that there is a descrepency between the +way Linux-PAM handles the <tt/const struct pam_message **msg/ +conversation function argument from the way that Solaris' PAM (and +derivitives, known to include HP/UX, <em/are there others?/) +does. Linux-PAM interprets the <tt/msg/ argument as entirely +equivalent to the following prototype <tt/const struct pam_message +*msg[]/ (which, in spirit, is consistent with the commonly used +prototypes for <tt/argv/ argument to the familiar <tt/main()/ +function: <tt/char **argv/; and <tt/char *argv[]/). Said another way +Linux-PAM interprets the <tt/msg/ argument as a pointer to an array of +<tt/num_meg/ read only 'struct pam_message' <em/pointers/. Solaris' +PAM implementation interprets this argument as a pointer to a pointer +to an array of <tt/num_meg/ <tt/pam_message/ structures. Fortunately, +perhaps, for most module/application developers when <tt/num_msg/ has +a value of one these two definitions are entirely +equivalent. Unfortunately, casually raising this number to two has led +to unanticipated compatibility problems. + +<p> +For what its worth the two known module writer work-arounds for trying +to maintain source level compatibility with both PAM implementations +are: +<itemize> +<item> never call the conversation function with <tt/num_msg/ greater +than one. +<item> set up <tt/msg/ as doubly referenced so both types of +conversation function can find the messages. That is, make +<p><tscreen> +<verb> +msg[n] = & (( *msg )[n]) +</verb> +</tscreen> +</itemize> +<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. The number of responses 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 possessed 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> +In general, this is always the right advice if the program is setuid, +or otherwise more privileged than the user that invokes it. In some +cases, avoiding this advice is convenient, but as an author of such an +application, you should consider well the ways in which your program +will be installed and used. (Its often the case that programs are not +intended to be setuid, but end up being installed that way for +convenience. If your program falls into this category, don't fall into +the trap of making this mistake.) + +<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)/. Note, modules can change the +values of <tt/PAM_USER/ and <tt/PAM_RUSER/ during any of the +<tt/pam_*()/ library calls. For this reason, the application should +take care to use the <tt/pam_get_item()/ every time it wishes to +establish who the authenticated user is (or will currently be). + +<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>Macros supplied + +<sect2>Safe duplication of strings + +<p> +<tscreen> +<verb> +x_strdup(const char *s) +</verb> +</tscreen> + +<p> +This macro is a replacement for the <tt/xstrdup()/ function that was +present in earlier versions of the library and which clashed horribly +with a number of applications. It returns 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/ is +<tt/NULL/ to begin with. + +<sect1>Functions supplied + +<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. + +</descrip> + +<p> +The following two function pointers are available for supporting binary +prompts in the conversation function. They are optimized for the +current incarnation of the <tt/libpamc/ library and are subject to +change. +<descrip> +<tag><tt>extern int (*pam_binary_handler_fn)(void *appdata, pamc_bp_t +*prompt_p);</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.) + +<tag><tt>extern int (*pam_binary_handler_free)(void *appdata, +pamc_bp_t *delete_me);</tt></tag> + +This function pointer is initialized to <tt/PAM_BP_RENEW(delete_me, 0, +0)/, but can be redefined as desired by the application. + +</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>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_getenvlist()/ +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 attached 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 attaching 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 attached 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 "procedure", 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@kernel.org) with many contributions from +<!-- insert credits here --> +<!-- + an sgml list of people to credit for their contributions to Linux-PAM + $Id: pam_appl.sgml,v 1.10 2004/09/22 09:37:47 kukuk Exp $ + --> +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,2000-1. All rights reserved. +<newline> +Email: <tt><morgan@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: pam_appl.sgml,v 1.10 2004/09/22 09:37:47 kukuk Exp $</tt> + +</article> diff --git a/Linux-PAM/doc/pam_modules.sgml b/Linux-PAM/doc/pam_modules.sgml new file mode 100644 index 00000000..9d77b25f --- /dev/null +++ b/Linux-PAM/doc/pam_modules.sgml @@ -0,0 +1,1505 @@ +<!doctype linuxdoc system> + +<!-- + + $Id: pam_modules.sgml,v 1.9 2002/05/10 06:00:12 agmorgan Exp $ + + Copyright (c) Andrew G. Morgan 1996-2001. 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@kernel.org</tt> +<date>DRAFT v0.76 2002/05/09 +<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 +</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 (often a 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 structure containing an a pointer 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> +<bf>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.</bf> + +<p> +The return values for this function are listed in the +<bf>Linux-PAM</bf> 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 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. + +There is a handy macro <tt/_pam_drop_reply()/ to be found in +<tt><security/_pam_macros.h></tt> that can be used to +conveniently cleanup a <tt/pam_response/ structure. (Note, this +include file is specific to the Linux-PAM sources, and whilst it will +work with Sun derived PAM implementations, it is not generally +distributed by Sun.) + +<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/ 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 PAM_FAIL_DELAY + .... +#endif /* 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 but before 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> +Prior to <bf/Linux-PAM-0.75/, and due to a deficiency with the way the +<tt/auth/ stack was handled in the case of the setcred stack being +processed, the module was required to attempt to return the same error +code as <tt/pam_sm_authenticate/ did. This was necessary to preserve +the logic followed by libpam as it executes the stack of +<em/authentication/ modules, when the application called either +<tt/pam_authenticate()/ or <tt/pam_setcred()/. Failing to do this, +led to confusion on the part of the System Administrator. + +<p> +For <bf/Linux-PAM-0.75/ and later, libpam handles the credential stack +much more sanely. The way the <tt/auth/ stack is navigated in order to +evaluate the <tt/pam_setcred()/ function call, independent of the +<tt/pam_sm_setcred()/ return codes, is exactly the same way that it +was navigated when evaluating the <tt/pam_authenticate()/ library +call. Typically, if a stack entry was ignored in evaluating +<tt/pam_authenticate()/, it will be ignored when libpam evaluates the +<tt/pam_setcred()/ function call. Otherwise, the return codes from +each module specific <tt/pam_sm_setcred()/ call are treated as +<tt/required/. + +<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> + +<p> +these, non-<tt/PAM_SUCCESS/, return values will typically lead to the +credential stack <em/failing/. The first such error will dominate in +the return value of <tt/pam_setcred()/. + +</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. + +<sect>An example module file + +<p> +At some point, we may include a fully commented example of a module in +this document. For now, we point the reader to these two locations in +the public CVS repository: +<itemize> +<item> A module that always succeeds: <tt><htmlurl +url="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/pam/Linux-PAM/modules/pam_permit/" +name="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/pam/Linux-PAM/modules/pam_permit/" +></tt> +<item> A module that always fails: <tt><htmlurl +url="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/pam/Linux-PAM/modules/pam_deny/" +name="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/pam/Linux-PAM/modules/pam_deny/" +></tt> +</itemize> + +<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@kernel.org/) with many contributions from +<!-- insert credits here --> +<!-- + an sgml list of people to credit for their contributions to Linux-PAM + $Id: pam_modules.sgml,v 1.9 2002/05/10 06:00:12 agmorgan Exp $ + --> +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-2002. All rights reserved. +<newline> +Email: <tt><morgan@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: pam_modules.sgml,v 1.9 2002/05/10 06:00:12 agmorgan Exp $</tt> + +</article> diff --git a/Linux-PAM/doc/pam_source.sgml b/Linux-PAM/doc/pam_source.sgml new file mode 100644 index 00000000..9ec0bbe6 --- /dev/null +++ b/Linux-PAM/doc/pam_source.sgml @@ -0,0 +1,1166 @@ +<!doctype linuxdoc system> + +<!-- + + $Id: pam_source.sgml,v 1.13 2004/09/28 13:48:46 kukuk Exp $ + + Copyright (c) Andrew G. Morgan 1996-2002. 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@kernel.org</tt> +<date>DRAFT v0.77 2002/07/10 +<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>/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 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>/lib/security/</tt>. This is generally +the location that seems to be compatible with the Linux File System +Standard (the FSSTND). On Solaris, which has its own licensed version +of PAM, and some other implementations of UN*X, these files can be +found in <tt>/usr/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> +Traditionally, 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 +as 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 configuration 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 Red Hat) 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 args +</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/, +<tt/optional/ and <tt/include/. + +<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/. + +<item> <tt/include/; this tells PAM to include all lines of given type +from the configuration file specified as an argument to this control. +The whole idea is to create few "systemwide" pam configs and include +parts of them in application pam configs. + + +</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 +module-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> +Each of the four keywords: <tt/required/; <tt/requisite/; +<tt/sufficient/; and <tt/optional/, have an equivalent expression in +terms of the <tt/[...]/ syntax. They are as follows: +<itemize> +<item><tt/required/ is equivalent to +<tt/[success=ok new_authtok_reqd=ok ignore=ignore default=bad]/ +<item><tt/requisite/ is equivalent to +<tt/[success=ok new_authtok_reqd=ok ignore=ignore default=die]/ +<item><tt/sufficient/ is equivalent to +<tt/[success=done new_authtok_reqd=done default=ignore]/ +<item><tt/optional/ is equivalent to +<tt/[success=ok new_authtok_reqd=ok default=ignore]/ +</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. + +<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>/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. + +Note, if you wish to include spaces in an argument, you should +surround that argument with square brackets. For example: +<tscreen> +<verb> +squid auth required pam_mysql.so user=passwd_query passwd=mada \ + db=eminence [query=select user_name from internet_service where \ + user_name='%u' and password=PASSWORD('%p') and \ + service='web_proxy'] +</verb> +</tscreen> +Note, when using this convention, you can include `<tt/[/' characters +inside the string, and if you wish to include a `<tt/]/' character +inside the string that will survive the argument parsing, you should +use `<tt/\[/'. In other words: +<tscreen> +<verb> +[..[..\]..] --> ..[..].. +</verb> +</tscreen> + +</descrip> + +<p> +Any line in (one of) the configuration 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 +(this is likely to be the mode your preferred distribution uses). The +other mode 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 pam_deny.so +OTHER account required pam_deny.so +OTHER password required pam_deny.so +OTHER session required 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 pam_warn.so +OTHER password required 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 pam_warn.so +auth required pam_deny.so +account required pam_deny.so +password required pam_warn.so +password required pam_deny.so +session required 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 UN*X access +# +OTHER auth required pam_unix.so +OTHER account required pam_unix.so +OTHER password required pam_unix.so +OTHER session required pam_unix.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 UN*X access (the listfile entry blocks access to +# users listed in /etc/ftpusers) +# +ftpd auth sufficient pam_ftp.so +ftpd auth required pam_unix_auth.so use_first_pass +ftpd auth required 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 UN*X authentication module that it +is not to prompt for a password but rely on one already having been +obtained by the <tt/pam_ftp/ module. + +<sect>Security issues of Linux-PAM + +<p> +This section will discuss good practices for using 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.so + account required pam_unix.so + password required pam_unix.so + session required pam_unix.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>/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>/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.kernel.org/pub/linux/libs/pam/pre/doc/rfc86.0.txt.gz" +name="http://www.kernel.org/pub/linux/libs/pam/pre/doc/rfc86.0.txt.gz"></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@kernel.org) +with many contributions from +<!-- insert-file CREDITS --> + +<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-2002. All rights reserved. +<newline> +Email: <tt><morgan@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: pam_source.sgml,v 1.13 2004/09/28 13:48:46 kukuk Exp $</tt> + +</article> diff --git a/Linux-PAM/doc/pdf/README b/Linux-PAM/doc/pdf/README new file mode 100644 index 00000000..5cae4e68 --- /dev/null +++ b/Linux-PAM/doc/pdf/README @@ -0,0 +1,3 @@ +$Id: README,v 1.1 2002/05/29 04:14:11 agmorgan Exp $ + +a directory for PDF versions of the documentation diff --git a/Linux-PAM/doc/ps/README b/Linux-PAM/doc/ps/README new file mode 100644 index 00000000..18ab6329 --- /dev/null +++ b/Linux-PAM/doc/ps/README @@ -0,0 +1,3 @@ +$Id: README,v 1.2 2001/11/27 05:37:30 agmorgan Exp $ + +this is the directory for the PostScript documentation diff --git a/Linux-PAM/doc/specs/draft-morgan-pam.raw b/Linux-PAM/doc/specs/draft-morgan-pam.raw new file mode 100644 index 00000000..2d55048e --- /dev/null +++ b/Linux-PAM/doc/specs/draft-morgan-pam.raw @@ -0,0 +1,764 @@ +Open-PAM working group ## A.G. Morgan +Internet Draft: ## Dec 8, 2001 +Document: draft-morgan-pam-08.txt ## +Expires: June 8, 2002 ## +Obsoletes: draft-morgan-pam-07.txt## + +## Pluggable Authentication Modules (PAM) ## + +#$ Status of this memo + +This document is a draft specification. Its contents are subject to +change with revision. The latest version of this draft may be obtained +from here: + + http://www.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 ## +## 0x46 PAM_BPC_STATUS - ping all active clients## +## 0x47 PAM_BPC_ABORT - please abort session ## + +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 to be considered as + representing some authentication mode that is a "public + standard" see reference [#$R#{PAM_STD_AGENTIDS}]. 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 needs to be +special client/server support for handling binary prompts. + +In some circumstances, a legacy network transfer protocol can carry +authentication information. In such cases, a desire to support legacy +clients (with no client-side support for PAM) will neccessitate the +'hardcoding' of an agent protocol into the server application. Whilst +against the spirit of PAM, this special casing can be managed by the +server's 'conversation function' (see below). The guiding principle +when implementing such support is for the application developer to +relegate the authentication process to the PAM module -- simply +performing a transcription of data from binary-prompt to legacy +network 'packet' and visa-versa for propagating replies back to the +driving PAM module. A common case of this is with network protocols +that define an initialization packet of "user+password". In such cases +one should attempt to support the "userpass" agent-id and its defined +protocol. + +#$ 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_ERROR;"ouch!"} | {5;PAM_BPC_OK;} ## +## | {11;PAM_BPC_ERROR;"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. + +#$$$$ Status of agents + + int pamc_status(pamc_handle_t *pch, pamc_bp_t *prompt_p); + +At any time, the client may ping all active agents for their status +(with a PAM_BPC_STATUS binary prompt). If any agent replies with +PAM_BPC_ABORT, the client is responsible for terminating the +connection to the server and then terminating all agents with a call +to pamc_end(). In such cases, the return value of pamc_status() is +PAM_BPC_FALSE. + +If the return status of pamc_status() is PAM_BPC_TRUE and *prompt_p is +non-NULL, then an agent is requesting access to a server module. + +XXX - how this information gets propagated to the server, and + ultimately to the server's module is yet to be determined. + +#$$$$ 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) + +Event driven support (XXX work in progress) + + pam_register_event() - app or module associates an event poller/handler + pam_select_event() - query for any outstanding event and act on any +] + +#$$$ 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 +] + +#$$$ The conversation function + +The server application, as part of its initialization of libpam, +provides a conversation function for use by modules and libpam. The +purpose of the conversation function is to enable direct communication +to the applicant ultimately via the client and selected agents. + +[ this section will contain a definition for the conversation + function, the conversation structure (appdata etc), and legitimate + return codes for the application supplied function. + + PAM_SUCCESS - ok conversation completed + PAM_CONV_ERR - conversation failed + PAM_CONV_AGAIN - application needs control to complete conv + PAM_CONV_RECONSIDER - application believes module should check if + it still needs to converse for this info + ] + +#$ 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 + +[#{PAM_STD_AGENTIDS}] Definitions for standard agents, "REGISTERED + AGENTS AND THEIR AGENT-ID'S", to be found here: + +## http://www.kernel.org/pub/linux/libs/pam/pre/doc/std-agent-ids.txt ## + +#$ Author's Address + +Andrew G. Morgan +Email: morgan@kernel.org + +## $Id: draft-morgan-pam.raw,v 1.2 2001/12/08 18:56:47 agmorgan Exp $ ## diff --git a/Linux-PAM/doc/specs/formatter/Makefile b/Linux-PAM/doc/specs/formatter/Makefile new file mode 100644 index 00000000..d73258d7 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/doc/specs/formatter/parse.lex b/Linux-PAM/doc/specs/formatter/parse.lex new file mode 100644 index 00000000..1d5c898e --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/doc/specs/formatter/parse.y b/Linux-PAM/doc/specs/formatter/parse.y new file mode 100644 index 00000000..6da47d17 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/doc/specs/rfc86.0.txt b/Linux-PAM/doc/specs/rfc86.0.txt new file mode 100644 index 00000000..6dd5e6ea --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/doc/specs/std-agent-id.raw b/Linux-PAM/doc/specs/std-agent-id.raw new file mode 100644 index 00000000..c97ce975 --- /dev/null +++ b/Linux-PAM/doc/specs/std-agent-id.raw @@ -0,0 +1,95 @@ +PAM working group ## A.G. Morgan + +## $Id: std-agent-id.raw,v 1.1 2001/12/08 18:56:47 agmorgan Exp $ ## + +## Pluggable Authentication Modules ## + +## REGISTERED AGENTS AND THEIR AGENT-ID'S ## + +#$ Purpose of this document + +#$$#{definition} Definition of an agent-id + +The most complete version of a "PAM agent-id" is contained in this +reference [#$R#{PAM_RFC2}]. A copy of a recent definition is +reproduced here for convenience. The reader is recommended to consult +reference [#{PAM_RFC2}] for definitions of other terms that are +used in this document. + +## -------------- ## + +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 to be considered as + representing some authentication mode that is a "public + standard". 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. + +## -------------- ## + +#$ Registered agent-id's + +The structure of this section is a single subsection for each +registered agent-id. This section includes a full definition of binary +prompts accepted by the agent and example responses of said +agent. Using the defining section alone, it should be possible for a +third party to create a conforming agent and modules that can +interoperate with other implementations of these objects. + +*$ "userpass" - the user+password agent + +Many legacy authentication systems are hardcoded to support one and +only one authentication method. Namely, + + username: joe + password: <secret> + +Indeed, this authentication method is often embedded into parts of the +transport protocol. The "user+password" agent with PAM agent-id: + + "userpass" + +Is intended to support this legacy authentication scheme. The protocol +for binary prompt exchange with this 'standard agent' is as follows: + +Case 1: module does not know the username, but expects the agent to + obtain this information and also the user's password: + + module: {LENGTH;PAM_BP_SELECT;userpass;'/'} + agent: {} + +Case 2: module has suggested username, but would like agent to confirm + it and gather password: + + module: {} + agent: {} + +Case 3: module knows username and will not permit the agent to change it: + + module: {} + agent: {} + +#$ References + +[#{PAM_RFC2}] Internet draft, "Pluggable Authentication Modules + (PAM)", available here: + +# http://linux.kernel.org/pub/linux/libs/pam/pre/doc/current-draft.txt # + +#$ Author's Address + +Andrew G. Morgan +Email: morgan@kernel.org diff --git a/Linux-PAM/doc/txts/README b/Linux-PAM/doc/txts/README new file mode 100644 index 00000000..f63820cf --- /dev/null +++ b/Linux-PAM/doc/txts/README @@ -0,0 +1,3 @@ +$Id: README,v 1.1.1.1 2000/06/20 22:11:12 agmorgan Exp $ + +This is a directory for text versions of the pam documentation diff --git a/Linux-PAM/dynamic/Makefile b/Linux-PAM/dynamic/Makefile new file mode 100644 index 00000000..e1e4f89e --- /dev/null +++ b/Linux-PAM/dynamic/Makefile @@ -0,0 +1,70 @@ +# +# $Id: Makefile,v 1.1 2001/12/09 22:51:12 agmorgan Exp $ +# +# + +include ../Make.Rules + +ifeq ($(WITH_LIBDEBUG),yes) + LIBNAME=pamd +else + LIBNAME=pam +endif +VERSION=.$(MAJOR_REL) +MODIFICATION=.$(MINOR_REL) + +# --------------------------------------------- + +dummy: ../Make.Rules all + +# --------------------------------------------- + +# dynamic library names + +PAMSHOBJ = pam.so +PAMSHOBJMAJ = $(PAMSHOBJ)$(VERSION) + +DLIBOBJECTS = pam.o + +# --------------------------------------------- +## rules + +all: dirs $(PAMSHOBJ) ../Make.Rules + +dirs: +ifeq ($(DYNAMIC_LIBPAM),yes) + $(MKDIR) dynamic +endif +ifeq ($(STATIC_LIBPAM),yes) + $(MKDIR) static +endif + +dynamic/%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +static/%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +$(PAMSHOBJ): $(DLIBOBJECTS) +ifeq ($(USESONAME),yes) + $(LD_L) $(SOSWITCH) $(PAMSHOBJMAJ) -o $@ $+ $(LINKLIBS) +else + $(LD_L) -o $@ $(DLIBOBJECTS) $(LINKLIBS) +endif + +install: all + $(MKDIR) $(FAKEROOT)$(libdir) +ifeq ($(DYNAMIC_LIBPAM),yes) + $(INSTALL) -m $(SHLIBMODE) $(PAMSHOBJ) $(FAKEROOT)$(libdir)/$(PAMSHOBJ) + $(LDCONFIG) +endif + +remove: + rm -f $(FAKEROOT)$(libdir)/$(LIBPAM) + $(LDCONFIG) + +clean: + rm -f a.out core *~ static/*.o dynamic/*.o + rm -f *.a *.o *.so ./include/security/*~ + if [ -d dynamic ]; then rmdir dynamic ; fi + if [ -d static ]; then rmdir static ; fi diff --git a/Linux-PAM/dynamic/pam.c b/Linux-PAM/dynamic/pam.c new file mode 100644 index 00000000..e0a51c32 --- /dev/null +++ b/Linux-PAM/dynamic/pam.c @@ -0,0 +1,180 @@ +/* + * $Id: pam.c,v 1.1 2001/12/09 22:51:12 agmorgan Exp $ + * + * If you want to dynamically load libpam using dlopen() or something, + * then dlopen( ' this shared object ' ); It takes care of exporting + * the right symbols to any modules loaded by libpam. + */ + +#include <stdio.h> +#include <dlfcn.h> +#include <security/pam_appl.h> +#include <security/_pam_macros.h> + +#ifndef LIBPAMPATH +#define LIBPAMPATH "/lib/libpam.so" +#endif + +static void *libpam_h = NULL; + +#define CONFIRM_PAM_FUNCTION(x, y, z, err) \ + do { \ + union { const void *tpointer; y (*fn) z ; } fptr; \ + fptr.tpointer = dlsym(libpam_h, #x); real_##x = fptr.fn; \ + if (real_##x == NULL) { \ + D(("unable to resolve '" #x "': %s", dlerror())); \ + return err; \ + } \ + } while (0) + + +extern void _init(void); + +void _init() +{ + if (libpam_h == NULL) { + libpam_h = dlopen(LIBPAMPATH, RTLD_GLOBAL|RTLD_NOW); + } +} + +extern void _fini(void); + +void _fini() +{ + if (libpam_h != NULL) { + dlclose(libpam_h); + } +} + +int pam_start(const char *service_name, const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh) +{ + static int (*real_pam_start)(const char *, const char *, + const struct pam_conv *, + pam_handle_t **); + CONFIRM_PAM_FUNCTION(pam_start, int, (const char *, const char *, + const struct pam_conv *, + pam_handle_t **), PAM_ABORT); + return real_pam_start(service_name, user, pam_conversation, pamh); +} + +int pam_end(pam_handle_t *pamh, int pam_status) +{ + static int (*real_pam_end)(pam_handle_t *, int); + CONFIRM_PAM_FUNCTION(pam_end, int, (pam_handle_t *, int), PAM_ABORT); + return real_pam_end(pamh, pam_status); +} + +int pam_set_item(pam_handle_t *pamh, int item_type, const void *item) +{ + static int (*real_pam_set_item)(pam_handle_t *, int, const void *); + CONFIRM_PAM_FUNCTION(pam_set_item, int, + (pam_handle_t *, int, const void *), PAM_ABORT); + return real_pam_set_item(pamh, item_type, item); +} + +int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) +{ + static int (*real_pam_get_item)(const pam_handle_t *, int, const void **); + CONFIRM_PAM_FUNCTION(pam_get_item, int, + (const pam_handle_t *, int, const void **), + PAM_ABORT); + return real_pam_get_item(pamh, item_type, item); +} + +int pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay) +{ + static int (*real_pam_fail_delay)(pam_handle_t *, unsigned int); + CONFIRM_PAM_FUNCTION(pam_fail_delay, int, (pam_handle_t *, unsigned int), + PAM_ABORT); + return real_pam_fail_delay(pamh, musec_delay); +} + +typedef const char * const_char_pointer; + +const_char_pointer pam_strerror(pam_handle_t *pamh, int errnum) +{ + static const_char_pointer (*real_pam_strerror)(pam_handle_t *, int); + CONFIRM_PAM_FUNCTION(pam_strerror, const_char_pointer, + (pam_handle_t *, int), NULL); + return real_pam_strerror(pamh, errnum); +} + +int pam_putenv(pam_handle_t *pamh, const char *name_value) +{ + static int (*real_pam_putenv)(pam_handle_t *, const char *); + CONFIRM_PAM_FUNCTION(pam_putenv, int, (pam_handle_t *, const char *), + PAM_ABORT); + return real_pam_putenv(pamh, name_value); +} + +const_char_pointer pam_getenv(pam_handle_t *pamh, const char *name) +{ + static const_char_pointer (*real_pam_getenv)(pam_handle_t *, const char *); + CONFIRM_PAM_FUNCTION(pam_getenv, const_char_pointer, + (pam_handle_t *, const char *), NULL); + return real_pam_getenv(pamh, name); +} + +typedef char ** char_ppointer; +char_ppointer pam_getenvlist(pam_handle_t *pamh) +{ + static char_ppointer (*real_pam_getenvlist)(pam_handle_t *); + CONFIRM_PAM_FUNCTION(pam_getenvlist, char_ppointer, (pam_handle_t *), + NULL); + return real_pam_getenvlist(pamh); +} + +/* Authentication management */ + +int pam_authenticate(pam_handle_t *pamh, int flags) +{ + static int (*real_pam_authenticate)(pam_handle_t *, int); + CONFIRM_PAM_FUNCTION(pam_authenticate, int, (pam_handle_t *, int), + PAM_ABORT); + return real_pam_authenticate(pamh, flags); +} + +int pam_setcred(pam_handle_t *pamh, int flags) +{ + static int (*real_pam_setcred)(pam_handle_t *, int); + CONFIRM_PAM_FUNCTION(pam_setcred, int, (pam_handle_t *, int), PAM_ABORT); + return real_pam_setcred(pamh, flags); +} + +/* Account Management API's */ + +int pam_acct_mgmt(pam_handle_t *pamh, int flags) +{ + static int (*real_pam_acct_mgmt)(pam_handle_t *, int); + CONFIRM_PAM_FUNCTION(pam_acct_mgmt, int, (pam_handle_t *, int), PAM_ABORT); + return real_pam_acct_mgmt(pamh, flags); +} + +/* Session Management API's */ + +int pam_open_session(pam_handle_t *pamh, int flags) +{ + static int (*real_pam_open_session)(pam_handle_t *, int); + CONFIRM_PAM_FUNCTION(pam_open_session, int, (pam_handle_t *, int), + PAM_ABORT); + return real_pam_open_session(pamh, flags); +} + +int pam_close_session(pam_handle_t *pamh, int flags) +{ + static int (*real_pam_close_session)(pam_handle_t *, int); + CONFIRM_PAM_FUNCTION(pam_close_session, int, (pam_handle_t *, int), + PAM_ABORT); + return real_pam_close_session(pamh, flags); +} + +/* Password Management API's */ + +int pam_chauthtok(pam_handle_t *pamh, int flags) +{ + static int (*real_pam_chauthtok)(pam_handle_t *, int); + CONFIRM_PAM_FUNCTION(pam_chauthtok, int, (pam_handle_t *, int), PAM_ABORT); + return real_pam_chauthtok(pamh, flags); +} diff --git a/Linux-PAM/dynamic/test.c b/Linux-PAM/dynamic/test.c new file mode 100644 index 00000000..35496fe4 --- /dev/null +++ b/Linux-PAM/dynamic/test.c @@ -0,0 +1,27 @@ +#include <stdio.h> +#include <dlfcn.h> +#include <unistd.h> + +#include <security/pam_appl.h> +#include <security/pam_misc.h> + +int main(int argc, char **argv) +{ + void *handle; + + handle = dlopen("./pam.so", RTLD_NOW); + if (handle == NULL) { + fprintf(stderr, "failed to load pam.so: %s\n", dlerror()); + exit(1); + } + + /* handle->XXX points to each of the PAM functions */ + + + if (dlclose(handle)) { + fprintf(stderr, "failed to unload pam.so: %s\n", dlerror()); + exit(1); + } + + exit(0); +} diff --git a/Linux-PAM/examples/Makefile b/Linux-PAM/examples/Makefile new file mode 100644 index 00000000..3ffea528 --- /dev/null +++ b/Linux-PAM/examples/Makefile @@ -0,0 +1,54 @@ +# +# $Id: Makefile,v 1.6 2002/06/27 04:04:54 agmorgan Exp $ +# + +include ../Make.Rules + +PROGS = blank xsh check_user +SRCS = blank.c xsh.c check_user.c +PROGSUID = + +ifeq ($(WITH_LIBDEBUG),yes) + LIBSUFFIX=d +else + LIBSUFFIX= +endif + +CFLAGS += -I$(absolute_srcdir)/libpam_misc/include + +LOADLIBES = -L$(absolute_objdir)/libpam -L$(absolute_objdir)/libpamc \ + -L$(absolute_objdir)/libpam_misc -lpam -lpam_misc + +ifeq ($(STATIC_LIBPAM),yes) + ifneq ($(DYNAMIC),) + CFLAGS += $(CC_STATIC) + LOADLIBES += $(LIBDL) + endif +endif + +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) + +clean: + rm -f *.a *.so *.o *~ $(PROGS) $(PROGSUID) + rm -f *.a *.out *.o *.so + +# note, the programs are test programs, they should not be +# installed on your system! + +install: all + if [ -n "$(PROGS)" ]; then cp $(PROGS) ../bin ; fi + if [ -n "$(PROGSUID)" ]; then \ + $(INSTALL) -m 4555 $(PROGSUID) ../bin ; fi + +remove: + cd ../bin ; rm -f $(PROGS) $(PROGSUID) + for x in $(PROGS) $(PROGSUID) ; do rm -f ../bin/$$x ; done diff --git a/Linux-PAM/examples/blank.c b/Linux-PAM/examples/blank.c new file mode 100644 index 00000000..20896b5c --- /dev/null +++ b/Linux-PAM/examples/blank.c @@ -0,0 +1,158 @@ +/* + * $Id: blank.c,v 1.2 2000/12/04 19:02:33 baggins Exp $ + */ + +/* 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/Linux-PAM/examples/check_user.c b/Linux-PAM/examples/check_user.c new file mode 100644 index 00000000..cb539c4e --- /dev/null +++ b/Linux-PAM/examples/check_user.c @@ -0,0 +1,60 @@ +/* + $Id: check_user.c,v 1.2 2000/12/04 19:02:33 baggins Exp $ + + 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 +*/ + +#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/Linux-PAM/examples/vpass.c b/Linux-PAM/examples/vpass.c new file mode 100644 index 00000000..9a07ee38 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/examples/wrap_xsh.sh b/Linux-PAM/examples/wrap_xsh.sh new file mode 100755 index 00000000..af01697e --- /dev/null +++ b/Linux-PAM/examples/wrap_xsh.sh @@ -0,0 +1,5 @@ +#!/bin/bash +export LD_PRELOAD=../libpam/libpam.so:../libpam_misc/libpam_misc.so +ldd ./xsh +./xsh "$@" + diff --git a/Linux-PAM/examples/xsh.c b/Linux-PAM/examples/xsh.c new file mode 100644 index 00000000..3f5246f5 --- /dev/null +++ b/Linux-PAM/examples/xsh.c @@ -0,0 +1,177 @@ +/* + * $Id: xsh.c,v 1.7 2004/09/24 09:18:21 kukuk Exp $ + */ + +/* Andrew Morgan (morgan@kernel.org) -- an example application + * that invokes a shell, based on blank.c */ + +#include <security/_pam_aconf.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <security/pam_appl.h> +#include <security/pam_misc.h> + +#include <pwd.h> +#include <sys/types.h> +#include <unistd.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; + const char *username=NULL; + const char *service="xsh"; + int retcode; + + /* did the user call with a username as an argument ? + * did they also */ + + if (argc > 3) { + fprintf(stderr,"usage: %s [username [service-name]]\n",argv[0]); + } + if ((argc >= 2) && (argv[1][0] != '-')) { + username = argv[1]; + } + if (argc == 3) { + service = argv[2]; + } + + /* initialize the Linux-PAM library */ + retcode = pam_start(service, username, &conv, &pamh); + bail_out(pamh,1,retcode,"pam_start"); + + /* fill in the RUSER and RHOST etc. fields */ + { + char buffer[100]; + struct passwd *pw; + const char *tty; + + pw = getpwuid(getuid()); + if (pw != NULL) { + retcode = pam_set_item(pamh, PAM_RUSER, pw->pw_name); + bail_out(pamh,1,retcode,"pam_set_item(PAM_RUSER)"); + } + + retcode = gethostname(buffer, sizeof(buffer)-1); + if (retcode) { + perror("failed to look up hostname"); + retcode = pam_end(pamh, PAM_ABORT); + bail_out(pamh,1,retcode,"pam_end"); + } + retcode = pam_set_item(pamh, PAM_RHOST, buffer); + bail_out(pamh,1,retcode,"pam_set_item(PAM_RHOST)"); + + tty = ttyname(fileno(stdin)); + if (tty) { + retcode = pam_set_item(pamh, PAM_TTY, tty); + bail_out(pamh,1,retcode,"pam_set_item(PAM_RHOST)"); + } + } + + /* 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; + } + + pam_get_item(pamh, PAM_USER, (const void **) &username); + fprintf(stderr, + "The user [%s] has been authenticated and `logged in'\n", + username); + + /* 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; + } + + /* `0' could be as above */ + retcode = pam_setcred(pamh, PAM_DELETE_CRED); + bail_out(pamh,0,retcode,"pam_setcred"); + if (retcode != PAM_SUCCESS) { + fprintf(stderr,"%s: problem deleting user credentials\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/Linux-PAM/install-sh b/Linux-PAM/install-sh new file mode 100755 index 00000000..b777f124 --- /dev/null +++ b/Linux-PAM/install-sh @@ -0,0 +1,322 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2004-07-05.00 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit 0;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit 0;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/Linux-PAM/libpam/Makefile b/Linux-PAM/libpam/Makefile new file mode 100644 index 00000000..94d92de6 --- /dev/null +++ b/Linux-PAM/libpam/Makefile @@ -0,0 +1,170 @@ +# +# $Id: Makefile,v 1.12 2005/03/29 20:41:20 toady Exp $ +# +# + +include ../Make.Rules + +# need to tell libpam about the default directory for PAMs +MOREFLAGS=-D"DEFAULT_MODULE_PATH=\"$(SECUREDIR)/\"" + +ifeq ($(WITH_LIBDEBUG),yes) + LIBNAME=libpamd + CFLAGS += -D"DEBUG" + CFLAGS += -g +else + LIBNAME=libpam +endif +ifeq ($(WITH_PRELUDE),yes) + CFLAGS += -DPRELUDE -DLIBPRELUDE_CONFIG_PREFIX=\"`libprelude-config --prefix`\" + LINKLIBS += -lprelude +endif +VERSION=.$(MAJOR_REL) +MODIFICATION=.$(MINOR_REL) + +# --------------------------------------------- + +dummy: ../Make.Rules all + +# --------------------------------------------- + +CFLAGS += $(DYNAMIC) $(STATIC) $(MOREFLAGS) \ + -DLIBPAM_VERSION_MAJOR=$(MAJOR_REL) \ + -DLIBPAM_VERSION_MINOR=$(MINOR_REL) \ + -DLIBPAM_VERSION_STRING=\"$(MAJOR_REL).$(MINOR_REL)\" + +# dynamic library names + +LIBPAM = $(LIBNAME).$(DYNTYPE) +LIBPAMNAME = $(LIBPAM)$(VERSION) +LIBPAMFULL = $(LIBPAMNAME)$(MODIFICATION) + +# static library name + +LIBPAMSTATIC = $(LIBNAME).a + +ifdef STATIC +# @echo Did you mean to set STATIC\? +MODULES = $(shell cat ../modules/_static_module_objects) +STATICOBJ = pam_static.o +else +MODULES = +endif + +ifeq ($(WITH_MEMORY_DEBUG),yes) +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_prelude.o pam_session.o pam_password.o \ + pam_env.o pam_log.o $(EXTRAS) + +ifeq ($(DYNAMIC_LIBPAM),yes) +# libpam.so needs -ldl, too. +DLIBOBJECTS = $(addprefix dynamic/,$(LIBOBJECTS) $(STATICOBJ)) +ifeq ($(STATICOBJ),yes) +dynamic/pam_static.o: pam_static.c ../modules/_static_module_objects + $(CC) $(CFLAGS) -c pam_static.c -o $@ +endif +endif + +ifeq ($(STATIC_LIBPAM),yes) +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) ../Make.Rules + +dirs: +ifeq ($(DYNAMIC_LIBPAM),yes) + $(MKDIR) dynamic +endif +ifeq ($(STATIC_LIBPAM),yes) + $(MKDIR) static +endif + +dynamic/%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +static/%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ +bootstrap-libpam: bootdir $(LIBPAM) +bootdir: + test -d dynamic || mkdir dynamic + +$(LIBPAM): $(DLIBOBJECTS) +ifeq ($(DYNAMIC_LIBPAM),yes) + ifeq ($(USESONAME),yes) + $(LD_L) $(SOSWITCH)$(LIBPAMNAME) -o $@ $(DLIBOBJECTS) \ + $(MODULES) $(LINKLIBS) + else + $(LD_L) -o $@ $(DLIBOBJECTS) $(MODULES) $(LINKLIBS) + endif + ifeq ($(NEEDSONAME),yes) + rm -f $(LIBPAMFULL) + ln -sf $(LIBPAM) $(LIBPAMFULL) + rm -f $(LIBPAMNAME) + ln -sf $(LIBPAM) $(LIBPAMNAME) + endif +endif + +$(LIBPAMSTATIC): $(SLIBOBJECTS) +ifeq ($(STATIC_LIBPAM),yes) + $(AR) cru $@ $(SLIBOBJECTS) $(MODULES) +ifdef RANLIB + $(RANLIB) $@ +endif +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 +ifeq ($(DYNAMIC_LIBPAM),yes) + $(INSTALL) -m $(SHLIBMODE) $(LIBPAM) $(FAKEROOT)$(libdir)/$(LIBPAMFULL) +ifndef FAKEROOT + $(LDCONFIG) +else + $(LDCONFIG) -n $(FAKEROOT)$(libdir) +endif + ifneq ($(DYNTYPE),"sl") + ( cd $(FAKEROOT)$(libdir) ; rm -f $(LIBPAM) ; \ + ln -sf $(LIBPAMNAME) $(LIBPAM) ) + endif +endif +ifeq ($(STATIC_LIBPAM),yes) + $(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) +ifndef FAKEROOT + $(LDCONFIG) +endif + rm -f $(FAKEROOT)$(libdir)/$(LIBPAMSTATIC) + +clean: + rm -f a.out core *~ static/*.o dynamic/*.o + rm -f *.orig $(LIBPAMNAME) $(LIBPAMFULL) + rm -f *.a *.o *.so ./include/security/*~ + if [ -d dynamic ]; then rmdir dynamic ; fi + if [ -d static ]; then rmdir static ; fi diff --git a/Linux-PAM/libpam/include/security/_pam_compat.h b/Linux-PAM/libpam/include/security/_pam_compat.h new file mode 100644 index 00000000..33520a6c --- /dev/null +++ b/Linux-PAM/libpam/include/security/_pam_compat.h @@ -0,0 +1,122 @@ +#ifndef _PAM_COMPAT_H +#define _PAM_COMPAT_H + +/* + * $Id: _pam_compat.h,v 1.1.1.1 2000/06/20 22:11:21 agmorgan Exp $ + * + * 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/Linux-PAM/libpam/include/security/_pam_macros.h b/Linux-PAM/libpam/include/security/_pam_macros.h new file mode 100644 index 00000000..2827fabf --- /dev/null +++ b/Linux-PAM/libpam/include/security/_pam_macros.h @@ -0,0 +1,187 @@ +#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> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.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, fd; + +#ifdef O_NOFOLLOW + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_NOFOLLOW|O_APPEND)) != -1) { +#else + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_APPEND)) != -1) { +#endif + if (!(logfile = fdopen(fd,"a"))) { + logfile = stderr; + must_close = 0; + close(fd); + } + } else { + logfile = stderr; + must_close = 0; + } + fprintf(logfile,"[%s:%s(%d)] ",file, fn, line); + fflush(logfile); + if (must_close) + fclose(logfile); +} + +static void _pam_output_debug(const char *format, ...) +{ + va_list args; + FILE *logfile; + int must_close = 1, fd; + + va_start(args, format); + +#ifdef O_NOFOLLOW + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_NOFOLLOW|O_APPEND)) != -1) { +#else + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_APPEND)) != -1) { +#endif + if (!(logfile = fdopen(fd,"a"))) { + logfile = stderr; + must_close = 0; + close(fd); + } + } else { + logfile = stderr; + must_close = 0; + } + vfprintf(logfile, format, args); + fprintf(logfile, "\n"); + fflush(logfile); + if (must_close) + 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/Linux-PAM/libpam/include/security/_pam_types.h b/Linux-PAM/libpam/include/security/_pam_types.h new file mode 100644 index 00000000..b4413ee3 --- /dev/null +++ b/Linux-PAM/libpam/include/security/_pam_types.h @@ -0,0 +1,324 @@ +/* + * <security/_pam_types.h> + * + * $Id: _pam_types.h,v 1.6 2005/03/16 00:06:01 toady Exp $ + * + * 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 + +#ifndef __LIBPAM_VERSION +# define __LIBPAM_VERSION __libpam_version +#endif +extern unsigned int __libpam_version; + +/* + * 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 authentication */ + /* 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 /* Ignore 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 - take care to also extend the libpam code: + * pam_strerror() and "libpam/pam_tokens.h" . + */ + +#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, void *appdata_ptr); + */ + +#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/Linux-PAM/libpam/include/security/pam_appl.h b/Linux-PAM/libpam/include/security/pam_appl.h new file mode 100644 index 00000000..69ee544d --- /dev/null +++ b/Linux-PAM/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: pam_appl.h,v 1.3 2000/11/19 23:54:02 agmorgan Exp $ + */ + +#ifndef _SECURITY_PAM_APPL_H +#define _SECURITY_PAM_APPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <security/_pam_types.h> /* Linux-PAM common defined types */ + +/* -------------- The Linux-PAM Framework layer API ------------- */ + +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/Linux-PAM/libpam/include/security/pam_malloc.h b/Linux-PAM/libpam/include/security/pam_malloc.h new file mode 100644 index 00000000..bbf31338 --- /dev/null +++ b/Linux-PAM/libpam/include/security/pam_malloc.h @@ -0,0 +1,71 @@ +/* + * $Id: pam_malloc.h,v 1.3 2001/11/26 03:04:47 agmorgan Exp $ + */ + +/* + * 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__) +#define strdup(s) pam_strdup(s,__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 *, int); +extern void *pam_calloc(size_t n,size_t s,const char *,const char *, int); +extern void pam_free(void *x,const char *,const char *, int); +extern void *pam_memalign(size_t a,size_t s + ,const char *,const char *, int); +extern void *pam_realloc(void *x,size_t s,const char *,const char *, int); +extern void *pam_valloc(size_t s,const char *,const char *, int); +extern void *pam_alloca(size_t s,const char *,const char *, int); +extern void pam_exit(int i,const char *,const char *, int); +extern char *pam_strdup(const char *,const char *,const char *, 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/Linux-PAM/libpam/include/security/pam_modules.h b/Linux-PAM/libpam/include/security/pam_modules.h new file mode 100644 index 00000000..1f20993f --- /dev/null +++ b/Linux-PAM/libpam/include/security/pam_modules.h @@ -0,0 +1,169 @@ +/* + * <security/pam_modules.h> + * + * $Id: pam_modules.h,v 1.3 2001/02/05 06:50:41 agmorgan Exp $ + * + */ + +#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 cannot 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/Linux-PAM/libpam/pam_account.c b/Linux-PAM/libpam/pam_account.c new file mode 100644 index 00000000..3a4fb1fc --- /dev/null +++ b/Linux-PAM/libpam/pam_account.c @@ -0,0 +1,23 @@ +/* pam_account.c - PAM Account Management */ + +#include "pam_private.h" + +#include <stdio.h> + +int pam_acct_mgmt(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("called")); + + IF_NO_PAMH("pam_acct_mgmt", pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + retval = _pam_dispatch(pamh, flags, PAM_ACCOUNT); + + return retval; +} diff --git a/Linux-PAM/libpam/pam_auth.c b/Linux-PAM/libpam/pam_auth.c new file mode 100644 index 00000000..f2743624 --- /dev/null +++ b/Linux-PAM/libpam/pam_auth.c @@ -0,0 +1,73 @@ +/* + * pam_auth.c -- PAM authentication + * + * $Id: pam_auth.c,v 1.5 2005/03/29 20:41:20 toady Exp $ + * + */ + +#include "pam_private.h" +#include "pam_prelude.h" + +#include <stdio.h> +#include <stdlib.h> + +int pam_authenticate(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("pam_authenticate called")); + + IF_NO_PAMH("pam_authenticate", pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + 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 */ + } + + 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")); + } + +#ifdef PRELUDE + prelude_send_alert(pamh, retval); +#endif + + return retval; +} + +int pam_setcred(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("pam_setcred called")); + + IF_NO_PAMH("pam_setcred", pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + if (! flags) { + flags = PAM_ESTABLISH_CRED; + } + + retval = _pam_dispatch(pamh, flags, PAM_SETCRED); + + D(("pam_setcred exit")); + + return retval; +} diff --git a/Linux-PAM/libpam/pam_data.c b/Linux-PAM/libpam/pam_data.c new file mode 100644 index 00000000..6a90bd51 --- /dev/null +++ b/Linux-PAM/libpam/pam_data.c @@ -0,0 +1,123 @@ +/* pam_data.c */ + +/* + * $Id: pam_data.c,v 1.3 2003/07/13 20:01:44 vorlon Exp $ + */ + +#include "pam_private.h" + +#include <stdlib.h> +#include <string.h> + +static struct pam_data *_pam_locate_data(const pam_handle_t *pamh, + const char *name) +{ + struct pam_data *data; + + D(("called")); + + 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; +} + +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; + + D(("called")); + + IF_NO_PAMH("pam_set_data", pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_APP(pamh)) { + D(("called from application!?")); + return 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; + + D(("called")); + + IF_NO_PAMH("pam_get_data", pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_APP(pamh)) { + D(("called from application!?")); + return PAM_SYSTEM_ERR; + } + + data = _pam_locate_data(pamh, module_data_name); + if (data) { + *datap = data->data; + return PAM_SUCCESS; + } + + return PAM_NO_MODULE_DATA; +} + +void _pam_free_data(pam_handle_t *pamh, int status) +{ + struct pam_data *last; + struct pam_data *data; + + D(("called")); + + 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/Linux-PAM/libpam/pam_delay.c b/Linux-PAM/libpam/pam_delay.c new file mode 100644 index 00000000..01304629 --- /dev/null +++ b/Linux-PAM/libpam/pam_delay.c @@ -0,0 +1,159 @@ +/* + * pam_delay.c + * + * Copyright (c) Andrew G. Morgan <morgan@kernel.org> 1996-9 + * All rights reserved. + * + * $Id: pam_delay.c,v 1.6 2003/07/13 20:01:44 vorlon Exp $ + * + */ + +/* + * This is a simple implementation of a delay on failure mechanism; an + * attempt to overcome authentication-time attacks in a simple manner. + */ + +#include "pam_private.h" +#include <unistd.h> +#include <time.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) +{ + unsigned 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/Linux-PAM/libpam/pam_dispatch.c b/Linux-PAM/libpam/pam_dispatch.c new file mode 100644 index 00000000..4af29f69 --- /dev/null +++ b/Linux-PAM/libpam/pam_dispatch.c @@ -0,0 +1,384 @@ +/* pam_dispatch.c - handles module function dispatch */ + +/* + * Copyright (c) 1998 Andrew G. Morgan <morgan@kernel.org> + * + * $Id: pam_dispatch.c,v 1.7 2005/01/07 15:31:26 t8m Exp $ + */ + +#include "pam_private.h" + +#include <stdlib.h> +#include <stdio.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 + +/* frozen chain required codes */ +#define _PAM_PLEASE_FREEZE 0 +#define _PAM_MAY_BE_FROZEN 1 +#define _PAM_MUST_BE_FROZEN 2 + +/* + * 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 use_cached_chain) +{ + 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, cached_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 config; 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; + } + + /* + * use_cached_chain is how we ensure that the setcred/close_session + * and chauthtok(2) modules are called in the same order as they did + * when they were invoked as auth/open_session/chauthtok(1). This + * feature was added in 0.75 to make the behavior of pam_setcred + * sane. It was debugged by release 0.76. + */ + if (use_cached_chain != _PAM_PLEASE_FREEZE) { + + /* a former stack execution should have frozen the chain */ + + cached_retval = *(h->cached_retval_p); + if (cached_retval == _PAM_INVALID_RETVAL) { + + /* This may be a problem condition. It implies that + the application is running setcred, close_session, + chauthtok(2nd) without having first run + authenticate, open_session, chauthtok(1st) + [respectively]. */ + + D(("use_cached_chain is set to [%d]," + " but cached_retval == _PAM_INVALID_RETVAL", + use_cached_chain)); + + /* In the case of close_session and setcred there is a + backward compatibility reason for allowing this, in + the chauthtok case we have encountered a bug in + libpam! */ + + if (use_cached_chain == _PAM_MAY_BE_FROZEN) { + /* (not ideal) force non-frozen stack control. */ + cached_retval = retval; + } else { + D(("BUG in libpam -" + " chain is required to be frozen but isn't")); + + /* cached_retval is already _PAM_INVALID_RETVAL */ + } + } + } else { + /* this stack execution is defining the frozen chain */ + cached_retval = h->cached_retval = retval; + } + + /* verify that the return value is a valid one */ + if ((cached_retval < PAM_SUCCESS) + || (cached_retval >= _PAM_RETURN_VALUES)) { + + retval = PAM_MUST_FAIL_CODE; + action = _PAM_ACTION_BAD; + } else { + /* We treat the current retval with some respect. It may + (for example, in the case of setcred) have a value that + needs to be propagated to the user. We want to use the + cached_retval to determine the modules to be executed + in the stacked chain, but we want to treat each + non-ignored module in the cached chain as now being + 'required'. We only need to treat the, + _PAM_ACTION_IGNORE, _PAM_ACTION_IS_JUMP and + _PAM_ACTION_RESET actions specially. */ + + action = h->actions[cached_retval]; + } + + D(("use_cached_chain=%d action=%d cached_retval=%d retval=%d", + use_cached_chain, action, cached_retval, 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) ) { + /* in case of using cached chain + we could get here with PAM_IGNORE - don't return it */ + if ( retval != PAM_IGNORE || cached_retval == retval ) { + 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 ( cached_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) ) { + + /* If we are evaluating a cached chain, we treat this + module as required (aka _PAM_ACTION_OK) as well as + executing the jump. */ + + if (use_cached_chain) { + if (impression == _PAM_UNDEF + || (impression == _PAM_POSITIVE + && status == PAM_SUCCESS) ) { + if ( retval != PAM_IGNORE || cached_retval == retval ) { + impression = _PAM_POSITIVE; + status = retval; + } + } + } + + /* 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, use_cached_chain; + _pam_boolean resumed; + + IF_NO_PAMH("_pam_dispatch", pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from a module!?")); + return 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; + } + + use_cached_chain = _PAM_PLEASE_FREEZE; + + switch (choice) { + case PAM_AUTHENTICATE: + h = pamh->handlers.conf.authenticate; + break; + case PAM_SETCRED: + h = pamh->handlers.conf.setcred; + use_cached_chain = _PAM_MAY_BE_FROZEN; + 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; + use_cached_chain = _PAM_MAY_BE_FROZEN; + break; + case PAM_CHAUTHTOK: + h = pamh->handlers.conf.chauthtok; + if (flags & PAM_UPDATE_AUTHTOK) { + use_cached_chain = _PAM_MUST_BE_FROZEN; + } + 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; + } + + __PAM_TO_MODULE(pamh); + + /* call the list of module functions */ + retval = _pam_dispatch_aux(pamh, flags, h, resumed, use_cached_chain); + resumed = PAM_FALSE; + + __PAM_TO_APP(pamh); + + /* 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/Linux-PAM/libpam/pam_end.c b/Linux-PAM/libpam/pam_end.c new file mode 100644 index 00000000..a0716175 --- /dev/null +++ b/Linux-PAM/libpam/pam_end.c @@ -0,0 +1,77 @@ +/* pam_end.c */ + +/* + * $Id: pam_end.c,v 1.3 2003/07/13 20:01:44 vorlon Exp $ + */ + +#include "pam_private.h" + +#include <stdlib.h> + +int pam_end(pam_handle_t *pamh, int pam_status) +{ + int ret; + + D(("entering pam_end()")); + + IF_NO_PAMH("pam_end", pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + /* 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/Linux-PAM/libpam/pam_env.c b/Linux-PAM/libpam/pam_env.c new file mode 100644 index 00000000..9027bc79 --- /dev/null +++ b/Linux-PAM/libpam/pam_env.c @@ -0,0 +1,392 @@ +/* + * 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: pam_env.c,v 1.5 2004/09/22 09:37:47 kukuk Exp $ + */ + +#include "pam_private.h" + +#include <string.h> +#include <stdlib.h> + +#ifdef sunos +#define memmove(x,y,z) bcopy(y,x,z) +#endif + +/* 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]); + } + _pam_drop(dump); + 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/Linux-PAM/libpam/pam_handlers.c b/Linux-PAM/libpam/pam_handlers.c new file mode 100644 index 00000000..ed03eda8 --- /dev/null +++ b/Linux-PAM/libpam/pam_handlers.c @@ -0,0 +1,1024 @@ +/* pam_handlers.c -- pam config file parsing and module loading */ + +/* + * created by Marc Ewing. + * Currently maintained by Andrew G. Morgan <morgan@kernel.org> + * + * $Id: pam_handlers.c,v 1.12 2005/02/07 08:18:53 kukuk Exp $ + * + */ + +#include "pam_private.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#ifdef PAM_DYNAMIC +# ifdef PAM_SHL +# include <dl.h> +# else /* PAM_SHL */ +# include <dlfcn.h> +# endif /* PAM_SHL */ +#endif /* PAM_DYNAMIC */ + +/* If not required, define as nothing */ +#ifndef SHLIB_SYM_PREFIX +# define SHLIB_SYM_PREFIX "" +#endif + +#define BUF_SIZE 1024 +#define MODULE_CHUNK 4 +#define UNKNOWN_MODULE_PATH "<*unknown module path*>" +#ifndef _PAM_ISA +#define _PAM_ISA "." +#endif + +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_ANY 0 +#define PAM_T_AUTH 1 +#define PAM_T_SESS 2 +#define PAM_T_ACCT 4 +#define PAM_T_PASS 8 + +static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name + , const char *service /* specific file */ + , int module_type /* specific type */ +#ifdef PAM_READ_BOTH_CONFS + , int not_other +#endif /* PAM_READ_BOTH_CONFS */ + ); + +static int _pam_parse_conf_file(pam_handle_t *pamh, FILE *f + , const char *known_service /* specific file */ + , int requested_module_type /* specific type */ +#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) { + int pam_include = 0; + + /* 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 (tok == NULL) { + /* module type does not exist */ + D(("_pam_init_handlers: empty module type for %s", this_service)); + _pam_system_log(LOG_ERR, "(%s) empty module type", this_service); + module_type = (requested_module_type != PAM_T_ANY) ? + requested_module_type : PAM_T_AUTH; /* most sensitive */ + must_fail = 1; /* install as normal but fail when dispatched */ + } else 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 = (requested_module_type != PAM_T_ANY) ? + requested_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)); + if (requested_module_type != PAM_T_ANY && + module_type != requested_module_type) { + D(("Skipping config entry: %s (requested=%d, found=%d)", + tok, requested_module_type, module_type)); + continue; + } + + /* 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 (tok == NULL) { + /* no module name given */ + D(("_pam_init_handlers: no control flag supplied")); + _pam_system_log(LOG_ERR, + "(%s) no control flag supplied", this_service); + _pam_set_default_control(actions, _PAM_ACTION_BAD); + must_fail = 1; + } else 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 if (!_pam_strCMP("include", tok)) { + D(("*PAM_F_INCLUDE*")); + pam_include = 1; + } 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 (pam_include) { + if (_pam_load_conf_file(pamh, tok, this_service, module_type +#ifdef PAM_READ_BOTH_CONFS + , !other +#endif /* PAM_READ_BOTH_CONFS */ + ) == PAM_SUCCESS) + continue; + _pam_set_default_control(actions, _PAM_ACTION_BAD); + mod_path = NULL; + must_fail = 1; + nexttok = NULL; + } else 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 ); +} + +static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name + , const char *service /* specific file */ + , int module_type /* specific type */ +#ifdef PAM_READ_BOTH_CONFS + , int not_other +#endif /* PAM_READ_BOTH_CONFS */ + ) +{ + FILE *f; + char *config_path = NULL; + int retval = PAM_ABORT; + + D(("_pam_load_conf_file called")); + + if (config_name == NULL) { + D(("no config file supplied")); + _pam_system_log(LOG_ERR, "(%s) no config file supplied", service); + return PAM_ABORT; + } + + if (config_name[0] != '/') { + if (asprintf (&config_path, PAM_CONFIG_DF, config_name) < 0) { + _pam_system_log(LOG_CRIT, "asprintf failed"); + return PAM_BUF_ERR; + } + config_name = config_path; + } + + D(("opening %s", config_name)); + f = fopen(config_name, "r"); + if (f != NULL) { + retval = _pam_parse_conf_file(pamh, f, service, module_type +#ifdef PAM_READ_BOTH_CONFS + , not_other +#endif /* PAM_READ_BOTH_CONFS */ + ); + fclose(f); + if (retval != PAM_SUCCESS) + _pam_system_log(LOG_ERR, + "_pam_load_conf_file: error reading %s: %s", + config_name, pam_strerror(pamh, retval)); + } else { + D(("unable to open %s", config_name)); + _pam_system_log(LOG_ERR, + "_pam_load_conf_file: unable to open %s", + config_name); + } + + _pam_drop(config_path); + return retval; +} + +/* 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, PAM_T_ANY +#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, PAM_T_ANY, 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 + , PAM_T_ANY +#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, PAM_T_ANY +#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" + */ + +static 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, *mod_full_isa_path=NULL, *isa=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) { + if (strstr(mod_path, "$ISA")) { + mod_full_isa_path = malloc(strlen(mod_path) + strlen(_PAM_ISA) + 1); + if (mod_full_isa_path == NULL) { + D(("_pam_handler: couldn't get memory for mod_path")); + _pam_system_log(LOG_ERR, "no memory for module path"); + success = PAM_ABORT; + } else { + strcpy(mod_full_isa_path, mod_path); + isa = strstr(mod_full_isa_path, "$ISA"); + if (isa) { + memmove(isa + strlen(_PAM_ISA), isa + 4, strlen(isa + 4) + 1); + memmove(isa, _PAM_ISA, strlen(_PAM_ISA)); + } + mod->dl_handle = +# ifdef PAM_SHL + shl_load(mod_full_isa_path, BIND_IMMEDIATE, 0L); +# else /* PAM_SHL */ + dlopen(mod_full_isa_path, RTLD_NOW); +# endif /* PAM_SHL */ + _pam_drop(mod_full_isa_path); + } + } + } + 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"); + 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)->cached_retval = _PAM_INVALID_RETVAL; + (*handler_p)->cached_retval_p = &((*handler_p)->cached_retval); + (*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)->cached_retval = _PAM_INVALID_RETVAL; /* ignored */ + /* Note, this next entry points to the handler_p value! */ + (*handler_p2)->cached_retval_p = &((*handler_p)->cached_retval); + (*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/Linux-PAM/libpam/pam_item.c b/Linux-PAM/libpam/pam_item.c new file mode 100644 index 00000000..1425c600 --- /dev/null +++ b/Linux-PAM/libpam/pam_item.c @@ -0,0 +1,335 @@ +/* pam_item.c */ + +/* + * $Id: pam_item.c,v 1.5 2004/09/22 09:37:47 kukuk Exp $ + */ + +#include "pam_private.h" + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#define RESET(X, Y) \ +{ \ + char *_TMP_ = (X); \ + if (_TMP_ != (Y)) { \ + (X) = (Y) ? _pam_strdup(Y) : NULL; \ + if (_TMP_) \ + free(_TMP_); \ + } \ +} + +/* handy version id */ + +unsigned int __libpam_version = LIBPAM_VERSION; + +/* 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: + /* + * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from + * modules. + */ + if (__PAM_FROM_MODULE(pamh)) { + 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_); + } + } else { + retval = PAM_BAD_ITEM; + } + + break; + + case PAM_OLDAUTHTOK: + /* + * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from + * modules. + */ + if (__PAM_FROM_MODULE(pamh)) { + 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_); + } + } else { + retval = PAM_BAD_ITEM; + } + + 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) +{ + int retval = PAM_SUCCESS; + + 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; + } + else + *item = NULL; + + 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: + /* + * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from + * modules. + */ + if (__PAM_FROM_MODULE(pamh)) { + *item = pamh->authtok; + } else { + retval = PAM_BAD_ITEM; + } + break; + + case PAM_OLDAUTHTOK: + /* + * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from + * modules. + */ + if (__PAM_FROM_MODULE(pamh)) { + *item = pamh->oldauthtok; + } else { + retval = PAM_BAD_ITEM; + } + break; + + case PAM_CONV: + *item = pamh->pam_conversation; + break; + + case PAM_FAIL_DELAY: + *item = pamh->fail_delay.delay_fn_ptr; + break; + + default: + retval = PAM_BAD_ITEM; + } + + return retval; +} + +/* + * This function is the 'preferred method to obtain the username'. + */ + +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 (user == NULL) { /* ensure that the module has supplied a destination */ + _pam_system_log(LOG_ERR, "pam_get_user: nowhere to record username"); + return PAM_PERM_DENIED; + } else + *user = NULL; + + 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 (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/Linux-PAM/libpam/pam_log.c b/Linux-PAM/libpam/pam_log.c new file mode 100644 index 00000000..c42fe015 --- /dev/null +++ b/Linux-PAM/libpam/pam_log.c @@ -0,0 +1,375 @@ +/* + * pam_log.c -- PAM system logging + * + * $Id: pam_log.c,v 1.2 2000/11/19 23:54:02 agmorgan Exp $ + * + */ + +#include "pam_private.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.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/Linux-PAM/libpam/pam_malloc.c b/Linux-PAM/libpam/pam_malloc.c new file mode 100644 index 00000000..98b35f62 --- /dev/null +++ b/Linux-PAM/libpam/pam_malloc.c @@ -0,0 +1,418 @@ +/* + * $Id: pam_malloc.c,v 1.5 2001/12/09 21:44:58 agmorgan Exp $ + */ + +/* + * 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, strdup 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@kernel.org> 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 +#undef strdup + +#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, fd; + +#ifdef O_NOFOLLOW + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_NOFOLLOW|O_APPEND)) != -1) { +#else + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_APPEND)) != -1) { +#endif + if (!(logfile = fdopen(fd,"a"))) { + logfile = stderr; + must_close = 0; + close(fd); + } + } else { + logfile = stderr; + must_close = 0; + } + fprintf(logfile, "[%s:%s(%d)->%s()] ", + last_file, last_call, last_line, last_fn); + fflush(logfile); + if (must_close) + 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>" + , 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 (%s:%s():%d) to free %p", file, fn, line, 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) +{ + D(("time to exit")); + + _fn("exit"); + + if (on(0)) err(("passed (%d)", i)); + if (on(PAM_MALLOC_LEAKED)) { + dump_memory_list("leaked"); + } + exit(i); +} + +char *pam_strdup(const char *orig, + const char *file, const char *fn, const int line) +{ + char *new; + + _fn("strdup"); + + if (on(PAM_MALLOC_FUNC)) err(("request for dup of [%s]", orig)); + + new = strdup(orig); + if (new == NULL) { + if (on(PAM_MALLOC_FAIL)) err(("returned NULL")); + } else { + if (on(PAM_MALLOC_REQUEST)) err(("request dup of [%s]", orig)); + add_new_ref(new, 1, strlen(new)+1); + } + + return new; +} + +/* end of file */ diff --git a/Linux-PAM/libpam/pam_map.c b/Linux-PAM/libpam/pam_map.c new file mode 100644 index 00000000..86b16577 --- /dev/null +++ b/Linux-PAM/libpam/pam_map.c @@ -0,0 +1,78 @@ +/* pam_map.c - PAM mapping interface + * + * $Id: pam_map.c,v 1.2 2000/12/04 19:02:34 baggins Exp $ + * + * 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. + * + */ + +#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/Linux-PAM/libpam/pam_misc.c b/Linux-PAM/libpam/pam_misc.c new file mode 100644 index 00000000..cb0572b1 --- /dev/null +++ b/Linux-PAM/libpam/pam_misc.c @@ -0,0 +1,321 @@ +/* pam_misc.c -- This is random stuff */ + +/* + * $Id: pam_misc.c,v 1.4 2003/07/13 20:01:44 vorlon Exp $ + */ + +#include "pam_private.h" + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <ctype.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-format 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. + * Note, any '[' inside the outer "[...]" pair will survive. + * Note, the first ']' will terminate this string, but + * that "\]" will get compressed into "]". That is: + * + * "[..[..\]..]..." --> "..[..].." + */ + char *to; + for (to=end=++from; *end && *end != ']'; ++to, ++end) { + if (*end == '\\' && end[1] == ']') + ++end; + if (to != end) { + *to = *end; + } + } + if (to != end) { + *to = '\0'; + } + /* 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) +{ + int old_caller_is = pamh->caller_is; + + /* + * this is for security. We reset the auth-tokens here. + */ + __PAM_TO_MODULE(pamh); + pam_set_item(pamh, PAM_AUTHTOK, NULL); + pam_set_item(pamh, PAM_OLDAUTHTOK, NULL); + pamh->caller_is = old_caller_is; +} + +/* + * 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/Linux-PAM/libpam/pam_password.c b/Linux-PAM/libpam/pam_password.c new file mode 100644 index 00000000..50c12adf --- /dev/null +++ b/Linux-PAM/libpam/pam_password.c @@ -0,0 +1,57 @@ +/* pam_password.c - PAM Password Management */ + +/* + * $Id: pam_password.c,v 1.3 2003/07/13 20:01:44 vorlon Exp $ + */ + +/* #define DEBUG */ + +#include "pam_private.h" + +#include <stdio.h> +#include <stdlib.h> + +int pam_chauthtok(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("called.")); + + IF_NO_PAMH("pam_chauthtok", pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return 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/Linux-PAM/libpam/pam_prelude.c b/Linux-PAM/libpam/pam_prelude.c new file mode 100644 index 00000000..656376f5 --- /dev/null +++ b/Linux-PAM/libpam/pam_prelude.c @@ -0,0 +1,605 @@ +/* + * pam_prelude.c -- prelude reporting + * http://www.prelude-ids.org + * + * (C) Sebastien Tricaud 2005 <toady@gscore.org> + */ + +#include <stdio.h> +#include <syslog.h> + +#ifdef PRELUDE + +#include <libprelude/prelude.h> +#include <libprelude/prelude-log.h> +#include <libprelude/idmef-message-print.h> + +#include "pam_prelude.h" +#include "pam_private.h" + + +#define ANALYZER_CLASS "pam" +#define ANALYZER_MODEL "PAM" +#define ANALYZER_MANUFACTURER "Sebastien Tricaud, http://www.kernel.org/pub/linux/libs/pam/" + +#define DEFAULT_ANALYZER_NAME "PAM" +#define DEFAULT_ANALYZER_CONFIG LIBPRELUDE_CONFIG_PREFIX "/etc/prelude/default/idmef-client.conf" + +#define PAM_VERSION LIBPAM_VERSION_STRING + +static const char *pam_get_item_service(pam_handle_t *pamh); +static const char *pam_get_item_user(pam_handle_t *pamh); +static const char *pam_get_item_user_prompt(pam_handle_t *pamh); +static const char *pam_get_item_tty(pam_handle_t *pamh); +static const char *pam_get_item_ruser(pam_handle_t *pamh); +static const char *pam_get_item_rhost(pam_handle_t *pamh); + +static int setup_analyzer(idmef_analyzer_t *analyzer); +static void pam_alert_prelude(const char *msg, void *data, pam_handle_t *pamh, int authval); +static int pam_alert_prelude_init(pam_handle_t *pamh, int authval); +static int generate_additional_data(idmef_alert_t *alert, const char *meaning, const char *data); + + +/******************* + * some syslogging * + *******************/ +static void +_pam_log(int err, const char *format, ...) +{ + va_list args; + va_start(args, format); + +#ifdef MAIN + vfprintf(stderr,format,args); + fprintf(stderr,"\n"); +#else + openlog("libpam", LOG_CONS|LOG_PID, LOG_AUTH); + vsyslog(err, format, args); + closelog(); +#endif + va_end(args); +} + +static const char * +pam_get_item_service(pam_handle_t *pamh) +{ + const char *service = NULL; + + pam_get_item(pamh, PAM_SERVICE, (const void **)&service); + + return (const char *)service; +} + +static const char * +pam_get_item_user(pam_handle_t *pamh) +{ + const char *user = NULL; + + pam_get_item(pamh, PAM_USER, (const void **)&user); + + return (const char *)user; +} + +static const char * +pam_get_item_user_prompt(pam_handle_t *pamh) +{ + const char *user_prompt = NULL; + + pam_get_item(pamh, PAM_USER_PROMPT, (const void **)&user_prompt); + + return (const char *)user_prompt; +} + +static const char * +pam_get_item_tty(pam_handle_t *pamh) +{ + const char *tty = NULL; + + pam_get_item(pamh, PAM_TTY, (const void **)&tty); + + return (const char *)tty; +} + +static const char * +pam_get_item_ruser(pam_handle_t *pamh) +{ + const char *ruser = NULL; + + pam_get_item(pamh, PAM_RUSER, (const void **)&ruser); + + return (const char *)ruser; +} + +static const char * +pam_get_item_rhost(pam_handle_t *pamh) +{ + const char *rhost = NULL; + + pam_get_item(pamh, PAM_RHOST, (const void **)&rhost); + + return (const char *)rhost; +} + +/***************************************************************** + * Returns a string concerning the authentication value provided * + *****************************************************************/ +static const char * +pam_get_alert_description(int authval) +{ + const char *retstring = NULL; + + switch(authval) { + case PAM_SUCCESS: + retstring = "Authentication success"; + break; + case PAM_OPEN_ERR: + retstring = "dlopen() failure when dynamically loading a service module"; + break; + case PAM_SYMBOL_ERR: + retstring = "Symbol not found"; + break; + case PAM_SERVICE_ERR: + retstring = "Error in service module"; + break; + case PAM_SYSTEM_ERR: + retstring = "System error"; + break; + case PAM_BUF_ERR: + retstring = "Memory buffer error"; + break; + case PAM_PERM_DENIED: + retstring = "Permission denied"; + break; + case PAM_AUTH_ERR: + retstring = "Authentication failure"; + break; + case PAM_CRED_INSUFFICIENT: + retstring = "Can not access authentication data due to insufficient credentials"; + break; + case PAM_AUTHINFO_UNAVAIL: + retstring = "Underlying authentication service can not retrieve authenticaiton information"; + break; + case PAM_USER_UNKNOWN: + retstring = "User not known to the underlying authentication module"; + break; + case PAM_MAXTRIES: + retstring = "An authentication service has maintained a retry count which has been reached. No further retries should be attempted"; + break; + case PAM_NEW_AUTHTOK_REQD: + retstring = "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"; + break; + case PAM_ACCT_EXPIRED: + retstring = "User account has expired"; + break; + case PAM_SESSION_ERR: + retstring = "Can not make/remove an entry for the specified session"; + break; + case PAM_CRED_UNAVAIL: + retstring = "Underlying authentication service can not retrieve user credentials unavailable"; + break; + case PAM_CRED_EXPIRED: + retstring = "User credentials expired"; + break; + case PAM_CRED_ERR: + retstring = "Failure setting user credentials"; + break; + case PAM_NO_MODULE_DATA: + retstring = "No module specific data is present"; + break; + case PAM_CONV_ERR: + retstring = "Conversation error"; + break; + case PAM_AUTHTOK_ERR: + retstring = "Authentication token manipulation error"; + break; + case PAM_AUTHTOK_RECOVER_ERR: + retstring = "Authentication information cannot be recovered"; + break; + case PAM_AUTHTOK_LOCK_BUSY: + retstring = "Authentication token lock busy"; + break; + case PAM_AUTHTOK_DISABLE_AGING: + retstring = "Authentication token aging disabled"; + break; + case PAM_TRY_AGAIN: + retstring = "Preliminary check by password service"; + break; + case PAM_IGNORE: + retstring = "Ignore underlying account module regardless of whether the control flag is required, optional, or sufficient"; + break; + case PAM_ABORT: + retstring = "Critical error (?module fail now request)"; + break; + case PAM_AUTHTOK_EXPIRED: + retstring = "User's authentication token has expired"; + break; + case PAM_MODULE_UNKNOWN: + retstring = "Module is not known"; + break; + case PAM_BAD_ITEM: + retstring = "Bad item passed to pam_*_item()"; + break; + case PAM_CONV_AGAIN: + retstring = "Conversation function is event driven and data is not available yet"; + break; + case PAM_INCOMPLETE: + retstring = "Please call this function again to complete authentication stack. Before calling again, verify that conversation is completed"; + break; + + default: + retstring = "Authentication Failure!. You should not see this message."; + } + + return retstring; + +} + +/* Courteously stolen from prelude-lml */ +static int +generate_additional_data(idmef_alert_t *alert, const char *meaning, const char *data) +{ + int ret; + prelude_string_t *str; + idmef_additional_data_t *adata; + + ret = idmef_alert_new_additional_data(alert, &adata, -1); + if ( ret < 0 ) + return ret; + + ret = idmef_additional_data_new_meaning(adata, &str); + if ( ret < 0 ) + return ret; + + ret = prelude_string_set_ref(str, meaning); + if ( ret < 0 ) + return ret; + + return idmef_additional_data_set_string_ref(adata, data); +} + +extern void +prelude_send_alert(pam_handle_t *pamh, int authval) +{ + + int ret; + + prelude_log_set_flags(PRELUDE_LOG_FLAGS_SYSLOG); + + ret = pam_alert_prelude_init(pamh, authval); + if ( ret < 0 ) + _pam_log(LOG_WARNING, + "No prelude alert sent"); + + prelude_deinit(); + +} + +static int +setup_analyzer(idmef_analyzer_t *analyzer) +{ + int ret; + prelude_string_t *string; + + ret = idmef_analyzer_new_model(analyzer, &string); + if ( ret < 0 ) + goto err; + prelude_string_set_constant(string, ANALYZER_MODEL); + + ret = idmef_analyzer_new_class(analyzer, &string); + if ( ret < 0 ) + goto err; + prelude_string_set_constant(string, ANALYZER_CLASS); + + ret = idmef_analyzer_new_manufacturer(analyzer, &string); + if ( ret < 0 ) + goto err; + prelude_string_set_constant(string, ANALYZER_MANUFACTURER); + + ret = idmef_analyzer_new_version(analyzer, &string); + if ( ret < 0 ) + goto err; + prelude_string_set_constant(string, PAM_VERSION); + + + return 0; + + err: + _pam_log(LOG_WARNING, + "%s: IDMEF error: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + + return -1; +} + +static void +pam_alert_prelude(const char *msg, void *data, pam_handle_t *pamh, int authval) +{ + int ret; + idmef_time_t *clienttime; + idmef_alert_t *alert; + prelude_string_t *str; + idmef_message_t *idmef = NULL; + idmef_classification_t *class; + prelude_client_t *client = (prelude_client_t *)data; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *user; + idmef_user_id_t *user_id; + idmef_process_t *process; + idmef_classification_t *classification; + idmef_impact_t *impact; + idmef_assessment_t *assessment; + idmef_node_t *node; + idmef_analyzer_t *analyzer; + + + ret = idmef_message_new(&idmef); + if ( ret < 0 ) + goto err; + + ret = idmef_message_new_alert(idmef, &alert); + if ( ret < 0 ) + goto err; + + ret = idmef_alert_new_classification(alert, &class); + if ( ret < 0 ) + goto err; + + ret = idmef_classification_new_text(class, &str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_new_ref(&str, msg); + if ( ret < 0 ) + goto err; + + idmef_classification_set_text(class, str); + + ret = idmef_time_new_from_gettimeofday(&clienttime); + if ( ret < 0 ) + goto err; + idmef_alert_set_create_time(alert, clienttime); + + idmef_alert_set_analyzer(alert, + idmef_analyzer_ref(prelude_client_get_analyzer(client)), + 0); + + /********** + * SOURCE * + **********/ + ret = idmef_alert_new_source(alert, &source, -1); + if ( ret < 0 ) + goto err; + + /* BEGIN: Sets the user doing authentication stuff */ + ret = idmef_source_new_user(source, &user); + if ( ret < 0 ) + goto err; + idmef_user_set_category(user, IDMEF_USER_CATEGORY_APPLICATION); + + ret = idmef_user_new_user_id(user, &user_id, 0); + if ( ret < 0 ) + goto err; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + + if ( pam_get_item_ruser(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_ruser(pamh)); + if ( ret < 0 ) + goto err; + + idmef_user_id_set_name(user_id, str); + } + /* END */ + /* BEGIN: Adds TTY infos */ + if ( pam_get_item_tty(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_tty(pamh)); + if ( ret < 0 ) + goto err; + + idmef_user_id_set_tty(user_id, str); + } + /* END */ + /* BEGIN: Sets the source node (rhost) */ + ret = idmef_source_new_node(source, &node); + if ( ret < 0 ) + goto err; + idmef_node_set_category(node, IDMEF_NODE_CATEGORY_HOSTS); + + if ( pam_get_item_rhost(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_rhost(pamh)); + if ( ret < 0 ) + goto err; + + idmef_node_set_name(node, str); + } + /* END */ + /* BEGIN: Describe the service */ + ret = idmef_source_new_process(source, &process); + if ( ret < 0 ) + goto err; + idmef_process_set_pid(process, getpid()); + + if ( pam_get_item_service(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_service(pamh)); + if ( ret < 0 ) + goto err; + + idmef_process_set_name(process, str); + } + /* END */ + + /********** + * TARGET * + **********/ + + ret = idmef_alert_new_target(alert, &target, -1); + if ( ret < 0 ) + goto err; + + + /* BEGIN: Sets the target node */ + analyzer = prelude_client_get_analyzer(client); + if ( ! analyzer ) goto err; + + node = idmef_analyzer_get_node(analyzer); + if ( ! node ) goto err; + idmef_target_set_node(target, node); + node = idmef_node_ref(node); + if ( ! node ) goto err; + /* END */ + /* BEGIN: Sets the user doing authentication stuff */ + ret = idmef_target_new_user(target, &user); + if ( ret < 0 ) + goto err; + idmef_user_set_category(user, IDMEF_USER_CATEGORY_APPLICATION); + + ret = idmef_user_new_user_id(user, &user_id, 0); + if ( ret < 0 ) + goto err; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_TARGET_USER); + + if ( pam_get_item_user(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_user(pamh)); + if ( ret < 0 ) + goto err; + + idmef_user_id_set_name(user_id, str); + } + /* END */ + /* BEGIN: Short description of the alert */ + ret = idmef_alert_new_classification(alert, &classification); + if ( ret < 0 ) + goto err; + + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, + authval == PAM_SUCCESS ? + "Authentication Success" : "Authentication Failure"); + if ( ret < 0 ) + goto err; + + idmef_classification_set_text(classification, str); + /* END */ + /* BEGIN: Long description of the alert */ + ret = idmef_alert_new_assessment(alert, &assessment); + if ( ret < 0 ) + goto err; + + ret = idmef_assessment_new_impact(assessment, &impact); + if ( ret < 0 ) + goto err; + + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, + pam_get_alert_description(authval)); + if ( ret < 0 ) + goto err; + + idmef_impact_set_description(impact, str); + /* END */ + /* BEGIN: Adding additional data */ + if ( pam_get_item_user_prompt(pamh) ) { + ret = generate_additional_data(alert, "Local User Prompt", + pam_get_item_user_prompt(pamh)); + if ( ret < 0 ) + goto err; + } + /* END */ + + prelude_client_send_idmef(client, idmef); + + if ( idmef ) + idmef_message_destroy(idmef); + + return; + err: + _pam_log(LOG_WARNING, + "%s: IDMEF error: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + + if ( idmef ) + idmef_message_destroy(idmef); + +} + +static int +pam_alert_prelude_init(pam_handle_t *pamh, int authval) +{ + + int ret; + prelude_client_t *client = NULL; + + ret = prelude_init(NULL, NULL); + if ( ret < 0 ) { + _pam_log(LOG_WARNING, + "%s: Unable to initialize the Prelude library: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + return -1; + } + + ret = prelude_client_new(&client, DEFAULT_ANALYZER_NAME); + if ( ! client ) { + _pam_log(LOG_WARNING, + "%s: Unable to create a prelude client object: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + + return -1; + } + + + ret = setup_analyzer(prelude_client_get_analyzer(client)); + if ( ret < 0 ) { + _pam_log(LOG_WARNING, + "%s: Unable to setup analyzer: %s\n", + prelude_strsource(ret), prelude_strerror(ret)); + + prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_FAILURE); + + return -1; + } + + ret = prelude_client_start(client); + if ( ret < 0 ) { + _pam_log(LOG_WARNING, + "%s: Unable to initialize prelude client: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + + prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_FAILURE); + + return -1; + } + + pam_alert_prelude("libpam alert" , client, pamh, authval); + + prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS); + + return 0; +} + +#endif PRELUDE diff --git a/Linux-PAM/libpam/pam_prelude.h b/Linux-PAM/libpam/pam_prelude.h new file mode 100644 index 00000000..13ee6fdb --- /dev/null +++ b/Linux-PAM/libpam/pam_prelude.h @@ -0,0 +1,16 @@ +/* + * pam_prelude.h -- prelude ids reporting + * http://www.prelude-ids.org + * + * (C) Sebastien Tricaud 2005 <toady@gscore.org> + */ + +#ifndef _SECURITY_PAM_PRELUDE_H +#define _SECURITY_PAM_PRELUDE_H + +#include <security/_pam_types.h> + +void prelude_send_alert(pam_handle_t *pamh, int authval); + +#endif /* _SECURITY_PAM_PRELUDE_H */ + diff --git a/Linux-PAM/libpam/pam_private.h b/Linux-PAM/libpam/pam_private.h new file mode 100644 index 00000000..3c8d8538 --- /dev/null +++ b/Linux-PAM/libpam/pam_private.h @@ -0,0 +1,334 @@ +/* + * pam_private.h + * + * $Id: pam_private.h,v 1.6 2004/09/15 12:06:17 kukuk Exp $ + * + * 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: CVS + */ + +#ifndef _PAM_PRIVATE_H +#define _PAM_PRIVATE_H + +#include <security/_pam_aconf.h> + +/* this is not used at the moment --- AGM */ +#define LIBPAM_VERSION (LIBPAM_VERSION_MAJOR*0x100 + LIBPAM_VERSION_MINOR) + +#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 */ + +#define _PAM_INVALID_RETVAL -1 /* default value for cached_retval */ + +struct handler { + int must_fail; + int (*func)(pam_handle_t *pamh, int flags, int argc, char **argv); + int actions[_PAM_RETURN_VALUES]; + /* set by authenticate, open_session, chauthtok(1st) + consumed by setcred, close_session, chauthtok(2nd) */ + int cached_retval; int *cached_retval_p; + 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; + unsigned caller_is; + 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); + +/* 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); + +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, ... ) +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))); +#else + ; +#endif + +#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> + +/* used to work out where control currently resides (in an application + or in a module) */ + +#define _PAM_CALLED_FROM_MODULE 1 +#define _PAM_CALLED_FROM_APP 2 + +#define __PAM_FROM_MODULE(pamh) ((pamh)->caller_is == _PAM_CALLED_FROM_MODULE) +#define __PAM_FROM_APP(pamh) ((pamh)->caller_is == _PAM_CALLED_FROM_APP) +#define __PAM_TO_MODULE(pamh) \ + do { (pamh)->caller_is = _PAM_CALLED_FROM_MODULE; } while (0) +#define __PAM_TO_APP(pamh) \ + do { (pamh)->caller_is = _PAM_CALLED_FROM_APP; } while (0) + +/* + * Copyright (C) 1995 by Red Hat Software, Marc Ewing + * Copyright (c) 1996-8,2001 by Andrew G. Morgan <morgan@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/Linux-PAM/libpam/pam_second.c b/Linux-PAM/libpam/pam_second.c new file mode 100644 index 00000000..fa3bdf78 --- /dev/null +++ b/Linux-PAM/libpam/pam_second.c @@ -0,0 +1,50 @@ +/* + * pam_second.c -- PAM secondary authentication + * (based on XSSO draft spec of March 1997) + * + * $Id: pam_second.c,v 1.3 2001/09/19 06:18:46 agmorgan Exp $ + * + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "pam_private.h" + +/* p 42 */ + +/* XXX - there are actually no plans to support this function. It does + not appear to be very well defined */ + +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 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/Linux-PAM/libpam/pam_session.c b/Linux-PAM/libpam/pam_session.c new file mode 100644 index 00000000..c468cf96 --- /dev/null +++ b/Linux-PAM/libpam/pam_session.c @@ -0,0 +1,37 @@ +/* pam_session.c - PAM Session Management */ + +/* + * $Id: pam_session.c,v 1.4 2003/07/13 20:01:44 vorlon Exp $ + */ + +#include "pam_private.h" + +#include <stdio.h> + +int pam_open_session(pam_handle_t *pamh, int flags) +{ + D(("called")); + + IF_NO_PAMH("pam_open_session", pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return 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); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + return _pam_dispatch(pamh, flags, PAM_CLOSE_SESSION); +} diff --git a/Linux-PAM/libpam/pam_start.c b/Linux-PAM/libpam/pam_start.c new file mode 100644 index 00000000..5d6e066a --- /dev/null +++ b/Linux-PAM/libpam/pam_start.c @@ -0,0 +1,122 @@ +/* pam_start.c */ + +/* Creator Marc Ewing + * Maintained by AGM + * + * $Id: pam_start.c,v 1.5 2004/09/14 13:48:41 kukuk Exp $ + * + */ + +#include "pam_private.h" + +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <syslog.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 == NULL) { + _pam_system_log(LOG_CRIT, "pam_start: invalid argument: pamh == NULL"); + return (PAM_BUF_ERR); + } + + if ((*pamh = calloc(1, sizeof(**pamh))) == NULL) { + _pam_system_log(LOG_CRIT, "pam_start: calloc failed for *pamh"); + return (PAM_BUF_ERR); + } + + /* All service names should be files below /etc/pam.d and nothing + else. Forbid paths. */ + if (strrchr(service_name, '/') != NULL) + service_name = strrchr(service_name, '/') + 1; + + /* Mark the caller as the application - permission to do certain + things is limited to a module or an application */ + + __PAM_TO_APP(*pamh); + + 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. */ + + 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/Linux-PAM/libpam/pam_static.c b/Linux-PAM/libpam/pam_static.c new file mode 100644 index 00000000..5a2b5a5d --- /dev/null +++ b/Linux-PAM/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: pam_static.c,v 1.1.1.1 2000/06/20 22:11:21 agmorgan Exp $ + */ + +/* 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/Linux-PAM/libpam/pam_strerror.c b/Linux-PAM/libpam/pam_strerror.c new file mode 100644 index 00000000..788c7a51 --- /dev/null +++ b/Linux-PAM/libpam/pam_strerror.c @@ -0,0 +1,93 @@ +/* pam_strerror.c */ + +/* + * $Id: pam_strerror.c,v 1.4 2005/01/07 15:31:26 t8m Exp $ + */ + +#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 exhausted 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 "The return value should be ignored by PAM dispatch"; + 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 PAM error"; +} diff --git a/Linux-PAM/libpam/pam_tokens.h b/Linux-PAM/libpam/pam_tokens.h new file mode 100644 index 00000000..69e79489 --- /dev/null +++ b/Linux-PAM/libpam/pam_tokens.h @@ -0,0 +1,106 @@ +/* + * pam_tokens.h + * + * $Id: pam_tokens.h,v 1.3 2001/01/22 06:07:29 agmorgan Exp $ + * + * 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. + * + */ + +#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 */ + "conv_again", /* 30 */ + "incomplete", /* 31 */ +/* add new return codes here */ + "default" /* this is _PAM_RETURN_VALUES and indicates + the default return action */ +}; + +/* + * Copyright (C) 1998,2001 Andrew G. Morgan <morgan@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/Linux-PAM/libpam_misc/Makefile b/Linux-PAM/libpam_misc/Makefile new file mode 100644 index 00000000..97166668 --- /dev/null +++ b/Linux-PAM/libpam_misc/Makefile @@ -0,0 +1,120 @@ +# +# $Id: Makefile,v 1.9 2005/03/29 20:41:20 toady Exp $ +# + +# lots of debugging information goes to /tmp/pam-debug.log +#MOREFLAGS += -D"DEBUG" + +include ../Make.Rules + +ifeq ($(WITH_LIBDEBUG),yes) + LIBNAME=libpam_miscd +else + LIBNAME=libpam_misc +endif +ifeq ($(WITH_PRELUDE),yes) + CFLAGS += -DPRELUDE -DLIBPRELUDE_CONFIG_PREFIX=\"`libprelude-config --prefix`\" +endif +VERSION=.$(MAJOR_REL) +MODIFICATION=.$(MINOR_REL) + +CFLAGS += $(MOREFLAGS) $(DYNAMIC) $(STATIC) +LINKLIBS += -L$(absolute_objdir)/libpam -lpam + +# dynamic library names + +LIBNAMED = $(LIBNAME).$(DYNTYPE) +LIBNAMEDNAME = $(LIBNAMED)$(VERSION) +LIBNAMEDFULL = $(LIBNAMEDNAME)$(MODIFICATION) + +# static library name + +LIBNAMEDSTATIC = $(LIBNAME).a + +LIBOBJECTS = help_env.o misc_conv.o + +ifeq ($(DYNAMIC_LIBPAM),yes) +DLIBOBJECTS = $(addprefix dynamic/,$(LIBOBJECTS)) +endif + +ifeq ($(STATIC_LIBPAM),yes) +SLIBOBJECTS = $(addprefix static/,$(LIBOBJECTS)) +endif + +# --------------------------------------------- +## rules + +all: dirs $(LIBNAMED) $(LIBNAMEDSTATIC) + +dirs: +ifeq ($(DYNAMIC_LIBPAM),yes) + $(MKDIR) dynamic +endif +ifeq ($(STATIC_LIBPAM),yes) + $(MKDIR) static +endif + +dynamic/%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +static/%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +$(LIBNAMED): $(DLIBOBJECTS) +ifeq ($(DYNAMIC_LIBPAM),yes) + ifeq ($(USESONAME),yes) + $(LD_L) $(SOSWITCH)$(LIBNAMEDNAME) -o $@ $(DLIBOBJECTS) $(MODULES) $(LINKLIBS) + else + $(LD_L) -o $@ $(DLIBOBJECTS) $(MODULES) $(LINKLIBS) + endif + ifeq ($(NEEDSONAME),yes) + rm -f $(LIBNAMEDFULL) + ln -s $(LIBNAMED) $(LIBNAMEDFULL) + rm -f $(LIBNAMEDNAME) + ln -s $(LIBNAMED) $(LIBNAMEDNAME) + endif +endif + +$(LIBNAMEDSTATIC): $(SLIBOBJECTS) +ifeq ($(STATIC_LIBPAM),yes) + $(AR) rcu $@ $(SLIBOBJECTS) $(MODULES) +ifdef RANLIB + $(AR) rc $@ $(SLIBOBJECTS) $(MODULES) + $(RANLIB) $@ +endif +endif + +install: all + $(MKDIR) $(FAKEROOT)$(INCLUDED) + $(INSTALL) -m 644 include/security/pam_misc.h $(FAKEROOT)$(INCLUDED) +ifeq ($(DYNAMIC_LIBPAM),yes) + $(MKDIR) $(FAKEROOT)$(libdir) + $(INSTALL) -m $(SHLIBMODE) $(LIBNAMED) $(FAKEROOT)$(libdir)/$(LIBNAMEDFULL) +ifndef FAKEROOT + $(LDCONFIG) +else + $(LDCONFIG) -n $(FAKEROOT)$(libdir) +endif + ifneq ($(DYNTYPE),"sl") + ( cd $(FAKEROOT)$(libdir) ; rm -f $(LIBNAMED) ; ln -s $(LIBNAMEDNAME) $(LIBNAMED) ) + endif +endif +ifeq ($(STATIC_LIBPAM),yes) + $(INSTALL) -m 644 $(LIBNAMEDSTATIC) $(FAKEROOT)$(libdir) +endif + +remove: + rm -f $(FAKEROOT)$(INCLUDED)/pam_misc.h + rm -f $(FAKEROOT)$(libdir)/$(LIBNAMEDFULL) + rm -f $(FAKEROOT)$(libdir)/$(LIBNAMED) +ifndef FAKEROOT + $(LDCONFIG) +endif + rm -f $(FAKEROOT)$(libdir)/$(LIBNAMEDSTATIC) + +clean: + rm -f a.out core *~ static/*.o dynamic/*.o + rm -f *.a *.out *.o *.so ./include/security/*~ + rm -f *.orig $(LIBNAMEDNAME) $(LIBNAMEDFULL) + if [ -d dynamic ]; then rmdir dynamic ; fi + if [ -d static ]; then rmdir static ; fi diff --git a/Linux-PAM/libpam_misc/help_env.c b/Linux-PAM/libpam_misc/help_env.c new file mode 100644 index 00000000..e1390984 --- /dev/null +++ b/Linux-PAM/libpam_misc/help_env.c @@ -0,0 +1,105 @@ +/* + * $Id: help_env.c,v 1.2 2000/12/04 19:02:34 baggins Exp $ + * + * This file was written by Andrew G. Morgan <morgan@parc.power.net> + * + */ + +#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/Linux-PAM/libpam_misc/include/security/pam_misc.h b/Linux-PAM/libpam_misc/include/security/pam_misc.h new file mode 100644 index 00000000..66c7ab4a --- /dev/null +++ b/Linux-PAM/libpam_misc/include/security/pam_misc.h @@ -0,0 +1,62 @@ +/* $Id: pam_misc.h,v 1.4 2001/05/01 04:27:37 agmorgan Exp $ */ + +#ifndef __PAMMISC_H +#define __PAMMISC_H + +#include <security/pam_appl.h> +#include <security/pam_client.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* 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)(void *appdata, pamc_bp_t *prompt_p); +extern void (*pam_binary_handler_free)(void *appdata, pamc_bp_t *prompt_p); +/* + * 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); + +#ifdef __cplusplus +} +#endif /* def __cplusplus */ + +#endif /* ndef __PAMMISC_H */ diff --git a/Linux-PAM/libpam_misc/misc_conv.c b/Linux-PAM/libpam_misc/misc_conv.c new file mode 100644 index 00000000..1c66f96c --- /dev/null +++ b/Linux-PAM/libpam_misc/misc_conv.c @@ -0,0 +1,386 @@ +/* + * $Id: misc_conv.c,v 1.6 2004/09/22 12:51:20 kukuk Exp $ + * + * A generic conversation function for text based applications + * + * Written by Andrew Morgan <morgan@linux.kernel.org> + */ + +#include <security/_pam_aconf.h> + +#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 */ + +/* + * These functions are for binary prompt manipulation. + * The manner in which a binary prompt is processed is application + * specific, so these function pointers are provided and can be + * initialized by the application prior to the conversation function + * being used. + */ + +static void pam_misc_conv_delete_binary(void *appdata, + pamc_bp_t *delete_me) +{ + PAM_BP_RENEW(delete_me, 0, 0); +} + +int (*pam_binary_handler_fn)(void *appdata, pamc_bp_t *prompt_p) = NULL; +void (*pam_binary_handler_free)(void *appdata, pamc_bp_t *prompt_p) + = pam_misc_conv_delete_binary; + +/* the following code is used to get text input */ + +static volatile 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 int read_string(int echo, const char *prompt, char **retstr) +{ + struct termios term_before, term_tmp; + char line[INPUTSIZE]; + struct sigaction old_sig; + int delay, nc = -1, have_term = 0; + sigset_t oset, nset; + + 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>")); + *retstr = NULL; + return -1; + } + memcpy(&term_tmp, &term_before, sizeof(term_tmp)); + if (!echo) { + term_tmp.c_lflag &= ~(ECHO); + } + have_term = 1; + + /* + * We make a simple attempt to block TTY signals from terminating + * the conversation without giving PAM a chance to clean up. + */ + + sigemptyset(&nset); + sigaddset(&nset, SIGINT); + sigaddset(&nset, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + + } 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 */ + D(("we got some user input")); + + if (nc > 0 && line[nc-1] == '\n') { /* <NUL> terminate */ + line[--nc] = '\0'; + } else { + if (echo) { + fprintf(stderr, "\n"); + } + line[nc] = '\0'; + } + *retstr = x_strdup(line); + _pam_overwrite(line); + + goto cleanexit; /* return malloc()ed string */ + + } else if (nc == 0) { /* Ctrl-D */ + D(("user did not want to type anything")); + + *retstr = NULL; + if (echo) { + fprintf(stderr, "\n"); + } + goto cleanexit; /* return malloc()ed "" */ + } else if (nc == -1) { + /* Don't loop forever if read() returns -1. */ + D(("error reading input from the user: %s", strerror(errno))); + if (echo) { + fprintf(stderr, "\n"); + } + *retstr = NULL; + goto cleanexit; /* return NULL */ + } + } + } + + /* getting here implies that the timer expired */ + + D(("the timer appears to have expired")); + + *retstr = NULL; + _pam_overwrite(line); + + cleanexit: + + if (have_term) { + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before); + } + + return nc; +} + +/* end of read_string functions */ + +/* + * This conversation function is supposed to be a generic PAM one. + * Unfortunately, it is _not_ completely compatible with the Solaris PAM + * codebase. + * + * Namely, for msgm's that contain multiple prompts, this function + * interprets "const struct pam_message **msgm" as equivalent to + * "const struct pam_message *msgm[]". The Solaris module + * implementation interprets the **msgm object as a pointer to a + * pointer to an array of "struct pam_message" objects (that is, a + * confusing amount of pointer indirection). + */ + +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; + int nc; + + switch (msgm[count]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + nc = read_string(CONV_ECHO_OFF,msgm[count]->msg, &string); + if (nc < 0) { + goto failed_conversation; + } + break; + case PAM_PROMPT_ECHO_ON: + nc = read_string(CONV_ECHO_ON,msgm[count]->msg, &string); + if (nc < 0) { + 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: + { + pamc_bp_t binary_prompt = NULL; + + if (!msgm[count]->msg || !pam_binary_handler_fn) { + goto failed_conversation; + } + + PAM_BP_RENEW(&binary_prompt, + PAM_BP_RCONTROL(msgm[count]->msg), + PAM_BP_LENGTH(msgm[count]->msg)); + PAM_BP_FILL(binary_prompt, 0, PAM_BP_LENGTH(msgm[count]->msg), + PAM_BP_RDATA(msgm[count]->msg)); + + if (pam_binary_handler_fn(appdata_ptr, + &binary_prompt) != PAM_SUCCESS + || (binary_prompt == NULL)) { + goto failed_conversation; + } + string = (char *) binary_prompt; + binary_prompt = 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; + } + } + + *response = reply; + reply = NULL; + + return PAM_SUCCESS; + +failed_conversation: + + D(("the conversation failed")); + + 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(appdata_ptr, + (pamc_bp_t *) &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/Linux-PAM/libpam_misc/xstrdup.c b/Linux-PAM/libpam_misc/xstrdup.c new file mode 100644 index 00000000..cce476e8 --- /dev/null +++ b/Linux-PAM/libpam_misc/xstrdup.c @@ -0,0 +1,31 @@ +/* $Id: xstrdup.c,v 1.1.1.1 2000/06/20 22:11:25 agmorgan Exp $ */ + +#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/Linux-PAM/libpamc/License b/Linux-PAM/libpamc/License new file mode 100644 index 00000000..90106954 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/libpamc/Makefile b/Linux-PAM/libpamc/Makefile new file mode 100644 index 00000000..ee5c2086 --- /dev/null +++ b/Linux-PAM/libpamc/Makefile @@ -0,0 +1,119 @@ +# +# $Id: Makefile,v 1.8 2005/03/29 20:41:20 toady Exp $ +# + +# lots of debugging information goes to /tmp/pam-debug.log +#MOREFLAGS += -D"DEBUG" + +include ../Make.Rules + +ifeq ($(DEBUG_REL),yes) + LIBNAME=libpamcd +else + LIBNAME=libpamc +endif +ifeq ($(WITH_PRELUDE),yes) + CFLAGS += -DPRELUDE -DLIBPRELUDE_CONFIG_PREFIX=\"`libprelude-config --prefix`\" +endif +VERSION=.$(MAJOR_REL) +MODIFICATION=.$(MINOR_REL) + +CFLAGS += $(MOREFLAGS) $(DYNAMIC) $(STATIC) + +# dynamic library names + +LIBNAMED = $(LIBNAME).$(DYNTYPE) +LIBNAMEDNAME = $(LIBNAMED)$(VERSION) +LIBNAMEDFULL = $(LIBNAMEDNAME)$(MODIFICATION) + +# static library name + +LIBNAMEDSTATIC = $(LIBNAME).a + +LIBOBJECTS = pamc_client.o pamc_converse.o pamc_load.o + +ifeq ($(DYNAMIC_LIBPAM),yes) +DLIBOBJECTS = $(addprefix dynamic/,$(LIBOBJECTS)) +endif + +ifeq ($(STATIC_LIBPAM),yes) +SLIBOBJECTS = $(addprefix static/,$(LIBOBJECTS)) +endif + +# --------------------------------------------- +## rules + +all: dirs $(LIBNAMED) $(LIBNAMEDSTATIC) + +dirs: +ifeq ($(DYNAMIC_LIBPAM),yes) + $(MKDIR) dynamic +endif +ifeq ($(STATIC_LIBPAM),yes) + $(MKDIR) static +endif + +dynamic/%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +static/%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +$(LIBNAMED): $(DLIBOBJECTS) +ifeq ($(DYNAMIC_LIBPAM),yes) + ifeq ($(USESONAME),yes) + $(LD_L) $(SOSWITCH)$(LIBNAMEDNAME) -o $@ $(DLIBOBJECTS) $(MODULES) $(LINKLIBS) + else + $(LD_L) -o $@ $(DLIBOBJECTS) $(MODULES) + endif + ifeq ($(NEEDSONAME),yes) + rm -f $(LIBNAMEDFULL) + ln -s $(LIBNAMED) $(LIBNAMEDFULL) + rm -f $(LIBNAMEDNAME) + ln -s $(LIBNAMED) $(LIBNAMEDNAME) + endif +endif + +$(LIBNAMEDSTATIC): $(SLIBOBJECTS) +ifeq ($(STATIC_LIBPAM),yes) + $(AR) rcu $@ $(SLIBOBJECTS) $(MODULES) +ifdef RANLIB + $(RANLIB) $@ +endif +endif + +install: all + $(MKDIR) $(FAKEROOT)$(INCLUDED) + $(INSTALL) -m 644 include/security/pam_client.h $(FAKEROOT)$(INCLUDED) +ifeq ($(DYNAMIC_LIBPAM),yes) + $(MKDIR) $(FAKEROOT)$(libdir) + $(INSTALL) -m $(SHLIBMODE) $(LIBNAMED) $(FAKEROOT)$(libdir)/$(LIBNAMEDFULL) +ifndef FAKEROOT + $(LDCONFIG) +else + $(LDCONFIG) -n $(FAKEROOT)$(libdir) +endif + ifneq ($(DYNTYPE),"sl") + ( cd $(FAKEROOT)$(libdir) ; rm -f $(LIBNAMED) ; ln -s $(LIBNAMEDNAME) $(LIBNAMED) ) + endif +endif +ifeq ($(STATIC_LIBPAM),yes) + $(INSTALL) -m 644 $(LIBNAMEDSTATIC) $(FAKEROOT)$(libdir) +endif + +remove: + rm -f $(FAKEROOT)$(INCLUDED)/pam_client.h + rm -f $(FAKEROOT)$(libdir)/$(LIBNAMEDFULL) + rm -f $(FAKEROOT)$(libdir)/$(LIBNAMED) +ifndef FAKEROOT + $(LDCONFIG) +endif + rm -f $(FAKEROOT)$(libdir)/$(LIBNAMEDSTATIC) + +clean: + rm -f a.out core *~ static/*.o dynamic/*.o + rm -f *.a *.out *.o *.so ./include/security/*~ + rm -f *.orig $(LIBNAMEDNAME) $(LIBNAMEDFULL) + if [ -d dynamic ]; then rmdir dynamic ; fi + if [ -d static ]; then rmdir static ; fi + diff --git a/Linux-PAM/libpamc/include/security/pam_client.h b/Linux-PAM/libpamc/include/security/pam_client.h new file mode 100644 index 00000000..99530322 --- /dev/null +++ b/Linux-PAM/libpamc/include/security/pam_client.h @@ -0,0 +1,197 @@ +/* + * $Id: pam_client.h,v 1.6 2001/09/19 06:18:46 agmorgan Exp $ + * + * 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 + +#ifdef __cplusplus +extern "C" { +#endif /* def __cplusplus */ + +#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_WOCTET(x,y) (*((y) + (__u8 *)(x))) +#define __PAM_BP_ROCTET(x,y) (*((y) + (const __u8 *)(x))) + +#define PAM_BP_MIN_SIZE (sizeof(__u32) + sizeof(__u8)) +#define PAM_BP_MAX_LENGTH 0x20000 /* an advisory limit */ +#define PAM_BP_WCONTROL(x) (__PAM_BP_WOCTET(x,4)) +#define PAM_BP_RCONTROL(x) (__PAM_BP_ROCTET(x,4)) +#define PAM_BP_SIZE(x) ((__PAM_BP_ROCTET(x,0)<<24)+ \ + (__PAM_BP_ROCTET(x,1)<<16)+ \ + (__PAM_BP_ROCTET(x,2)<< 8)+ \ + (__PAM_BP_ROCTET(x,3) )) +#define PAM_BP_LENGTH(x) (PAM_BP_SIZE(x) - PAM_BP_MIN_SIZE) +#define PAM_BP_WDATA(x) (PAM_BP_MIN_SIZE + (__u8 *) (x)) +#define PAM_BP_RDATA(x) (PAM_BP_MIN_SIZE + (const __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; \ + __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_WOCTET(*(old_p), 3) = __size & 0xFF; \ + __PAM_BP_WOCTET(*(old_p), 2) = (__size>>=8) & 0xFF; \ + __PAM_BP_WOCTET(*(old_p), 1) = (__size>>=8) & 0xFF; \ + __PAM_BP_WOCTET(*(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 { \ + size_t 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_WDATA(prompt), (data), (length)); \ +} while (0) + +#define PAM_BP_EXTRACT(prmpt, offset, length, data) \ +do { \ + size_t __bp_length; \ + const __u8 *__prompt = (const __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_RDATA(__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) + +#ifdef __cplusplus +} +#endif /* def __cplusplus */ + +#endif /* PAM_CLIENT_H */ diff --git a/Linux-PAM/libpamc/libpamc.h b/Linux-PAM/libpamc/libpamc.h new file mode 100644 index 00000000..1c9397c9 --- /dev/null +++ b/Linux-PAM/libpamc/libpamc.h @@ -0,0 +1,63 @@ +/* + * $Id: libpamc.h,v 1.2 2000/11/19 23:54:03 agmorgan Exp $ + * + * Copyright (c) Andrew G. Morgan <morgan@ftp.kernel.org> + * + */ + +#ifndef LIBPAMC_H +#define LIBPAMC_H + +#include <security/pam_client.h> +#include <security/_pam_macros.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/Linux-PAM/libpamc/pamc_client.c b/Linux-PAM/libpamc/pamc_client.c new file mode 100644 index 00000000..19eff429 --- /dev/null +++ b/Linux-PAM/libpamc/pamc_client.c @@ -0,0 +1,189 @@ +/* + * $Id: pamc_client.c,v 1.1.1.1 2000/06/20 22:11:25 agmorgan Exp $ + * + * 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/Linux-PAM/libpamc/pamc_converse.c b/Linux-PAM/libpamc/pamc_converse.c new file mode 100644 index 00000000..131789fb --- /dev/null +++ b/Linux-PAM/libpamc/pamc_converse.c @@ -0,0 +1,211 @@ +/* + * $Id: pamc_converse.c,v 1.2 2001/01/20 22:29:47 agmorgan Exp $ + * + * 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_RCONTROL(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/Linux-PAM/libpamc/pamc_load.c b/Linux-PAM/libpamc/pamc_load.c new file mode 100644 index 00000000..01304cc1 --- /dev/null +++ b/Linux-PAM/libpamc/pamc_load.c @@ -0,0 +1,477 @@ +/* + * $Id: pamc_load.c,v 1.1.1.1 2000/06/20 22:11:26 agmorgan Exp $ + * + * 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/Linux-PAM/libpamc/test/agents/secret@here b/Linux-PAM/libpamc/test/agents/secret@here new file mode 100755 index 00000000..afdcbaa8 --- /dev/null +++ b/Linux-PAM/libpamc/test/agents/secret@here @@ -0,0 +1,308 @@ +#!/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; + if ($debug) { + print STDERR "agent: ". "telling md5: <$data>\n"; + } + 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/Linux-PAM/libpamc/test/modules/Makefile b/Linux-PAM/libpamc/test/modules/Makefile new file mode 100644 index 00000000..48065462 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/libpamc/test/modules/pam_secret.c b/Linux-PAM/libpamc/test/modules/pam_secret.c new file mode 100644 index 00000000..95f26014 --- /dev/null +++ b/Linux-PAM/libpamc/test/modules/pam_secret.c @@ -0,0 +1,671 @@ +/* + * $Id: pam_secret.c,v 1.3 2004/09/14 14:22:39 kukuk Exp $ + * + * 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)); + return NULL; + } + + 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_WDATA(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_RDATA(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_RDATA(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_WDATA(new->current_prompt), + PAM_BP_RDATA(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_WDATA(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_RDATA(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_RDATA(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/Linux-PAM/libpamc/test/regress/Makefile b/Linux-PAM/libpamc/test/regress/Makefile new file mode 100644 index 00000000..cba474f9 --- /dev/null +++ b/Linux-PAM/libpamc/test/regress/Makefile @@ -0,0 +1,7 @@ +CFLAGS = -g -I ../../include + +test.libpamc: test.libpamc.o + $(CC) -o $@ $(CFLAGS) $< -L ../.. -lpamc + +clean: + rm -f test.libpamc test.libpamc.o diff --git a/Linux-PAM/libpamc/test/regress/run_test.sh b/Linux-PAM/libpamc/test/regress/run_test.sh new file mode 100755 index 00000000..6922f03d --- /dev/null +++ b/Linux-PAM/libpamc/test/regress/run_test.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +LD_LIBRARY_PATH=../.. ; export LD_LIBRARY_PATH +PAMC_AGENT_PATH="../agents" ; export PAMC_AGENT_PATH + +./test.libpamc diff --git a/Linux-PAM/libpamc/test/regress/test.libpamc.c b/Linux-PAM/libpamc/test/regress/test.libpamc.c new file mode 100644 index 00000000..b7bc4e4b --- /dev/null +++ b/Linux-PAM/libpamc/test/regress/test.libpamc.c @@ -0,0 +1,342 @@ +/* + * 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, "echo -n '", 0); + append_string(&temp_packet, raw, 0); + append_string(&temp_packet, "'|/usr/bin/md5sum -", 1); + + fprintf(stderr, "am attempting to run [%s]\n", temp_packet.buffer); + + 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); + + fprintf(stderr, "server received[%d]: {%d|0x%.2x|%s}\n", + data_length, + PAM_BP_SIZE(prompt), PAM_BP_RCONTROL(prompt), + PAM_BP_RDATA(prompt)); +} + +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_RCONTROL(prompt) != PAM_BPC_OK) { + fprintf(stderr, "server: prompt had unexpected control type: %u\n", + PAM_BP_RCONTROL(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_RCONTROL(prompt) != PAM_BPC_DONE) { + fprintf(stderr, "server: 2nd prompt had unexpected control type: %u\n", + PAM_BP_RCONTROL(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/Linux-PAM/libpamc/test/regress/test.secret@here b/Linux-PAM/libpamc/test/regress/test.secret@here new file mode 100755 index 00000000..2e0b9b94 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/Makefile b/Linux-PAM/modules/Makefile new file mode 100644 index 00000000..58be5a24 --- /dev/null +++ b/Linux-PAM/modules/Makefile @@ -0,0 +1,58 @@ +# $Id: Makefile,v 1.4 2004/09/28 13:48:47 kukuk Exp $ +# +# Makefile +# +# This makefile controls the build process of shared and static PAM modules. +# +# + +include ../Make.Rules + +MODDIRS=$(shell /bin/ls -d pam_*/Makefile | cut -f1 -d/) + +all: + @echo building the static modutil library + make -C pammodutil all + @echo modules sources available 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 + make -C pammodutil clean diff --git a/Linux-PAM/modules/README b/Linux-PAM/modules/README new file mode 100644 index 00000000..73d3cf0c --- /dev/null +++ b/Linux-PAM/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@linux.kernel.org>, 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/Linux-PAM/modules/Simple.Rules b/Linux-PAM/modules/Simple.Rules new file mode 100644 index 00000000..57b582de --- /dev/null +++ b/Linux-PAM/modules/Simple.Rules @@ -0,0 +1,109 @@ +# $Id: Simple.Rules,v 1.7 2004/09/28 13:48:47 kukuk Exp $ +# +# For simple modules with no significant dependencies, set $(TITLE) +# and include this file. +# +# There are a few ways to customize this set of rules. Namely, define +# +# $(MODULE_SIMPLE_EXTRACLEAN) +# $(MODULE_SIMPLE_CLEAN) +# $(MODULE_SIMPLE_REMOVE) +# $(MODULE_SIMPLE_INSTALL) +# $(MODULE_SIMPLE_EXTRALIBS) - other things to link with the module +# $(MODULE_SIMPLE_EXTRAFILES) - other files to build (no .c suffix) +# + +-include ../Make.Rules + +LIBFILES = $(TITLE) $(MODULE_SIMPLE_EXTRAFILES) +LIBSRC = $(addsuffix .c,$(LIBFILES)) +LIBOBJ = $(addsuffix .o,$(LIBFILES)) +LIBOBJD = $(addprefix dynamic/,$(LIBOBJ)) +LIBOBJS = $(addprefix static/,$(LIBOBJ)) + +LINK_PAMMODUTILS = -L../pammodutil -lpammodutil -L../../libpam -lpam +INCLUDE_PAMMODUTILS = -I../pammodutil/include + +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) $(INCLUDE_PAMMODUTILS) $(DYNAMIC) $(TARGET_ARCH) -c $< -o $@ + +static/%.o : %.c + $(CC) $(CFLAGS) $(INCLUDE_PAMMODUTILS) $(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) $(MODULE_SIMPLE_EXTRALIBS) $(NEED_LINK_LIB_C) $(LINK_PAMMODUTILS) + +endif + +ifdef STATIC +$(LIBOBJS): $(LIBSRC) +endif + +ifdef STATIC +$(LIBSTATIC): $(LIBOBJS) + $(LD) -r -o $@ $(LIBOBJS) $(MODULE_SIMPLE_EXTRALIBS) $(LINK_PAMMODUTILS) +endif + +install: all + $(MKDIR) $(FAKEROOT)$(SECUREDIR) +ifdef DYNAMIC + $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR) +endif +ifdef MAN3 + test -d $(FAKEROOT)$(mandir)/man3 || $(MKDIR) $(FAKEROOT)$(mandir)/man3 + $(INSTALL) -m $(MANMODE) $(MAN3) $(FAKEROOT)$(mandir)/man3/ +endif +ifdef MAN5 + test -d $(FAKEROOT)$(mandir)/man5 || $(MKDIR) $(FAKEROOT)$(mandir)/man5 + $(INSTALL) -m $(MANMODE) $(MAN5) $(FAKEROOT)$(mandir)/man5/ +endif +ifdef MAN8 + test -d $(FAKEROOT)$(mandir)/man8 || $(MKDIR) $(FAKEROOT)$(mandir)/man8 + $(INSTALL) -m $(MANMODE) $(MAN8) $(FAKEROOT)$(mandir)/man8/ +endif + $(MODULE_SIMPLE_INSTALL) + +remove: + rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so + $(MODULE_SIMPLE_REMOVE) + +clean: + rm -f $(LIBOBJD) $(LIBOBJS) core *~ + $(MODULE_SIMPLE_CLEAN) + rm -f *.a *.o *.so *.bak + rm -rf dynamic static + $(MODULE_SIMPLE_EXTRACLEAN) + +.c.o: + $(CC) $(CFLAGS) -c $< + diff --git a/Linux-PAM/modules/dont_makefile b/Linux-PAM/modules/dont_makefile new file mode 100644 index 00000000..48307f02 --- /dev/null +++ b/Linux-PAM/modules/dont_makefile @@ -0,0 +1,21 @@ +######################################################################### +# $Id: dont_makefile,v 1.2 2000/11/19 23:54:03 agmorgan Exp $ +######################################################################### +# 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" + +remove: clean + +install: clean + +clean: + @echo "Nothing to do" + +######################################################################### +# all over.. +######################################################################### diff --git a/Linux-PAM/modules/download-all b/Linux-PAM/modules/download-all new file mode 100755 index 00000000..9b6cf655 --- /dev/null +++ b/Linux-PAM/modules/download-all @@ -0,0 +1,30 @@ +#!/bin/sh +# +# $Id: download-all,v 1.1.1.1 2000/06/20 22:11:29 agmorgan Exp $ +# +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/Linux-PAM/modules/install_conf b/Linux-PAM/modules/install_conf new file mode 100755 index 00000000..80f6be29 --- /dev/null +++ b/Linux-PAM/modules/install_conf @@ -0,0 +1,49 @@ +#!/bin/bash + +FAKEROOT=$1 +CONFD=$1$2 +CONFILE=$1$3 +MODULE=$4 +CONF=$5 + +IGNORE_AGE=./.ignore_age +QUIET_INSTALL=../../.quiet_install + +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 $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/Linux-PAM/modules/pam_access/Makefile b/Linux-PAM/modules/pam_access/Makefile new file mode 100644 index 00000000..d8a71ee5 --- /dev/null +++ b/Linux-PAM/modules/pam_access/Makefile @@ -0,0 +1,24 @@ +# $Id: Makefile,v 1.3 2003/07/13 18:41:04 vorlon Exp $ +# +# 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!). +# + +include ../../Make.Rules + +TITLE=pam_access +LOCAL_CONFILE=./access.conf +INSTALLED_CONFILE=$(SCONFIGD)/access.conf +ifeq ($(HAVE_LIBNSL),yes) +MODULE_SIMPLE_EXTRALIBS=-lnsl +endif + +DEFS=-DDEFAULT_CONF_FILE=\"$(INSTALLED_CONFILE)\" +CFLAGS += $(DEFS) + +MODULE_SIMPLE_INSTALL=bash -f ../install_conf "$(FAKEROOT)" "$(SCONFIGD)" "$(INSTALLED_CONFILE)" "$(TITLE)" "$(LOCAL_CONFILE)" +MODULE_SIMPLE_REMOVE=rm -f $(FAKEROOT)$(INSTALLED_CONFILE) +MODULE_SIMPLE_CLEAN=rm -f ./.ignore_age + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_access/README b/Linux-PAM/modules/pam_access/README new file mode 100644 index 00000000..ddd4725f --- /dev/null +++ b/Linux-PAM/modules/pam_access/README @@ -0,0 +1,44 @@ +# Description of its configuration file +# +# (The default config file is "/etc/security/access.conf". This +# default can be overridden with a module config argument +# 'accessfile=<full-path>'): +# +# Login access control table. +# +# When someone logs in, the table is scanned for the first entry that +# matches the (user, host) combination, or, in case of non-networked +# logins, the first entry that matches the (user, tty) combination. The +# permissions field of that table entry determines whether the login will +# be accepted or refused. +# +# Format of the login access control table is three fields separated by a +# ":" character: +# +# permission : users : origins +# +# The first field should be a "+" (access granted) or "-" (access denied) +# character. +# +# The second field should be a list of one or more login names, group +# names, or ALL (always matches). A pattern of the form user@host is +# matched when the login name matches the "user" part, and when the +# "host" part matches the local machine name. +# +# The third field should be a list of one or more tty names (for +# non-networked logins), host names, domain names (begin with "."), host +# addresses, internet network numbers (end with "."), ALL (always +# matches) 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/Linux-PAM/modules/pam_access/access.conf b/Linux-PAM/modules/pam_access/access.conf new file mode 100644 index 00000000..cec2be0c --- /dev/null +++ b/Linux-PAM/modules/pam_access/access.conf @@ -0,0 +1,65 @@ +# 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: +# +# [Note, if you supply a 'fieldsep=|' argument to the pam_access.so +# module, you can change the field separation character to be +# '|'. This is useful for configurations where you are trying to use +# pam_access with X applications that provide PAM_TTY values that are +# the display variable like "host:0".] +# +# 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. +# +# TTY NAMES: Must be in the form returned by ttyname(3) less the initial +# "/dev" (e.g. tty1 or vc/1) +# +############################################################################## +# +# Disallow non-root logins on tty1 +# +#-:ALL EXCEPT root:tty1 +# +# Disallow console logins to all but a few accounts. +# +#-:ALL EXCEPT wheel shutdown sync:LOCAL +# +# 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/Linux-PAM/modules/pam_access/pam_access.c b/Linux-PAM/modules/pam_access/pam_access.c new file mode 100644 index 00000000..4f6cf574 --- /dev/null +++ b/Linux-PAM/modules/pam_access/pam_access.c @@ -0,0 +1,490 @@ +/* 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.) + * + ************************************************************************ + * Copyright message from logdaemon-5.6 (original file name DISCLAIMER) + ************************************************************************ + * Copyright 1995 by Wietse Venema. All rights reserved. Individual files + * may be covered by other copyrights (as noted in the file itself.) + * + * This material was originally written and compiled by Wietse Venema at + * Eindhoven University of Technology, The Netherlands, in 1990, 1991, + * 1992, 1993, 1994 and 1995. + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all such + * copies. + * + * This software is provided "as is" and without any expressed or implied + * warranties, including, without limitation, the implied warranties of + * merchantibility and fitness for any particular purpose. + ************************************************************************* + */ + +#include <security/_pam_aconf.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#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> +#include <rpcsvc/ypclnt.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> +#include <security/_pam_modutil.h> + +/* 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 + +#ifdef DEFAULT_CONF_FILE +# define PAM_ACCESS_CONFIG DEFAULT_CONF_FILE +#else +# define PAM_ACCESS_CONFIG "/etc/security/access.conf" +#endif + + /* Delimiters for fields and for lists of users, ttys or hosts. */ + +static const char *fs = ":"; /* field separator */ +static const 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; + const char *config_file; + const char *service; +}; + +/* --- 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(); +} + +/* Parse module config arguments */ + +static int parse_args(struct login_info *loginfo, int argc, const char **argv) +{ + int i; + + for (i=0; i<argc; ++i) { + if (!strncmp("fieldsep=", argv[i], 9)) { + + /* the admin wants to override the default field separators */ + fs = argv[i]+9; + + } else if (!strncmp("accessfile=", argv[i], 11)) { + FILE *fp = fopen(11 + argv[i], "r"); + + if (fp) { + loginfo->config_file = 11 + argv[i]; + fclose(fp); + } else { + _log_err("for service [%s] failed to open accessfile=[%s]" + , loginfo->service, 11 + argv[i]); + return 0; + } + + } else { + _log_err("unrecognized option [%s]", argv[i]); + } + } + + return 1; /* OK */ +} + +typedef int match_func (pam_handle_t *, char *, struct login_info *); + +static int list_match (pam_handle_t *, char *, struct login_info *, + match_func *); +static int user_match (pam_handle_t *, char *, struct login_info *); +static int from_match (pam_handle_t *, char *, struct login_info *); +static int string_match (pam_handle_t *, char *, char *); + +/* login_access - match username/group and host/tty with access control file */ + +static int +login_access (pam_handle_t *pamh, 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 */ + + /* + * 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(item->config_file, "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", + item->config_file, 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; + + /* Allow trailing: in last field fo froms */ + if (!(perm = strtok(line, fs)) + || !(users = strtok((char *) 0, fs)) + || !(froms = strtok((char *) 0, fs))) { + _log_err("%s: line %d: bad field count", + item->config_file, lineno); + continue; + } + if (perm[0] != '+' && perm[0] != '-') { + _log_err("%s: line %d: bad first field", + item->config_file, lineno); + continue; + } + match = (list_match(pamh, froms, item, from_match) + && list_match(pamh, users, item, user_match)); + } + (void) fclose(fp); + } else if (errno != ENOENT) { + _log_err("cannot open %s: %m", item->config_file); + return NO; + } + return (match == 0 || (line[0] == '+')); +} + +/* list_match - match an item against a list of tokens with exceptions */ + +static int list_match(pam_handle_t *pamh, + 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) (pamh, 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(pamh, (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]; + + if (gethostname(name, MAXHOSTNAMELEN) == 0) { + name[MAXHOSTNAMELEN] = 0; + return (name); + } + return NULL; +} + +/* netgroup_match - match group against machine or user */ + +static int netgroup_match(char *group, char *machine, char *user) +{ + static char *mydomain = NULL; + + if (mydomain == 0) + yp_get_default_domain(&mydomain); + return (innetgr(group, machine, user, mydomain)); +} + +/* user_match - match a username against one token */ + +static int user_match(pam_handle_t *pamh, char *tok, struct login_info *item) +{ + char *string = item->user->pw_name; + struct login_info fake_item; + 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(); + if (fake_item.from == NULL) + return NO; + return (user_match (pamh, tok, item) && from_match (pamh, at + 1, &fake_item)); + } else if (tok[0] == '@') /* netgroup */ + return (netgroup_match(tok + 1, (char *) 0, string)); + else if (string_match (pamh, tok, string)) /* ALL or exact match */ + return YES; + else if (_pammodutil_user_in_group_nam_nam (pamh, item->user->pw_name, tok)) + /* try group membership */ + return YES; + + return NO; +} + +/* from_match - match a host or tty against a list of tokens */ + +static int +from_match (pam_handle_t *pamh, 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 (pamh, 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+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 (pam_handle_t *pamh, 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); +} + +/* --- public account management functions --- */ + +PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc + ,const char **argv) +{ + struct login_info loginfo; + const char *user=NULL, *service=NULL; + char *from=NULL; + struct passwd *user_pw; + + if ((pam_get_item(pamh, PAM_SERVICE, (const void **)&service) + != PAM_SUCCESS) || (service == NULL) || (*service == ' ')) { + _log_err("cannot find the 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; + } + + /* 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) || (*from=='\0')) { + + /* 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 (from[0] == '/') { /* full path */ + from++; + from = strchr(from, '/'); + from++; + } + } + + if ((user_pw=_pammodutil_getpwnam(pamh, user))==NULL) return (PAM_USER_UNKNOWN); + + /* + * Bundle up the arguments to avoid unnecessary clumsiness later on. + */ + loginfo.user = user_pw; + loginfo.from = from; + loginfo.service = service; + loginfo.config_file = PAM_ACCESS_CONFIG; + + /* parse the argument list */ + + if (!parse_args(&loginfo, argc, argv)) { + _log_err("failed to parse the module arguments"); + return PAM_ABORT; + } + + if (login_access(pamh, &loginfo)) { + 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/Linux-PAM/modules/pam_cracklib/Makefile b/Linux-PAM/modules/pam_cracklib/Makefile new file mode 100644 index 00000000..371ac0a8 --- /dev/null +++ b/Linux-PAM/modules/pam_cracklib/Makefile @@ -0,0 +1,32 @@ +# +# $Id: Makefile,v 1.3 2001/02/10 22:15:23 agmorgan Exp $ +# +# 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@kernel.org> 2000/10/08 +# + +include ../../Make.Rules + +TITLE=pam_cracklib + +ifeq ($(HAVE_LIBCRACK),yes) +BUILD_THIS_MODULE=yes +MODULE_SIMPLE_EXTRALIBS=-lcrack + +# These two should really be provided by ../../pam_aconf.h +CFLAGS+=-DCRACKLIB_DICTPATH=\"$(CRACKLIB_DICTPATH)\" + +ifeq ($(HAVE_LIBCRYPT),yes) + MODULE_SIMPLE_EXTRALIBS += -lcrypt +endif + +endif + +ifeq ($(BUILD_THIS_MODULE),yes) + include ../Simple.Rules +else + include ../dont_makefile +endif diff --git a/Linux-PAM/modules/pam_cracklib/README b/Linux-PAM/modules/pam_cracklib/README new file mode 100644 index 00000000..69662f73 --- /dev/null +++ b/Linux-PAM/modules/pam_cracklib/README @@ -0,0 +1,37 @@ + +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. + + difok=N How many characters can be the same in the new + password relative to the old + difignore=N How many characters long should the password be + before we ignore difok. + + minlen=N The minimum simplicity count for a good password. + + dcredit=N + ucredit=N + lcredit=N + ocredit=N Weight, digits, upper, lower, other characters with + count N. Use these values to compute the + 'unsimplicity' of the password. + + use_authtok Get the proposed password from PAM_AUTHTOK + +MODULE SERVICES PROVIDED: + passwd chauthtok + +AUTHOR: + Cristian Gafton <gafton@redhat.com> + diff --git a/Linux-PAM/modules/pam_cracklib/pam_cracklib.c b/Linux-PAM/modules/pam_cracklib/pam_cracklib.c new file mode 100644 index 00000000..8f3e4c42 --- /dev/null +++ b/Linux-PAM/modules/pam_cracklib/pam_cracklib.c @@ -0,0 +1,891 @@ +/* + * pam_cracklib module + * $Id: pam_cracklib.c,v 1.9 2004/09/15 12:06:17 kukuk Exp $ + */ + +/* + * 0.9. switch to using a distance algorithm in similar() + * 0.86. added support for setting minimum numbers of digits, uppers, + * lowers, and others + * 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. + */ + +#include <security/_pam_aconf.h> + +#include <stdio.h> +#ifdef HAVE_CRYPT_H +# 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%spassword: " +#define PROMPT2 "Retype new %s%spassword: " +#define MISTYPED_PASS "Sorry, passwords do not match" + +#ifdef MIN +#undef MIN +#endif +#define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) + +/* + * 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 + +struct cracklib_options { + int retry_times; + int diff_ok; + int diff_ignore; + int min_length; + int dig_credit; + int up_credit; + int low_credit; + int oth_credit; + int use_authtok; + char prompt_type[BUFSIZ]; +}; + +#define CO_RETRY_TIMES 1 +#define CO_DIFF_OK 5 +#define CO_DIFF_IGNORE 23 +#define CO_MIN_LENGTH 9 +# define CO_MIN_LENGTH_BASE 5 +#define CO_DIG_CREDIT 1 +#define CO_UP_CREDIT 1 +#define CO_LOW_CREDIT 1 +#define CO_OTH_CREDIT 1 +#define CO_USE_AUTHTOK 0 + +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)) + strncpy(opt->prompt_type, *argv+5, sizeof(opt->prompt_type) - 1); + else if (!strncmp(*argv,"retry=",6)) { + opt->retry_times = strtol(*argv+6,&ep,10); + if (!ep || (opt->retry_times < 1)) + opt->retry_times = CO_RETRY_TIMES; + } else if (!strncmp(*argv,"difok=",6)) { + opt->diff_ok = strtol(*argv+6,&ep,10); + if (!ep || (opt->diff_ok < 0)) + opt->diff_ok = CO_DIFF_OK; + } else if (!strncmp(*argv,"difignore=",10)) { + opt->diff_ignore = strtol(*argv+10,&ep,10); + if (!ep || (opt->diff_ignore < 0)) + opt->diff_ignore = CO_DIFF_IGNORE; + } else if (!strncmp(*argv,"minlen=",7)) { + opt->min_length = strtol(*argv+7,&ep,10); + if (!ep || (opt->min_length < CO_MIN_LENGTH_BASE)) + opt->min_length = CO_MIN_LENGTH_BASE; + } else if (!strncmp(*argv,"dcredit=",8)) { + opt->dig_credit = strtol(*argv+8,&ep,10); + if (!ep) + opt->dig_credit = 0; + } else if (!strncmp(*argv,"ucredit=",8)) { + opt->up_credit = strtol(*argv+8,&ep,10); + if (!ep) + opt->up_credit = 0; + } else if (!strncmp(*argv,"lcredit=",8)) { + opt->low_credit = strtol(*argv+8,&ep,10); + if (!ep) + opt->low_credit = 0; + } else if (!strncmp(*argv,"ocredit=",8)) { + opt->oth_credit = strtol(*argv+8,&ep,10); + if (!ep) + 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); + } + } + opt->prompt_type[sizeof(opt->prompt_type) - 1] = '\0'; + + 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 = NULL; + + retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv); + + if ( retval == PAM_SUCCESS && conv ) { + 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)); + if ( retval == PAM_SUCCESS ) + retval = PAM_BAD_ITEM; /* conv was NULL */ + } + + 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; +} + +/* + * Calculate how different two strings are in terms of the number of + * character removals, additions, and changes needed to go from one to + * the other + */ + +static int distdifferent(const char *old, const char *new, int i, int j) +{ + char c, d; + + if ((i == 0) || (strlen(old) < i)) { + c = 0; + } else { + c = old[i - 1]; + } + if ((j == 0) || (strlen(new) < j)) { + d = 0; + } else { + d = new[j - 1]; + } + return (c != d); +} + +static int distcalculate(int **distances, const char *old, const char *new, + int i, int j) +{ + int tmp = 0; + + if (distances[i][j] != -1) { + return distances[i][j]; + } + + tmp = distcalculate(distances, old, new, i - 1, j - 1); + tmp = MIN(tmp, distcalculate(distances, old, new, i, j - 1)); + tmp = MIN(tmp, distcalculate(distances, old, new, i - 1, j)); + tmp += distdifferent(old, new, i, j); + + distances[i][j] = tmp; + + return tmp; +} + +static int distance(const char *old, const char *new) +{ + int **distances = NULL; + int m, n, i, j, r; + + m = strlen(old); + n = strlen(new); + distances = malloc(sizeof(int*) * (m + 1)); + + for (i = 0; i <= m; i++) { + distances[i] = malloc(sizeof(int) * (n + 1)); + for(j = 0; j <= n; j++) { + distances[i][j] = -1; + } + } + for (i = 0; i <= m; i++) { + distances[i][0] = i; + } + for (j = 0; j <= n; j++) { + distances[0][j] = j; + } + distances[0][0] = 0; + + r = distcalculate(distances, old, new, m, n); + + for (i = 0; i <= m; i++) { + memset(distances[i], 0, sizeof(int) * (n + 1)); + free(distances[i]); + } + free(distances); + + return r; +} + +static int similar(struct cracklib_options *opt, + const char *old, const char *new) +{ + if (distance(old, new) >= opt->diff_ok) { + return 0; + } + + if (strlen(new) >= (strlen(old) * 2)) { + return 0; + } + + /* passwords are too similar */ + 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 ((opt->dig_credit >= 0) && (digits > opt->dig_credit)) + digits = opt->dig_credit; + + if ((opt->up_credit >= 0) && (uppers > opt->up_credit)) + uppers = opt->up_credit; + + if ((opt->low_credit >= 0) && (lowers > opt->low_credit)) + lowers = opt->low_credit; + + if ((opt->oth_credit >= 0) && (others > opt->oth_credit)) + others = opt->oth_credit; + + size = opt->min_length; + + if (opt->dig_credit >= 0) + size -= digits; + else if (digits < opt->dig_credit * -1) + return 1; + + if (opt->up_credit >= 0) + size -= uppers; + else if (uppers < opt->up_credit * -1) + return 1; + + if (opt->low_credit >= 0) + size -= lowers; + else if (lowers < opt->low_credit * -1) + return 1; + + if (opt->oth_credit >= 0) + size -= others; + else if (others < opt->oth_credit * -1) + return 1; + + 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 && similar(opt, oldmono, newmono)) + msg = "is too similar 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 || user == NULL) { + 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)); + snprintf(remark,sizeof(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; + + D(("called.")); + + memset(&options, 0, sizeof(options)); + options.retry_times = CO_RETRY_TIMES; + options.diff_ok = CO_DIFF_OK; + options.diff_ignore = CO_DIFF_IGNORE; + options.min_length = CO_MIN_LENGTH; + options.dig_credit = CO_DIG_CREDIT; + options.up_credit = CO_UP_CREDIT; + options.low_credit = CO_LOW_CREDIT; + options.oth_credit = CO_OTH_CREDIT; + options.use_authtok = CO_USE_AUTHTOK; + memset(options.prompt_type, 0, BUFSIZ); + strcpy(options.prompt_type,"UNIX"); + + ctrl = _pam_parse(&options, argc, argv); + + 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 */ + snprintf(buf,sizeof(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)); + snprintf(prompt,sizeof(prompt),PROMPT1, + options.prompt_type, options.prompt_type[0]?" ":""); + 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); + snprintf(remark,sizeof(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)); + snprintf(prompt,sizeof(prompt),PROMPT2, + options.prompt_type, options.prompt_type[0]?" ":""); + 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/Linux-PAM/modules/pam_debug/Makefile b/Linux-PAM/modules/pam_debug/Makefile new file mode 100644 index 00000000..56e9a14e --- /dev/null +++ b/Linux-PAM/modules/pam_debug/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.1 2001/10/10 05:00:11 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_debug + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_debug/README b/Linux-PAM/modules/pam_debug/README new file mode 100644 index 00000000..85401651 --- /dev/null +++ b/Linux-PAM/modules/pam_debug/README @@ -0,0 +1,15 @@ +# $Id: README,v 1.1 2001/10/10 05:00:11 agmorgan Exp $ +# + +This module returns what its module arguments tell it to return. It +can be used for debugging libpam and/or an application. + +Here are some example ways to use it: + +auth requisite pam_permit.so +auth [success=2 default=ok] pam_debug.so auth=perm_denied cred=success +auth [default=reset] pam_debug.so auth=success cred=perm_denied +auth [success=done default=die] pam_debug.so +auth optional pam_debug.so auth=perm_denied cred=perm_denied +auth sufficient pam_debug.so auth=success cred=success + diff --git a/Linux-PAM/modules/pam_debug/pam_debug.c b/Linux-PAM/modules/pam_debug/pam_debug.c new file mode 100644 index 00000000..819cd651 --- /dev/null +++ b/Linux-PAM/modules/pam_debug/pam_debug.c @@ -0,0 +1,177 @@ +/* pam_permit module */ + +/* + * $Id: pam_debug.c,v 1.2 2004/09/15 12:06:17 kukuk Exp $ + * + * Written by Andrew Morgan <morgan@kernel.org> 2001/02/04 + * + */ + +#define DEFAULT_USER "nobody" + +#include <stdio.h> + +/* + * This module is intended as a debugging aide for determining how + * the PAM stack is operating. + * + * 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> + +#define _PAM_ACTION_UNDEF (-10) +#include "../../libpam/pam_tokens.h" + +/* --- authentication management functions --- */ + +static int state(pam_handle_t *pamh, const char *text) +{ + int retval; + struct pam_conv *conv; + struct pam_message msg[1], *mesg[1]; + struct pam_response *response; + + retval = pam_get_item(pamh, PAM_CONV, (const void **)&conv); + if ((retval != PAM_SUCCESS) || (conv == NULL)) { + D(("failed to obtain conversation function")); + return PAM_ABORT; + } + + msg[0].msg_style = PAM_TEXT_INFO; + msg[0].msg = text; + mesg[0] = &msg[0]; + + retval = conv->conv(1, (const struct pam_message **) mesg, + &response, conv->appdata_ptr); + if (retval != PAM_SUCCESS) { + D(("conversation failed")); + } + + return retval; +} + +static int parse_args(int retval, const char *event, + pam_handle_t *pamh, int argc, const char **argv) +{ + int i; + + for (i=0; i<argc; ++i) { + int length = strlen(event); + if (!strncmp(event, argv[i], length) && (argv[i][length] == '=')) { + int j; + const char *return_string = argv[i] + (length+1); + + for (j=0; j<_PAM_RETURN_VALUES; ++j) { + if (!strcmp(return_string, _pam_token_returns[j])) { + retval = j; + state(pamh, argv[i]); + break; + } + } + break; + } + } + + return retval; +} + +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")); + retval = pam_set_item(pamh, PAM_USER, (const void *) DEFAULT_USER); + if (retval != PAM_SUCCESS) + return retval; + } + user = NULL; /* clean up */ + + retval = parse_args(PAM_SUCCESS, "auth", pamh, argc, argv); + + return retval; +} + +PAM_EXTERN +int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return parse_args(PAM_SUCCESS, "cred", pamh, argc, argv); +} + +/* --- account management functions --- */ + +PAM_EXTERN +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return parse_args(PAM_SUCCESS, "acct", pamh, argc, argv); +} + +/* --- password management --- */ + +PAM_EXTERN +int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + if (flags & PAM_PRELIM_CHECK) { + return parse_args(PAM_SUCCESS, "prechauthtok", pamh, argc, argv); + } else { + return parse_args(PAM_SUCCESS, "chauthtok", pamh, argc, argv); + } +} + +/* --- session management --- */ + +PAM_EXTERN +int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc, + const char **argv) +{ + return parse_args(PAM_SUCCESS, "open_session", pamh, argc, argv); +} + +PAM_EXTERN +int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc + ,const char **argv) +{ + return parse_args(PAM_SUCCESS, "close_session", pamh, argc, argv); +} + +/* end of module definition */ + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_permit_modstruct = { + "pam_debug", + 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/Linux-PAM/modules/pam_deny/Makefile b/Linux-PAM/modules/pam_deny/Makefile new file mode 100644 index 00000000..2fdd6e11 --- /dev/null +++ b/Linux-PAM/modules/pam_deny/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:03 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_deny + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_deny/README b/Linux-PAM/modules/pam_deny/README new file mode 100644 index 00000000..2eb96d4e --- /dev/null +++ b/Linux-PAM/modules/pam_deny/README @@ -0,0 +1,4 @@ +# $Id: README,v 1.1.1.1 2000/06/20 22:11:33 agmorgan Exp $ +# + +this module always fails, it ignores all options. diff --git a/Linux-PAM/modules/pam_deny/pam_deny.c b/Linux-PAM/modules/pam_deny/pam_deny.c new file mode 100644 index 00000000..8be1a8a8 --- /dev/null +++ b/Linux-PAM/modules/pam_deny/pam_deny.c @@ -0,0 +1,81 @@ +/* pam_permit module */ + +/* + * $Id: pam_deny.c,v 1.2 2000/12/04 19:02:34 baggins Exp $ + * + * Written by Andrew Morgan <morgan@parc.power.net> 1996/3/11 + * + */ + +/* + * 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/Linux-PAM/modules/pam_env/Makefile b/Linux-PAM/modules/pam_env/Makefile new file mode 100644 index 00000000..189f1ee3 --- /dev/null +++ b/Linux-PAM/modules/pam_env/Makefile @@ -0,0 +1,22 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:03 agmorgan Exp $ +# +# 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!). +# + +include ../../Make.Rules + +TITLE=pam_env +LOCAL_CONFILE=./pam_env.conf-example +INSTALLED_CONFILE=$(SCONFIGD)/pam_env.conf + +DEFS=-DDEFAULT_CONF_FILE=\"$(INSTALLED_CONFILE)\" +CFLAGS += $(DEFS) + +MODULE_SIMPLE_INSTALL=bash -f ../install_conf "$(FAKEROOT)" "$(SCONFIGD)" "$(INSTALLED_CONFILE)" "$(TITLE)" "$(LOCAL_CONFILE)" +MODULE_SIMPLE_REMOVE=rm -f $(FAKEROOT)$(INSTALLED_CONFILE) +MODULE_SIMPLE_CLEAN=rm -f ./.ignore_age + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_env/README b/Linux-PAM/modules/pam_env/README new file mode 100644 index 00000000..5053618a --- /dev/null +++ b/Linux-PAM/modules/pam_env/README @@ -0,0 +1,72 @@ +# $Date: 2000/06/20 22:11:33 $ +# $Author: agmorgan $ +# $Id: README,v 1.1.1.1 2000/06/20 22:11:33 agmorgan Exp $ +# +# 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/Linux-PAM/modules/pam_env/pam_env.c b/Linux-PAM/modules/pam_env/pam_env.c new file mode 100644 index 00000000..a3cf7684 --- /dev/null +++ b/Linux-PAM/modules/pam_env/pam_env.c @@ -0,0 +1,843 @@ +/* pam_mail module */ + +/* + * $Id: pam_env.c,v 1.5 2004/09/15 12:06:17 kukuk Exp $ + * + * Written by Dave Kinchlea <kinch@kinch.ark.com> 1997/01/31 + * Inspired by Andrew Morgan <morgan@kernel.org>, 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 0 + +#include <security/_pam_aconf.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 declarations */ + 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 + */ + + /* No unexpanded variable can be bigger than BUF_SIZE */ + char type, tmpval[BUF_SIZE]; + + /* I know this shouldn't be hard-coded but it's so much easier this way */ + char tmp[MAX_ENV]; + + D(("Remember to initialize tmp!")); + memset(tmp, 0, MAX_ENV); + + /* + * (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, sizeof(tmpval)); + tmpval[sizeof(tmpval)-1] = '\0'; + 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 && retval == PAM_SUCCESS) + retval = _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 && retval == PAM_SUCCESS) + retval = _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/Linux-PAM/modules/pam_env/pam_env.conf-example b/Linux-PAM/modules/pam_env/pam_env.conf-example new file mode 100644 index 00000000..612a31c2 --- /dev/null +++ b/Linux-PAM/modules/pam_env/pam_env.conf-example @@ -0,0 +1,76 @@ +# $Date: 2004/11/16 14:27:41 $ +# $Author: toady $ +# $Id: pam_env.conf-example,v 1.2 2004/11/16 14:27:41 toady Exp $ +# +# 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. +# Note that many environment variables that you would like to use +# may not be set by the time the module is called. +# For example, HOME is used below several times, but +# many PAM applications don't make it available by the time you need it. +# +# +# 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/Linux-PAM/modules/pam_filter/.upperLOWER b/Linux-PAM/modules/pam_filter/.upperLOWER new file mode 100644 index 00000000..2531b468 --- /dev/null +++ b/Linux-PAM/modules/pam_filter/.upperLOWER @@ -0,0 +1 @@ +a test filter that transposes upper and lower case characters diff --git a/Linux-PAM/modules/pam_filter/Makefile b/Linux-PAM/modules/pam_filter/Makefile new file mode 100644 index 00000000..e7d7041e --- /dev/null +++ b/Linux-PAM/modules/pam_filter/Makefile @@ -0,0 +1,126 @@ +# +# $Id: Makefile,v 1.4 2001/11/11 07:43:54 agmorgan Exp $ +# +# 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 + +include ../../Make.Rules + +TITLE=pam_filter +FILTERS=upperLOWER +FILTERSDIR=$(SECUREDIR)/pam_filter +export FILTERSDIR + +CFLAGS += -Iinclude + +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 ####################### + +# +# this is where we compile this module +# + +all: dirs $(LIBSHARED) $(LIBSTATIC) register filters + +dirs: + if [ ! -r include/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 + +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 + +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) + +clean: + @for i in $(FILTERS) ; do \ + if [ -d $$i ]; then \ + $(MAKE) -C $$i clean ; \ + fi ; \ + done + rm -f $(LIBSHARED) $(LIBOBJD) $(LIBOBJS) core *~ + rm -f include/security + rm -fr dynamic static + rm -f *.a *.o *.so *.bak + +endif diff --git a/Linux-PAM/modules/pam_filter/README b/Linux-PAM/modules/pam_filter/README new file mode 100644 index 00000000..12c4aeb5 --- /dev/null +++ b/Linux-PAM/modules/pam_filter/README @@ -0,0 +1,94 @@ +# +# $Id: README,v 1.1.1.1 2000/06/20 22:11:35 agmorgan Exp $ +# +# 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/Linux-PAM/modules/pam_filter/include/pam_filter.h b/Linux-PAM/modules/pam_filter/include/pam_filter.h new file mode 100644 index 00000000..69e3a3e2 --- /dev/null +++ b/Linux-PAM/modules/pam_filter/include/pam_filter.h @@ -0,0 +1,32 @@ +/* + * $Id: pam_filter.h,v 1.1.1.1 2000/06/20 22:11:36 agmorgan Exp $ + * + * 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/Linux-PAM/modules/pam_filter/pam_filter.c b/Linux-PAM/modules/pam_filter/pam_filter.c new file mode 100644 index 00000000..9aa23f29 --- /dev/null +++ b/Linux-PAM/modules/pam_filter/pam_filter.c @@ -0,0 +1,744 @@ +/* + * $Id: pam_filter.c,v 1.6 2004/11/16 14:27:41 toady Exp $ + * + * written by Andrew Morgan <morgan@transmeta.com> with much help from + * Richard Stevens' UNIX Network Programming book. + */ + +#include <security/_pam_aconf.h> + +#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 <termios.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, retval; + + *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 /* strlen('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 /* strlen('SERVICE='); */ +#define SERVICE_NAME "SERVICE=" + + retval = pam_get_item(pamh, PAM_SERVICE, (const void **)&tmp); + if (retval != PAM_SUCCESS || tmp == NULL) { + _pam_log(LOG_CRIT,"service name not found"); + if (levp) { + free(levp[0]); + free(levp); + } + return -1; + } + 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 /* strlen('USER='); */ +#define USER_NAME "USER=" + + tmp = NULL; + 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 /* strlen('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 termios 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 termios terminal handling... */ + + if ( tcgetattr(STDIN_FILENO, &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 termios 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 ( tcsetattr(STDIN_FILENO, TCSAFLUSH, &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) tcsetattr(STDIN_FILENO, TCSAFLUSH, &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 ( tcsetattr(fd[1], TCSANOW, &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) tcsetattr(STDIN_FILENO, TCSANOW, &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/Linux-PAM/modules/pam_filter/upperLOWER/Makefile b/Linux-PAM/modules/pam_filter/upperLOWER/Makefile new file mode 100644 index 00000000..c75f4964 --- /dev/null +++ b/Linux-PAM/modules/pam_filter/upperLOWER/Makefile @@ -0,0 +1,42 @@ +# +# $Id: Makefile,v 1.4 2004/09/22 09:37:47 kukuk Exp $ +# +# This directory contains a pam_filter filter executable +# +# Created by Andrew Morgan <morgan@transmeta.com> 1996/3/11 +# + +include ../../../Make.Rules + +TITLE=upperLOWER + +# + +CFLAGS += -I../include -I../../pammodutil/include + +OBJS = $(TITLE).o + +LIBS += $(GLIB_LIBS) -L../../pammodutil -lpammodutil +LDFLAGS = $(LIBS) + +####################### don't edit below ####################### + +all: $(TITLE) + +$(TITLE): $(OBJS) + $(CC) $(CFLAGS) -o $(TITLE) $(OBJS) $(LDFLAGS) + $(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 *~ + +.c.o: + $(CC) $(CFLAGS) -c $< + diff --git a/Linux-PAM/modules/pam_filter/upperLOWER/upperLOWER.c b/Linux-PAM/modules/pam_filter/upperLOWER/upperLOWER.c new file mode 100644 index 00000000..ee3544a1 --- /dev/null +++ b/Linux-PAM/modules/pam_filter/upperLOWER/upperLOWER.c @@ -0,0 +1,167 @@ +/* + * $Id: upperLOWER.c,v 1.5 2004/09/22 09:37:48 kukuk Exp $ + * + * 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... + */ + +#include <security/_pam_aconf.h> + +#ifdef MEMORY_DEBUG +# undef exit +#endif /* MEMORY_DEBUG */ + +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <security/pam_filter.h> +#include <security/pam_modules.h> +#include <security/_pam_macros.h> +#include <security/_pam_modutil.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]); + } + } +} + +extern char **environ; + +int main(int argc, char **argv) +{ + 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; environ[i]; ++i) { + fprintf(stderr,"-> %s\r\n",environ[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 = _pammodutil_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 (_pammodutil_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 = _pammodutil_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 (_pammodutil_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 = _pammodutil_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 (_pammodutil_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/Linux-PAM/modules/pam_ftp/Makefile b/Linux-PAM/modules/pam_ftp/Makefile new file mode 100644 index 00000000..456161bf --- /dev/null +++ b/Linux-PAM/modules/pam_ftp/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:03 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_ftp + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_ftp/README b/Linux-PAM/modules/pam_ftp/README new file mode 100644 index 00000000..6d03330c --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_ftp/pam_ftp.c b/Linux-PAM/modules/pam_ftp/pam_ftp.c new file mode 100644 index 00000000..64df95ac --- /dev/null +++ b/Linux-PAM/modules/pam_ftp/pam_ftp.c @@ -0,0 +1,299 @@ +/* pam_ftp module */ + +/* + * $Id: pam_ftp.c,v 1.3 2004/09/22 09:37:48 kukuk Exp $ + * + * 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" + +#include <security/_pam_aconf.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 && conv ) { + + 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)); + if (retval == PAM_SUCCESS) + retval = PAM_BAD_ITEM; /* conv was NULL */ + } + + 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/Linux-PAM/modules/pam_group/Makefile b/Linux-PAM/modules/pam_group/Makefile new file mode 100644 index 00000000..06c88998 --- /dev/null +++ b/Linux-PAM/modules/pam_group/Makefile @@ -0,0 +1,21 @@ +# $Id: Makefile,v 1.3 2003/11/27 07:49:46 kukuk Exp $ +# +# 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!). +# + +include ../../Make.Rules + +TITLE=pam_group +LOCAL_CONFILE=./group.conf +INSTALLED_CONFILE=$(SCONFIGD)/group.conf + +DEFS=-DDEFAULT_CONF_FILE=\"$(INSTALLED_CONFILE)\" +CFLAGS += $(DEFS) + +MODULE_SIMPLE_INSTALL=bash -f ../install_conf "$(FAKEROOT)" "$(SCONFIGD)" "$(INSTALLED_CONFILE)" "$(TITLE)" "$(LOCAL_CONFILE)" +MODULE_SIMPLE_REMOVE=rm -f $(FAKEROOT)$(INSTALLED_CONFILE) +MODULE_SIMPLE_CLEAN=rm -f ./.ignore_age + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_group/group.conf b/Linux-PAM/modules/pam_group/group.conf new file mode 100644 index 00000000..e721b990 --- /dev/null +++ b/Linux-PAM/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 allowed 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/Linux-PAM/modules/pam_group/pam_group.c b/Linux-PAM/modules/pam_group/pam_group.c new file mode 100644 index 00000000..c7b75fe2 --- /dev/null +++ b/Linux-PAM/modules/pam_group/pam_group.c @@ -0,0 +1,865 @@ +/* pam_group module */ + +/* + * $Id: pam_group.c,v 1.7 2004/09/24 13:13:20 kukuk Exp $ + * + * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/7/6 + */ + +const static char rcsid[] = +"$Id: pam_group.c,v 1.7 2004/09/24 13:13:20 kukuk Exp $;\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> + +#ifdef DEFAULT_CONF_FILE +# define PAM_GROUP_CONF DEFAULT_CONF_FILE /* from external define */ +#else +# define PAM_GROUP_CONF "/etc/security/group.conf" +#endif +#define PAM_GROUP_BUFLEN 1000 +#define FIELD_SEPARATOR ';' /* this is new as of .02 */ + +#ifdef TRUE +# undef TRUE +#endif +#ifdef FALSE +# undef FALSE +#endif + +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> +#include <security/_pam_modutil.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; + } +} + +/* This function should initially be called with buf = NULL. If + * an error occurs, the file descriptor is closed. Subsequent + * calls with a closed descriptor will cause buf to be deallocated. + * Therefore, always check buf after calling this to see if an error + * occurred. + */ +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); + close(fd); + 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__ ); + close(fd); + return -1; + } + break; + case '\\': + if ((*buf)[i+1] == '\n') { + shift_bytes(i + *buf, 2, *to - (i+2)); + *to -= 2; + } else { + ++i; /* we don't escape non-newline characters */ + } + 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 == '.' || 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(pam_handle_t *pamh, 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 = _pammodutil_getgrnam(pamh, 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(pam_handle_t *pamh, 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(pamh, 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 | PAM_REINITIALIZE_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(pamh, 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(pamh,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/Linux-PAM/modules/pam_issue/Makefile b/Linux-PAM/modules/pam_issue/Makefile new file mode 100644 index 00000000..1bd2be21 --- /dev/null +++ b/Linux-PAM/modules/pam_issue/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:04 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_issue + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_issue/pam_issue.c b/Linux-PAM/modules/pam_issue/pam_issue.c new file mode 100644 index 00000000..5665966e --- /dev/null +++ b/Linux-PAM/modules/pam_issue/pam_issue.c @@ -0,0 +1,327 @@ +/* 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 <time.h> + +#include <security/_pam_macros.h> + +#define PAM_SM_AUTH + +#include <security/pam_modules.h> + +static int _user_prompt_set = 0; + +static 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; + const char *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 (fstat(fileno(fd), &st) < 0) { + fclose(fd); + if (issue_file) + free(issue_file); + return PAM_IGNORE; + } + + retval = pam_get_item(pamh, PAM_USER_PROMPT, + (const void **) &cur_prompt); + if (retval != PAM_SUCCESS) { + fclose(fd); + if (issue_file) + free(issue_file); + return PAM_IGNORE; + } + if (cur_prompt == NULL) { + cur_prompt = ""; + } + + /* first read in the issue file */ + + if (parse_esc) { + prompt_tmp = do_prompt(fd); + if (prompt_tmp == NULL) { + fclose(fd); + if (issue_file) + free(issue_file); + return PAM_IGNORE; + } + } else { + int count = 0; + + prompt_tmp = malloc(st.st_size + 1); + if (prompt_tmp == NULL) { + fclose(fd); + if (issue_file) + free(issue_file); + return PAM_IGNORE; + } + memset (prompt_tmp, '\0', st.st_size + 1); + count = fread(prompt_tmp, 1, st.st_size, fd); + if (count != st.st_size) { + fclose(fd); + retval = PAM_IGNORE; + goto cleanup; + } + 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 + */ + { + char *prompt_tmp_tmp = prompt_tmp; + + prompt_tmp = realloc(prompt_tmp, tot_size + 1); + if (prompt_tmp == NULL) { + prompt_tmp = prompt_tmp_tmp; + retval = PAM_IGNORE; + goto cleanup; + } + } + + 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); + + cleanup: + free(issue_file); + free(prompt_tmp); + + } else { + D(("could not open issue_file: %s", issue_file)); + free(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; +} + +static char *do_prompt(FILE *fd) +{ + int c, size = 1024; + char *issue; + char buf[1024]; + struct utsname uts; + + if (fd == NULL) + return NULL; + + issue = (char *)malloc(size); + if (issue == 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) { + char *old_issue = issue; + + size += strlen(buf) + 1; + issue = (char *) realloc (issue, size); + if (issue == NULL) { + free(old_issue); + return NULL; + } + } + strcat(issue, buf); + } else { + buf[0] = c; buf[1] = '\0'; + if ((strlen(issue) + strlen(buf)) < size + 1) { + char *old_issue = issue; + + size += strlen(buf) + 1; + issue = (char *) realloc (issue, size); + if (issue == NULL) { + free(old_issue); + return NULL; + } + } + 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/Linux-PAM/modules/pam_lastlog/Makefile b/Linux-PAM/modules/pam_lastlog/Makefile new file mode 100644 index 00000000..e8062714 --- /dev/null +++ b/Linux-PAM/modules/pam_lastlog/Makefile @@ -0,0 +1,19 @@ +# +# $Id: Makefile,v 1.3 2001/02/10 22:33:10 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +ifeq ($(HAVE_LIBUTIL),yes) + MODULE_SIMPLE_EXTRALIBS += -lutil +endif + +TITLE=pam_lastlog + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_lastlog/pam_lastlog.c b/Linux-PAM/modules/pam_lastlog/pam_lastlog.c new file mode 100644 index 00000000..c9c5e24e --- /dev/null +++ b/Linux-PAM/modules/pam_lastlog/pam_lastlog.c @@ -0,0 +1,470 @@ +/* pam_lastlog module */ + +/* + * $Id: pam_lastlog.c,v 1.8 2004/09/24 13:13:20 kukuk Exp $ + * + * 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. + */ + +#include <security/_pam_aconf.h> + +#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> +#include <security/_pam_modutil.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 && conv) { + + 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)); + if (retval == PAM_SUCCESS) + retval = PAM_BAD_ITEM; /* conv was NULL */ + } + + 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 = (_pammodutil_read (last_fd, (char *) &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) { + time_t ll_time; + char *the_time; + char *remark; + + ll_time = last_login.ll_time; + the_time = ctime(&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 */ + { + time_t ll_time; + const char *remote_host=NULL + , *terminal_line=DEFAULT_TERM; + + /* set this login date */ + D(("set the most recent login time")); + + (void) time(&ll_time); /* set the time */ + last_login.ll_time = ll_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)); + last_login.ll_host[sizeof(last_login.ll_host) - 1] = '\0'; + 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)); + last_login.ll_host[sizeof(last_login.ll_host) - 1] = '\0'; + 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")); + _pammodutil_write (last_fd, (char *) &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 = _pammodutil_getpwnam (pamh, 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/Linux-PAM/modules/pam_limits/Makefile b/Linux-PAM/modules/pam_limits/Makefile new file mode 100644 index 00000000..d15fd9f7 --- /dev/null +++ b/Linux-PAM/modules/pam_limits/Makefile @@ -0,0 +1,37 @@ +# +# $Id: Makefile,v 1.4 2004/09/28 13:48:47 kukuk Exp $ +# +# 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!). +# + +include ../../Make.Rules + +TITLE=pam_limits + +ifeq ($(OS),linux) + +LOCAL_CONFILE=./limits.skel +INSTALLED_CONFILE=$(SCONFIGD)/limits.conf + +DEFS=-DDEFAULT_CONF_FILE=\"$(INSTALLED_CONFILE)\" +CFLAGS += $(DEFS) + +MODULE_SIMPLE_INSTALL=bash -f ../install_conf "$(FAKEROOT)" "$(SCONFIGD)" "$(INSTALLED_CONFILE)" "$(TITLE)" "$(LOCAL_CONFILE)" +MODULE_SIMPLE_REMOVE=rm -f $(FAKEROOT)$(INSTALLED_CONFILE) +MODULE_SIMPLE_CLEAN=rm -f ./.ignore_age +ifeq ($(HAVE_LIBCAP),yes) +MODULE_SIMPLE_EXTRALIBS=-lcap +endif + +include ../Simple.Rules + +#else +#include ../dont_makefile +#endif +else + +include ../dont_makefile + +endif diff --git a/Linux-PAM/modules/pam_limits/README b/Linux-PAM/modules/pam_limits/README new file mode 100644 index 00000000..32afb197 --- /dev/null +++ b/Linux-PAM/modules/pam_limits/README @@ -0,0 +1,110 @@ + +pam_limits module: + Imposing user limits on login. + +THEORY OF OPERATION: + +First, make a root-only-readable file (/etc/security/limits.conf by +default or INSTALLED_CONFILE 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 three values: + - "soft" for enforcing the soft limits + - "hard" for enforcing hard limits + - "-" for enforcing both soft and 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 + - priority - lower the priority by given value (value can be -ve) + - locks - max locked files (Linux 2.4 and higher) + - sigpending - max number of pending signals (Linux 2.6 and higher) + - msgqueue - max memory used by POSIX message queues (bytes) + (Linux 2.6 and higher) + +Note, if you specify a type of '-' but neglect to supply the item and +value fields then the module will never enforce any limits on the +specified user/group etc. . + +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 has 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 (they apply for 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. + + change_uid change real uid to the user for who the limits + are set up. Use this option if you have problems + like login not forking a shell for user who has + no processes. Be warned that something else + may break when you do this. + + utmp_early some broken applications actually allocate a + utmp entry for the user before the user is + admitted to the system. If the service you are + configuring PAM for does this, you can use + this module argument to compensate for this + brokenness. + +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, and Jens Sorensen for Linux 2.4 updates. diff --git a/Linux-PAM/modules/pam_limits/limits.skel b/Linux-PAM/modules/pam_limits/limits.skel new file mode 100644 index 00000000..9ba31b19 --- /dev/null +++ b/Linux-PAM/modules/pam_limits/limits.skel @@ -0,0 +1,47 @@ +# /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 +# - the wildcard %, can be also used with %group syntax, +# for maxlogin limit +# +#<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 +# - locks - max number of file locks the user can hold +# - sigpending - max number of pending signals +# - msgqueue - max memory used by POSIX message queues (bytes) +# +#<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/Linux-PAM/modules/pam_limits/pam_limits.c b/Linux-PAM/modules/pam_limits/pam_limits.c new file mode 100644 index 00000000..1482833a --- /dev/null +++ b/Linux-PAM/modules/pam_limits/pam_limits.c @@ -0,0 +1,685 @@ +/* + * 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 + +#include <security/_pam_aconf.h> + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <errno.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_ALLGROUP 2 /* limit was set by a group entry */ +#define LIMITS_DEF_ALL 3 /* limit was set by an default entry */ +#define LIMITS_DEF_DEFAULT 4 /* limit was set by an default entry */ +#define LIMITS_DEF_NONE 5 /* this limit was not set yet */ + +static const char *limits_def_names[] = { + "USER", + "GROUP", + "ALLGROUP", + "ALL", + "DEFAULT", + "NONE", + NULL +}; + +struct user_limits_struct { + int src_soft; + int src_hard; + struct rlimit limit; +}; + +/* internal data */ +struct pam_limit_s { + int login_limit; /* the max logins limit */ + int login_limit_def; /* which entry set the login limit */ + int flag_numsyslogins; /* whether to limit logins only for a + specific user or to count all logins */ + int priority; /* the priority to run user process with */ + int supported[RLIM_NLIMITS]; + struct user_limits_struct limits[RLIM_NLIMITS]; + char conf_file[BUFSIZ]; + int utmp_after_pam_call; + char login_group[LINE_LENGTH]; +}; + +#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> +#include <security/_pam_modutil.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 +#define PAM_DO_SETREUID 0x0002 +#define PAM_UTMP_EARLY 0x0004 + +static int _pam_parse(int argc, const char **argv, struct pam_limit_s *pl) +{ + 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)) { + strncpy(pl->conf_file,*argv+5,sizeof(pl->conf_file)-1); + } else if (!strncmp(*argv,"change_uid",10)) { + ctrl |= PAM_DO_SETREUID; + } else if (!strcmp(*argv,"utmp_early")) { + ctrl |= PAM_UTMP_EARLY; + } else { + _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv); + } + } + pl->conf_file[sizeof(pl->conf_file) - 1] = '\0'; + + return ctrl; +} + + +/* limits stuff */ +#ifdef DEFAULT_CONF_FILE +# define LIMITS_FILE DEFAULT_CONF_FILE +#else +# define LIMITS_FILE "/etc/security/limits.conf" +#endif + +#define LIMITED_OK 0 /* limit setting appeared to work */ +#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 (pam_handle_t *pamh, const char *name, int limit, int ctrl, + struct pam_limit_s *pl) +{ + struct utmp *ut; + unsigned int count; + + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_DEBUG, "checking logins for '%s' (maximum of %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(); + + /* Because there is no definition about when an application + actually adds a utmp entry, some applications bizarrely do the + utmp call before the have PAM authenticate them to the system: + you're logged it, sort of...? Anyway, you can use the + "utmp_early" module argument in your PAM config file to make + allowances for this sort of problem. (There should be a PAM + standard for this, since if a module wants to actually map a + username then any early utmp entry will be for the unmapped + name = broken.) */ + + if (ctrl & PAM_UTMP_EARLY) { + count = 0; + } else { + count = 1; + } + + while((ut = getutent())) { +#ifdef USER_PROCESS + if (ut->ut_type != USER_PROCESS) { + continue; + } +#endif + if (ut->UT_USER[0] == '\0') { + continue; + } + if (!pl->flag_numsyslogins) { + if (((pl->login_limit_def == LIMITS_DEF_USER) + || (pl->login_limit_def == LIMITS_DEF_GROUP) + || (pl->login_limit_def == LIMITS_DEF_DEFAULT)) + && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0) { + continue; + } + if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP) + && !_pammodutil_user_in_group_nam_nam(pamh, ut->UT_USER, pl->login_group)) { + 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; +} + +static int init_limits(struct pam_limit_s *pl) +{ + int i; + int retval = PAM_SUCCESS; + + D(("called.")); + + for(i = 0; i < RLIM_NLIMITS; i++) { + int r = getrlimit(i, &pl->limits[i].limit); + if (r == -1) { + if (errno == EINVAL) { + pl->supported[i] = 0; + } else { + retval = !PAM_SUCCESS; + } + } else { + pl->supported[i] = 1; + pl->limits[i].src_soft = LIMITS_DEF_NONE; + pl->limits[i].src_hard = LIMITS_DEF_NONE; + } + } + + pl->priority = 0; + pl->login_limit = -2; + pl->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, struct pam_limit_s *pl) +{ + int limit_item; + int limit_type = 0; + long limit_value; + char *endptr; + const char *value_orig = lim_value; + + if (ctrl & PAM_DEBUG_ARG) + _pam_log(LOG_DEBUG, "%s: processing %s %s %s for %s\n", + __FUNCTION__,lim_type,lim_item,lim_value, + limits_def_names[source]); + + 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; +#ifdef RLIMIT_LOCKS + else if (strcmp(lim_item, "locks") == 0) + limit_item = RLIMIT_LOCKS; +#endif +#ifdef RLIMIT_SIGPENDING + else if (strcmp(lim_item, "sigpending") == 0) + limit_item = RLIMIT_SIGPENDING; +#endif +#ifdef RLIMIT_MSGQUEUE + else if (strcmp(lim_item, "msgqueue") == 0) + limit_item = RLIMIT_MSGQUEUE; +#endif + else if (strcmp(lim_item, "maxlogins") == 0) { + limit_item = LIMIT_LOGIN; + pl->flag_numsyslogins = 0; + } else if (strcmp(lim_item, "maxsyslogins") == 0) { + limit_item = LIMIT_NUMSYSLOGINS; + pl->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); + + /* special case value when limiting logins */ + 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; + } + + /* one more special case when limiting logins */ + if ((source == LIMITS_DEF_ALL || source == LIMITS_DEF_ALLGROUP) + && (limit_item != LIMIT_LOGIN)) { + if (ctrl & PAM_DEBUG_ARG) + _pam_log(LOG_DEBUG, + "'%%' domain valid for maxlogins type only"); + return; + } + + 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 (pl->limits[limit_item].src_soft < source) { + return; + } else { + pl->limits[limit_item].limit.rlim_cur = limit_value; + pl->limits[limit_item].src_soft = source; + } + } + if (limit_type & LIMIT_HARD) { + if (pl->limits[limit_item].src_hard < source) { + return; + } else { + pl->limits[limit_item].limit.rlim_max = limit_value; + pl->limits[limit_item].src_hard = source; + } + } + } else { + /* recent kernels support negative priority limits (=raise priority) */ + + if (limit_item == LIMIT_PRI) { + pl->priority = limit_value; + } else { + if (pl->login_limit_def < source) { + return; + } else { + pl->login_limit = limit_value; + pl->login_limit_def = source; + } + } + } + return; +} + +static int parse_config_file(pam_handle_t *pamh, const char *uname, int ctrl, + struct pam_limit_s *pl) +{ + FILE *fil; + char buf[LINE_LENGTH]; + +#define CONF_FILE (pl->conf_file[0])?pl->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++; + strncpy(buf, tptr, sizeof(buf)-1); + buf[sizeof(buf)-1] = '\0'; + + /* 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); + D(("scanned line[%d]: domain[%s], ltype[%s], item[%s], value[%s]", + i, 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, pl); + else if (domain[0]=='@') { + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_DEBUG, "checking if %s is in group %s", + uname, domain + 1); + } + if (_pammodutil_user_in_group_nam_nam(pamh, uname, domain+1)) + process_limit(LIMITS_DEF_GROUP, ltype, item, value, ctrl, + pl); + } else if (domain[0]=='%') { + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_DEBUG, "checking if %s is in group %s", + uname, domain + 1); + } + if (strcmp(domain,"%") == 0) + process_limit(LIMITS_DEF_ALL, ltype, item, value, ctrl, + pl); + else if (_pammodutil_user_in_group_nam_nam(pamh, uname, domain+1)) { + strcpy(pl->login_group, domain+1); + process_limit(LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl, + pl); + } + } else if (strcmp(domain, "*") == 0) + process_limit(LIMITS_DEF_DEFAULT, ltype, item, value, ctrl, + pl); + } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */ + if (strcmp(uname, domain) == 0) { + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_DEBUG, "no limits for '%s'", uname); + } + fclose(fil); + return PAM_IGNORE; + } else if (domain[0] == '@' && _pammodutil_user_in_group_nam_nam(pamh, uname, domain+1)) { + if (ctrl & PAM_DEBUG_ARG) { + _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' - skipped", buf); + } + } + fclose(fil); + return PAM_SUCCESS; +} + +static int setup_limits(pam_handle_t *pamh, + const char *uname, uid_t uid, int ctrl, + struct pam_limit_s *pl) +{ + int i; + int status; + int retval = LIMITED_OK; + + if (uid == 0) { + /* do not impose limits (+ve limits anyway) on the superuser */ + if (pl->priority > 0) { + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_DEBUG, "user '%s' has UID 0 - no limits imposed", + uname); + } + pl->priority = 0; + } + } + + for (i=0, status=LIMITED_OK; i<RLIM_NLIMITS; i++) { + if (pl->limits[i].limit.rlim_cur > pl->limits[i].limit.rlim_max) + pl->limits[i].limit.rlim_cur = pl->limits[i].limit.rlim_max; + if (!pl->supported[i]) { + /* skip it if its not known to the system */ + continue; + } + status |= setrlimit(i, &pl->limits[i].limit); + } + + if (status) { + retval = LIMIT_ERR; + } + + status = setpriority(PRIO_PROCESS, 0, pl->priority); + if (status != 0) { + retval = LIMIT_ERR; + } + + if (uid == 0) { + D(("skip login limit check for uid=0")); + } else if (pl->login_limit > 0) { + if (check_logins(pamh, uname, pl->login_limit, ctrl, pl) == LOGIN_ERR) { + retval |= LOGIN_ERR; + } + } else if (pl->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; + struct pam_limit_s pl; + + D(("called.")); + + memset(&pl, 0, sizeof(pl)); + + ctrl = _pam_parse(argc, argv, &pl); + 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; + } + + pwd = getpwnam(user_name); + if (!pwd) { + if (ctrl & PAM_DEBUG_ARG) + _pam_log(LOG_WARNING, "open_session username '%s' does not exist", + user_name); + return PAM_SESSION_ERR; + } + + retval = init_limits(&pl); + if (retval != PAM_SUCCESS) { + _pam_log(LOG_WARNING, "cannot initialize"); + return PAM_IGNORE; + } + + retval = parse_config_file(pamh, pwd->pw_name, ctrl, &pl); + if (retval == PAM_IGNORE) { + D(("the configuration file has an applicable '<domain> -' entry")); + return PAM_SUCCESS; + } + if (retval != PAM_SUCCESS) { + _pam_log(LOG_WARNING, "error parsing the configuration file"); + return PAM_IGNORE; + } + + if (ctrl & PAM_DO_SETREUID) { + setreuid(pwd->pw_uid, -1); + } + retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, &pl); + if (retval != LIMITED_OK) { + 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/Linux-PAM/modules/pam_listfile/Makefile b/Linux-PAM/modules/pam_listfile/Makefile new file mode 100644 index 00000000..c5447c94 --- /dev/null +++ b/Linux-PAM/modules/pam_listfile/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:04 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_listfile + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_listfile/README b/Linux-PAM/modules/pam_listfile/README new file mode 100644 index 00000000..b65e7dbb --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_listfile/pam_listfile.c b/Linux-PAM/modules/pam_listfile/pam_listfile.c new file mode 100644 index 00000000..965c471d --- /dev/null +++ b/Linux-PAM/modules/pam_listfile/pam_listfile.c @@ -0,0 +1,425 @@ +/* + * $Id: pam_listfile.c,v 1.6 2004/09/24 13:13:20 kukuk Exp $ + * + */ + +/* + * 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. + */ + +#include <security/_pam_aconf.h> + +#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 +#define PAM_SM_ACCOUNT + +#include <security/pam_modules.h> +#include <security/_pam_macros.h> +#include <security/_pam_modutil.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; +} + +/* --- 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 + +#define LESSER(a, b) ((a) < (b) ? (a) : (b)) + +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 */ + + apply_type=APPLY_TYPE_NULL; + memset(apply_val,0,sizeof(apply_val)); + + for(i=0; i < argc; i++) { + { + const char *junk; + + memset(mybuf,'\0',sizeof(mybuf)); + memset(myval,'\0',sizeof(mybuf)); + junk = strchr(argv[i], '='); + if((junk == NULL) || (junk - argv[i]) >= sizeof(mybuf)) { + _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "Bad option: \"%s\"", + argv[i]); + continue; + } + strncpy(mybuf, argv[i], LESSER(junk - argv[i], sizeof(mybuf) - 1)); + strncpy(myval, junk + 1, sizeof(myval) - 1); + } + 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; + memset(apply_val,'\0',sizeof(apply_val)); + 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(!_pammodutil_user_in_group_nam_nam(pamh,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((citem == PAM_TTY) && citemp) { + /* Normalize the TTY name. */ + if(strncmp(citemp, "/dev/", 5) == 0) { + citemp += 5; + } + } + + 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: + userinfo = _pammodutil_getpwnam(pamh, citemp); + if (userinfo == NULL) { + _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "getpwnam(%s) failed", + citemp); + return onerr; + } + grpinfo = _pammodutil_getgrgid(pamh, userinfo->pw_gid); + if (grpinfo == NULL) { + _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "getgrgid(%d) failed", + (int)userinfo->pw_gid); + return onerr; + } + 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); + } + } + endgrent(); + itemlist[i] = NULL; + break; + case EI_SHELL: + /* 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 */ + userinfo = _pammodutil_getpwnam(pamh, citemp); + if (userinfo == NULL) { + _pam_log(LOG_ERR,LOCAL_LOG_PREFIX "getpwnam(%s) failed", + citemp); + return onerr; + } + citemp = userinfo->pw_shell; + 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,sizeof(aline),inf) != NULL) + && retval) { + if(strlen(aline) == 0) + continue; + 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,sizeof(aline),inf) != NULL) + && retval) { + char *a = aline; + if(strlen(aline) == 0) + continue; + if(aline[strlen(aline) - 1] == '\n') + aline[strlen(aline) - 1] = '\0'; + if(strlen(aline) == 0) + continue; + if(aline[strlen(aline) - 1] == '\r') + aline[strlen(aline) - 1] = '\0'; + if(citem == PAM_TTY) + if(strncmp(a, "/dev/", 5) == 0) + a += 5; + retval = strcmp(a,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; +} + +PAM_EXTERN +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return pam_sm_authenticate(pamh, 0, argc, argv); +} + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_listfile_modstruct = { + "pam_listfile", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + NULL, + NULL, + NULL, +}; + +#endif /* PAM_STATIC */ + +/* end of module definition */ + diff --git a/Linux-PAM/modules/pam_localuser/Makefile b/Linux-PAM/modules/pam_localuser/Makefile new file mode 100644 index 00000000..3dc61aa0 --- /dev/null +++ b/Linux-PAM/modules/pam_localuser/Makefile @@ -0,0 +1,14 @@ +# $Id: Makefile,v 1.1 2004/09/24 11:49:37 kukuk Exp $ +# +# 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!). +# +# + +include ../../Make.Rules + +TITLE=pam_localuser +MAN8=pam_localuser.8 + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_localuser/README b/Linux-PAM/modules/pam_localuser/README new file mode 100644 index 00000000..b8cdf524 --- /dev/null +++ b/Linux-PAM/modules/pam_localuser/README @@ -0,0 +1,17 @@ +pam_localuser: + Succeeds iff the PAM_USER is listed in /etc/passwd. This seems to be a + common policy need (allowing only a subset of network-wide users, and + any locally-defined users, to access services). Simpler than using + awk to generate a file for use with pam_listfile (-F: '{print $1}'), + I guess. + +RECOGNIZED ARGUMENTS: + debug write debugging messages to syslog + file=FILE scan FILE instead of /etc/passwd + +MODULE SERVICES PROVIDED: + auth,account scan the FILE (/etc/passwd by default) and return + a success code if an entry is found for the user + +AUTHOR: + Nalin Dahyabhai <nalin@redhat.com> diff --git a/Linux-PAM/modules/pam_localuser/pam_localuser.8 b/Linux-PAM/modules/pam_localuser/pam_localuser.8 new file mode 100644 index 00000000..ce0a9465 --- /dev/null +++ b/Linux-PAM/modules/pam_localuser/pam_localuser.8 @@ -0,0 +1,36 @@ +.\" Copyright 2000 Red Hat, Inc. +.TH pam_localuser 8 2000/7/21 "Red Hat" "System Administrator's Manual" + +.SH NAME +pam_localuser \- require users to be listed in /etc/passwd + +.SH SYNOPSIS +.B account sufficient /lib/security/pam_localuser.so \fIargs\fP +.br +.B account required /lib/security/pam_wheel.so group=devel + +.SH DESCRIPTION +pam_localuser.so exists to help implement site-wide login policies, where +they typically include a subset of the network's users and a few accounts +that are local to a particular workstation. Using pam_localuser.so and +pam_wheel.so or pam_listfile.so is an effective way to restrict access to +either local users and/or a subset of the network's users. + +This could also be implemented using pam_listfile.so and a very short awk +script invoked by cron, but it's common enough to have been separated out. + +.SH ARGUMENTS +.IP debug +turns on debugging +.IP file=\fBFILE\fP +uses a file other than \fB/etc/passwd\fP. + +.SH FILES +/etc/passwd + +.SH BUGS +Let's hope not, but if you find any, please report them via the "Bug Track" +link at http://bugzilla.redhat.com/bugzilla/ + +.SH AUTHOR +Nalin Dahyabhai <nalin@redhat.com> diff --git a/Linux-PAM/modules/pam_localuser/pam_localuser.c b/Linux-PAM/modules/pam_localuser/pam_localuser.c new file mode 100644 index 00000000..e5496089 --- /dev/null +++ b/Linux-PAM/modules/pam_localuser/pam_localuser.c @@ -0,0 +1,159 @@ +/* + * Copyright 2001, 2004 Red Hat, Inc. + * + * 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. + */ + +#include "../../_pam_aconf.h" + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <stdio.h> +#include <stdarg.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> + +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#include "../../libpam/include/security/pam_modules.h" +#include "../../libpam/include/security/_pam_macros.h" + +#define MODULE_NAME "pam_localuser" + +PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + int i, ret = PAM_SUCCESS; + FILE *fp; + int debug = 0; + const char *filename = "/etc/passwd"; + char line[LINE_MAX], name[LINE_MAX]; + const char* user; + + /* process arguments */ + for(i = 0; i < argc; i++) { + if(strcmp("debug", argv[i]) == 0) { + debug = 1; + } + } + for(i = 0; i < argc; i++) { + if(strncmp("file=", argv[i], 5) == 0) { + filename = argv[i] + 5; + if(debug) { + openlog(MODULE_NAME, LOG_PID, LOG_AUTHPRIV); + syslog(LOG_DEBUG, "set filename to \"%s\"", + filename); + closelog(); + } + } + } + + /* open the file */ + fp = fopen(filename, "r"); + if(fp == NULL) { + openlog(MODULE_NAME, LOG_PID, LOG_AUTHPRIV); + syslog(LOG_ERR, "error opening \"%s\": %s", filename, + strerror(errno)); + closelog(); + return PAM_SYSTEM_ERR; + } + + if(pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) { + openlog(MODULE_NAME, LOG_PID, LOG_AUTHPRIV); + syslog(LOG_ERR, "user name not specified yet"); + closelog(); + fclose(fp); + return PAM_SYSTEM_ERR; + } + + if ((user == NULL) || (strlen(user) == 0)) { + openlog(MODULE_NAME, LOG_PID, LOG_AUTHPRIV); + syslog(LOG_ERR, "user name not valid"); + closelog(); + fclose(fp); + return PAM_SYSTEM_ERR; + } + + /* scan the file, using fgets() instead of fgetpwent() because i + * don't want to mess with applications which call fgetpwent() */ + ret = PAM_PERM_DENIED; + snprintf(name, sizeof(name), "%s:", user); + i = strlen(name); + while(fgets(line, sizeof(line), fp) != NULL) { + if(debug) { + openlog(MODULE_NAME, LOG_PID, LOG_AUTHPRIV); + syslog(LOG_DEBUG, "checking \"%s\"", line); + closelog(); + } + if(strncmp(name, line, i) == 0) { + ret = PAM_SUCCESS; + break; + } + } + + /* okay, we're done */ + fclose(fp); + return ret; +} + +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_sm_authenticate(pamh, flags, argc, argv); +} + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_localuser_modstruct = { + "pam_localuser", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + NULL, + NULL, + NULL, +}; + +#endif diff --git a/Linux-PAM/modules/pam_mail/Makefile b/Linux-PAM/modules/pam_mail/Makefile new file mode 100644 index 00000000..2d9b8e9a --- /dev/null +++ b/Linux-PAM/modules/pam_mail/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:04 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_mail + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_mail/README b/Linux-PAM/modules/pam_mail/README new file mode 100644 index 00000000..155bd1db --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_mail/pam_mail.c b/Linux-PAM/modules/pam_mail/pam_mail.c new file mode 100644 index 00000000..7987cb28 --- /dev/null +++ b/Linux-PAM/modules/pam_mail/pam_mail.c @@ -0,0 +1,504 @@ +/* pam_mail module */ + +/* + * $Id: pam_mail.c,v 1.6 2004/11/16 14:27:41 toady Exp $ + * + * 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 + */ + +#include <security/_pam_aconf.h> + +#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 + +#define DEFAULT_MAIL_DIRECTORY PAM_PATH_MAILDIR +#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." + +/* + * 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> +#include <security/_pam_modutil.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 && conv ) { + + 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)); + if (retval == PAM_SUCCESS) + retval = PAM_BAD_ITEM; /* conv was NULL */ + } + + 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 = _pammodutil_getpwnam(pamh, 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 { + _pam_drop(folder); + _log_err(LOG_CRIT, "out of memory for mail folder"); + return PAM_BUF_ERR; + } + } + 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 && ! (ctrl & PAM_NO_ENV) ) + (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/Linux-PAM/modules/pam_mkhomedir/Makefile b/Linux-PAM/modules/pam_mkhomedir/Makefile new file mode 100644 index 00000000..f017f4a4 --- /dev/null +++ b/Linux-PAM/modules/pam_mkhomedir/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.3 2000/11/19 23:54:04 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_mkhomedir + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_mkhomedir/README b/Linux-PAM/modules/pam_mkhomedir/README new file mode 100644 index 00000000..2a3e705e --- /dev/null +++ b/Linux-PAM/modules/pam_mkhomedir/README @@ -0,0 +1,25 @@ +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: + + auth requisite pam_securetty.so + auth sufficient pam_ldap.so + auth required pam_unix.so + auth optional pam_group.so + auth optional pam_mail.so + account requisite pam_time.so + account sufficient pam_ldap.so + account required pam_unix.so + session required pam_mkhomedir.so skel=/etc/skel/ umask=0022 + session required pam_unix.so + session optional pam_lastlog.so + password required pam_unix.so + +Released under the GNU LGPL version 2 or later +Originally written by Jason Gunthorpe <jgg@debian.org> Feb 1999 + diff --git a/Linux-PAM/modules/pam_mkhomedir/pam_mkhomedir.c b/Linux-PAM/modules/pam_mkhomedir/pam_mkhomedir.c new file mode 100644 index 00000000..f63177bf --- /dev/null +++ b/Linux-PAM/modules/pam_mkhomedir/pam_mkhomedir.c @@ -0,0 +1,521 @@ +/* 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> +#include <security/_pam_modutil.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"; /* THIS MODULE IS NOT THREAD SAFE */ + +/* 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)) { + strncpy(SkelDir,*argv+5,sizeof(SkelDir)); + SkelDir[sizeof(SkelDir)-1] = '\0'; + } 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 && conv) + { + + 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)); + if (retval == PAM_SUCCESS) + retval = PAM_BAD_ITEM; /* conv was NULL */ + } + + 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, + const char *source, const char *dest) +{ + char remark[BUFSIZ]; + DIR *D; + struct dirent *Dir; + + /* Mention what is happening, if the notification fails that is OK */ + if (snprintf(remark,sizeof(remark),"Creating directory '%s'.", dest) == -1) + return PAM_PERM_DENIED; + + make_remark(pamh, ctrl, remark); + + /* Create the new directory */ + if (mkdir(dest,0700) != 0) + { + _log_err(LOG_DEBUG, "unable to create directory %s",dest); + return PAM_PERM_DENIED; + } + if (chmod(dest,0777 & (~UMask)) != 0 || + chown(dest,pwd->pw_uid,pwd->pw_gid) != 0) + { + _log_err(LOG_DEBUG, "unable to change perms on directory %s",dest); + return PAM_PERM_DENIED; + } + + /* See if we need to copy the skel dir over. */ + if ((source == NULL) || (strlen(source) == 0)) + { + return PAM_SUCCESS; + } + + /* Scan the directory */ + D = opendir(source); + if (D == 0) + { + _log_err(LOG_DEBUG, "unable to read directory %s",source); + return PAM_PERM_DENIED; + } + + for (Dir = readdir(D); Dir != 0; Dir = readdir(D)) + { + int SrcFd; + int DestFd; + int Res; + struct stat St; +#ifndef PATH_MAX + char *newsource = NULL, *newdest = NULL; + /* track length of buffers */ + int nslen = 0, ndlen = 0; + int slen = strlen(source), dlen = strlen(dest); +#else + char newsource[PATH_MAX], newdest[PATH_MAX]; +#endif + + /* Skip some files.. */ + if (strcmp(Dir->d_name,".") == 0 || + strcmp(Dir->d_name,"..") == 0) + continue; + + /* Determine what kind of file it is. */ +#ifndef PATH_MAX + nslen = slen + strlen(Dir->d_name) + 2; + + if (nslen <= 0) + return PAM_BUF_ERR; + + if ( (newsource = malloc(nslen)) == NULL ) + return PAM_BUF_ERR; + + sprintf(newsource, "%s/%s", source, Dir->d_name); +#else + snprintf(newsource,sizeof(newsource),"%s/%s",source,Dir->d_name); +#endif + + if (lstat(newsource,&St) != 0) +#ifndef PATH_MAX + { + free(newsource); + newsource = NULL; + continue; + } +#else + continue; +#endif + + + /* We'll need the new file's name. */ +#ifndef PATH_MAX + ndlen = dlen + strlen(Dir->d_name)+2; + + if (ndlen <= 0) + return PAM_BUF_ERR; + + if ( (newdest = malloc(ndlen)) == NULL ) { + free(newsource); + return PAM_BUF_ERR; + } + + sprintf(newdest, "%s/%s", dest, Dir->d_name); +#else + snprintf(newdest,sizeof(newdest),"%s/%s",dest,Dir->d_name); +#endif + + /* If it's a directory, recurse. */ + if (S_ISDIR(St.st_mode)) + { + int retval = create_homedir(pamh, ctrl, pwd, newsource, newdest); + +#ifndef PATH_MAX + free(newsource); newsource = NULL; + free(newdest); newdest = NULL; +#endif + + if (retval != PAM_SUCCESS) { + closedir(D); + return retval; + } + continue; + } + + /* If it's a symlink, create a new link. */ + if (S_ISLNK(St.st_mode)) + { + int pointedlen = 0; +#ifndef PATH_MAX + char *pointed = NULL; + { + int size = 100; + + while (1) { + pointed = (char *) malloc(size); + if ( ! pointed ) { + free(newsource); + free(newdest); + return PAM_BUF_ERR; + } + pointedlen = readlink (newsource, pointed, size); + if ( pointedlen < 0 ) break; + if ( pointedlen < size ) break; + free (pointed); + size *= 2; + } + } + if ( pointedlen < 0 ) + free(pointed); + else + pointed[pointedlen] = 0; +#else + char pointed[PATH_MAX]; + memset(pointed, 0, sizeof(pointed)); + + pointedlen = readlink(newsource, pointed, sizeof(pointed) - 1); +#endif + + if ( pointedlen >= 0 ) { + if(symlink(pointed, newdest) == 0) + { + if (lchown(newdest,pwd->pw_uid,pwd->pw_gid) != 0) + { + closedir(D); + _log_err(LOG_DEBUG, "unable to change perms on link %s", + newdest); +#ifndef PATH_MAX + free(pointed); + free(newsource); + free(newdest); +#endif + return PAM_PERM_DENIED; + } + } +#ifndef PATH_MAX + free(pointed); +#endif + } +#ifndef PATH_MAX + free(newsource); newsource = NULL; + free(newdest); newdest = NULL; +#endif + continue; + } + + /* If it's not a regular file, it's probably not a good idea to create + * the new device node, FIFO, or whatever it is. */ + if (!S_ISREG(St.st_mode)) + { +#ifndef PATH_MAX + free(newsource); newsource = NULL; + free(newdest); newdest = NULL; +#endif + continue; + } + + /* Open the source file */ + if ((SrcFd = open(newsource,O_RDONLY)) < 0 || fstat(SrcFd,&St) != 0) + { + closedir(D); + _log_err(LOG_DEBUG, "unable to open src file %s",newsource); + +#ifndef PATH_MAX + free(newsource); newsource = NULL; + free(newdest); newdest = NULL; +#endif + + return PAM_PERM_DENIED; + } + stat(newsource,&St); + + /* Open the dest file */ + if ((DestFd = open(newdest,O_WRONLY | O_TRUNC | O_CREAT,0600)) < 0) + { + close(SrcFd); + closedir(D); + _log_err(LOG_DEBUG, "unable to open dest file %s",newdest); + +#ifndef PATH_MAX + free(newsource); newsource = NULL; + free(newdest); newdest = NULL; +#endif + 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) + { + close(SrcFd); + close(DestFd); + closedir(D); + _log_err(LOG_DEBUG, "unable to chang perms on copy %s",newdest); + +#ifndef PATH_MAX + free(newsource); newsource = NULL; + free(newdest); newdest = NULL; +#endif + + return PAM_PERM_DENIED; + } + + /* Copy the file */ + do + { + Res = _pammodutil_read(SrcFd,remark,sizeof(remark)); + + if (Res == 0) + continue; + + if (Res > 0) { + if (_pammodutil_write(DestFd,remark,Res) == Res) + continue; + } + + /* If we get here, pammodutil_read returned a -1 or + _pammodutil_write returned something unexpected. */ + close(SrcFd); + close(DestFd); + closedir(D); + _log_err(LOG_DEBUG, "unable to perform IO"); + +#ifndef PATH_MAX + free(newsource); newsource = NULL; + free(newdest); newdest = NULL; +#endif + + return PAM_PERM_DENIED; + } + while (Res != 0); + close(SrcFd); + close(DestFd); + +#ifndef PATH_MAX + free(newsource); newsource = NULL; + free(newdest); newdest = NULL; +#endif + + } + closedir(D); + + 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 = _pammodutil_getpwnam (pamh, 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,SkelDir,pwd->pw_dir); +} + +/* 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/Linux-PAM/modules/pam_motd/Makefile b/Linux-PAM/modules/pam_motd/Makefile new file mode 100644 index 00000000..fb83807a --- /dev/null +++ b/Linux-PAM/modules/pam_motd/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:04 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_motd + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_motd/pam_motd.c b/Linux-PAM/modules/pam_motd/pam_motd.c new file mode 100644 index 00000000..ce695f92 --- /dev/null +++ b/Linux-PAM/modules/pam_motd/pam_motd.c @@ -0,0 +1,133 @@ +/* pam_motd module */ + +/* + * Modified for pam_motd by Ben Collins <bcollins@debian.org> + * + * Based off of: + * $Id: pam_motd.c,v 1.3 2004/09/22 09:37:49 kukuk Exp $ + * + * Written by Michael K. Johnson <johnsonm@redhat.com> 1996/10/24 + * + */ + +#include <security/_pam_aconf.h> + +#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> +#include <security/_pam_modutil.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; + const char *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 (and a memory leak)", 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) { + close(fd); + return retval; + } + message.msg = mtmp = malloc(st.st_size+1); + /* if malloc failed... */ + if (!message.msg) { + close(fd); + return retval; + } + if (_pammodutil_read(fd, mtmp, st.st_size) == 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 */ + if (pam_get_item(pamh, PAM_CONV, (const void **)&conversation) == + PAM_SUCCESS && conversation) { + conversation->conv(1, (const struct pam_message **)&pmessage, + &resp, conversation->appdata_ptr); + if (resp) + _pam_drop_reply(resp, 1); + } + } + free(mtmp); + } + + 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/Linux-PAM/modules/pam_nologin/Makefile b/Linux-PAM/modules/pam_nologin/Makefile new file mode 100644 index 00000000..2ad38ffd --- /dev/null +++ b/Linux-PAM/modules/pam_nologin/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:04 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_nologin + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_nologin/README b/Linux-PAM/modules/pam_nologin/README new file mode 100644 index 00000000..5de704c3 --- /dev/null +++ b/Linux-PAM/modules/pam_nologin/README @@ -0,0 +1,23 @@ +# $Id: README,v 1.2 2002/06/27 05:43:28 agmorgan Exp $ +# + +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. + +The default return value for this module is PAM_IGNORE, you can +override this with the successok module argument. + +module services provided: + + auth _authenticate and _setcred + account _acct_mgmt + +optional arguments: + + file=<alternative-nologin-pathname> - choose a different file + successok - return PAM_SUCCESS if no file + +[Original README by Michael K. Johnson] + + diff --git a/Linux-PAM/modules/pam_nologin/pam_nologin.c b/Linux-PAM/modules/pam_nologin/pam_nologin.c new file mode 100644 index 00000000..8e7e124a --- /dev/null +++ b/Linux-PAM/modules/pam_nologin/pam_nologin.c @@ -0,0 +1,206 @@ +/* pam_nologin module */ + +/* + * $Id: pam_nologin.c,v 1.6 2005/01/07 15:31:26 t8m Exp $ + * + * Written by Michael K. Johnson <johnsonm@redhat.com> 1996/10/24 + * + */ + +#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 +#define PAM_SM_ACCOUNT + +#include <security/pam_modules.h> + +#include <security/_pam_modutil.h> + +/* + * parse some command line options + */ +struct opt_s { + int retval_when_nofile; + const char *nologin_file; +}; + +static void parse_args(pam_handle_t *pamh, int argc, const char **argv, + struct opt_s *opts) +{ + int i; + + memset(opts, 0, sizeof(*opts)); + + opts->retval_when_nofile = PAM_IGNORE; + opts->nologin_file = "/etc/nologin"; + + for (i=0; i<argc; ++i) { + if (!strcmp("successok", argv[i])) { + opts->retval_when_nofile = PAM_SUCCESS; + } else if (!memcmp("file=", argv[i], 5)) { + opts->nologin_file = argv[i] + 5; + } else { + /* XXX - ignore for now. Later, we'll use the logging + function in pammodutils */ + } + } +} + +/* + * do the meat of the work for this module + */ + +static int perform_check(pam_handle_t *pamh, struct opt_s *opts) +{ + const char *username; + int retval = PAM_SUCCESS; + int fd; + + retval = opts->retval_when_nofile; + + if ((pam_get_user(pamh, &username, NULL) != PAM_SUCCESS) || !username) { + return PAM_USER_UNKNOWN; + } + + if ((fd = open(opts->nologin_file, O_RDONLY, 0)) >= 0) { + + 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; + + user_pwd = _pammodutil_getpwnam(pamh, username); + if (user_pwd == NULL) { + + retval = PAM_USER_UNKNOWN; + message.msg_style = PAM_ERROR_MSG; + + } else if (user_pwd->pw_uid) { + + retval = PAM_AUTH_ERR; + message.msg_style = PAM_ERROR_MSG; + + } else { + + /* root can still log in; lusers cannot */ + message.msg_style = PAM_TEXT_INFO; + + } + + /* fill in message buffer with contents of /etc/nologin */ + if (fstat(fd, &st) < 0) { + /* give up trying to display message */ + goto clean_up_fd; + } + + message.msg = mtmp = malloc(st.st_size+1); + if (!message.msg) { + /* if malloc failed... */ + retval = PAM_BUF_ERR; + goto clean_up_fd; + } + + if (_pammodutil_read(fd, mtmp, st.st_size) == st.st_size) { + mtmp[st.st_size] = '\000'; + + /* + * Use conversation function to give user contents + * of /etc/nologin + */ + + if (pam_get_item(pamh, PAM_CONV, (const void **)&conversation) + == PAM_SUCCESS && conversation && conversation->conv) { + (void) conversation->conv(1, + (const struct pam_message **)&pmessage, + &resp, conversation->appdata_ptr); + + if (resp) { + _pam_drop_reply(resp, 1); + } + } + } + else + retval = PAM_SYSTEM_ERR; + + free(mtmp); + + clean_up_fd: + + close(fd); + } + + return retval; +} + +/* --- authentication management functions --- */ + +PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + struct opt_s opts; + + parse_args(pamh, argc, argv, &opts); + + return perform_check(pamh, &opts); +} + +PAM_EXTERN +int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + struct opt_s opts; + + parse_args(pamh, argc, argv, &opts); + + return opts.retval_when_nofile; +} + +/* --- account management function --- */ + +PAM_EXTERN +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + struct opt_s opts; + + parse_args(pamh, argc, argv, &opts); + + return perform_check(pamh, &opts); +} + + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_nologin_modstruct = { + "pam_nologin", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + NULL, + NULL, + NULL, +}; + +#endif /* PAM_STATIC */ + +/* end of module definition */ diff --git a/Linux-PAM/modules/pam_permit/Makefile b/Linux-PAM/modules/pam_permit/Makefile new file mode 100644 index 00000000..49f3b3dd --- /dev/null +++ b/Linux-PAM/modules/pam_permit/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:04 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_permit + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_permit/README b/Linux-PAM/modules/pam_permit/README new file mode 100644 index 00000000..52e7364e --- /dev/null +++ b/Linux-PAM/modules/pam_permit/README @@ -0,0 +1,4 @@ +# $Id: README,v 1.1.1.1 2000/06/20 22:11:46 agmorgan Exp $ +# + +this module always returns PAM_SUCCESS, it ignores all options. diff --git a/Linux-PAM/modules/pam_permit/pam_permit.c b/Linux-PAM/modules/pam_permit/pam_permit.c new file mode 100644 index 00000000..08d464b8 --- /dev/null +++ b/Linux-PAM/modules/pam_permit/pam_permit.c @@ -0,0 +1,114 @@ +/* pam_permit module */ + +/* + * $Id: pam_permit.c,v 1.3 2004/09/22 09:37:49 kukuk Exp $ + * + * Written by Andrew Morgan <morgan@parc.power.net> 1996/3/11 + * + */ + +#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")); + retval = pam_set_item(pamh, PAM_USER, (const void *) DEFAULT_USER); + if (retval != PAM_SUCCESS) + return PAM_USER_UNKNOWN; + } + 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/Linux-PAM/modules/pam_pwdb/BUGS b/Linux-PAM/modules/pam_pwdb/BUGS new file mode 100644 index 00000000..d51686e5 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/BUGS @@ -0,0 +1,3 @@ +$Id: BUGS,v 1.2 2000/12/04 19:02:34 baggins Exp $ + +As of Linux-PAM-0.52 this is new. No known bugs yet. diff --git a/Linux-PAM/modules/pam_pwdb/CHANGELOG b/Linux-PAM/modules/pam_pwdb/CHANGELOG new file mode 100644 index 00000000..a3614031 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/CHANGELOG @@ -0,0 +1,10 @@ +$Id: CHANGELOG,v 1.1.1.1 2000/06/20 22:11:46 agmorgan Exp $ + +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/Linux-PAM/modules/pam_pwdb/Makefile b/Linux-PAM/modules/pam_pwdb/Makefile new file mode 100644 index 00000000..228c6704 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/Makefile @@ -0,0 +1,127 @@ +# $Id: Makefile,v 1.3 2004/09/28 13:48:47 kukuk Exp $ +# +# 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 +# + +include ../../Make.Rules + +ifeq ($(HAVE_LIBPWDB),yes) + +EXTRALS += -lpwdb +EXTRAS += -DCHKPWD_HELPER=\"$(SUPLEMENTED)/$(CHKPWD)\" + +ifeq ($(HAVE_LIBCRYPT),yes) + EXTRALS += -lcrypt +endif +ifeq ($(HAVE_LIBNSL),yes) + EXTRALS += -lnsl +endif + +TITLE=pam_pwdb +CHKPWD=pwdb_chkpwd + +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) $(CFLAGS) -o $(CHKPWD) $^ $(LDFLAGS) -lpwdb $(EXTRALS) + +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 $(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 + rm -f *.a *.o *.so *.bak + rm -fr dynamic static + +else + +include ../dont_makefile + +endif diff --git a/Linux-PAM/modules/pam_pwdb/README b/Linux-PAM/modules/pam_pwdb/README new file mode 100644 index 00000000..4f420855 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_pwdb/TODO b/Linux-PAM/modules/pam_pwdb/TODO new file mode 100644 index 00000000..520a262e --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/TODO @@ -0,0 +1,34 @@ +$Id: TODO,v 1.1.1.1 2000/06/20 22:11:47 agmorgan Exp $ + + * 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/Linux-PAM/modules/pam_pwdb/bigcrypt.-c b/Linux-PAM/modules/pam_pwdb/bigcrypt.-c new file mode 100644 index 00000000..321f2491 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_pwdb/md5.c b/Linux-PAM/modules/pam_pwdb/md5.c new file mode 100644 index 00000000..44282941 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/md5.c @@ -0,0 +1,255 @@ +/* $Id: md5.c,v 1.2 2000/12/04 19:02:34 baggins Exp $ + * + * 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/Linux-PAM/modules/pam_pwdb/md5.h b/Linux-PAM/modules/pam_pwdb/md5.h new file mode 100644 index 00000000..75c4dbac --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/md5.h @@ -0,0 +1,30 @@ +#ifndef MD5_H +#define MD5_H + +typedef unsigned int uint32; + +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/Linux-PAM/modules/pam_pwdb/md5_crypt.c b/Linux-PAM/modules/pam_pwdb/md5_crypt.c new file mode 100644 index 00000000..826087f2 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/md5_crypt.c @@ -0,0 +1,138 @@ +/* $Id: md5_crypt.c,v 1.2 2000/12/04 19:02:34 baggins Exp $ + * + * ---------------------------------------------------------------------------- + * "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/Linux-PAM/modules/pam_pwdb/pam_pwdb.c b/Linux-PAM/modules/pam_pwdb/pam_pwdb.c new file mode 100644 index 00000000..8c75ac23 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/pam_pwdb.c @@ -0,0 +1,248 @@ +/* + * $Id: pam_pwdb.c,v 1.3 2000/11/19 23:54:04 agmorgan Exp $ + * + * 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: pam_pwdb.c,v 1.3 2000/11/19 23:54:04 agmorgan Exp $\n" +" - PWDB Pluggable Authentication module. <morgan@linux.kernel.org>" +; + +/* #define DEBUG */ + +#include <security/_pam_aconf.h> + +#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); + 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/Linux-PAM/modules/pam_pwdb/pam_unix_acct.-c b/Linux-PAM/modules/pam_pwdb/pam_unix_acct.-c new file mode 100644 index 00000000..adcb6538 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/pam_unix_acct.-c @@ -0,0 +1,272 @@ +/* + * $Id: pam_unix_acct.-c,v 1.2 2000/12/04 19:02:34 baggins Exp $ + * + * See end of file for copyright information + */ + +static const char rcsid_acct[] = +"$Id: pam_unix_acct.-c,v 1.2 2000/12/04 19:02:34 baggins Exp $\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/Linux-PAM/modules/pam_pwdb/pam_unix_auth.-c b/Linux-PAM/modules/pam_pwdb/pam_unix_auth.-c new file mode 100644 index 00000000..31230394 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/pam_unix_auth.-c @@ -0,0 +1,131 @@ +/* + * $Id: pam_unix_auth.-c,v 1.1.1.1 2000/06/20 22:11:49 agmorgan Exp $ + * + * See end of file for Copyright information. + */ + +static const char rcsid_auth[] = +"$Id: pam_unix_auth.-c,v 1.1.1.1 2000/06/20 22:11:49 agmorgan Exp $: 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/Linux-PAM/modules/pam_pwdb/pam_unix_md.-c b/Linux-PAM/modules/pam_pwdb/pam_unix_md.-c new file mode 100644 index 00000000..65476732 --- /dev/null +++ b/Linux-PAM/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(""); /* called from only one place so this is safe */ + } + + return x; /* this must be deleted elsewhere */ +} +#endif /* PWDB_NO_MD_COMPAT */ diff --git a/Linux-PAM/modules/pam_pwdb/pam_unix_passwd.-c b/Linux-PAM/modules/pam_pwdb/pam_unix_passwd.-c new file mode 100644 index 00000000..7ed65000 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/pam_unix_passwd.-c @@ -0,0 +1,373 @@ +/* $Id: pam_unix_passwd.-c,v 1.3 2001/11/12 06:57:38 agmorgan Exp $ */ + +static const char rcsid_pass[] = +"$Id: pam_unix_passwd.-c,v 1.3 2001/11/12 06:57:38 agmorgan Exp $\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. + * + * This includes the fact that the password is _not_ NULL. + */ + + /* + * 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/Linux-PAM/modules/pam_pwdb/pam_unix_pwupd.-c b/Linux-PAM/modules/pam_pwdb/pam_unix_pwupd.-c new file mode 100644 index 00000000..a1fc65ff --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/pam_unix_pwupd.-c @@ -0,0 +1,260 @@ +/* + * $Id: pam_unix_pwupd.-c,v 1.1.1.1 2000/06/20 22:11:51 agmorgan Exp $ + * + * 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/Linux-PAM/modules/pam_pwdb/pam_unix_sess.-c b/Linux-PAM/modules/pam_pwdb/pam_unix_sess.-c new file mode 100644 index 00000000..395bd9bb --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/pam_unix_sess.-c @@ -0,0 +1,98 @@ +/* + * $Id: pam_unix_sess.-c,v 1.2 2000/12/04 19:02:34 baggins Exp $ + * + * See end for Copyright information + */ + +static const char rcsid_sess[] = +"$Id: pam_unix_sess.-c,v 1.2 2000/12/04 19:02:34 baggins Exp $\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/Linux-PAM/modules/pam_pwdb/pwdb_chkpwd.c b/Linux-PAM/modules/pam_pwdb/pwdb_chkpwd.c new file mode 100644 index 00000000..36c248ef --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/pwdb_chkpwd.c @@ -0,0 +1,221 @@ +/* + * $Id: pwdb_chkpwd.c,v 1.4 2001/12/09 21:44:58 agmorgan Exp $ + * + * 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. + * + */ + +#include <security/_pam_aconf.h> + +#ifdef MEMORY_DEBUG +# undef exit +# undef strdup +# undef free +#endif /* MEMORY_DEBUG */ + +#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(int argc, char **argv) +{ + const struct pwdb *pw=NULL; + const struct pwdb_entry *pwe=NULL; + char pass[MAXPASS+1]; + int npass, force_failure=0; + 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); + } + if (argc == 2) { + if (pwdb_get_entry(pw, "user", &pwe) == PWDB_SUCCESS) { + if (pwe == NULL) { + force_failure = 1; + } else { + if (strcmp((const char *) pwe->value, argv[1])) { + force_failure = 1; + } + pwdb_entry_delete(&pwe); + } + } + } + + /* 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); + + if ((retval != UNIX_FAILED) && force_failure) { + retval = UNIX_FAILED; + } + + /* 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/Linux-PAM/modules/pam_pwdb/support.-c b/Linux-PAM/modules/pam_pwdb/support.-c new file mode 100644 index 00000000..bfa4e8a1 --- /dev/null +++ b/Linux-PAM/modules/pam_pwdb/support.-c @@ -0,0 +1,963 @@ +/* + * $Id: support.-c,v 1.6 2004/09/15 12:06:17 kukuk Exp $ + * + * 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_NOREAP 22 /* don't reap child process */ +/* -------------- */ +#define UNIX_CTRLS_ 23 /* 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 }, +/* UNIX_NOREAP */ {"noreap", _ALL_ON_, 010000000 }, +}; + +#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 <signal.h> +#include <sys/types.h> +#include <sys/wait.h> + +static int pwdb_run_helper_binary(pam_handle_t *pamh, const char *passwd, + unsigned int ctrl, const char *user) +{ + int retval, child, fds[2]; + void (*sighandler)(int) = NULL; + + D(("called.")); + /* create a pipe for the password */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + return PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + sighandler = signal(SIGCHLD, SIG_DFL); + } + + /* fork */ + child = fork(); + if (child == 0) { + static char *args[] = { NULL, 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); + args[1] = x_strdup(user); + + 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 */ + 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[0]); /* we close this after the write because we want + to avoid a possible SIGPIPE. */ + 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; + } + + if (sighandler != NULL) { + (void) signal(SIGCHLD, sighandler); /* restore old signal handler */ + } + + 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, ctrl, name); + } 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 { + + /* there is no way that p can be NULL (one can be "") */ + 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/Linux-PAM/modules/pam_radius/Makefile b/Linux-PAM/modules/pam_radius/Makefile new file mode 100644 index 00000000..aa149d3e --- /dev/null +++ b/Linux-PAM/modules/pam_radius/Makefile @@ -0,0 +1,95 @@ +# +# 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 +# + +include ../../Make.Rules + +TITLE=pam_radius +CONFD=$(CONFIGED)/security +export CONFD +CONFILE=$(CONFD)/radius.conf +export CONFILE + +ifeq ($(HAVE_LIBPWDB),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 ####################### + +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 *~ + rm -f *.a *.o *.so *.bak dynamic/* static/* + rm -rf dynamic static + +.c.o: + $(CC) $(CFLAGS) -c $< + +else + +include ../dont_makefile + +endif diff --git a/Linux-PAM/modules/pam_radius/README b/Linux-PAM/modules/pam_radius/README new file mode 100644 index 00000000..253308fd --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_radius/pam_radius.c b/Linux-PAM/modules/pam_radius/pam_radius.c new file mode 100644 index 00000000..b412edf9 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_radius/pam_radius.h b/Linux-PAM/modules/pam_radius/pam_radius.h new file mode 100644 index 00000000..67230243 --- /dev/null +++ b/Linux-PAM/modules/pam_radius/pam_radius.h @@ -0,0 +1,40 @@ +/* + * $Id: pam_radius.h,v 1.2 2000/11/19 23:54:05 agmorgan Exp $ + */ + +#ifndef PAM_RADIUS_H +#define PAM_RADIUS_H + +#include <security/_pam_aconf.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/Linux-PAM/modules/pam_rhosts/Makefile b/Linux-PAM/modules/pam_rhosts/Makefile new file mode 100644 index 00000000..d12e00c0 --- /dev/null +++ b/Linux-PAM/modules/pam_rhosts/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:05 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_rhosts_auth + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_rhosts/README b/Linux-PAM/modules/pam_rhosts/README new file mode 100644 index 00000000..d2e93d1d --- /dev/null +++ b/Linux-PAM/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 useful, 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/Linux-PAM/modules/pam_rhosts/pam_rhosts_auth.c b/Linux-PAM/modules/pam_rhosts/pam_rhosts_auth.c new file mode 100644 index 00000000..979580ec --- /dev/null +++ b/Linux-PAM/modules/pam_rhosts/pam_rhosts_auth.c @@ -0,0 +1,802 @@ +/*---------------------------------------------------------------------- + * 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. + */ + +#include <security/_pam_aconf.h> + +#define USER_RHOSTS_FILE "/.rhosts" /* prefixed by user's home dir */ + +#ifdef __linux__ +#include <endian.h> +#endif + +#ifdef HAVE_SYS_FSUID_H +#include <sys/fsuid.h> +#endif /* HAVE_SYS_FSUID_H */ + +#ifdef HAVE_NET_IF_H +#include <sys/if.h> +#endif + +#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> + +#include <pwd.h> +#include <grp.h> +#include <sys/file.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <stdint.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> +#include <security/_pam_modutil.h> + +#ifdef _ISOC9X_SOURCE +#include <inttypes.h> +#define U32 uint32_t +#else +/* to the best of my knowledge, all modern UNIX boxes have 32 bits integers */ +#define U32 unsigned int +#endif /* _ISOC9X_SOURCE */ + +/* Use the C99 type; older platforms will need this to be typedef'ed + elsewhere */ +#define U32 uint32_t + + +/* + * 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 (strncmp(arg, "superuser=", sizeof("superuser=")-1) == 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); + } else if (strncmp("+",lhost,1) == 0) { + /* '+hostname' is supposed to be equivalent to 'hostname' */ + lhost++; + } + + + /* 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 = _pammodutil_getpwnam(pamh, 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 = _pammodutil_getgrgid(pamh, 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 = NULL; + const char *ruser = NULL, *rhost = NULL; + 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 */ + D(("getting rhost")); + 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 */ + D(("getting ruser")); + 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 */ + D(("getting 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 = _pammodutil_getpwnam(pamh, 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 diff --git a/Linux-PAM/modules/pam_rootok/Makefile b/Linux-PAM/modules/pam_rootok/Makefile new file mode 100644 index 00000000..b908b115 --- /dev/null +++ b/Linux-PAM/modules/pam_rootok/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:05 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_rootok + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_rootok/README b/Linux-PAM/modules/pam_rootok/README new file mode 100644 index 00000000..43b92e6c --- /dev/null +++ b/Linux-PAM/modules/pam_rootok/README @@ -0,0 +1,18 @@ +# $Id: README,v 1.2 2001/11/27 05:37:30 agmorgan Exp $ +# + +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 _authentication and _setcred (blank) + +Andrew Morgan diff --git a/Linux-PAM/modules/pam_rootok/pam_rootok.c b/Linux-PAM/modules/pam_rootok/pam_rootok.c new file mode 100644 index 00000000..e1e09b6e --- /dev/null +++ b/Linux-PAM/modules/pam_rootok/pam_rootok.c @@ -0,0 +1,110 @@ +/* pam_rootok module */ + +/* + * $Id: pam_rootok.c,v 1.3 2002/05/26 23:00:28 agmorgan Exp $ + * + * 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> +#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> + +/* 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, "authentication %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/Linux-PAM/modules/pam_securetty/Makefile b/Linux-PAM/modules/pam_securetty/Makefile new file mode 100644 index 00000000..8ac853c5 --- /dev/null +++ b/Linux-PAM/modules/pam_securetty/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:05 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_securetty + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_securetty/README b/Linux-PAM/modules/pam_securetty/README new file mode 100644 index 00000000..1df095c9 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_securetty/pam_securetty.c b/Linux-PAM/modules/pam_securetty/pam_securetty.c new file mode 100644 index 00000000..3a9ae421 --- /dev/null +++ b/Linux-PAM/modules/pam_securetty/pam_securetty.c @@ -0,0 +1,232 @@ +/* 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> +#include <ctype.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> +#include <security/_pam_modutil.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; +} + +static int securetty_perform_check(pam_handle_t *pamh, int flags, int ctrl, + const char *function_name) +{ + int retval = PAM_AUTH_ERR; + const char *username; + char *uttyname; + char ttyfileline[256]; + char ptname[256]; + struct stat ttyfileinfo; + struct passwd *user_pwd; + FILE *ttyfile; + + /* log a trail for debugging */ + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_DEBUG, "pam_securetty called via %s function", + function_name); + } + + 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); + } + + user_pwd = _pammodutil_getpwnam(pamh, 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; + } + + 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; + } + + 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; + } + + if (isdigit(uttyname[0])) { + snprintf(ptname, sizeof(ptname), "pts/%s", uttyname); + } else { + ptname[0] = '\0'; + } + + retval = 1; + + while ((fgets(ttyfileline, sizeof(ttyfileline)-1, ttyfile) != NULL) + && retval) { + if (ttyfileline[strlen(ttyfileline) - 1] == '\n') + ttyfileline[strlen(ttyfileline) - 1] = '\0'; + + retval = ( strcmp(ttyfileline, uttyname) + && (!ptname[0] || strcmp(ptname, uttyname)) ); + } + fclose(ttyfile); + + if (retval) { + _pam_log(LOG_WARNING, "access denied: tty '%s' is not secure !", + uttyname); + + retval = PAM_AUTH_ERR; + } else { + if ((retval == PAM_SUCCESS) && (ctrl & PAM_DEBUG_ARG)) { + _pam_log(LOG_DEBUG, "access allowed for '%s' on '%s'", + username, uttyname); + } + retval = PAM_SUCCESS; + + } + + return retval; +} + +/* --- authentication management functions --- */ + +PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + int ctrl; + + /* parse the arguments */ + ctrl = _pam_parse(argc, argv); + + return securetty_perform_check(pamh, flags, ctrl, __FUNCTION__); +} + +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) +{ + int ctrl; + + /* parse the arguments */ + ctrl = _pam_parse(argc, argv); + + /* take the easy route */ + return securetty_perform_check(pamh, flags, ctrl, __FUNCTION__); +} + + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_securetty_modstruct = { + "pam_securetty", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + NULL, + NULL, + NULL, +}; + +#endif /* PAM_STATIC */ + +/* end of module definition */ diff --git a/Linux-PAM/modules/pam_shells/Makefile b/Linux-PAM/modules/pam_shells/Makefile new file mode 100644 index 00000000..f1d7ff51 --- /dev/null +++ b/Linux-PAM/modules/pam_shells/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:05 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_shells + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_shells/README b/Linux-PAM/modules/pam_shells/README new file mode 100644 index 00000000..aa63a827 --- /dev/null +++ b/Linux-PAM/modules/pam_shells/README @@ -0,0 +1,9 @@ +pam_shells: + Authentication is granted if the users shell is listed in + /etc/shells. + + 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/Linux-PAM/modules/pam_shells/pam_shells.c b/Linux-PAM/modules/pam_shells/pam_shells.c new file mode 100644 index 00000000..64359eac --- /dev/null +++ b/Linux-PAM/modules/pam_shells/pam_shells.c @@ -0,0 +1,158 @@ +/* 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 <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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 +#define PAM_SM_ACCOUNT + +#include <security/pam_modules.h> +#include <security/_pam_modutil.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(); +} + +static int perform_check(pam_handle_t *pamh, int flags) +{ + 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 || (userName[0] == '\0')) { + + /* Don't let them use a NULL username... */ + retval = pam_get_user(pamh,&userName,NULL); + if (retval != PAM_SUCCESS) + return PAM_SERVICE_ERR; + + /* It could still be NULL the second time. */ + if (!userName || (userName[0] == '\0')) + return PAM_SERVICE_ERR; + } + + pw = _pammodutil_getpwnam(pamh, userName); + if (!pw) { + return PAM_AUTH_ERR; /* user doesn't exist */ + } + userShell = pw->pw_shell; + + if (stat(SHELL_FILE,&sb)) { + _pam_log(LOG_ERR, "%s cannot be stat'd (it probably does not exist)", + SHELL_FILE); + return PAM_AUTH_ERR; /* must have /etc/shells */ + } + + if ((sb.st_mode & S_IWOTH) || !S_ISREG(sb.st_mode)) { + _pam_log(LOG_ERR, "%s is either world writable or not a normal file", + SHELL_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 %s", SHELL_FILE); + return PAM_SERVICE_ERR; + } + + retval = 1; + + while(retval && (fgets(shellFileLine, 255, shellFile) != NULL)) { + if (shellFileLine[strlen(shellFileLine) - 1] == '\n') + shellFileLine[strlen(shellFileLine) - 1] = '\0'; + retval = strcmp(shellFileLine, userShell); + } + + fclose(shellFile); + + if (retval) { + return PAM_AUTH_ERR; + } else { + return PAM_SUCCESS; + } +} + +/* --- authentication management functions (only) --- */ + +PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return perform_check(pamh, flags); +} + +PAM_EXTERN +int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,const char **argv) +{ + return PAM_SUCCESS; +} + +/* --- account management functions (only) --- */ + +PAM_EXTERN +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return perform_check(pamh, flags); +} + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_shells_modstruct = { + "pam_shells", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + NULL, + NULL, + NULL, +}; + +#endif /* PAM_STATIC */ + +/* end of module definition */ diff --git a/Linux-PAM/modules/pam_stress/Makefile b/Linux-PAM/modules/pam_stress/Makefile new file mode 100644 index 00000000..3512c853 --- /dev/null +++ b/Linux-PAM/modules/pam_stress/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:05 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_stress + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_stress/README b/Linux-PAM/modules/pam_stress/README new file mode 100644 index 00000000..b4273f50 --- /dev/null +++ b/Linux-PAM/modules/pam_stress/README @@ -0,0 +1,66 @@ +# +# $Id: README,v 1.1.1.1 2000/06/20 22:11:57 agmorgan Exp $ +# +# 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/Linux-PAM/modules/pam_stress/pam_stress.c b/Linux-PAM/modules/pam_stress/pam_stress.c new file mode 100644 index 00000000..2d361c3e --- /dev/null +++ b/Linux-PAM/modules/pam_stress/pam_stress.c @@ -0,0 +1,567 @@ +/* pam_stress module */ + +/* $Id: pam_stress.c,v 1.4 2004/09/22 09:37:50 kukuk Exp $ + * + * created by Andrew Morgan <morgan@linux.kernel.org> 1996/3/12 + */ + +#include <security/_pam_aconf.h> + +#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> + + +/* 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; + + retval = pam_get_item(pamh,PAM_CONV,(const void **)&conv); + if (retval == PAM_SUCCESS && conv) { + 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"); + if (retval == PAM_SUCCESS) + retval = PAM_BAD_ITEM; /* conv was null */ + } + + 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) ) { + if ((pass = strdup(pass)) == NULL) + return PAM_BUF_ERR; + } 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; + } + if (resp) + 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 (retval != PAM_SUCCESS || !username) { + _pam_log(LOG_WARNING, "pam_sm_authenticate: failed to get username"); + if (retval == PAM_SUCCESS) + retval = PAM_USER_UNKNOWN; /* username was null */ + return retval; + } + else if ((ctrl & PAM_ST_DEBUG) && (retval == PAM_SUCCESS)) { + _pam_log(LOG_DEBUG, "pam_sm_authenticate: username = %s", username); + } + + /* 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); + _pam_overwrite(pass); /* clean up local copy of password */ + free(pass); + pass = NULL; + if (retval != PAM_SUCCESS) { + _pam_log(LOG_WARNING, "pam_sm_authenticate: " + "failed to store new password"); + return retval; + } + + /* 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) { + int retval; + void *text = strdup("yes"); + if (!text) + return PAM_BUF_ERR; + retval = pam_set_data(pamh,"stress_new_pwd",text,wipe_up); + if (retval != PAM_SUCCESS) { + _pam_log(LOG_DEBUG, + "pam_sm_acct_mgmt: failed setting stress_new_pwd"); + free(text); + return retval; + } + + 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 || !username) + || (pam_get_item(pamh, PAM_SERVICE, (const void **) &service) + != PAM_SUCCESS || !service)) { + _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 || !username) + || (pam_get_item(pamh, PAM_SERVICE, (const void **)&service) + != PAM_SUCCESS || !service)) { + _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); + _pam_overwrite(pass); + free(pass); + pass = NULL; + if (retval != PAM_SUCCESS) { + _pam_log(LOG_DEBUG + ,"pam_sm_chauthtok: could not set OLDAUTHTOK"); + return retval; + } + } + + /* 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 unchanged"; + 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/Linux-PAM/modules/pam_succeed_if/Makefile b/Linux-PAM/modules/pam_succeed_if/Makefile new file mode 100644 index 00000000..cea9be3b --- /dev/null +++ b/Linux-PAM/modules/pam_succeed_if/Makefile @@ -0,0 +1,16 @@ +# +# $Id: Makefile,v 1.1 2004/09/24 11:42:39 kukuk Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_succeed_if +MAN8=$(TITLE).8 + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_succeed_if/README b/Linux-PAM/modules/pam_succeed_if/README new file mode 100644 index 00000000..fdb278ef --- /dev/null +++ b/Linux-PAM/modules/pam_succeed_if/README @@ -0,0 +1,68 @@ +pam_succeed_if: + Succeed or fail based on account characteristics. + + pam_succeed_if.so is designed to succeed or fail authentication based + on characteristics of the account belonging to the user being + authenticated. + + The module can be given one or more conditions as module arguments, and + authentication will succeed only if all of the conditions are met. + + Conditions are expressed in the form + + ATTRIBUTE OPERATOR VALUE + + Recognized attributes: + + LOGIN - The user's login name. + UID - The user's UID. + GID - The user's primary GID. + SHELL - The user's shell. + HOME - The user's home directory. + + Recognized operators: + + < - Arithmetic less-than. + <= - Arithmetic less-than-or-equal-to. + > - Arithmetic greater-than. + >= - Arithmetic greater-than-or-equal-to. + eq - Arithmetic equality. + = - String equality. + ne - Arithmetic inequality. + != - String inequality. + =~ - Wildcard match. + !~ - Wildcard mismatch. + ingroup - Group membership check. [*] + notingroup - Group non-membership check. [*] + + * The "ingroup" and "notingroup" operators should only be + used with the USER attribute. + + Examples: + + Deny authentication to all users except those in the wheel + group, before even asking for a password: + auth requisite pam_succeed_if.so user ingroup wheel + + Assume all users with UID less than 500 ("system users") have + valid accounts. + account sufficient pam_succeed_if.so uid < 500 + + Deny login to all nologin users. + auth requisite pam_succeed_if.so shell !~ nologin + +RECOGNIZED ARGUMENTS: + debug write debugging messages to syslog + use_uid perform checks on the account of the user under whose + UID the application is running instead of the user + being authenticated + quiet don't log failure or success to syslog + quiet_fail don't log failure to syslog + quiet_success don't log success to syslog + + +MODULE SERVICES PROVIDED: + authentication, account management + +AUTHOR: + Nalin Dahyabhai <nalin@redhat.com> diff --git a/Linux-PAM/modules/pam_succeed_if/pam_succeed_if.8 b/Linux-PAM/modules/pam_succeed_if/pam_succeed_if.8 new file mode 100644 index 00000000..da95a033 --- /dev/null +++ b/Linux-PAM/modules/pam_succeed_if/pam_succeed_if.8 @@ -0,0 +1,37 @@ +.\" Copyright 2003, 2004 Red Hat, Inc. +.\" Written by Nalin Dahyabhai <nalin@redhat.com> +.TH pam_succeed_if 8 2004/12/27 "Linux-PAM" "System Administrator's Manual" + +.SH NAME +pam_succeed_if \- succeed or fail based on account characteristics + +.SH SYNOPSIS +.B account sufficient pam_succeed_if.so uid < 500 + +.SH DESCRIPTION +pam_succeed_if.so is designed to succeed or fail authentication based on +characteristics of the account belonging to the user being authenticated. + +The module can be given one or more conditions as module arguments, and +authentication will succeed only if all of the conditions are met. + +.SH ARGUMENTS +.IP debug +Turns on debugging messages sent to syslog. +.IP use_uid +Evaluate conditions using the account of the user whose UID the application +is running under instead of the user being authenticated. +.IP quiet +Don't log failure or success to the system log. +.IP quiet_fail +Don't log failure to the system log. +.IP quiet_success +Don't log success to the system log. + + +.SH BUGS +Let's hope not, but if you find any, please report them via the "Bug Track" +link at http://bugzilla.redhat.com/bugzilla/ + +.SH AUTHOR +Nalin Dahyabhai <nalin@redhat.com> diff --git a/Linux-PAM/modules/pam_succeed_if/pam_succeed_if.c b/Linux-PAM/modules/pam_succeed_if/pam_succeed_if.c new file mode 100644 index 00000000..23974afa --- /dev/null +++ b/Linux-PAM/modules/pam_succeed_if/pam_succeed_if.c @@ -0,0 +1,470 @@ +/****************************************************************************** + * A simple user-attribute based module for PAM. + * + * Copyright (c) 2003 Red Hat, Inc. + * Written by Nalin Dahyabhai <nalin@redhat.com> + * + * 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 _GNU_SOURCE + +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <security/pam_modules.h> +#include <security/_pam_modutil.h> + +#define MODULE "pam_succeed_if" + +static void +log_error(int priority, const char *fmt, ...) +{ + va_list va; + char *fmt2; + fmt2 = malloc(strlen(fmt) + strlen(MODULE) + 3); + va_start(va, fmt); + if (fmt2 == NULL) { + vsyslog(LOG_AUTHPRIV | priority, fmt, va); + } else { + snprintf(fmt2, strlen(fmt) + strlen(MODULE) + 3, + "%s: %s", MODULE, fmt); + vsyslog(LOG_AUTHPRIV | priority, fmt2, va); + free(fmt2); + } + va_end(va); +} + +/* Basically, run cmp(atol(left), atol(right)), returning PAM_SUCCESS if + * the function returns non-zero, PAM_AUTH_ERR if it returns zero, and + * PAM_SYSTEM_ERR if the arguments can't be parsed as numbers. */ +static int +evaluate_num(const char *left, const char *right, int (*cmp)(int, int)) +{ + long l, r; + char *p; + int ret = PAM_SUCCESS; + + errno = 0; + l = strtol(left, &p, 0); + if ((p == NULL) || (*p != '\0') || errno) { + log_error(LOG_INFO, "\"%s\" is not a number", left); + ret = PAM_SERVICE_ERR; + } + + r = strtol(right, &p, 0); + if ((p == NULL) || (*p != '\0') || errno) { + log_error(LOG_INFO, "\"%s\" is not a number", right); + ret = PAM_SERVICE_ERR; + } + + if (ret != PAM_SUCCESS) { + return ret; + } + + return cmp(l, r) ? PAM_SUCCESS : PAM_AUTH_ERR; +} + +/* Simple numeric comparison callbacks. */ +static int +eq(int i, int j) +{ + return i == j; +} +static int +ne(int i, int j) +{ + return i != j; +} +static int +lt(int i, int j) +{ + return i < j; +} +static int +le(int i, int j) +{ + return lt(i, j) || eq(i, j); +} +static int +gt(int i, int j) +{ + return i > j; +} +static int +ge(int i, int j) +{ + return gt(i, j) || eq(i, j); +} + +/* Test for numeric equality. */ +static int +evaluate_eqn(const char *left, const char *right) +{ + return evaluate_num(left, right, eq); +} +/* Test for string equality. */ +static int +evaluate_eqs(const char *left, const char *right) +{ + return (strcmp(left, right) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Test for numeric inequality. */ +static int +evaluate_nen(const char *left, const char *right) +{ + return evaluate_num(left, right, ne); +} +/* Test for string inequality. */ +static int +evaluate_nes(const char *left, const char *right) +{ + return (strcmp(left, right) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Test for numeric less-than-ness(?) */ +static int +evaluate_lt(const char *left, const char *right) +{ + return evaluate_num(left, right, lt); +} +/* Test for numeric less-than-or-equal-ness(?) */ +static int +evaluate_le(const char *left, const char *right) +{ + return evaluate_num(left, right, le); +} +/* Test for numeric greater-than-ness(?) */ +static int +evaluate_gt(const char *left, const char *right) +{ + return evaluate_num(left, right, gt); +} +/* Test for numeric greater-than-or-equal-ness(?) */ +static int +evaluate_ge(const char *left, const char *right) +{ + return evaluate_num(left, right, ge); +} +/* Check for file glob match. */ +static int +evaluate_glob(const char *left, const char *right) +{ + return (fnmatch(right, left, 0) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Check for file glob mismatch. */ +static int +evaluate_noglob(const char *left, const char *right) +{ + return (fnmatch(right, left, 0) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Return PAM_SUCCESS if the user is in the group. */ +static int +evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *group) +{ + int ret; + ret = _pammodutil_user_in_group_nam_nam(pamh, user, group); + switch (ret) { + case 1: + return PAM_SUCCESS; + break; + default: + break; + } + return PAM_AUTH_ERR; +} +/* Return PAM_SUCCESS if the user is NOT in the group. */ +static int +evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *group) +{ + int ret; + ret = _pammodutil_user_in_group_nam_nam(pamh, user, group); + switch (ret) { + case 0: + return PAM_SUCCESS; + break; + default: + break; + } + return PAM_AUTH_ERR; +} + +/* Match a triple. */ +static int +evaluate(pam_handle_t *pamh, int debug, + const char *left, const char *qual, const char *right, + struct passwd *pwd) +{ + char buf[LINE_MAX] = ""; + const char *attribute = left; + /* Figure out what we're evaluating here, and convert it to a string.*/ + if ((strcasecmp(left, "login") == 0) || + (strcasecmp(left, "name") == 0) || + (strcasecmp(left, "user") == 0)) { + snprintf(buf, sizeof(buf), "%s", pwd->pw_name); + left = buf; + } + if (strcasecmp(left, "uid") == 0) { + snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_uid); + left = buf; + } + if (strcasecmp(left, "gid") == 0) { + snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_gid); + left = buf; + } + if (strcasecmp(left, "shell") == 0) { + snprintf(buf, sizeof(buf), "%s", pwd->pw_shell); + left = buf; + } + if ((strcasecmp(left, "home") == 0) || + (strcasecmp(left, "dir") == 0) || + (strcasecmp(left, "homedir") == 0)) { + snprintf(buf, sizeof(buf), "%s", pwd->pw_dir); + left = buf; + } + /* If we have no idea what's going on, return an error. */ + if (left != buf) { + log_error(LOG_CRIT, "unknown attribute \"%s\"", left); + return PAM_SERVICE_ERR; + } + if (debug) { + log_error(LOG_DEBUG, "'%s' resolves to '%s'", attribute, left); + } + + /* Attribute value < some threshold. */ + if ((strcasecmp(qual, "<") == 0) || + (strcasecmp(qual, "lt") == 0)) { + return evaluate_lt(left, right); + } + /* Attribute value <= some threshold. */ + if ((strcasecmp(qual, "<=") == 0) || + (strcasecmp(qual, "le") == 0)) { + return evaluate_le(left, right); + } + /* Attribute value > some threshold. */ + if ((strcasecmp(qual, ">") == 0) || + (strcasecmp(qual, "gt") == 0)) { + return evaluate_gt(left, right); + } + /* Attribute value >= some threshold. */ + if ((strcasecmp(qual, ">=") == 0) || + (strcasecmp(qual, "ge") == 0)) { + return evaluate_ge(left, right); + } + /* Attribute value == some threshold. */ + if (strcasecmp(qual, "eq") == 0) { + return evaluate_eqn(left, right); + } + /* Attribute value = some string. */ + if (strcasecmp(qual, "=") == 0) { + return evaluate_eqs(left, right); + } + /* Attribute value != some threshold. */ + if (strcasecmp(qual, "ne") == 0) { + return evaluate_nen(left, right); + } + /* Attribute value != some string. */ + if (strcasecmp(qual, "!=") == 0) { + return evaluate_nes(left, right); + } + /* Attribute value matches some pattern. */ + if ((strcasecmp(qual, "=~") == 0) || + (strcasecmp(qual, "glob") == 0)) { + return evaluate_glob(left, right); + } + if ((strcasecmp(qual, "!~") == 0) || + (strcasecmp(qual, "noglob") == 0)) { + return evaluate_noglob(left, right); + } + /* User is in this group. */ + if (strcasecmp(qual, "ingroup") == 0) { + return evaluate_ingroup(pamh, pwd->pw_name, right); + } + /* User is not in this group. */ + if (strcasecmp(qual, "notingroup") == 0) { + return evaluate_notingroup(pamh, pwd->pw_name, right); + } + /* Fail closed. */ + return PAM_SERVICE_ERR; +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + const char *prompt; + const char *user; + struct passwd *pwd; + int ret, i, count, use_uid, debug; + const char *left, *right, *qual; + int quiet_fail, quiet_succ; + + /* Get the user prompt. */ + ret = pam_get_item(pamh, PAM_USER_PROMPT, (const void**) &prompt); + if ((ret != PAM_SUCCESS) || (prompt == NULL) || (strlen(prompt) == 0)) { + prompt = "login: "; + } + + quiet_fail = 0; + quiet_succ = 0; + for (use_uid = 0, debug = 0, i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug++; + } + if (strcmp(argv[i], "use_uid") == 0) { + use_uid++; + } + if (strcmp(argv[i], "quiet") == 0) { + quiet_fail++; + quiet_succ++; + } + if (strcmp(argv[i], "quiet_fail") == 0) { + quiet_fail++; + } + if (strcmp(argv[i], "quiet_success") == 0) { + quiet_succ++; + } + } + + if (use_uid) { + /* Get information about the user. */ + pwd = _pammodutil_getpwuid(pamh, getuid()); + if (pwd == NULL) { + log_error(LOG_CRIT, + "error retrieving information about user %ld", + (long)getuid()); + return PAM_SERVICE_ERR; + } + } else { + /* Get the user's name. */ + ret = pam_get_user(pamh, &user, prompt); + if ((ret != PAM_SUCCESS) || (user == NULL)) { + log_error(LOG_CRIT, "error retrieving user name: %s", + pam_strerror(pamh, ret)); + return ret; + } + + /* Get information about the user. */ + pwd = _pammodutil_getpwnam(pamh, user); + if (pwd == NULL) { + log_error(LOG_CRIT, + "error retrieving information about user %s", + user); + return PAM_SERVICE_ERR; + } + } + + /* Walk the argument list. */ + i = count = 0; + left = qual = right = NULL; + while (i <= argc) { + if ((left != NULL) && (qual != NULL) && (right != NULL)) { + ret = evaluate(pamh, debug, + left, qual, right, + pwd); + if (ret != PAM_SUCCESS) { + if(!quiet_fail) + log_error(LOG_INFO, + "requirement \"%s %s %s\" " + "not met by user \"%s\"", + left, qual, right, user); + break; + } + else + if(!quiet_succ) + log_error(LOG_INFO, + "requirement \"%s %s %s\" " + "was met by user \"%s\"", + left, qual, right, user); + left = qual = right = NULL; + } + if ((i < argc) && (strcmp(argv[i], "debug") == 0)) { + i++; + continue; + } + if ((i < argc) && (strcmp(argv[i], "use_uid") == 0)) { + i++; + continue; + } + if ((i < argc) && (strcmp(argv[i], "quiet") == 0)) { + i++; + continue; + } + if ((i < argc) && (strcmp(argv[i], "quiet_fail") == 0)) { + i++; + continue; + } + if ((i < argc) && (strcmp(argv[i], "quiet_success") == 0)) { + i++; + continue; + } + if ((i < argc) && (left == NULL)) { + left = argv[i++]; + count++; + continue; + } + if ((i < argc) && (qual == NULL)) { + qual = argv[i++]; + count++; + continue; + } + if ((i < argc) && (right == NULL)) { + right = argv[i++]; + count++; + continue; + } + i++; + } + + return ret; +} + +int +pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return PAM_SUCCESS; +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return pam_sm_authenticate(pamh, flags, argc, argv); +} diff --git a/Linux-PAM/modules/pam_tally/Makefile b/Linux-PAM/modules/pam_tally/Makefile new file mode 100644 index 00000000..40617a1a --- /dev/null +++ b/Linux-PAM/modules/pam_tally/Makefile @@ -0,0 +1,109 @@ +# +# $Id: Makefile,v 1.5 2004/09/24 13:13:21 kukuk Exp $ +# +# 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!). +# +# + +include ../../Make.Rules + +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 + +LINK_PAMMODUTILS = -L../pammodutil -lpammodutil -L../../libpam -lpam +INCLUDE_PAMMODUTILS = -I../pammodutil/include + +LDFLAGS += $(LINK_PAMMODUTILS) +CFLAGS += $(INCLUDE_PAMMODUTILS) + +####################### don't edit below ####################### + +all: dirs $(LIBSHARED) $(LIBSTATIC) register $(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) $(LDFLAGS) + +$(APPLICATION): $(APPOBJD) $(TITLE).c + $(CC) $(CFLAGS) -o $@ $(APPOBJD) $(LDFLAGS) $(LOADLIBES) + +endif + +ifdef STATIC +$(LIBOBJS): $(LIBSRC) + +$(LIBSTATIC): $(LIBOBJS) + $(LD) -r -o $@ $(LIBOBJS) + +$(APPLICATION): $(APPOBJS) $(TITLE).c + $(CC) $(CFLAGS) -o $@ $(APPOBJS) $(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 *~ + rm -f *.a *.o *.so *.bak dynamic/* static/* $(APPLICATION) + rm -rf dynamic static + +.c.o: + $(CC) $(CFLAGS) -c $< diff --git a/Linux-PAM/modules/pam_tally/README b/Linux-PAM/modules/pam_tally/README new file mode 100644 index 00000000..c8b715bd --- /dev/null +++ b/Linux-PAM/modules/pam_tally/README @@ -0,0 +1,116 @@ +SUMMARY: + pam_tally.so: + + 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) + * audit (will display the username typed if the user is not found) + + (auth) + Authentication phase first checks if user should be denied access + and if not it increments attempted login counter. Then on call to + pam_setcred it resets the attempts counter if the user is NOT + magic root. + * deny=n (deny access if tally for this user exceeds n) + + * lock_time=n (always deny for n seconds after failed attempt) + + * unlock_time=n (allow access after n seconds after the last + failed attempt with exceeded tally) + + * magic_root (access attempts by root as requesting user ignore + deny and don't change counter. + Use this for su and similar services.) + + * 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.) + + * per_user (If /var/log/faillog contains a non-zero + .fail_max/.fail_locktime field for this user then use it + instead of deny=n/lock_time=n parameter.) + + * no_lock_time (Don't use .fail_locktime filed in + /var/log/faillog for this user) + + * no_reset (don't reset count on successful entry, + only decrement) + + + (account) + Account phase resets attempts counter if the user is NOT magic root. + This phase can be used optionaly for services which don't call + pam_setcred correctly or if the reset should be done regardless + of the failure of the account phase of other modules. + + * magic_root (access attempts by root as requesting user + don't change counter. + Use this for su and similar services.) + + * no_reset (don't reset count on successful entry, + only decrement) + + 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 + - Tomas Mraz <tmraz@redhat.com>, v0.2 5 January 2005 + +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 denies access if attempted logins exceed some +threshold and it increments a per-uid counter for each attempted login, +in the account section, it resets that counter to zero on successful +login. If the module isn't used in the account section it resets the counter +to zero on call to pam_setcred. + +Root is treated specially: + +1. When a process already running as root tries to access some service and the +'magic_root' flag is set, the access is `magic', and bypasses pam_tally's +checks: handy for `su'ing from root into an account otherwise blocked. +NOTE: This was changed from the previous version of pam_tally where the default +was to treat root as magic and there were the 'no_magic_root' flag. However +for most of services the current default make sense. + +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 application. + +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. + +IMPORTANT NOTICE: +In the original version of pam_tally there was a bug where the information +if the password was correct or not was leaked by returning error from +different pam management phases. This was solved by moving the denying +functionality to the auth phase. However it's necessary to update the pam +configuration by moving the required options (as deny=N) to the auth phase. diff --git a/Linux-PAM/modules/pam_tally/faillog.h b/Linux-PAM/modules/pam_tally/faillog.h new file mode 100644 index 00000000..0f16261b --- /dev/null +++ b/Linux-PAM/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: faillog.h,v 1.1.1.1 2000/06/20 22:11:59 agmorgan Exp $ + * + * 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/Linux-PAM/modules/pam_tally/pam_tally.c b/Linux-PAM/modules/pam_tally/pam_tally.c new file mode 100644 index 00000000..1e48662e --- /dev/null +++ b/Linux-PAM/modules/pam_tally/pam_tally.c @@ -0,0 +1,840 @@ +/* + * pam_tally.c + * + * $Id: pam_tally.c,v 1.15 2005/01/24 14:04:17 t8m Exp $ + */ + + +/* By Tim Baverstock <warwick@mmm.co.uk>, Multi Media Machine Ltd. + * 5 March 1997 + * + * Stuff stolen from pam_rootok and pam_listfile + * + * Changes by Tomas Mraz <tmraz@redhat.com> 5 January 2005 + * Audit option added for Tomas patch by Sebastien Tricaud <toady@gscore.org> 13 January 2005 + */ + +#include <security/_pam_aconf.h> + +#if defined(MAIN) && defined(MEMORY_DEBUG) +# undef exit +#endif /* defined(MAIN) && defined(MEMORY_DEBUG) */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdlib.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> +#include <security/_pam_modutil.h> + +/*---------------------------------------------------------------------*/ + +#define DEFAULT_LOGFILE "/var/log/faillog" +#define MODULE_NAME "pam_tally" + +#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 + +struct fail_s { + struct faillog fs_faillog; +#ifndef MAIN + time_t fs_fail_time; +#endif /* ndef MAIN */ +}; + +struct tally_options { + const char *filename; + tally_t deny; + long lock_time; + long unlock_time; + unsigned int ctrl; +}; + +#define PHASE_UNKNOWN 0 +#define PHASE_AUTH 1 +#define PHASE_ACCOUNT 2 +#define PHASE_SESSION 3 + +#define OPT_MAGIC_ROOT 01 +#define OPT_FAIL_ON_ERROR 02 +#define OPT_DENY_ROOT 04 +#define OPT_PER_USER 010 +#define OPT_NO_LOCK_TIME 020 +#define OPT_NO_RESET 040 +#define OPT_AUDIT 100 + + +/*---------------------------------------------------------------------*/ + +/* some syslogging */ + +static void _pam_log(int err, const char *format, ...) +{ + va_list args; + va_start(args, format); + +#ifdef MAIN + vfprintf(stderr,format,args); + fprintf(stderr,"\n"); +#else + openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH); + vsyslog(err, format, args); + closelog(); +#endif + va_end(args); +} + +/*---------------------------------------------------------------------*/ + +/* --- Support function: parse arguments --- */ + +static void log_phase_no_auth( int phase, const char *argv ) +{ + if ( phase != PHASE_AUTH ) { + _pam_log(LOG_ERR, + MODULE_NAME ": option %s allowed in auth phase only", argv); + } +} + +static int tally_parse_args( struct tally_options *opts, int phase, + int argc, const char **argv ) +{ + memset(opts, 0, sizeof(*opts)); + opts->filename = DEFAULT_LOGFILE; + + for ( ; argc-- > 0; ++argv ) { + + if ( ! strncmp( *argv, "file=", 5 ) ) { + const char *from = *argv + 5; + if ( *from!='/' || strlen(from)>FILENAME_MAX-1 ) { + _pam_log(LOG_ERR, + MODULE_NAME ": filename not /rooted or too long; ", + *argv); + return PAM_AUTH_ERR; + } + opts->filename = from; + } + else if ( ! strcmp( *argv, "onerr=fail" ) ) { + opts->ctrl |= OPT_FAIL_ON_ERROR; + } + else if ( ! strcmp( *argv, "onerr=succeed" ) ) { + opts->ctrl &= ~OPT_FAIL_ON_ERROR; + } + else if ( ! strcmp( *argv, "magic_root" ) ) { + opts->ctrl |= OPT_MAGIC_ROOT; + } + else if ( ! strcmp( *argv, "even_deny_root_account" ) ) { + log_phase_no_auth(phase, *argv); + opts->ctrl |= OPT_DENY_ROOT; + } + else if ( ! strncmp( *argv, "deny=", 5 ) ) { + log_phase_no_auth(phase, *argv); + if ( sscanf((*argv)+5,TALLY_FMT,&opts->deny) != 1 ) { + _pam_log(LOG_ERR,"bad number supplied; %s",*argv); + return PAM_AUTH_ERR; + } + } + else if ( ! strncmp( *argv, "lock_time=", 10 ) ) { + log_phase_no_auth(phase, *argv); + if ( sscanf((*argv)+10,"%ld",&opts->lock_time) != 1 ) { + _pam_log(LOG_ERR,"bad number supplied; %s",*argv); + return PAM_AUTH_ERR; + } + } + else if ( ! strncmp( *argv, "unlock_time=", 12 ) ) { + log_phase_no_auth(phase, *argv); + if ( sscanf((*argv)+12,"%ld",&opts->unlock_time) != 1 ) { + _pam_log(LOG_ERR,"bad number supplied; %s",*argv); + return PAM_AUTH_ERR; + } + } + else if ( ! strcmp( *argv, "per_user" ) ) + { + log_phase_no_auth(phase, *argv); + opts->ctrl |= OPT_PER_USER; + } + else if ( ! strcmp( *argv, "no_lock_time") ) + { + log_phase_no_auth(phase, *argv); + opts->ctrl |= OPT_NO_LOCK_TIME; + } + else if ( ! strcmp( *argv, "no_reset" ) ) { + opts->ctrl |= OPT_NO_RESET; + } + else if ( ! strcmp ( *argv, "audit") ) { + opts->ctrl |= OPT_AUDIT; + } + else { + _pam_log(LOG_ERR, MODULE_NAME ": unknown option; %s",*argv); + } + } + + return PAM_SUCCESS; +} + +/*---------------------------------------------------------------------*/ + +/* --- 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, struct tally_options *opts) + { + const char *user = NULL; + 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 = _pammodutil_getpwnam( pamh, user ) ) ) { + opts->ctrl & OPT_AUDIT ? + _pam_log(LOG_ERR,MODULE_NAME ": pam_get_uid; no such user %s",user) : + _pam_log(LOG_ERR,MODULE_NAME ": pam_get_uid; no such user"); + return PAM_USER_UNKNOWN; + } + + if ( uid ) *uid = pw->pw_uid; + if ( userp ) *userp = user; + return PAM_SUCCESS; + } + +/*---------------------------------------------------------------------*/ + +/* --- Support functions: set/get tally data --- */ + +static void _cleanup( pam_handle_t *pamh, void *data, int error_status ) + { + free(data); + } + +static void tally_set_data( pam_handle_t *pamh, time_t oldtime ) + { + time_t *data; + + if ( (data=malloc(sizeof(time_t))) != NULL ) { + *data = oldtime; + pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup); + } + } + +static int tally_get_data( pam_handle_t *pamh, time_t *oldtime ) + { + int rv; + const void *data; + + rv = pam_get_data(pamh, MODULE_NAME, &data); + if ( rv == PAM_SUCCESS && oldtime != NULL ) { + *oldtime = *(const time_t *)data; + pam_set_data(pamh, MODULE_NAME, NULL, NULL); + } + else { + rv = -1; + *oldtime = 0; + } + return rv; + } + +/*---------------------------------------------------------------------*/ + +/* --- 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 fail_s *fsp) + { + 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(struct faillog), SEEK_SET ) ) { + _pam_log(LOG_ALERT, "fseek failed %s", filename); + fclose(*TALLY); + return PAM_AUTH_ERR; + } + + if ( fileinfo.st_size <= uid * sizeof(struct faillog) ) { + + memset(fsp, 0, sizeof(struct faillog)); + *tally=0; + fsp->fs_faillog.fail_time = time(NULL); + + } else if (( fread((char *) &fsp->fs_faillog, + sizeof(struct faillog), 1, *TALLY) )==0 ) { + + *tally=0; /* Assuming a gappy filesystem */ + + } else { + + *tally = fsp->fs_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, + struct fail_s *fsp) + { + if ( tally!=TALLY_HI ) + { + if ( fseek( *TALLY, uid * sizeof(struct faillog), SEEK_SET ) ) { + _pam_log(LOG_ALERT, "fseek failed %s", filename); + return PAM_AUTH_ERR; + } + fsp->fs_faillog.fail_cnt = tally; + if (fwrite((char *) &fsp->fs_faillog, + sizeof(struct faillog), 1, *TALLY)==0 ) { + _pam_log(LOG_ALERT, "tally update (fwrite) 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 ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS)) + +/*---------------------------------------------------------------------*/ + +/* --- tally bump function: bump tally for uid by (signed) inc --- */ + +static int tally_bump (int inc, time_t *oldtime, + pam_handle_t *pamh, + uid_t uid, + const char *user, + struct tally_options *opts) { + tally_t + tally = 0; /* !TALLY_HI --> Log opened for update */ + + FILE + *TALLY = NULL; + const char + *remote_host = NULL, + *cur_tty = NULL; + struct fail_s fs, *fsp = &fs; + int i; + + i=get_tally( &tally, uid, opts->filename, &TALLY, fsp ); + + /* to remember old fail time (for locktime) */ + fsp->fs_fail_time = fsp->fs_faillog.fail_time; + if ( inc > 0 ) { + if ( oldtime ) { + *oldtime = fsp->fs_faillog.fail_time; + } + fsp->fs_faillog.fail_time = time(NULL); + } else { + if ( oldtime ) { + fsp->fs_faillog.fail_time = *oldtime; + } + } + (void) pam_get_item(pamh, PAM_RHOST, (const void **)&remote_host); + if (!remote_host) { + + (void) pam_get_item(pamh, PAM_TTY, (const void **)&cur_tty); + if (!cur_tty) { + strncpy(fsp->fs_faillog.fail_line, "unknown", + sizeof(fsp->fs_faillog.fail_line) - 1); + fsp->fs_faillog.fail_line[sizeof(fsp->fs_faillog.fail_line)-1] = 0; + } else { + strncpy(fsp->fs_faillog.fail_line, cur_tty, + sizeof(fsp->fs_faillog.fail_line)-1); + fsp->fs_faillog.fail_line[sizeof(fsp->fs_faillog.fail_line)-1] = 0; + } + + } else { + strncpy(fsp->fs_faillog.fail_line, remote_host, + (size_t)sizeof(fsp->fs_faillog.fail_line)); + fsp->fs_faillog.fail_line[sizeof(fsp->fs_faillog.fail_line)-1] = 0; + } + if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); } + + if ( !(opts->ctrl & OPT_MAGIC_ROOT) || getuid() ) { /* magic_root doesn't change tally */ + + 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, opts->filename, &TALLY, fsp ); + if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); } + + return PAM_SUCCESS; +} + +static int tally_check (time_t oldtime, + pam_handle_t *pamh, + uid_t uid, + const char *user, + struct tally_options *opts) { + tally_t + deny = opts->deny; + tally_t + tally = 0; /* !TALLY_HI --> Log opened for update */ + long + lock_time = opts->lock_time; + + struct fail_s fs, *fsp = &fs; + FILE *TALLY=0; + int i; + + i=get_tally( &tally, uid, opts->filename, &TALLY, fsp ); + if (TALLY) fclose(TALLY); + if ( i != PAM_SUCCESS ) { RETURN_ERROR( i ); } + + if ( !(opts->ctrl & OPT_MAGIC_ROOT) || getuid() ) { /* magic_root skips tally check */ + + /* 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 ( (fsp->fs_faillog.fail_max) && (opts->ctrl & OPT_PER_USER) ) { + deny = fsp->fs_faillog.fail_max; + } + if ( (fsp->fs_faillog.fail_locktime) && (opts->ctrl & OPT_PER_USER) ) { + lock_time = fsp->fs_faillog.fail_locktime; + } + if (lock_time && oldtime + && !(opts->ctrl & OPT_NO_LOCK_TIME) ) + { + if ( lock_time + oldtime > time(NULL) ) + { + _pam_log(LOG_NOTICE, + "user %s ("UID_FMT") has time limit [%lds left]" + " since last failure.", + user,uid, + oldtime+lock_time + -time(NULL)); + return PAM_AUTH_ERR; + } + } + if (opts->unlock_time && oldtime) + { + if ( opts->unlock_time + oldtime <= time(NULL) ) + { /* ignore deny check after unlock_time elapsed */ + return PAM_SUCCESS; + } + } + if ( + ( deny != 0 ) && /* deny==0 means no deny */ + ( tally > deny ) && /* tally>deny means exceeded */ + ( ((opts->ctrl & OPT_DENY_ROOT) || 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 */ + } + } + + return PAM_SUCCESS; +} + +static int tally_reset (pam_handle_t *pamh, + uid_t uid, + const char *user, + struct tally_options *opts) { + tally_t + tally = 0; /* !TALLY_HI --> Log opened for update */ + + struct fail_s fs, *fsp = &fs; + FILE *TALLY=0; + int i; + + i=get_tally( &tally, uid, opts->filename, &TALLY, fsp ); + if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); } + + /* resets if not magic root + */ + + if ( (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid()) + && !(opts->ctrl & OPT_NO_RESET) ) + { tally=0; } + + if (tally == 0) + { + fsp->fs_faillog.fail_time = (time_t) 0; + strcpy(fsp->fs_faillog.fail_line, ""); + } + + i=set_tally( tally, uid, opts->filename, &TALLY, fsp ); + 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 ) { + int + rvcheck, rvbump; + time_t + oldtime = 0; + struct tally_options + options, *opts = &options; + uid_t + uid; + const char + *user; + + rvcheck = tally_parse_args(opts, PHASE_AUTH, argc, argv); + if ( rvcheck != PAM_SUCCESS ) + RETURN_ERROR( rvcheck ); + + rvcheck = pam_get_uid(pamh, &uid, &user, opts); + if ( rvcheck != PAM_SUCCESS ) + RETURN_ERROR( rvcheck ); + + rvbump = tally_bump(1, &oldtime, pamh, uid, user, opts); + rvcheck = tally_check(oldtime, pamh, uid, user, opts); + + tally_set_data(pamh, oldtime); + + return rvcheck != PAM_SUCCESS ? rvcheck : rvbump; +} + +PAM_FUNCTION( pam_sm_setcred ) { + int + rv; + time_t + oldtime = 0; + struct tally_options + options, *opts = &options; + uid_t + uid; + const char + *user; + + rv = tally_parse_args(opts, PHASE_AUTH, argc, argv); + if ( rv != PAM_SUCCESS ) + RETURN_ERROR( rv ); + + rv = pam_get_uid(pamh, &uid, &user, opts); + if ( rv != PAM_SUCCESS ) + RETURN_ERROR( rv ); + + if ( tally_get_data(pamh, &oldtime) != 0 ) + /* no data found */ + return PAM_SUCCESS; + + if ( (rv=tally_bump(-1, &oldtime, pamh, uid, user, opts)) != PAM_SUCCESS ) + return rv; + return tally_reset(pamh, uid, user, opts); +} + +#endif + +/*---------------------------------------------------------------------*/ + +/* --- authentication management functions (only) --- */ + +#ifdef PAM_SM_ACCOUNT + +/* To reset failcount of user on successfull login */ + +PAM_FUNCTION( pam_sm_acct_mgmt ) { + int + rv; + time_t + oldtime = 0; + struct tally_options + options, *opts = &options; + uid_t + uid; + const char + *user; + + rv = tally_parse_args(opts, PHASE_ACCOUNT, argc, argv); + if ( rv != PAM_SUCCESS ) + RETURN_ERROR( rv ); + + rv = pam_get_uid(pamh, &uid, &user, opts); + if ( rv != PAM_SUCCESS ) + RETURN_ERROR( rv ); + + if ( tally_get_data(pamh, &oldtime) != 0 ) + /* no data found */ + return PAM_SUCCESS; + + if ( (rv=tally_bump(-1, &oldtime, pamh, uid, user, opts)) != PAM_SUCCESS ) + return rv; + return tally_reset(pamh, uid, user, opts); +} + +#endif /* #ifdef PAM_SM_ACCOUNT */ + +/*-----------------------------------------------------------------------*/ + +#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 + NULL, + NULL, + NULL, +}; + +#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 ) { + + struct fail_s fs, *fsp = &fs; + + 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; + struct tally_options opts; + int i; + + memset(&opts, 0, sizeof(opts)); + opts.ctrl = OPT_AUDIT; + i=pam_get_uid( NULL, &uid, NULL, &opts); + if ( i != PAM_SUCCESS ) { + fprintf(stderr,"%s: %s\n",*argv,pam_errors(i)); + exit(0); + } + + i=get_tally( &tally, uid, cline_filename, &TALLY, fsp ); + 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, fsp ); + 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 *) &fsp->fs_faillog, + sizeof (struct faillog), 1, TALLY) + || ! fsp->fs_faillog.fail_cnt ) { + continue; + } + tally = fsp->fs_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/Linux-PAM/modules/pam_tally/pam_tally_app.c b/Linux-PAM/modules/pam_tally/pam_tally_app.c new file mode 100644 index 00000000..9e6e1faf --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_time/Makefile b/Linux-PAM/modules/pam_time/Makefile new file mode 100644 index 00000000..4aa4e276 --- /dev/null +++ b/Linux-PAM/modules/pam_time/Makefile @@ -0,0 +1,21 @@ +# $Id: Makefile,v 1.2 2000/11/19 23:54:05 agmorgan Exp $ +# +# 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!). +# + +include ../../Make.Rules + +TITLE=pam_time +LOCAL_CONFILE=./time.conf +INSTALLED_CONFILE=$(SCONFIGD)/time.conf + +DEFS=-DDEFAULT_CONF_FILE=\"$(INSTALLED_CONFILE)\" +CFLAGS += $(DEFS) + +MODULE_SIMPLE_INSTALL=bash -f ../install_conf "$(FAKEROOT)" "$(SCONFIGD)" "$(INSTALLED_CONFILE)" "$(TITLE)" "$(LOCAL_CONFILE)" +MODULE_SIMPLE_REMOVE=rm -f $(FAKEROOT)$(INSTALLED_CONFILE) +MODULE_SIMPLE_CLEAN=rm -f ./.ignore_age + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_time/README b/Linux-PAM/modules/pam_time/README new file mode 100644 index 00000000..0228b907 --- /dev/null +++ b/Linux-PAM/modules/pam_time/README @@ -0,0 +1,30 @@ +$Id: README,v 1.2 2000/12/04 19:02:35 baggins Exp $ + +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> diff --git a/Linux-PAM/modules/pam_time/pam_time.c b/Linux-PAM/modules/pam_time/pam_time.c new file mode 100644 index 00000000..c04180f2 --- /dev/null +++ b/Linux-PAM/modules/pam_time/pam_time.c @@ -0,0 +1,624 @@ +/* pam_time module */ + +/* + * $Id: pam_time.c,v 1.5 2004/09/22 09:37:50 kukuk Exp $ + * + * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/6/22 + * (File syntax and much other inspiration from the shadow package + * shadow-960129) + */ + +static const char rcsid[] = +"$Id: pam_time.c,v 1.5 2004/09/22 09:37:50 kukuk Exp $;\n" +"\t\tVersion 0.22 for Linux-PAM\n" +"Copyright (C) Andrew G. Morgan 1996 <morgan@linux.kernel.org>\n"; + +#include <security/_pam_aconf.h> + +#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> + +#ifdef DEFAULT_CONF_FILE +# define PAM_TIME_CONF DEFAULT_CONF_FILE /* from external define */ +#else +# define PAM_TIME_CONF "/etc/security/time.conf" +#endif +#define PAM_TIME_BUFLEN 1000 +#define FIELD_SEPARATOR ';' /* this is new as of .02 */ + +#ifdef TRUE +# undef TRUE +#endif +#ifdef FALSE +# undef FALSE +#endif + +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); + close(fd); + 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__ ); + close(fd); + return -1; + } + break; + case '\\': + if ((*buf)[i+1] == '\n') { + shift_bytes(i + *buf, 2, *to - (i+2)); + *to -= 2; + } else { + ++i; /* we don't escape non-newline characters */ + } + 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 == '.' || 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/Linux-PAM/modules/pam_time/time.conf b/Linux-PAM/modules/pam_time/time.conf new file mode 100644 index 00000000..d2062fdb --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_unix/CHANGELOG b/Linux-PAM/modules/pam_unix/CHANGELOG new file mode 100644 index 00000000..509ce0a3 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/CHANGELOG @@ -0,0 +1,55 @@ +$Id: CHANGELOG,v 1.1.1.1 2000/06/20 22:12:01 agmorgan Exp $ + +* 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/Linux-PAM/modules/pam_unix/Makefile b/Linux-PAM/modules/pam_unix/Makefile new file mode 100644 index 00000000..7f32e073 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/Makefile @@ -0,0 +1,191 @@ +# $Id: Makefile,v 1.8 2004/11/08 08:58:37 kukuk Exp $ +# +# This Makefile controls a build process of the pam_unix modules +# for Linux-PAM. You should not modify this Makefile. +# + +include ../../Make.Rules + +######################################################################## +# some options... uncomment to take effect +######################################################################## + +# Unless someone wants to work out how to make this work with the new +# autoconf stuff, you should use a separate module for this type of thing +# pam_cracklib perhaps..? +# do you want cracklib? +#ifeq ($(HAVE_CRACKLIB),yes) +#USE_CRACKLIB=-D"USE_CRACKLIB" +#endif + +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 +EXTRAS += -DCRACKLIB_DICTS=\"$(CRACKLIB_DICTPATH)\" + +ifeq ($(HAVE_LIBCRYPT),yes) + EXTRALS += -lcrypt +endif +ifeq ($(HAVE_LIBNSL),yes) + EXTRALS += -lnsl +endif +# do you want to use lckpwdf? +ifeq ($(WITH_LCKPWDF),yes) +USE_LCKPWDF=-D"USE_LCKPWDF" +# do you need to include the locking functions in the source? +ifeq ($(HAVE_LCKPWDF),no) + NEED_LCKPWDF=-D"NEED_LCKPWDF" +endif +endif + +ifeq ($(HAVE_LIBNSL),yes) + LIBNSL = -lnsl +endif + +ifeq ($(HAVE_LIBCRYPT),yes) + LIBCRYPT=-lcrypt +endif + +CHKPWD=unix_chkpwd + +BIGCRYPT=bigcrypt + +EXTRAS += -DCHKPWD_HELPER=\"$(SUPLEMENTED)/$(CHKPWD)\" + +LINK_PAMMODUTILS = -L../pammodutil -lpammodutil +INCLUDE_PAMMODUTILS = -I../pammodutil/include + +######################################################################## + +CFLAGS += $(USE_CRACKLIB) $(USE_LCKPWDF) $(NEED_LCKPWDF) $(EXTRAS) \ + $(INCLUDE_PAMMODUTILS) + +LDLIBS = $(EXTRALS) $(LINK_PAMMODUTILS) + +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) $(BIGCRYPT) \ + 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) $(LIBCRYPT) $(NEED_LINK_LIB_C) -L../../libpam -lpam +endif + +ifdef STATIC +$(LIBOBJS): $(LIBSRC) + +$(LIBSTATIC): $(LIBOBJS) + $(LD) -r -o $@ $(LIBOBJS) $(PLUS) $(CRACKLIB) $(LDLIBS) $(LIBNSL) $(LIBCRYPT) +endif + +$(CHKPWD): unix_chkpwd.o md5_good.o md5_broken.o \ + md5_crypt_good.o md5_crypt_broken.o \ + bigcrypt.o + $(CC) $(CFLAGS) -o $(CHKPWD) $^ $(LDLIBS) $(LIBCRYPT) + +$(BIGCRYPT): bigcrypt_main.o bigcrypt.o + $(CC) -o $(BIGCRYPT) $^ $(LDLIBS) $(LIBCRYPT) + +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) + for x in pam_unix_auth pam_unix_acct pam_unix_passwd pam_unix_session;\ + do ln -sf $(LIBSHARED) $(FAKEROOT)$(SECUREDIR)/$$x.so ; done +endif + $(MKDIR) $(FAKEROOT)$(SUPLEMENTED) + install -m 4555 $(CHKPWD) $(FAKEROOT)$(SUPLEMENTED) + +remove: + rm -f $(FAKEROOT)$(SECUREDIR)/$(LIBSHARED) + for x in pam_unix_auth pam_unix_acct pam_unix_passwd pam_unix_session;\ + do rm -f $(FAKEROOT)$(SECUREDIR)/$$x.so ; done + rm -f $(FAKEROOT)$(SUPLEMENTED)/$(CHKPWD) + +clean: + rm -f $(LIBOBJD) $(LIBOBJS) $(CHKPWD) $(BIGCRYPT) *.o *.so core + rm -f *~ *.a *.out *.bak + rm -rf dynamic static + +.c.o: + $(CC) -c $(CFLAGS) $< + diff --git a/Linux-PAM/modules/pam_unix/README b/Linux-PAM/modules/pam_unix/README new file mode 100644 index 00000000..afeee3da --- /dev/null +++ b/Linux-PAM/modules/pam_unix/README @@ -0,0 +1,37 @@ +pam_unix 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 + broken_shadow - ignore errors reading shadow information for + users in the account management module + + invalid arguments are logged to syslog. diff --git a/Linux-PAM/modules/pam_unix/bigcrypt.c b/Linux-PAM/modules/pam_unix/bigcrypt.c new file mode 100644 index 00000000..6b73f3d2 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/bigcrypt.c @@ -0,0 +1,124 @@ +/* + * 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 <stdlib.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) +{ + char *dec_c2_cryptbuf; + + 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 */ + dec_c2_cryptbuf = malloc(CBUF_SIZE); + if (!dec_c2_cryptbuf) { + return NULL; + } + 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/Linux-PAM/modules/pam_unix/bigcrypt_main.c b/Linux-PAM/modules/pam_unix/bigcrypt_main.c new file mode 100644 index 00000000..70819072 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/bigcrypt_main.c @@ -0,0 +1,18 @@ +#include <stdio.h> +#include <string.h> + +extern const char *bigcrypt(const char *password, const char *salt); + +int +main(int argc, char **argv) +{ + if (argc < 3) { + fprintf(stderr, "Usage: %s password salt\n", + strchr(argv[0], '/') ? + (strchr(argv[0], '/') + 1) : + argv[0]); + return 0; + } + fprintf(stdout, "%s\n", bigcrypt(argv[1], argv[2])); + return 0; +} diff --git a/Linux-PAM/modules/pam_unix/lckpwdf.-c b/Linux-PAM/modules/pam_unix/lckpwdf.-c new file mode 100644 index 00000000..b5ff4585 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_unix/md5.c b/Linux-PAM/modules/pam_unix/md5.c new file mode 100644 index 00000000..7ee9ed00 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/md5.c @@ -0,0 +1,256 @@ +/* + * $Id: md5.c,v 1.1.1.1 2000/06/20 22:12:03 agmorgan Exp $ + * + * 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/Linux-PAM/modules/pam_unix/md5.h b/Linux-PAM/modules/pam_unix/md5.h new file mode 100644 index 00000000..103f168a --- /dev/null +++ b/Linux-PAM/modules/pam_unix/md5.h @@ -0,0 +1,31 @@ + +#ifndef MD5_H +#define MD5_H + +typedef unsigned int uint32; + +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/Linux-PAM/modules/pam_unix/md5_crypt.c b/Linux-PAM/modules/pam_unix/md5_crypt.c new file mode 100644 index 00000000..8b7bc66b --- /dev/null +++ b/Linux-PAM/modules/pam_unix/md5_crypt.c @@ -0,0 +1,154 @@ +/* + * $Id: md5_crypt.c,v 1.2 2001/07/10 20:24:16 vorlon Exp $ + * + * ---------------------------------------------------------------------------- + * "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 <stdlib.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 */ + char *passwd, *p; + 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; + + /* TODO: now that we're using malloc'ed memory, get rid of the + strange constant buffer size. */ + passwd = malloc(120); + + /* 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/Linux-PAM/modules/pam_unix/pam_unix_acct.c b/Linux-PAM/modules/pam_unix/pam_unix_acct.c new file mode 100644 index 00000000..02e07ba6 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/pam_unix_acct.c @@ -0,0 +1,213 @@ +/* + * 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. + */ + +#include <security/_pam_aconf.h> + +#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> +#include <security/_pam_modutil.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(pamh, 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, pamh + ,"could not identify user (from uid=%d)" + ,getuid()); + return PAM_USER_UNKNOWN; + } + + pwent = _pammodutil_getpwnam(pamh, uname); + if (!pwent) { + _log_err(LOG_ALERT, pamh + ,"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 = _pammodutil_getspnam (pamh, 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 (_unix_shadowed (pwent)) + spent = _pammodutil_getspnam (pamh, uname); + else + return PAM_SUCCESS; + + if (!spent) + if (on(UNIX_BROKEN_SHADOW,ctrl)) + 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)) { + _log_err(LOG_NOTICE, pamh + ,"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 (spent->sp_lstchg == 0) { + _log_err(LOG_NOTICE, pamh + ,"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 (curdays < spent->sp_lstchg) { + _log_err(LOG_DEBUG, pamh + ,"account %s has password changed in future" + ,uname); + return PAM_SUCCESS; + } + if ((curdays - spent->sp_lstchg > spent->sp_max) + && (curdays - spent->sp_lstchg > spent->sp_inact) + && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact) + && (spent->sp_max != -1) && (spent->sp_inact != -1)) { + _log_err(LOG_NOTICE, pamh + ,"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; + } + if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) { + _log_err(LOG_DEBUG, pamh + ,"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, pamh + ,"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/Linux-PAM/modules/pam_unix/pam_unix_auth.c b/Linux-PAM/modules/pam_unix/pam_unix_auth.c new file mode 100644 index 00000000..39e0cde5 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/pam_unix_auth.c @@ -0,0 +1,229 @@ +/* + * 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 */ + +#include <security/_pam_aconf.h> + +#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 \ +do { \ + if (on(UNIX_LIKE_AUTH, ctrl) && ret_data) { \ + D(("recording return code for next time [%d]", \ + retval)); \ + *ret_data = retval; \ + pam_set_data(pamh, "unix_setcred_return", \ + (void *) ret_data, setcred_free); \ + } else if (ret_data) \ + free (ret_data); \ + D(("done. [%s]", pam_strerror(pamh, retval))); \ + return retval; \ +} while (0) + + +static void setcred_free (pam_handle_t * pamh, void *ptr, int err) +{ + if (ptr) + free (ptr); +} + + +PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags + ,int argc, const char **argv) +{ + unsigned int ctrl; + int retval, *ret_data = NULL; + const char *name, *p; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, argc, argv); + + /* Get a few bytes so we can pass our return value to + pam_sm_setcred(). */ + if (on(UNIX_LIKE_AUTH, ctrl)) + ret_data = malloc(sizeof(int)); + + /* get the user'name' */ + + retval = pam_get_user(pamh, &name, NULL); + 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, pamh, "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(pamh, 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, pamh, "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) +{ + int retval; + int *pretval = NULL; + + D(("called.")); + + retval = PAM_SUCCESS; + + D(("recovering return code from auth call")); + /* We will only find something here if UNIX_LIKE_AUTH is set -- + don't worry about an explicit check of argv. */ + pam_get_data(pamh, "unix_setcred_return", (const void **) &pretval); + if(pretval) { + retval = *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/Linux-PAM/modules/pam_unix/pam_unix_passwd.c b/Linux-PAM/modules/pam_unix/pam_unix_passwd.c new file mode 100644 index 00000000..2ea57cc6 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/pam_unix_passwd.c @@ -0,0 +1,1131 @@ +/* + * 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. + */ + +#include <security/_pam_aconf.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <malloc.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.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/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 <security/_pam_modutil.h> + +#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" +#ifndef CRACKLIB_DICTS +#define CRACKLIB_DICTS "/usr/share/dict/cracklib_dict" +#endif +#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 = 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 */ + x = Goodcrypt_md5(pass_new, (const char *) result); + + return x; +} + +static char *getNISserver(pam_handle_t *pamh) +{ + char *master; + char *domainname; + int port, err; + + if ((err = yp_get_default_domain(&domainname)) != 0) { + _log_err(LOG_WARNING, pamh, "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, pamh, "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, pamh, + "yppasswdd not running on NIS master host\n"); + return NULL; + } + if (port >= IPPORT_RESERVED) { + _log_err(LOG_WARNING, pamh, + "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_ABORT; + + 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) { + char *md5pass = Goodcrypt_md5(newpass, s_pas); + if (!strcmp(md5pass, s_pas)) { + _pam_delete(md5pass); + retval = PAM_AUTHTOK_ERR; + break; + } + s_pas = strtok(NULL, ":,"); + _pam_delete(md5pass); + } + break; + } + } + fclose(opwfile); + + return retval; +} + +static int save_old_password(pam_handle_t *pamh, + 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 npas; + FILE *pwfile, *opwfile; + int err = 0; + int oldmask; + int found = 0; + struct passwd *pwd = NULL; + struct stat st; + + if (howmany < 0) { + return PAM_SUCCESS; + } + + if (oldpass == NULL) { + return PAM_SUCCESS; + } + + oldmask = umask(077); + pwfile = fopen(OPW_TMPFILE, "w"); + umask(oldmask); + if (pwfile == NULL) { + return PAM_AUTHTOK_ERR; + } + + opwfile = fopen(OLD_PASSWORDS_FILE, "r"); + if (opwfile == NULL) { + fclose(pwfile); + return PAM_AUTHTOK_ERR; + } + + if (fstat (fileno (opwfile), &st) == -1) + { + fclose (opwfile); + fclose (pwfile); + return PAM_AUTHTOK_ERR; + } + + if (fchown (fileno (pwfile), st.st_uid, st.st_gid) == -1) + { + fclose (opwfile); + fclose (pwfile); + return PAM_AUTHTOK_ERR; + } + if (fchmod (fileno (pwfile), st.st_mode) == -1) + { + fclose (opwfile); + fclose (pwfile); + 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, ":"); + 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) + snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n", + s_luser, s_uid, npas, pass); + else + snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n", + s_luser, s_uid, npas, s_pas, pass); + _pam_delete(pass); + if (fputs(nbuf, pwfile) < 0) { + err = 1; + break; + } + found = 1; + } else if (fputs(buf, pwfile) < 0) { + err = 1; + break; + } + } + fclose(opwfile); + + if (!found) { + pwd = _pammodutil_getpwnam(pamh, forwho); + if (pwd == NULL) { + err = 1; + } else { + pass = crypt_md5_wrapper(oldpass); + snprintf(nbuf, sizeof(nbuf), "%s:%d:1:%s\n", + forwho, pwd->pw_uid, pass); + _pam_delete(pass); + if (fputs(nbuf, pwfile) < 0) { + err = 1; + } + } + } + + if (fclose(pwfile)) { + D(("error writing entries to old passwords file: %s\n", + strerror(errno))); + err = 1; + } + + if (!err) { + if (!rename(OPW_TMPFILE, OLD_PASSWORDS_FILE)) { + return PAM_SUCCESS; + } + } + + unlink(OPW_TMPFILE); + return PAM_AUTHTOK_ERR; +} + +static int _update_passwd(pam_handle_t *pamh, + const char *forwho, const char *towhat) +{ + struct passwd *tmpent = NULL; + struct stat st; + FILE *pwfile, *opwfile; + int err = 1; + int oldmask; + + oldmask = umask(077); + pwfile = fopen(PW_TMPFILE, "w"); + umask(oldmask); + if (pwfile == NULL) { + return PAM_AUTHTOK_ERR; + } + + opwfile = fopen("/etc/passwd", "r"); + if (opwfile == NULL) { + fclose(pwfile); + return PAM_AUTHTOK_ERR; + } + + if (fstat (fileno (opwfile), &st) == -1) + { + fclose (opwfile); + fclose (pwfile); + return PAM_AUTHTOK_ERR; + } + + if (fchown (fileno (pwfile), st.st_uid, st.st_gid) == -1) + { + fclose (opwfile); + fclose (pwfile); + return PAM_AUTHTOK_ERR; + } + if (fchmod (fileno (pwfile), st.st_mode) == -1) + { + fclose (opwfile); + fclose (pwfile); + } + + tmpent = fgetpwent (opwfile); + while (tmpent) { + if (!strcmp(tmpent->pw_name, forwho)) { + /* To shut gcc up */ + union { + const char *const_charp; + char *charp; + } assigned_passwd; + assigned_passwd.const_charp = towhat; + + tmpent->pw_passwd = assigned_passwd.charp; + err = 0; + } + if (putpwent(tmpent, pwfile)) { + D(("error writing entry to password file: %s\n", strerror(errno))); + err = 1; + break; + } + tmpent = fgetpwent(opwfile); + } + fclose(opwfile); + + if (fclose(pwfile)) { + D(("error writing entries to password file: %s\n", strerror(errno))); + err = 1; + } + + if (!err) { + if (!rename(PW_TMPFILE, "/etc/passwd")) { + _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho); + return PAM_SUCCESS; + } + } + + unlink(PW_TMPFILE); + return PAM_AUTHTOK_ERR; +} + +static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat) +{ + struct spwd *spwdent = NULL, *stmpent = NULL; + struct stat st; + FILE *pwfile, *opwfile; + int err = 1; + int oldmask; + + spwdent = getspnam(forwho); + if (spwdent == NULL) { + return PAM_USER_UNKNOWN; + } + oldmask = umask(077); + pwfile = fopen(SH_TMPFILE, "w"); + umask(oldmask); + if (pwfile == NULL) { + return PAM_AUTHTOK_ERR; + } + + opwfile = fopen("/etc/shadow", "r"); + if (opwfile == NULL) { + fclose(pwfile); + return PAM_AUTHTOK_ERR; + } + + if (fstat (fileno (opwfile), &st) == -1) + { + fclose (opwfile); + fclose (pwfile); + return PAM_AUTHTOK_ERR; + } + + if (fchown (fileno (pwfile), st.st_uid, st.st_gid) == -1) + { + fclose (opwfile); + fclose (pwfile); + return PAM_AUTHTOK_ERR; + } + if (fchmod (fileno (pwfile), st.st_mode) == -1) + { + fclose (opwfile); + fclose (pwfile); + return PAM_AUTHTOK_ERR; + } + + stmpent = fgetspent(opwfile); + while (stmpent) { + + if (!strcmp(stmpent->sp_namp, forwho)) { + stmpent->sp_pwdp = towhat; + stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24); + err = 0; + D(("Set password %s for %s", stmpent->sp_pwdp, forwho)); + } + + if (putspent(stmpent, pwfile)) { + D(("error writing entry to shadow file: %s\n", strerror(errno))); + err = 1; + break; + } + + stmpent = fgetspent(opwfile); + } + fclose(opwfile); + + if (fclose(pwfile)) { + D(("error writing entries to shadow file: %s\n", strerror(errno))); + err = 1; + } + + if (!err) { + if (!rename(SH_TMPFILE, "/etc/shadow")) { + _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho); + return PAM_SUCCESS; + } + } + + unlink(SH_TMPFILE); + return PAM_AUTHTOK_ERR; +} + +static int _do_setpass(pam_handle_t* pamh, const char *forwho, char *fromwhat, + char *towhat, unsigned int ctrl, int remember) +{ + struct passwd *pwd = NULL; + int retval = 0; + + D(("called")); + + pwd = getpwnam(forwho); + + if (pwd == NULL) { + retval = PAM_AUTHTOK_ERR; + goto done; + } + + if (_unix_comesfromsource(pamh, forwho, 1, 0)) { + /* first, save old password */ + if (save_old_password(pamh, forwho, fromwhat, remember)) { + retval = PAM_AUTHTOK_ERR; + goto done; + } + if (on(UNIX_SHADOW, ctrl) || _unix_shadowed(pwd)) { + retval = _update_shadow(pamh, forwho, towhat); + if (retval == PAM_SUCCESS) + if (!_unix_shadowed(pwd)) + retval = _update_passwd(pamh, forwho, "x"); + } else { + retval = _update_passwd(pamh, forwho, towhat); + } + } else if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) { + struct timeval timeout; + struct yppasswd yppwd; + CLIENT *clnt; + char *master; + int status; + int err = 0; + + /* Unlock passwd file to avoid deadlock */ +#ifdef USE_LCKPWDF + ulckpwdf(); +#endif + /* Make RPC call to NIS server */ + if ((master = getNISserver(pamh)) == 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 ? 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) { + D(("Error while changing NIS password.\n")); + retval = PAM_TRY_AGAIN; + } + D(("The password has%s been changed on %s.", + (err || status) ? " not" : "", master)); + _log_err(LOG_NOTICE, pamh, "password%s changed for %s on %s", + (err || status) ? " not" : "", pwd->pw_name, master); + + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + if ((err || status) != 0) { + retval = PAM_TRY_AGAIN; + } +#ifdef DEBUG + sleep(5); +#endif + return retval; + } + +done: +#ifdef USE_LCKPWDF + ulckpwdf(); +#endif + + 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 */ + pwd = getpwnam(user); /* Get password file entry... */ + if (pwd == NULL) + return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */ + + if (_unix_shadowed(pwd)) { + /* ...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; + const 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, pamh, "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, pamh, "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(("length check [%s]", remark)); +#endif + if (on(UNIX_REMEMBER_PASSWD, ctrl)) { + if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR) + remark = "Password has been already used. Choose another."; + if (retval == PAM_ABORT) { + _log_err(LOG_ERR, pamh, "can't open %s file to check old passwords", + OLD_PASSWORDS_FILE); + return retval; + } + } + } + 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, i; + int remember = -1; + + /* <DO NOT free() THESE> */ + const char *user; + char *pass_old, *pass_new; + /* </DO NOT free() THESE> */ + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, &remember, argc, argv); + + /* + * First get the name of a user + */ + retval = pam_get_user(pamh, &user, NULL); + 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, pamh, "bad username [%s]", user); + return PAM_USER_UNKNOWN; + } + if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) + _log_err(LOG_DEBUG, pamh, "username [%s] obtained", + user); + } else { + if (on(UNIX_DEBUG, ctrl)) + _log_err(LOG_DEBUG, pamh, + "password - could not identify user"); + return retval; + } + + D(("Got username of %s", user)); + + /* + * Before we do anything else, check to make sure that the user's + * info is in one of the databases we can modify from this module, + * which currently is 'files' and 'nis'. We have to do this because + * getpwnam() doesn't tell you *where* the information it gives you + * came from, nor should it. That's our job. + */ + if (_unix_comesfromsource(pamh, user, 1, 1) == 0) { + _log_err(LOG_DEBUG, pamh, + "user \"%s\" does not exist in /etc/passwd or NIS", + user); + return PAM_USER_UNKNOWN; + } else { + struct passwd *pwd; + _unix_getpwnam(pamh, user, 1, 1, &pwd); + if (pwd == NULL) { + _log_err(LOG_DEBUG, pamh, + "user \"%s\" has corrupted passwd entry", + user); + return PAM_USER_UNKNOWN; + } + if (!_unix_shadowed(pwd) && + (strchr(pwd->pw_passwd, '*') != NULL)) { + _log_err(LOG_DEBUG, pamh, + "user \"%s\" does not have modifiable password", + user); + return PAM_USER_UNKNOWN; + } + } + + /* + * 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(pamh, 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, pamh, + "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 + ,(const char **) &pass_old); + free(Announce); + + if (retval != PAM_SUCCESS) { + _log_err(LOG_NOTICE, pamh + ,"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, pamh, + "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, pamh, "user not authenticated"); + 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, pamh + ,"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, pamh, + "new password not acceptable"); + pass_new = pass_old = NULL; /* tidy up */ + return retval; + } +#ifdef USE_LCKPWDF + /* These values for the number of attempts and the sleep time + are, of course, completely arbitrary. + My reading of the PAM docs is that, once pam_chauthtok() has been + called with PAM_UPDATE_AUTHTOK, we are obliged to take any + reasonable steps to make sure the token is updated; so retrying + for 1/10 sec. isn't overdoing it. */ + i=0; + while((retval = lckpwdf()) != 0 && i < 100) { + usleep(1000); + i++; + } + if(retval != 0) { + return PAM_AUTHTOK_LOCK_BUSY; + } +#endif + + if (pass_old) { + retval = _unix_verify_password(pamh, user, pass_old, ctrl); + if (retval != PAM_SUCCESS) { + _log_err(LOG_NOTICE, pamh, "user password changed by another process"); +#ifdef USE_LCKPWDF + ulckpwdf(); +#endif + return retval; + } + } + + retval = _unix_verify_shadow(user, ctrl); + if (retval != PAM_SUCCESS) { + _log_err(LOG_NOTICE, pamh, "user not authenticated 2"); +#ifdef USE_LCKPWDF + ulckpwdf(); +#endif + return retval; + } + + retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new); + if (retval != PAM_SUCCESS) { + _log_err(LOG_NOTICE, pamh, + "new password not acceptable 2"); + 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 + * [Problems that followed from this are fixed as per + * Bug 521314.] + */ + char *temp = malloc(9); + + if (temp == NULL) { + _log_err(LOG_CRIT, pamh, + "out of memory for password"); + 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 */ + tpass = bigcrypt(temp, salt); + + _pam_delete(temp); /* tidy up */ + } else { + tpass = bigcrypt(pass_new, salt); + } + } + + D(("password processed")); + + /* update the password database(s) -- race conditions..? */ + + retval = _do_setpass(pamh, user, pass_old, tpass, ctrl, + remember); + /* _do_setpass has called ulckpwdf for us */ + + _pam_delete(tpass); + pass_old = pass_new = NULL; + } else { /* something has broken with the module */ + _log_err(LOG_ALERT, pamh, + "password received unknown request"); + retval = PAM_ABORT; + } + + D(("retval was %d", retval)); + + 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/Linux-PAM/modules/pam_unix/pam_unix_sess.c b/Linux-PAM/modules/pam_unix/pam_unix_sess.c new file mode 100644 index 00000000..a29a7085 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/pam_unix_sess.c @@ -0,0 +1,146 @@ +/* + * $Id: pam_unix_sess.c,v 1.5 2005/03/23 14:35:21 t8m Exp $ + * + * 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. + */ + +#include <security/_pam_aconf.h> + +#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> +#include <security/_pam_modutil.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; + const char *login_name; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, argc, argv); + + retval = pam_get_item(pamh, PAM_USER, (void *) &user_name); + if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) { + _log_err(LOG_CRIT, pamh, + "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 || *service == '\0' || retval != PAM_SUCCESS) { + _log_err(LOG_CRIT, pamh, + "open_session - error recovering service"); + return PAM_SESSION_ERR; + } + login_name = _pammodutil_getlogin(pamh); + if (login_name == NULL) { + login_name = ""; + } + _log_err(LOG_INFO, pamh, "session opened for user %s by %s(uid=%d)", + user_name, login_name, 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(pamh, flags, NULL, argc, argv); + + retval = pam_get_item(pamh, PAM_USER, (void *) &user_name); + if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) { + _log_err(LOG_CRIT, pamh, + "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 || *service == '\0' || retval != PAM_SUCCESS) { + _log_err(LOG_CRIT, pamh, + "close_session - error recovering service"); + return PAM_SESSION_ERR; + } + _log_err(LOG_INFO, pamh, "session closed for user %s" + ,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/Linux-PAM/modules/pam_unix/support.c b/Linux-PAM/modules/pam_unix/support.c new file mode 100644 index 00000000..1584f2f1 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/support.c @@ -0,0 +1,1076 @@ +/* + * $Id: support.c,v 1.25 2005/01/10 09:45:37 kukuk Exp $ + * + * Copyright information at end of file. + */ + +#define _BSD_SOURCE + +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <malloc.h> +#include <pwd.h> +#include <shadow.h> +#include <limits.h> +#include <utmp.h> +#include <errno.h> +#include <signal.h> +#include <ctype.h> +#include <rpcsvc/ypclnt.h> + +#include <security/_pam_macros.h> +#include <security/pam_modules.h> +#include <security/_pam_modutil.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, pam_handle_t *pamh, const char *format,...) +{ + char *service = NULL; + char logname[256]; + va_list args; + + pam_get_item(pamh, PAM_SERVICE, (const void **) &service); + if (service) { + strncpy(logname, service, sizeof(logname)); + logname[sizeof(logname) - 1 - strlen("(pam_unix)")] = '\0'; + strncat(logname, "(pam_unix)", strlen("(pam_unix)")); + } else { + strncpy(logname, "pam_unix", sizeof(logname) - 1); + } + + va_start(args, format); + openlog(logname, 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, pamh, "conversation failure [%s]" + ,pam_strerror(pamh, retval)); + } + } else if (retval != PAM_CONV_AGAIN) { + _log_err(LOG_ERR, pamh + ,"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; +} + +/* + * set the control flags for the UNIX module. + */ + +int _set_ctrl(pam_handle_t *pamh, 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_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, pamh, + "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 == INT_MIN) || (*remember == INT_MAX)) + *remember = -1; + if (*remember > 400) + *remember = 400; + } + } + } + + ++argv; /* step to next argument */ + } + + if (flags & PAM_DISALLOW_NULL_AUTHTOK) { + D(("DISALLOW_NULL_AUTHTOK")); + set(UNIX__NONULL, 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; +} + +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 uid; /* uid of calling user */ + int euid; /* euid of calling process */ + 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; + const char *ruser = NULL; + const char *rhost = NULL; + const char *tty = 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); + (void) pam_get_item(pamh, PAM_RUSER, + (const void **)&ruser); + (void) pam_get_item(pamh, PAM_RHOST, + (const void **)&rhost); + (void) pam_get_item(pamh, PAM_TTY, + (const void **)&tty); + _log_err(LOG_NOTICE, pamh, + "%d more authentication failure%s; " + "logname=%s uid=%d euid=%d " + "tty=%s ruser=%s rhost=%s " + "%s%s", + failure->count - 1, failure->count == 2 ? "" : "s", + failure->name, failure->uid, failure->euid, + tty ? tty : "", ruser ? ruser : "", + rhost ? rhost : "", + (failure->user && failure->user[0] != '\0') + ? " user=" : "", failure->user + ); + + if (failure->count > UNIX_MAX_RETRIES) { + _log_err(LOG_ALERT, pamh + ,"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_getpwnam() searches only /etc/passwd and NIS to find user information + */ +static void _unix_cleanup(pam_handle_t *pamh, void *data, int error_status) +{ + free(data); +} + +int _unix_getpwnam(pam_handle_t *pamh, const char *name, + int files, int nis, struct passwd **ret) +{ + FILE *passwd; + char buf[16384]; + int matched = 0, buflen; + char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p; + + memset(buf, 0, sizeof(buf)); + + if (!matched && files) { + int userlen = strlen(name); + passwd = fopen("/etc/passwd", "r"); + if (passwd != NULL) { + while (fgets(buf, sizeof(buf), passwd) != NULL) { + if ((buf[userlen] == ':') && + (strncmp(name, buf, userlen) == 0)) { + p = buf + strlen(buf) - 1; + while (isspace(*p) && (p >= buf)) { + *p-- = '\0'; + } + matched = 1; + break; + } + } + fclose(passwd); + } + } + + if (!matched && nis) { + char *userinfo = NULL, *domain = NULL; + int len = 0, i; + len = yp_get_default_domain(&domain); + if (len == YPERR_SUCCESS) { + len = yp_bind(domain); + } + if (len == YPERR_SUCCESS) { + i = yp_match(domain, "passwd.byname", name, + strlen(name), &userinfo, &len); + yp_unbind(domain); + if ((i == YPERR_SUCCESS) && (len < sizeof(buf))) { + strncpy(buf, userinfo, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + matched = 1; + } + } + } + + if (matched && (ret != NULL)) { + *ret = NULL; + + slogin = buf; + + spasswd = strchr(slogin, ':'); + if (spasswd == NULL) { + return matched; + } + *spasswd++ = '\0'; + + suid = strchr(spasswd, ':'); + if (suid == NULL) { + return matched; + } + *suid++ = '\0'; + + sgid = strchr(suid, ':'); + if (sgid == NULL) { + return matched; + } + *sgid++ = '\0'; + + sgecos = strchr(sgid, ':'); + if (sgecos == NULL) { + return matched; + } + *sgecos++ = '\0'; + + shome = strchr(sgecos, ':'); + if (shome == NULL) { + return matched; + } + *shome++ = '\0'; + + sshell = strchr(shome, ':'); + if (sshell == NULL) { + return matched; + } + *sshell++ = '\0'; + + buflen = sizeof(struct passwd) + + strlen(slogin) + 1 + + strlen(spasswd) + 1 + + strlen(suid) + 1 + + strlen(sgid) + 1 + + strlen(sgecos) + 1 + + strlen(shome) + 1 + + strlen(sshell) + 1; + *ret = malloc(buflen); + if (*ret == NULL) { + return matched; + } + memset(*ret, '\0', buflen); + + (*ret)->pw_uid = strtol(suid, &p, 10); + if ((strlen(sgid) == 0) || (*p != '\0')) { + free(*ret); + *ret = NULL; + return matched; + } + + (*ret)->pw_gid = strtol(sgid, &p, 10); + if ((strlen(sgid) == 0) || (*p != '\0')) { + free(*ret); + *ret = NULL; + return matched; + } + + p = ((char*)(*ret)) + sizeof(struct passwd); + (*ret)->pw_name = strcpy(p, slogin); + p += strlen(p) + 1; + (*ret)->pw_passwd = strcpy(p, spasswd); + p += strlen(p) + 1; + (*ret)->pw_gecos = strcpy(p, sgecos); + p += strlen(p) + 1; + (*ret)->pw_dir = strcpy(p, shome); + p += strlen(p) + 1; + (*ret)->pw_shell = strcpy(p, sshell); + + snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name); + + if (pam_set_data(pamh, buf, + *ret, _unix_cleanup) != PAM_SUCCESS) { + free(*ret); + *ret = NULL; + } + } + + return matched; +} + +/* + * _unix_comsefromsource() is a quick check to see if information about a given + * user comes from a particular source (just files and nis for now) + * + */ +int _unix_comesfromsource(pam_handle_t *pamh, + const char *name, int files, int nis) +{ + return _unix_getpwnam(pamh, name, files, nis, NULL); +} + +/* + * _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 (pam_handle_t *pamh, 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 */ + + /* Get password file entry... */ + pwd = _pammodutil_getpwnam (pamh, name); + + 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 = _pammodutil_getspnam (pamh, 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 (_unix_shadowed(pwd)) { + /* + * ...and shadow password file entry for this user, + * if shadowing is enabled + */ + spwdent = _pammodutil_getspnam(pamh, 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, const char *user) +{ + int retval, child, fds[2]; + void (*sighandler)(int) = NULL; + + D(("called.")); + /* create a pipe for the password */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + return PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + sighandler = signal(SIGCHLD, SIG_DFL); + } + + /* fork */ + child = fork(); + if (child == 0) { + static char *envp[] = { NULL }; + char *args[] = { NULL, NULL, 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); + args[1] = x_strdup(user); + + 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 */ + /* 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[0]); /* close here to avoid possible SIGPIPE above */ + 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; + } + + if (sighandler != NULL) { + (void) signal(SIGCHLD, sighandler); /* restore old signal handler */ + } + + 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 = _pammodutil_getpwnam (pamh, 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 = _pammodutil_getspnam (pamh, 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 (_unix_shadowed(pwd)) { + /* + * ...and shadow password file entry for this user, + * if shadowing is enabled + */ + spwdent = _pammodutil_getspnam (pamh, 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, pamh, "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") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) { + 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, name); + if (pwd == NULL && !on(UNIX_AUDIT,ctrl) + && retval != PAM_SUCCESS) + { + name = NULL; + } + } else { + D(("user's record unavailable")); + p = NULL; + if (pwd == NULL) + retval = PAM_USER_UNKNOWN; + else + retval = PAM_AUTHINFO_UNAVAIL; + 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, pamh, + "check pass; user (%s) unknown", name); + } else { + name = NULL; + if (on(UNIX_DEBUG, ctrl) || pwd == NULL) { + _log_err(LOG_ALERT, pamh, + "check pass; user unknown"); + } else { + /* don't log failure as another pam module can succeed */ + goto cleanup; + } + } + } + } else { + int salt_len = strlen(salt); + if (!salt_len) { + /* 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 (!p || (*salt == '*') || (salt_len < 13)) { + retval = PAM_AUTH_ERR; + } else { + if (!strncmp(salt, "$1$", 3)) { + pp = Goodcrypt_md5(p, salt); + if (strcmp(pp, salt) != 0) { + _pam_delete(pp); + 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)); + + /* + * Note, we are comparing the bigcrypt of the password with + * the contents of the password field. If the latter was + * encrypted with regular crypt (and not bigcrypt) it will + * have been truncated for storage relative to the output + * of bigcrypt here. As such we need to compare only the + * stored string with the subset of bigcrypt's result. + * Bug 521314: The strncmp comparison is for legacy support. + */ + if (strncmp(pp, salt, salt_len) == 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) { + + const char *login_name; + + login_name = _pammodutil_getlogin(pamh); + if (login_name == NULL) { + login_name = ""; + } + + new->user = x_strdup(name ? name : ""); + new->uid = getuid(); + new->euid = geteuid(); + new->name = x_strdup(login_name); + + /* 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; + const char *ruser=NULL; + const char *rhost=NULL; + const char *tty=NULL; + + (void) pam_get_item(pamh, PAM_SERVICE, + (const void **)&service); + (void) pam_get_item(pamh, PAM_RUSER, + (const void **)&ruser); + (void) pam_get_item(pamh, PAM_RHOST, + (const void **)&rhost); + (void) pam_get_item(pamh, PAM_TTY, + (const void **)&tty); + + _log_err(LOG_NOTICE, pamh, + "authentication failure; " + "logname=%s uid=%d euid=%d " + "tty=%s ruser=%s rhost=%s " + "%s%s", + new->name, new->uid, new->euid, + tty ? tty : "", + ruser ? ruser : "", + rhost ? rhost : "", + (new->user && new->user[0] != '\0') + ? " user=" : "", + new->user + ); + new->count = 1; + } + + pam_set_data(pamh, data_name, new, _cleanup_failures); + + } else { + _log_err(LOG_CRIT, pamh, + "no memory for failure recorder"); + } + } + } + +cleanup: + if (data_name) + _pam_delete(data_name); + if (salt) + _pam_delete(salt); + if (pp) + _pam_delete(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; + 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 **) pass); + if (retval != PAM_SUCCESS) { + /* very strange. */ + _log_err(LOG_ALERT, pamh + ,"pam_get_item returned error to unix-read-password" + ); + return retval; + } else if (*pass != NULL) { /* we have a password! */ + 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_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, pamh + ,"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, pamh, + "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 **) pass)) + != PAM_SUCCESS) { + + *pass = NULL; + _log_err(LOG_CRIT, pamh, "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, pamh + ,"error manipulating password data [%s]" + ,pam_strerror(pamh, retval)); + _pam_delete(token); + return retval; + } + *pass = token; + token = NULL; /* break link to password */ + } + + return PAM_SUCCESS; +} + +int _unix_shadowed(const struct passwd *pwd) +{ + if (pwd != NULL) { + if (strcmp(pwd->pw_passwd, "x") == 0) { + return 1; + } + if ((pwd->pw_passwd[0] == '#') && + (pwd->pw_passwd[1] == '#') && + (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) { + return 1; + } + } + return 0; +} + +/* ****************************************************************** * + * 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/Linux-PAM/modules/pam_unix/support.h b/Linux-PAM/modules/pam_unix/support.h new file mode 100644 index 00000000..39abadd5 --- /dev/null +++ b/Linux-PAM/modules/pam_unix/support.h @@ -0,0 +1,155 @@ +/* + * $Id: support.h,v 1.8 2004/10/06 13:42:36 kukuk Exp $ + */ + +#ifndef _PAM_UNIX_SUPPORT_H +#define _PAM_UNIX_SUPPORT_H + +#include <pwd.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_NOREAP 21 /* don't reap child process */ +#define UNIX_BROKEN_SHADOW 22 /* ignore errors reading password aging + * information during acct management */ +/* -------------- */ +#define UNIX_CTRLS_ 23 /* 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}, +/* UNIX_NOREAP */ {"noreap", _ALL_ON_, 04000000}, +/* UNIX_BROKEN_SHADOW */ {"broken_shadow", _ALL_ON_, 010000000}, +}; + +#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 void _log_err(int err, pam_handle_t *pamh, const char *format,...); +extern int _make_remark(pam_handle_t * pamh, unsigned int ctrl + ,int type, const char *text); +extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int argc, + const char **argv); +extern int _unix_getpwnam (pam_handle_t *pamh, + const char *name, int files, int nis, + struct passwd **ret); +extern int _unix_comesfromsource (pam_handle_t *pamh, + const char *name, int files, int nis); +extern int _unix_blankpasswd(pam_handle_t *pamh,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); +extern int _unix_shadowed(const struct passwd *pwd); + +#endif /* _PAM_UNIX_SUPPORT_H */ diff --git a/Linux-PAM/modules/pam_unix/unix_chkpwd.c b/Linux-PAM/modules/pam_unix/unix_chkpwd.c new file mode 100644 index 00000000..be32348f --- /dev/null +++ b/Linux-PAM/modules/pam_unix/unix_chkpwd.c @@ -0,0 +1,360 @@ +/* + * $Id: unix_chkpwd.c,v 1.11 2004/11/16 14:27:42 toady Exp $ + * + * 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. + * + */ + +#include <security/_pam_aconf.h> + +#ifdef MEMORY_DEBUG +# undef exit +# undef strdup +# undef free +#endif /* MEMORY_DEBUG */ + +#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 int _unix_shadowed(const struct passwd *pwd) +{ + char hashpass[1024]; + if (pwd != NULL) { + if (strcmp(pwd->pw_passwd, "x") == 0) { + return 1; + } + if (strlen(pwd->pw_name) < sizeof(hashpass) - 2) { + strcpy(hashpass, "##"); + strcpy(hashpass + 2, pwd->pw_name); + if (strcmp(pwd->pw_passwd, hashpass) == 0) { + return 1; + } + } + } + return 0; +} + +static void su_sighandler(int sig) +{ +#ifndef SA_RESETHAND + /* emulate the behaviour of the SA_RESETHAND flag */ + if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV ) + signal(sig, SIG_DFL); +#endif + 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; +#ifdef SA_RESETHAND + action.sa_flags = SA_RESETHAND; +#endif + (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 nullok) +{ + struct passwd *pwd = NULL; + struct spwd *spwdent = NULL; + char *salt = NULL; + char *pp = NULL; + int retval = UNIX_FAILED; + int salt_len; + + /* UNIX passwords area */ + setpwent(); + pwd = getpwnam(name); /* Get password file entry... */ + endpwent(); + if (pwd != NULL) { + if (_unix_shadowed(pwd)) { + /* + * ...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; + } + + salt_len = strlen(salt); + if (salt_len == 0) + return (nullok == 0) ? UNIX_FAILED : UNIX_PASSED; + else if (p == NULL || strlen(p) == 0) + return UNIX_FAILED; + + /* 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 if ((*salt == '*') || (salt_len < 13)) { + retval = UNIX_FAILED; + } else { + pp = bigcrypt(p, salt); + /* + * Note, we are comparing the bigcrypt of the password with + * the contents of the password field. If the latter was + * encrypted with regular crypt (and not bigcrypt) it will + * have been truncated for storage relative to the output + * of bigcrypt here. As such we need to compare only the + * stored string with the subset of bigcrypt's result. + * Bug 521314: the strncmp comparison is for legacy support. + */ + if (strncmp(pp, salt, salt_len) == 0) { + retval = UNIX_PASSED; + } + } + p = NULL; /* no longer needed here */ + + /* clean up */ + { + char *tp = pp; + if (pp != NULL) { + while (tp && *tp) + *tp++ = '\0'; + free(pp); + } + pp = tp = NULL; + } + + return retval; +} + +static char *getuidname(uid_t uid) +{ + struct passwd *pw; + static char username[32]; + + pw = getpwuid(uid); + if (pw == NULL) + return NULL; + + strncpy(username, pw->pw_name, sizeof(username)); + username[sizeof(username) - 1] = '\0'; + + return username; +} + +int main(int argc, char *argv[]) +{ + char pass[MAXPASS + 1]; + char option[8]; + int npass, nullok; + int force_failure = 0; + 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 + */ + user = getuidname(getuid()); + if (argc == 2) { + /* if the caller specifies the username, verify that user + matches it */ + if (strcmp(user, argv[1])) { + force_failure = 1; + } + } + + /* read the nullok/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) + nullok = 1; + else + nullok = 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, nullok); + + } else { + /* does pass agree with the official one? */ + + pass[npass] = '\0'; /* NUL terminate */ + retval = _unix_verify_password(user, pass, nullok); + + } + } + + memset(pass, '\0', MAXPASS); /* clear memory of the password */ + + /* return pass or fail */ + + if ((retval != UNIX_PASSED) || force_failure) { + return UNIX_FAILED; + } else { + return UNIX_PASSED; + } +} + +/* + * 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/Linux-PAM/modules/pam_unix/yppasswd.h b/Linux-PAM/modules/pam_unix/yppasswd.h new file mode 100644 index 00000000..6b414be0 --- /dev/null +++ b/Linux-PAM/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/Linux-PAM/modules/pam_unix/yppasswd_xdr.c b/Linux-PAM/modules/pam_unix/yppasswd_xdr.c new file mode 100644 index 00000000..b1a60b4c --- /dev/null +++ b/Linux-PAM/modules/pam_unix/yppasswd_xdr.c @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#include <security/_pam_aconf.h> + +#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/Linux-PAM/modules/pam_userdb/Makefile b/Linux-PAM/modules/pam_userdb/Makefile new file mode 100644 index 00000000..4da7310d --- /dev/null +++ b/Linux-PAM/modules/pam_userdb/Makefile @@ -0,0 +1,41 @@ +# +# 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: Makefile,v 1.6 2004/09/14 14:22:40 kukuk Exp $ +# Created by Cristian Gafton <gafton@redhat.com> + +include ../../Make.Rules + +TITLE=pam_userdb + +ifeq ($(HAVE_NDBM_H),yes) + WHICH_DB=ndbm + ifeq ($(HAVE_LIBNDBM),yes) + MODULE_SIMPLE_EXTRALIBS = -lndbm + endif +else +ifeq ($(HAVE_LIBDB),yes) + WHICH_DB=db + MODULE_SIMPLE_EXTRALIBS = -ldb +else + WHICH_DB=none +endif +endif + +ifeq ($(HAVE_LIBCRYPT),yes) + MODULE_SIMPLE_EXTRALIBS += -lcrypt +endif + +ifeq ($(WHICH_DB),none) + +include ../dont_makefile + +else + +MODULE_SIMPLE_EXTRAFILES = conv + +include ../Simple.Rules + +endif diff --git a/Linux-PAM/modules/pam_userdb/README b/Linux-PAM/modules/pam_userdb/README new file mode 100644 index 00000000..1cab7b74 --- /dev/null +++ b/Linux-PAM/modules/pam_userdb/README @@ -0,0 +1,61 @@ +pam_userdb: + Look up users in a .db database and verify their password against + what is contained in that database. The database will have been + created using db_load. + +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. Some versions of DB will + automatically append ".db" to whatever pathname you + supply here. + + crypt=[mode] indicates whether encrypted or plaintext passwords + are stored in the database. If [mode] is "crypt", + passwords should be stored in the database in + crypt(3) form. If [mode] is "none" or any other + value, passwords should be stored in the database in + plaintext. + + icase make the password verification to be case insensitive + (ie when working with registration numbers and such) + only works with plaintext password storage. + + dump dump all the entries in the database to the log (eek, + don't do this by default!) + + use_authtok use the authentication token previously obtained by + another module that did the conversation with the + application. If this token can not be obtained then + the module will try to converse again. This option can + be used for stacking different modules that need to + deal with the authentication tokens. + + unknown_ok do not return error when checking for a user that is + not in the database. This can be used to stack more + than one pam_userdb module that will check a + username/password pair in more than a database. + + key_only the username and password are concatenated together + in the database hash as 'username-password' with a + random value. if the concatenation of the username and + password with a dash in the middle returns any result, + the user is valid. this is useful in cases where + the username may not be unique but the username and + password pair are. + +MODULE SERVICES PROVIDED: + auth _authentication and _setcred (blank) + +EXAMPLE USE: + auth sufficient pam_userdb.so icase db=/tmp/dbtest.db + +AUTHOR: + Cristian Gafton <gafton@redhat.com> + + + +$Id: README,v 1.3 2004/09/28 13:48:47 kukuk Exp $ diff --git a/Linux-PAM/modules/pam_userdb/conv.c b/Linux-PAM/modules/pam_userdb/conv.c new file mode 100644 index 00000000..de5d12f2 --- /dev/null +++ b/Linux-PAM/modules/pam_userdb/conv.c @@ -0,0 +1,123 @@ +/* + * Conversation related functions + */ + +/* $Id */ +/* Copyright at the end of the file */ + +#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/Linux-PAM/modules/pam_userdb/create.pl b/Linux-PAM/modules/pam_userdb/create.pl new file mode 100644 index 00000000..28088102 --- /dev/null +++ b/Linux-PAM/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: create.pl,v 1.2 2004/09/28 13:48:47 kukuk Exp $ + +use DB_File; + +my $database = $ARGV[0]; +die "Use: create.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/Linux-PAM/modules/pam_userdb/pam_userdb.c b/Linux-PAM/modules/pam_userdb/pam_userdb.c new file mode 100644 index 00000000..86c7238b --- /dev/null +++ b/Linux-PAM/modules/pam_userdb/pam_userdb.c @@ -0,0 +1,490 @@ +/* pam_userdb module */ + +/* + * $Id: pam_userdb.c,v 1.7 2004/09/28 13:48:47 kukuk Exp $ + * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10 + * See the end of the file for Copyright Information + */ + +#include <security/_pam_aconf.h> + +#include <stdio.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 HAVE_NDBM_H +# include <ndbm.h> +#else +# ifdef HAVE_DB_H +# define DB_DBM_HSEARCH 1 /* use the dbm interface */ +# include <db.h> +# else +# error "failed to find a libdb or equivalent" +# endif +#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 + +#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(); +} + +static int +_pam_parse (int argc, const char **argv, + char **database, char **cryptmode) +{ + int ctrl; + + *database = NULL; + *cryptmode = NULL; + + /* 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 (!strcasecmp(*argv, "unknown_ok")) + ctrl |= PAM_UNKNOWN_OK_ARG; + else if (!strcasecmp(*argv, "key_only")) + ctrl |= PAM_KEY_ONLY_ARG; + else if (!strncasecmp(*argv,"db=", 3)) + { + *database = strdup((*argv) + 3); + if ((*database == NULL) || (strlen (*database) == 0)) + _pam_log(LOG_ERR, + "pam_parse: could not parse argument \"%s\"", + *argv); + } + else if (!strncasecmp(*argv,"crypt=", 6)) + { + *cryptmode = strdup((*argv) + 6); + if ((*cryptmode == NULL) || (strlen (*cryptmode) == 0)) + _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 *database, const char *cryptmode, + const char *user, const char *pass, int ctrl) +{ + 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; + } + + /* dump out the database contents for debugging */ + 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)); + if (ctrl & PAM_KEY_ONLY_ARG) { + key.dptr = malloc(strlen(user) + 1 + strlen(pass) + 1); + sprintf(key.dptr, "%s-%s", user, pass); + key.dsize = strlen(key.dptr); + } else { + key.dptr = x_strdup(user); + key.dsize = strlen(user); + } + + 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; + + if (ctrl & PAM_KEY_ONLY_ARG) + { + dbm_close (dbm); + return 0; /* found it, data contents don't matter */ + } + + if (strncasecmp(cryptmode, "crypt", 5) == 0) { + + /* crypt(3) password storage */ + + char *cryptpw; + char salt[2]; + + if (data.dsize != 13) { + compare = -2; + } else if (ctrl & PAM_ICASE_ARG) { + compare = -2; + } else { + salt[0] = *data.dptr; + salt[1] = *(data.dptr + 1); + + cryptpw = crypt (pass, salt); + + if (cryptpw) { + compare = strncasecmp (data.dptr, cryptpw, data.dsize); + } else { + compare = -2; + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_INFO, "crypt() returned NULL"); + } + }; + + }; + + } else { + + /* Unknown password encryption method - + * default to plaintext password storage + */ + + if (strlen(pass) != data.dsize) { + compare = 1; /* wrong password len -> wrong password */ + } else if (ctrl & PAM_ICASE_ARG) { + compare = strncasecmp(data.dptr, pass, data.dsize); + } else { + compare = strncmp(data.dptr, pass, data.dsize); + } + + if (strncasecmp(cryptmode, "none", 4) && ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_INFO, "invalid value for crypt parameter: %s", + cryptmode); + _pam_log(LOG_INFO, "defaulting to plaintext password mode"); + } + + } + + dbm_close(dbm); + if (compare == 0) + return 0; /* match */ + else + return -1; /* wrong */ + } else { + int saw_user = 0; + + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_INFO, "error returned by dbm_fetch: %s", + strerror(errno)); + } + + /* probably we should check dbm_error() here */ + + if ((ctrl & PAM_KEY_ONLY_ARG) == 0) { + dbm_close(dbm); + return 1; /* not key_only, so no entry => no entry for the user */ + } + + /* now handle the key_only case */ + for (key = dbm_firstkey(dbm); + key.dptr != NULL; + key = dbm_nextkey(dbm)) { + int compare; + /* first compare the user portion (case sensitive) */ + compare = strncmp(key.dptr, user, strlen(user)); + if (compare == 0) { + /* assume failure */ + compare = -1; + /* if we have the divider where we expect it to be... */ + if (key.dptr[strlen(user)] == '-') { + saw_user = 1; + if (key.dsize == strlen(user) + 1 + strlen(pass)) { + if (ctrl & PAM_ICASE_ARG) { + /* compare the password portion (case insensitive)*/ + compare = strncasecmp(key.dptr + strlen(user) + 1, + pass, + strlen(pass)); + } else { + /* compare the password portion (case sensitive) */ + compare = strncmp(key.dptr + strlen(user) + 1, + pass, + strlen(pass)); + } + } + } + if (compare == 0) { + dbm_close(dbm); + return 0; /* match */ + } + } + } + dbm_close(dbm); + if (saw_user) + return -1; /* saw the user, but password mismatch */ + else + 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; + char *database = NULL; + char *cryptmode = NULL; + int retval = PAM_AUTH_ERR, ctrl; + + /* parse arguments */ + ctrl = _pam_parse(argc, argv, &database, &cryptmode); + if ((database == NULL) || (strlen(database) == 0)) { + if (ctrl & PAM_DEBUG_ARG) + _pam_log(LOG_DEBUG,"can not get the database name"); + return PAM_SERVICE_ERR; + } + + /* 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 a password */ + retval = conversation(pamh); + if (retval != PAM_SUCCESS) { + _pam_log(LOG_ERR, "could not obtain password for `%s'", + username); + return PAM_CONV_ERR; + } + + /* Check if we got a password. The docs say that if we didn't have one, + * and use_authtok was specified as an argument, that we converse with the + * user anyway, so check for one and handle a failure for that case. If + * use_authtok wasn't specified, then we've already asked once and needn't + * do so again. */ + retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &password); + if ((retval != PAM_SUCCESS) && ((ctrl & PAM_USE_AUTHTOK_ARG) != 0)) { + retval = conversation(pamh); + if (retval != PAM_SUCCESS) { + _pam_log(LOG_ERR, "could not obtain password for `%s'", + username); + return PAM_CONV_ERR; + } + } + + /* Get the password */ + retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&password); + if (retval != PAM_SUCCESS) { + _pam_log(LOG_ERR, "Could not retrieve 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(database, cryptmode, username, password, ctrl); + 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) +{ + const char *username; + char *database = NULL; + char *cryptmode = NULL; + int retval = PAM_AUTH_ERR, ctrl; + + /* parse arguments */ + ctrl = _pam_parse(argc, argv, &database, &cryptmode); + + /* 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; + } + + /* Now use the username to look up password in the database file */ + retval = user_lookup(database, cryptmode, username, "", ctrl); + switch (retval) { + case -2: + /* some sort of system error. The log was already printed */ + return PAM_SERVICE_ERR; + case -1: + /* incorrect password, but we don't care */ + /* FALL THROUGH */ + case 0: + /* authentication succeeded. dumbest password ever. */ + return PAM_SUCCESS; + case 1: + /* the user does not exist in the database */ + return PAM_USER_UNKNOWN; + 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; + } + + return PAM_SUCCESS; +} + + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_userdb_modstruct = { + "pam_userdb", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + 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/Linux-PAM/modules/pam_userdb/pam_userdb.h b/Linux-PAM/modules/pam_userdb/pam_userdb.h new file mode 100644 index 00000000..af03676b --- /dev/null +++ b/Linux-PAM/modules/pam_userdb/pam_userdb.h @@ -0,0 +1,64 @@ + +#ifndef _PAM_USERSDB_H +#define _PAM_USERSDB_H +/* $Id: pam_userdb.h,v 1.2 2004/09/28 13:48:47 kukuk Exp $ */ + +/* Header files */ +#include <security/pam_appl.h> + +/* argument parsing */ +#define PAM_DEBUG_ARG 0x0001 +#define PAM_ICASE_ARG 0x0002 +#define PAM_DUMP_ARG 0x0004 +#define PAM_USE_AUTHTOK_ARG 0x0008 +#define PAM_UNKNOWN_OK_ARG 0x0010 +#define PAM_KEY_ONLY_ARG 0x0020 + +/* 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/Linux-PAM/modules/pam_warn/Makefile b/Linux-PAM/modules/pam_warn/Makefile new file mode 100644 index 00000000..b1420538 --- /dev/null +++ b/Linux-PAM/modules/pam_warn/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:06 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_warn + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_warn/README b/Linux-PAM/modules/pam_warn/README new file mode 100644 index 00000000..3c4bde8a --- /dev/null +++ b/Linux-PAM/modules/pam_warn/README @@ -0,0 +1,26 @@ +# $Id: README,v 1.1.1.1 2000/06/20 22:12:10 agmorgan Exp $ +# + +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/Linux-PAM/modules/pam_warn/pam_warn.c b/Linux-PAM/modules/pam_warn/pam_warn.c new file mode 100644 index 00000000..90170c01 --- /dev/null +++ b/Linux-PAM/modules/pam_warn/pam_warn.c @@ -0,0 +1,127 @@ +/* pam_warn module */ + +/* + * $Id: pam_warn.c,v 1.2 2002/05/29 04:44:43 agmorgan Exp $ + * + * 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 */ + +#define OBTAIN(item, value, default_value) do { \ + (void) pam_get_item(pamh, item, (const void **) &value); \ + value = value ? value : default_value ; \ +} while (0) + +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(); +} + +static void log_items(pam_handle_t *pamh, const char *function) +{ + const char *service=NULL, *user=NULL, *terminal=NULL, + *rhost=NULL, *ruser=NULL; + + OBTAIN(PAM_SERVICE, service, "<unknown>"); + OBTAIN(PAM_TTY, terminal, "<unknown>"); + OBTAIN(PAM_USER, user, "<unknown>"); + OBTAIN(PAM_RUSER, ruser, "<unknown>"); + OBTAIN(PAM_RHOST, rhost, "<unknown>"); + + _pam_log(LOG_NOTICE, "function=[%s] service=[%s] terminal=[%s] user=[%s]" + " ruser=[%s] rhost=[%s]\n", + function, service, terminal, user, ruser, rhost); +} + +/* --- authentication management functions (only) --- */ + +PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + log_items(pamh, __FUNCTION__); + return PAM_IGNORE; +} + +PAM_EXTERN +int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + log_items(pamh, __FUNCTION__); + return PAM_IGNORE; +} + +/* password updating functions */ + +PAM_EXTERN +int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc,const char **argv) +{ + log_items(pamh, __FUNCTION__); + return PAM_IGNORE; +} + +PAM_EXTERN int +pam_sm_acct_mgmt (pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + log_items(pamh, __FUNCTION__); + return PAM_IGNORE; +} + +PAM_EXTERN int +pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + log_items(pamh, __FUNCTION__); + return PAM_IGNORE; +} + +PAM_EXTERN int +pam_sm_close_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + log_items(pamh, __FUNCTION__); + return PAM_IGNORE; +} + +#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/Linux-PAM/modules/pam_wheel/Makefile b/Linux-PAM/modules/pam_wheel/Makefile new file mode 100644 index 00000000..67947f81 --- /dev/null +++ b/Linux-PAM/modules/pam_wheel/Makefile @@ -0,0 +1,15 @@ +# +# $Id: Makefile,v 1.2 2000/11/19 23:54:06 agmorgan Exp $ +# +# 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> 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_wheel + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_wheel/README b/Linux-PAM/modules/pam_wheel/README new file mode 100644 index 00000000..2cd156c0 --- /dev/null +++ b/Linux-PAM/modules/pam_wheel/README @@ -0,0 +1,39 @@ + +pam_wheel: + only permit root authentication to 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... :-) + Conversely, if the user is not in the group, return + PAM_IGNORE (unless 'trust' was also specified, in + which case we return PAM_SUCCESS). + + group=xxxx Instead of checking the wheel or GID 0 groups, use + the xxxx group to perform the authentification. + + root_only The check for wheel membership is done only + if the uid of requested account is 0. + +MODULE SERVICES PROVIDED: + auth _authentication, _setcred (blank) and _acct_mgmt + +AUTHOR: + Cristian Gafton <gafton@redhat.com> + diff --git a/Linux-PAM/modules/pam_wheel/pam_wheel.c b/Linux-PAM/modules/pam_wheel/pam_wheel.c new file mode 100644 index 00000000..92cd44b9 --- /dev/null +++ b/Linux-PAM/modules/pam_wheel/pam_wheel.c @@ -0,0 +1,327 @@ +/* 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 +#define PAM_SM_ACCOUNT + +#include <security/pam_modules.h> +#include <security/_pam_modutil.h> + +/* 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 && *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 +#define PAM_ROOT_ONLY_ARG 0x0020 + +static int _pam_parse(int argc, const char **argv, char *use_group, + size_t group_length) +{ + int ctrl=0; + + memset(use_group, '\0', group_length); + + /* 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 (!strcmp(*argv,"root_only")) + ctrl |= PAM_ROOT_ONLY_ARG; + else if (!strncmp(*argv,"group=",6)) + strncpy(use_group,*argv+6,group_length-1); + else { + _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv); + } + } + + return ctrl; +} + +static int perform_check(pam_handle_t *pamh, int flags, int ctrl, + const char *use_group) +{ + const char *username = NULL; + const char *fromsu; + struct passwd *pwd, *tpwd = NULL; + struct group *grp; + int retval = PAM_AUTH_ERR; + + 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; + } + + pwd = _pammodutil_getpwnam (pamh, username); + if (!pwd) { + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_NOTICE,"unknown user %s",username); + } + return PAM_USER_UNKNOWN; + } + if (ctrl & PAM_ROOT_ONLY_ARG) { + /* su to a non uid 0 account ? */ + if (pwd->pw_uid != 0) { + return PAM_IGNORE; + } + } + + if (ctrl & PAM_USE_UID_ARG) { + tpwd = _pammodutil_getpwuid (pamh, 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 = _pammodutil_getlogin(pamh); + if (fromsu) { + tpwd = _pammodutil_getpwnam (pamh, fromsu); + } + if (!fromsu || !tpwd) { + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_NOTICE, "who is running me ?!"); + } + return PAM_SERVICE_ERR; + } + } + + /* + * At this point fromsu = username-of-invoker; tpwd = pwd ptr for fromsu + */ + + if (!use_group[0]) { + if ((grp = _pammodutil_getgrnam (pamh, "wheel")) == NULL) { + grp = _pammodutil_getgrgid (pamh, 0); + } + } else { + grp = _pammodutil_getgrnam (pamh, use_group); + } + + if (!grp || (!grp->gr_mem && (tpwd->pw_gid != grp->gr_gid))) { + 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; + } + } + + /* + * test if the user is a member of the group, or if the + * user has the "wheel" (sic) group as its primary group. + */ + + if (is_on_list(grp->gr_mem, fromsu) || (tpwd->pw_gid == grp->gr_gid)) { + + if (ctrl & PAM_DENY_ARG) { + retval = PAM_PERM_DENIED; + + } else if (ctrl & PAM_TRUST_ARG) { + retval = PAM_SUCCESS; /* this can be a sufficient check */ + + } else { + retval = PAM_IGNORE; + } + + } else { + + if (ctrl & PAM_DENY_ARG) { + + if (ctrl & PAM_TRUST_ARG) { + retval = PAM_SUCCESS; /* this can be a sufficient check */ + } else { + retval = PAM_IGNORE; + } + + } else { + retval = PAM_PERM_DENIED; + } + } + + if (ctrl & PAM_DEBUG_ARG) { + if (retval == PAM_IGNORE) { + _pam_log(LOG_NOTICE, "Ignoring access request '%s' for '%s'", + fromsu, username); + } else { + _pam_log(LOG_NOTICE, "Access %s to '%s' for '%s'", + (retval != PAM_SUCCESS) ? "denied":"granted", + fromsu, username); + } + } + + return retval; +} + +/* --- authentication management functions --- */ + +PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + char use_group[BUFSIZ]; + int ctrl; + + ctrl = _pam_parse(argc, argv, use_group, sizeof(use_group)); + + return perform_check(pamh, flags, ctrl, use_group); +} + +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) +{ + char use_group[BUFSIZ]; + int ctrl; + + ctrl = _pam_parse(argc, argv, use_group, sizeof(use_group)); + + return perform_check(pamh, flags, ctrl, use_group); +} + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_wheel_modstruct = { + "pam_wheel", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + NULL, + NULL, + NULL, + NULL, +}; + +#endif /* PAM_STATIC */ + +/* + * 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/Linux-PAM/modules/pam_xauth/Makefile b/Linux-PAM/modules/pam_xauth/Makefile new file mode 100644 index 00000000..385466a2 --- /dev/null +++ b/Linux-PAM/modules/pam_xauth/Makefile @@ -0,0 +1,12 @@ +# +# 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!). +# + +include ../../Make.Rules + +TITLE=pam_xauth +MAN8=pam_xauth.8 + +include ../Simple.Rules diff --git a/Linux-PAM/modules/pam_xauth/README b/Linux-PAM/modules/pam_xauth/README new file mode 100644 index 00000000..dd65292f --- /dev/null +++ b/Linux-PAM/modules/pam_xauth/README @@ -0,0 +1,41 @@ +pam_xauth: + Forward xauth cookies from user to user, normally used by su, sudo, or + userhelper. + + Primitive access control is provided by ~/.xauth/export in the invoking + user's home directory and ~/.xauth/import in the target user's home + directory. + + If a user has a ~/.xauth/import file, the user will only receive cookies + from users listed in the file. If there is no ~/.xauth/import file, + the user will accept cookies from any other user. + + If a user has a .xauth/export file, the user will only forward cookies + to users listed in the file. If there is no ~/.xauth/export file, and + the invoking user is not "root", the user will forward cookies to + any other user. If there is no ~/.xauth/export file, and the invoking + user is "root", the user will NOT forward cookies to other users. + + Both the import and export files support wildcards (such as "*"). Both + the import and export files can be empty, signifying that no users are + allowed. + +RECOGNIZED ARGUMENTS: + debug write debugging messages to syslog + xauthpath= the path to the xauth program, by default + /usr/X11R6/bin/xauth + systemuser= highest user id assigned to system users, defaults + to 499 (pam_xauth will refuse to forward creds to + target users with id equal to or below this number, + except for root and possibly another specified user) + targetuser= a target user id which is excepted from the systemuser + checks + + +MODULE SERVICES PROVIDED: + session open session copies xauth cookie to new user + close session deletes copied xauth cookie + +AUTHOR: + Nalin Dahyabhai <nalin@redhat.com>, based on original version by + Michael K. Johnson <johnsonm@redhat.com> diff --git a/Linux-PAM/modules/pam_xauth/pam_xauth.8 b/Linux-PAM/modules/pam_xauth/pam_xauth.8 new file mode 100644 index 00000000..9acb7249 --- /dev/null +++ b/Linux-PAM/modules/pam_xauth/pam_xauth.8 @@ -0,0 +1,82 @@ +.\" Copyright 2001,2003 Red Hat, Inc. +.\" Written by Nalin Dahyabhai <nalin@redhat.com>, based on the original +.\" version by Michael K. Johnson +.TH pam_xauth 8 2003/7/24 "Red Hat Linux" "System Administrator's Manual" +.SH NAME +pam_xauth \- forward xauth keys between users +.SH SYNOPSIS +.B session optional /lib/security/pam_xauth.so \fIarguments\fP +.SH DESCRIPTION +pam_xauth.so is designed to forward xauth keys (sometimes referred +to as "cookies") between users. + +Without pam_xauth, when xauth is enabled and a user uses the \fBsu\fP command +to assume another user's priviledges, that user is no longer able to access +the original user's X display because the new user does not have the key +needed to access the display. pam_xauth solves the problem by forwarding the +key from the user running su (the source user) to the user whose +identity the source user is assuming (the target user) when the session +is created, and destroying the key when the session is torn down. + +This means, for example, that when you run \fBsu\fP from an xterm sesssion, +you will be able to run X programs without explicitly dealing with the +xauth command or ~/.Xauthority files. + +pam_xauth will only forward keys if xauth can list a key connected +to the $DISPLAY environment variable. + +Primitive access control is provided by \fB~/.xauth/export\fP in the invoking +user's home directory and \fB~/.xauth/import\fP in the target user's home +directory. + +If a user has a \fB~/.xauth/import\fP file, the user will only receive cookies +from users listed in the file. If there is no \fB~/.xauth/import\fP file, +the user will accept cookies from any other user. + +If a user has a \fB.xauth/export\fP file, the user will only forward cookies +to users listed in the file. If there is no \fB~/.xauth/export\fP file, and +the invoking user is not \fBroot\fP, the user will forward cookies to +any other user. If there is no \fB~/.xauth/export\fP file, and the invoking +user is \fBroot\fP, the user will \fInot\fP forward cookies to other users. + +Both the import and export files support wildcards (such as \fI*\fP). Both +the import and export files can be empty, signifying that no users are allowed. + +.SH ARGUMENTS +.IP debug +Turns on debugging messages sent to syslog. +.IP xauthpath=\fI/usr/X11R6/bin/xauth\fP +Specify the path the xauth program (the default is /usr/X11R6/bin/xauth). +.IP systemuser=\fInumber\fP +Specify the highest UID which will be assumed to belong to a "system" user. +pam_xauth will refuse to forward credentials to users with UID less than or +equal to this number, except for root and the "targetuser", if specified. +.IP targetuser=\fInumber\fP +Specify a single target UID which is exempt from the systemuser check. +.SH "IMPLEMENTATION DETAILS" +pam_xauth will work \fIonly\fP if it is used from a setuid application +in which the getuid() call returns the id of the user running the +application, and for which PAM can supply the name of the account that +the user is attempting to assume. The typical application of this +type is \fBsu\fP. The application must call both pam_open_session() and +pam_close_session() with the ruid set to the uid of the calling user +and the euid set to root, and must have provided as the PAM_USER item +the name of the target user. + +pam_xauth calls \fBxauth\fP as the source user to extract the key for +$DISPLAY, then calls xauth as the target user to merge the key +into the a temporary database and later remove the database. + +pam_xauth cannot be told not to remove the keys when the session +is closed. +.SH "SEE ALSO" +\fI/usr/share/doc/pam*/html/index.html\fP +.SH FILES +\fI~/.xauth/import\fP +\fI~/.xauth/export\fP +.SH BUGS +Let's hope not, but if you find any, please report them via the "Bug Track" +link at http://bugzilla.redhat.com/bugzilla/ +.SH AUTHOR +Nalin Dahyabhai <nalin@redhat.com>, based on original version by +Michael K. Johnson <johnsonm@redhat.com> diff --git a/Linux-PAM/modules/pam_xauth/pam_xauth.c b/Linux-PAM/modules/pam_xauth/pam_xauth.c new file mode 100644 index 00000000..2bf72eb6 --- /dev/null +++ b/Linux-PAM/modules/pam_xauth/pam_xauth.c @@ -0,0 +1,641 @@ +/* + * Copyright 2001-2003 Red Hat, Inc. + * + * 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. + */ + +/* "$Id: pam_xauth.c,v 1.4 2005/03/14 09:42:28 kukuk Exp $" */ + +#include "../../_pam_aconf.h" +#include <sys/types.h> +#include <sys/fsuid.h> +#include <sys/wait.h> +#include <errno.h> +#include <fnmatch.h> +#include <grp.h> +#include <limits.h> +#include <netdb.h> +#include <pwd.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include <security/pam_modules.h> +#include <security/_pam_macros.h> +#include <security/_pam_modutil.h> + +#define DATANAME "pam_xauth_cookie_file" +#define XAUTHBIN "/usr/X11R6/bin/xauth" +#define XAUTHENV "XAUTHORITY" +#define HOMEENV "HOME" +#define XAUTHDEF ".Xauthority" +#define XAUTHTMP ".xauthXXXXXX" + +/* Run a given command (with a NULL-terminated argument list), feeding it the + * given input on stdin, and storing any output it generates. */ +static int +run_coprocess(const char *input, char **output, + uid_t uid, gid_t gid, const char *command, ...) +{ + int ipipe[2], opipe[2], i; + char buf[LINE_MAX]; + pid_t child; + char *buffer = NULL; + size_t buffer_size = 0; + va_list ap; + + *output = NULL; + + /* Create stdio pipery. */ + if (pipe(ipipe) == -1) { + return -1; + } + if (pipe(opipe) == -1) { + close(ipipe[0]); + close(ipipe[1]); + return -1; + } + + /* Fork off a child. */ + child = fork(); + if (child == -1) { + close(ipipe[0]); + close(ipipe[1]); + close(opipe[0]); + close(opipe[1]); + return -1; + } + + if (child == 0) { + /* We're the child. */ + char *args[10]; + const char *tmp; + /* Drop privileges. */ + setgid(gid); + setgroups(0, NULL); + setuid(uid); + /* Initialize the argument list. */ + memset(args, 0, sizeof(args)); + /* Set the pipe descriptors up as stdin and stdout, and close + * everything else, including the original values for the + * descriptors. */ + dup2(ipipe[0], STDIN_FILENO); + dup2(opipe[1], STDOUT_FILENO); + for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) { + if ((i != STDIN_FILENO) && (i != STDOUT_FILENO)) { + close(i); + } + } + /* Convert the varargs list into a regular array of strings. */ + va_start(ap, command); + args[0] = strdup(command); + for (i = 1; i < ((sizeof(args) / sizeof(args[0])) - 1); i++) { + tmp = va_arg(ap, const char*); + if (tmp == NULL) { + break; + } + args[i] = strdup(tmp); + } + /* Run the command. */ + execvp(command, args); + /* Never reached. */ + exit(1); + } + + /* We're the parent, so close the other ends of the pipes. */ + close(ipipe[0]); + close(opipe[1]); + /* Send input to the process (if we have any), then send an EOF. */ + if (input) { + (void)_pammodutil_write(ipipe[1], input, strlen(input)); + } + close(ipipe[1]); + + /* Read data output until we run out of stuff to read. */ + i = _pammodutil_read(opipe[0], buf, sizeof(buf)); + while ((i != 0) && (i != -1)) { + char *tmp; + /* Resize the buffer to hold the data. */ + tmp = realloc(buffer, buffer_size + i + 1); + if (tmp == NULL) { + /* Uh-oh, bail. */ + if (buffer != NULL) { + free(buffer); + } + close(opipe[0]); + waitpid(child, NULL, 0); + return -1; + } + /* Save the new buffer location, copy the newly-read data into + * the buffer, and make sure the result will be + * nul-terminated. */ + buffer = tmp; + memcpy(buffer + buffer_size, buf, i); + buffer[buffer_size + i] = '\0'; + buffer_size += i; + /* Try to read again. */ + i = _pammodutil_read(opipe[0], buf, sizeof(buf)); + } + /* No more data. Clean up and return data. */ + close(opipe[0]); + *output = buffer; + waitpid(child, NULL, 0); + return 0; +} + +/* Free a data item. */ +static void +cleanup(pam_handle_t *pamh, void *data, int err) +{ + free(data); +} + +/* Check if we want to allow export to the other user, or import from the + * other user. */ +static int +check_acl(pam_handle_t *pamh, + const char *sense, const char *this_user, const char *other_user, + int noent_code, int debug) +{ + char path[PATH_MAX]; + struct passwd *pwd; + FILE *fp; + int i; + uid_t euid; + /* Check this user's <sense> file. */ + pwd = _pammodutil_getpwnam(pamh, this_user); + if (pwd == NULL) { + syslog(LOG_ERR, "pam_xauth: error determining " + "home directory for '%s'", this_user); + return PAM_SESSION_ERR; + } + /* Figure out what that file is really named. */ + i = snprintf(path, sizeof(path), "%s/.xauth/%s", pwd->pw_dir, sense); + if ((i >= sizeof(path)) || (i < 0)) { + syslog(LOG_ERR, "pam_xauth: name of user's home directory " + "is too long"); + return PAM_SESSION_ERR; + } + euid = geteuid(); + setfsuid(pwd->pw_uid); + fp = fopen(path, "r"); + setfsuid(euid); + if (fp != NULL) { + char buf[LINE_MAX], *tmp; + /* Scan the file for a list of specs of users to "trust". */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + tmp = memchr(buf, '\r', sizeof(buf)); + if (tmp != NULL) { + *tmp = '\0'; + } + tmp = memchr(buf, '\n', sizeof(buf)); + if (tmp != NULL) { + *tmp = '\0'; + } + if (fnmatch(buf, other_user, 0) == 0) { + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: %s %s " + "allowed by %s", + other_user, sense, path); + } + fclose(fp); + return PAM_SUCCESS; + } + } + /* If there's no match in the file, we fail. */ + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: %s not listed in %s", + other_user, path); + } + fclose(fp); + return PAM_PERM_DENIED; + } else { + /* Default to okay if the file doesn't exist. */ + switch (errno) { + case ENOENT: + if (noent_code == PAM_SUCCESS) { + if (debug) { + syslog(LOG_DEBUG, "%s does not exist, " + "ignoring", path); + } + } else { + if (debug) { + syslog(LOG_DEBUG, "%s does not exist, " + "failing", path); + } + } + return noent_code; + default: + if (debug) { + syslog(LOG_ERR, "%s opening %s", + strerror(errno), path); + } + return PAM_PERM_DENIED; + } + } +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + char xauthpath[] = XAUTHBIN; + char *cookiefile = NULL, *xauthority = NULL, + *cookie = NULL, *display = NULL, *tmp = NULL; + const char *user, *xauth = xauthpath; + struct passwd *tpwd, *rpwd; + int fd, i, debug = 0; + uid_t systemuser = 499, targetuser = 0, euid; + + /* Parse arguments. We don't understand many, so no sense in breaking + * this into a separate function. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + continue; + } + if (strncmp(argv[i], "xauthpath=", 10) == 0) { + xauth = argv[i] + 10; + continue; + } + if (strncmp(argv[i], "targetuser=", 11) == 0) { + long l = strtol(argv[i] + 11, &tmp, 10); + if ((strlen(argv[i] + 11) > 0) && (*tmp == '\0')) { + targetuser = l; + } else { + syslog(LOG_WARNING, "pam_xauth: invalid value " + "for targetuser (`%s')", argv[i] + 11); + } + continue; + } + if (strncmp(argv[i], "systemuser=", 11) == 0) { + long l = strtol(argv[i] + 11, &tmp, 10); + if ((strlen(argv[i] + 11) > 0) && (*tmp == '\0')) { + systemuser = l; + } else { + syslog(LOG_WARNING, "pam_xauth: invalid value " + "for systemuser (`%s')", argv[i] + 11); + } + continue; + } + syslog(LOG_WARNING, "pam_xauth: unrecognized option `%s'", + argv[i]); + } + + /* If DISPLAY isn't set, we don't really care, now do we? */ + if ((display = getenv("DISPLAY")) == NULL) { + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: user has no DISPLAY," + " doing nothing"); + } + return PAM_SUCCESS; + } + + /* Read the target user's name. */ + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_xauth: error determining target " + "user's name"); + return PAM_SESSION_ERR; + } + rpwd = _pammodutil_getpwuid(pamh, getuid()); + if (rpwd == NULL) { + syslog(LOG_ERR, "pam_xauth: error determining invoking " + "user's name"); + return PAM_SESSION_ERR; + } + + /* Get the target user's UID and primary GID, which we'll need to set + * on the xauthority file we create later on. */ + tpwd = _pammodutil_getpwnam(pamh, user); + if (tpwd == NULL) { + syslog(LOG_ERR, "pam_xauth: error determining target " + "user's UID"); + return PAM_SESSION_ERR; + } + + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: requesting user %lu/%lu, " + "target user %lu/%lu", + (unsigned long) rpwd->pw_uid, + (unsigned long) rpwd->pw_gid, + (unsigned long) tpwd->pw_uid, + (unsigned long) tpwd->pw_gid); + } + + /* If the UID is a system account (and not the superuser), forget + * about forwarding keys. */ + if ((tpwd->pw_uid != 0) && + (tpwd->pw_uid != targetuser) && + (tpwd->pw_uid <= systemuser)) { + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: not forwarding cookies " + "to user ID %ld", (long) tpwd->pw_uid); + } + return PAM_SESSION_ERR; + } + + /* Check that both users are amenable to this. By default, this + * boils down to this policy: + * export(ruser=root): only if <user> is listed in .xauth/export + * export(ruser=*) if <user> is listed in .xauth/export, or + * if .xauth/export does not exist + * import(user=*): if <ruser> is listed in .xauth/import, or + * if .xauth/import does not exist */ + i = (getuid() != 0) ? PAM_SUCCESS : PAM_PERM_DENIED; + i = check_acl(pamh, "export", rpwd->pw_name, user, i, debug); + if (i != PAM_SUCCESS) { + return PAM_SESSION_ERR; + } + i = PAM_SUCCESS; + i = check_acl(pamh, "import", user, rpwd->pw_name, i, debug); + if (i != PAM_SUCCESS) { + return PAM_SESSION_ERR; + } + + /* Figure out where the source user's .Xauthority file is. */ + if (getenv(XAUTHENV) != NULL) { + cookiefile = strdup(getenv(XAUTHENV)); + } else { + cookiefile = malloc(strlen(rpwd->pw_dir) + 1 + + strlen(XAUTHDEF) + 1); + if (cookiefile == NULL) { + return PAM_SESSION_ERR; + } + strcpy(cookiefile, rpwd->pw_dir); + strcat(cookiefile, "/"); + strcat(cookiefile, XAUTHDEF); + } + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: reading keys from `%s'", + cookiefile); + } + + /* Read the user's .Xauthority file. Because the current UID is + * the original user's UID, this will only fail if something has + * gone wrong, or we have no cookies. */ + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: running \"%s %s %s %s %s\" as " + "%lu/%lu", + xauth, + "-f", + cookiefile, + "nlist", + display, + (unsigned long) getuid(), + (unsigned long) getgid()); + } + if (run_coprocess(NULL, &cookie, + getuid(), getgid(), + xauth, "-f", cookiefile, "nlist", display, + NULL) == 0) { + /* Check that we got a cookie. If not, we get creative. */ + if (((cookie == NULL) || (strlen(cookie) == 0)) && + ((strncmp(display, "localhost:", 10) == 0) || + (strncmp(display, "localhost/unix:", 15) == 0))) { + char *t, *screen; + size_t tlen, slen; + /* Free the useless cookie string. */ + if (cookie != NULL) { + free(cookie); + cookie = NULL; + } + /* Allocate enough space to hold an adjusted name. */ + tlen = strlen(display) + LINE_MAX + 1; + t = malloc(tlen); + if (t != NULL) { + memset(t, 0, tlen); + if (gethostname(t, tlen - 1) != -1) { + /* Append the protocol and then the + * screen number. */ + if (strlen(t) < tlen - 6) { + strcat(t, "/unix:"); + } + screen = strchr(display, ':'); + if (screen != NULL) { + screen++; + slen = strlen(screen); + if (strlen(t) + slen < tlen) { + strcat(t, screen); + } + } + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: " + "no key for `%s', trying" + " `%s'", display, t); + } + /* Read the cookie for this display. */ + if (debug) { + syslog(LOG_DEBUG, + "pam_xauth: running " + "\"%s %s %s %s %s\" as " + "%lu/%lu", + xauth, + "-f", + cookiefile, + "nlist", + t, + (unsigned long) getuid(), + (unsigned long) getgid()); + } + run_coprocess(NULL, &cookie, + getuid(), getgid(), + xauth, "-f", cookiefile, + "nlist", t, NULL); + } + free(t); + t = NULL; + } + } + + /* Check that we got a cookie, this time for real. */ + if ((cookie == NULL) || (strlen(cookie) == 0)) { + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: no key"); + } + return PAM_SESSION_ERR; + } + + /* Generate the environment variable + * "XAUTHORITY=<homedir>/filename". */ + xauthority = malloc(strlen(XAUTHENV) + 1 + + strlen(tpwd->pw_dir) + 1 + + strlen(XAUTHTMP) + 1); + if (xauthority == NULL) { + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: no free memory"); + } + free(cookiefile); + free(cookie); + return PAM_SESSION_ERR; + } + strcpy(xauthority, XAUTHENV); + strcat(xauthority, "="); + strcat(xauthority, tpwd->pw_dir); + strcat(xauthority, "/"); + strcat(xauthority, XAUTHTMP); + + /* Generate a new file to hold the data. */ + euid = geteuid(); + setfsuid(tpwd->pw_uid); + fd = mkstemp(xauthority + strlen(XAUTHENV) + 1); + setfsuid(euid); + if (fd == -1) { + syslog(LOG_ERR, "pam_xauth: error creating " + "temporary file `%s': %s", + xauthority + strlen(XAUTHENV) + 1, + strerror(errno)); + free(cookiefile); + free(cookie); + free(xauthority); + return PAM_SESSION_ERR; + } + /* Set permissions on the new file and dispose of the + * descriptor. */ + fchown(fd, tpwd->pw_uid, tpwd->pw_gid); + close(fd); + + /* Get a copy of the filename to save as a data item for + * removal at session-close time. */ + free(cookiefile); + cookiefile = strdup(xauthority + strlen(XAUTHENV) + 1); + + /* Save the filename. */ + if (pam_set_data(pamh, DATANAME, cookiefile, cleanup) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_xauth: error saving name of " + "temporary file `%s'", cookiefile); + unlink(cookiefile); + free(xauthority); + free(cookiefile); + free(cookie); + return PAM_SESSION_ERR; + } + + /* Unset any old XAUTHORITY variable in the environment. */ + if (getenv (XAUTHENV)) + unsetenv (XAUTHENV); + + /* Set the new variable in the environment. */ + if (pam_putenv (pamh, xauthority) != PAM_SUCCESS) + syslog (LOG_DEBUG, "pam_xauth: can't set environment variable '%s'", + xauthority); + putenv (xauthority); /* The environment owns this string now. */ + + /* set $DISPLAY in pam handle to make su - work */ + { + char *d = (char *) malloc (strlen ("DISPLAY=") + + strlen (display) + 1); + if (d == NULL) + { + syslog (LOG_DEBUG, "pam_xauth: memory exhausted\n"); + return PAM_SESSION_ERR; + } + strcpy (d, "DISPLAY="); + strcat (d, display); + + if (pam_putenv (pamh, d) != PAM_SUCCESS) + syslog (LOG_DEBUG, + "pam_xauth: can't set environment variable '%s'", + d); + free (d); + } + + /* Merge the cookie we read before into the new file. */ + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: writing key `%s' to " + "temporary file `%s'", cookie, cookiefile); + } + if (debug) { + syslog(LOG_DEBUG, + "pam_xauth: running \"%s %s %s %s %s\" as " + "%lu/%lu", + xauth, + "-f", + cookiefile, + "nmerge", + "-", + (unsigned long) tpwd->pw_uid, + (unsigned long) tpwd->pw_gid); + } + run_coprocess(cookie, &tmp, + tpwd->pw_uid, tpwd->pw_gid, + xauth, "-f", cookiefile, "nmerge", "-", NULL); + + /* We don't need to keep a copy of these around any more. */ + free(cookie); + cookie = NULL; + } + return PAM_SUCCESS; +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + void *cookiefile; + int i, debug = 0; + + /* Parse arguments. We don't understand many, so no sense in breaking + * this into a separate function. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + continue; + } + if (strncmp(argv[i], "xauthpath=", 10) == 0) { + continue; + } + if (strncmp(argv[i], "systemuser=", 11) == 0) { + continue; + } + if (strncmp(argv[i], "targetuser=", 11) == 0) { + continue; + } + syslog(LOG_WARNING, "pam_xauth: unrecognized option `%s'", + argv[i]); + } + + /* Try to retrieve the name of a file we created when the session was + * opened. */ + if (pam_get_data(pamh, DATANAME, (const void**) &cookiefile) == PAM_SUCCESS) { + /* We'll only try to remove the file once. */ + if (strlen((char*)cookiefile) > 0) { + if (debug) { + syslog(LOG_DEBUG, "pam_xauth: removing `%s'", + (char*)cookiefile); + } + unlink((char*)cookiefile); + *((char*)cookiefile) = '\0'; + } + } + return PAM_SUCCESS; +} diff --git a/Linux-PAM/modules/pammodutil/Makefile b/Linux-PAM/modules/pammodutil/Makefile new file mode 100644 index 00000000..bad1bf62 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/Makefile @@ -0,0 +1,55 @@ +# +# $Id: Makefile,v 1.4 2004/09/24 13:13:22 kukuk Exp $ +# +# + +include ../../Make.Rules + +LIBNAME=libpammodutil + +# --------------------------------------------- + +dummy: all + +# --------------------------------------------- + +CFLAGS += $(PIC) $(STATIC) $(MOREFLAGS) \ + -DLIBPAM_VERSION_MAJOR=$(MAJOR_REL) \ + -DLIBPAM_VERSION_MINOR=$(MINOR_REL) + +# all the object files we care about +LIBOBJECTS = modutil_cleanup.o modutil_getpwnam.o modutil_getpwuid.o \ + modutil_getspnam.o modutil_getgrnam.o modutil_getgrgid.o \ + modutil_ingroup.o modutil_getlogin.o modutil_ioloop.o + +# static library name +LIBSTATIC = $(LIBNAME).a + +SLIBOBJECTS = $(addprefix static/,$(LIBOBJECTS) $(STATICOBJ)) + +# --------------------------------------------- +## rules + +all: dirs $(LIBSTATIC) ../../Make.Rules + +dirs: + $(MKDIR) static + +static/%.o : %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +$(LIBSTATIC): $(SLIBOBJECTS) + ar cr $@ $(SLIBOBJECTS) + $(RANLIB) $@ + +install: + @echo "at this time, we're not installing $(LIBSTATIC)" + +remove: + @echo "at this time, there is nothing to remove" + +clean: + rm -f a.out core *~ static/*.o + rm -f *.a *.o + if [ -d dynamic ]; then rmdir dynamic ; fi + if [ -d static ]; then rmdir static ; fi diff --git a/Linux-PAM/modules/pammodutil/README b/Linux-PAM/modules/pammodutil/README new file mode 100644 index 00000000..ea44f310 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/README @@ -0,0 +1,15 @@ +$Id: README,v 1.1 2001/12/09 22:15:12 agmorgan Exp $ + +This is a libarary of routines for use by modules. The routines seem +to have a common use for modules, but are not part of libpam and never +will be. They are also a convenient layer of abstraction for providing +thread-safe functions that may require use of pam_handle_t 'data' +items to make their thread-safeness tied to the use of a single +pam_handle_t per thread. + +Functions provided so far are all listed in + + include/security/_pam_modutil.h + +. + diff --git a/Linux-PAM/modules/pammodutil/include/security/_pam_modutil.h b/Linux-PAM/modules/pammodutil/include/security/_pam_modutil.h new file mode 100644 index 00000000..ec0c8964 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/include/security/_pam_modutil.h @@ -0,0 +1,66 @@ +#ifndef _PAM_MODUTIL_H +#define _PAM_MODUTIL_H + +/* + * $Id: _pam_modutil.h,v 1.4 2004/09/24 13:13:22 kukuk Exp $ + * + * This file is a list of handy libc wrappers that attempt to provide some + * thread-safe and other convenient functionality to modules in a form that + * is common, but not dynamically linked with yet another dynamic pam + * library extension. + * + * A number of these functions reserve space in a pam_[sg]et_data item. + * In all cases, the name of the item is prefixed with "_pammodutil_*". + * + * On systems that simply can't support thread safe programming, these + * functions don't support it either - sorry. + * + * Copyright (c) 2001-2002 Andrew Morgan <morgan@kernel.org> + */ + +#include <pwd.h> +#include <grp.h> +#include <shadow.h> +#include <sys/types.h> + +extern struct passwd *_pammodutil_getpwnam(pam_handle_t *pamh, + const char *user); + +extern struct passwd *_pammodutil_getpwuid(pam_handle_t *pamh, + uid_t uid); + +extern struct group *_pammodutil_getgrnam(pam_handle_t *pamh, + const char *group); + +extern struct group *_pammodutil_getgrgid(pam_handle_t *pamh, + gid_t gid); + +extern struct spwd *_pammodutil_getspnam(pam_handle_t *pamh, + const char *user); + +extern int _pammodutil_user_in_group_nam_nam(pam_handle_t *pamh, + const char *user, + const char *group); + +extern int _pammodutil_user_in_group_nam_gid(pam_handle_t *pamh, + const char *user, + gid_t group); + +extern int _pammodutil_user_in_group_uid_nam(pam_handle_t *pamh, + uid_t user, + const char *group); + +extern int _pammodutil_user_in_group_uid_gid(pam_handle_t *pamh, + uid_t user, + gid_t group); + +extern void _pammodutil_cleanup(pam_handle_t *pamh, void *data, + int error_status); + +extern const char *_pammodutil_getlogin(pam_handle_t *pamh); + +extern int _pammodutil_read(int fd, char *buffer, int count); + +extern int _pammodutil_write(int fd, const char *buffer, int count); + +#endif /* _PAM_MODUTIL_H */ diff --git a/Linux-PAM/modules/pammodutil/modutil_cleanup.c b/Linux-PAM/modules/pammodutil/modutil_cleanup.c new file mode 100644 index 00000000..5477481f --- /dev/null +++ b/Linux-PAM/modules/pammodutil/modutil_cleanup.c @@ -0,0 +1,16 @@ +/* + * $Id: modutil_cleanup.c,v 1.1 2001/12/09 22:15:12 agmorgan Exp $ + * + * This function provides a common pam_set_data() friendly version of free(). + */ + +#include "pammodutil.h" + +void _pammodutil_cleanup(pam_handle_t *pamh, void *data, int error_status) +{ + if (data) { + /* junk it */ + (void) free(data); + } +} + diff --git a/Linux-PAM/modules/pammodutil/modutil_getgrgid.c b/Linux-PAM/modules/pammodutil/modutil_getgrgid.c new file mode 100644 index 00000000..f97a9239 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/modutil_getgrgid.c @@ -0,0 +1,150 @@ +/* + * $Id: modutil_getgrgid.c,v 1.3 2005/03/30 14:59:41 kukuk Exp $ + * + * This function provides a thread safer version of getgrgid() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pammodutil.h" + +#include <errno.h> +#include <limits.h> +#include <grp.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +static pthread_mutex_t _pammodutil_mutex = PTHREAD_MUTEX_INITIALIZER; +static void _pammodutil_lock(void) +{ + pthread_mutex_lock(&_pammodutil_mutex); +} +static void _pammodutil_unlock(void) +{ + pthread_mutex_unlock(&_pammodutil_mutex); +} + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +static int longlen(long number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct group *_pammodutil_getgrgid(pam_handle_t *pamh, gid_t gid) +{ +#ifdef HAVE_GETGRGID_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct group *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct group) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the user - so delete the memory */ + if (buffer) { + free(buffer); + } + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the grp structure */ + errno = 0; + status = getgrgid_r(gid, buffer, + sizeof(struct group) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getgrgid") + 1 + + longlen((long)gid) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getgrgid_%ld_%d", + (long) gid, i); + _pammodutil_lock(); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, _pammodutil_cleanup); + } + _pammodutil_unlock(); + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= 2; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("grp structure took %u bytes or so of memory", + length+sizeof(struct group))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETGRGID_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getgrgid(). So, we use the standard libc function. + */ + + return getgrgid(gid); + +#endif /* def HAVE_GETGRGID_R */ +} diff --git a/Linux-PAM/modules/pammodutil/modutil_getgrnam.c b/Linux-PAM/modules/pammodutil/modutil_getgrnam.c new file mode 100644 index 00000000..0727618c --- /dev/null +++ b/Linux-PAM/modules/pammodutil/modutil_getgrnam.c @@ -0,0 +1,139 @@ +/* + * $Id: modutil_getgrnam.c,v 1.3 2005/03/30 14:59:41 kukuk Exp $ + * + * This function provides a thread safer version of getgrnam() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pammodutil.h" + +#include <errno.h> +#include <limits.h> +#include <grp.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +static pthread_mutex_t _pammodutil_mutex = PTHREAD_MUTEX_INITIALIZER; +static void _pammodutil_lock(void) +{ + pthread_mutex_lock(&_pammodutil_mutex); +} +static void _pammodutil_unlock(void) +{ + pthread_mutex_unlock(&_pammodutil_mutex); +} + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct group *_pammodutil_getgrnam(pam_handle_t *pamh, const char *group) +{ +#ifdef HAVE_GETGRNAM_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct group *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct group) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the group - so delete the memory */ + if (buffer) { + free(buffer); + } + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the grp structure */ + errno = 0; + status = getgrnam_r(group, buffer, + sizeof(struct group) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getgrnam") + 1 + + strlen(group) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getgrnam_%s_%d", group, i); + _pammodutil_lock(); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, _pammodutil_cleanup); + } + _pammodutil_unlock(); + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= 2; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("grp structure took %u bytes or so of memory", + length+sizeof(struct group))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETGRNAM_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getgrnam(). So, we use the standard libc function. + */ + + return getgrnam(group); + +#endif /* def HAVE_GETGRNAM_R */ +} diff --git a/Linux-PAM/modules/pammodutil/modutil_getlogin.c b/Linux-PAM/modules/pammodutil/modutil_getlogin.c new file mode 100644 index 00000000..0e4a48d8 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/modutil_getlogin.c @@ -0,0 +1,72 @@ +/* + * $Id: modutil_getlogin.c,v 1.4 2004/11/08 10:57:15 t8m Exp $ + * + * A central point for invoking getlogin(). Hopefully, this is a + * little harder to spoof than all the other versions that are out + * there. + */ + +#include "pammodutil.h" + +#include <stdlib.h> +#include <unistd.h> +#include <utmp.h> + +#define _PAMMODUTIL_GETLOGIN "_pammodutil_getlogin" + +const char *_pammodutil_getlogin(pam_handle_t *pamh) +{ + int status; + char *logname; + const char *curr_tty; + char *curr_user; + struct utmp *ut, line; + + status = pam_get_data(pamh, _PAMMODUTIL_GETLOGIN, + (const void **) &logname); + if (status == PAM_SUCCESS) { + return logname; + } + + status = pam_get_item(pamh, PAM_TTY, (const void **) &curr_tty); + if ((status != PAM_SUCCESS) || (curr_tty == NULL)) { + curr_tty = ttyname(0); + } + + if ((curr_tty == NULL) || memcmp(curr_tty, "/dev/", 5)) { + return NULL; + } + + curr_tty += 5; /* strlen("/dev/") */ + logname = NULL; + + setutent(); + strncpy(line.ut_line, curr_tty, sizeof(line.ut_line)); + + if ((ut = getutline(&line)) == NULL) { + goto clean_up_and_go_home; + } + + curr_user = calloc(sizeof(line.ut_user)+1, 1); + if (curr_user == NULL) { + goto clean_up_and_go_home; + } + + strncpy(curr_user, ut->ut_user, sizeof(ut->ut_user)); + /* calloc already zeroed the memory */ + + status = pam_set_data(pamh, _PAMMODUTIL_GETLOGIN, curr_user, + _pammodutil_cleanup); + if (status != PAM_SUCCESS) { + free(curr_user); + goto clean_up_and_go_home; + } + + logname = curr_user; + +clean_up_and_go_home: + + endutent(); + + return logname; +} diff --git a/Linux-PAM/modules/pammodutil/modutil_getpwnam.c b/Linux-PAM/modules/pammodutil/modutil_getpwnam.c new file mode 100644 index 00000000..eb359544 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/modutil_getpwnam.c @@ -0,0 +1,139 @@ +/* + * $Id: modutil_getpwnam.c,v 1.4 2005/03/30 14:59:41 kukuk Exp $ + * + * This function provides a thread safer version of getpwnam() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pammodutil.h" + +#include <errno.h> +#include <limits.h> +#include <pthread.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> + +static pthread_mutex_t _pammodutil_mutex = PTHREAD_MUTEX_INITIALIZER; +static void _pammodutil_lock(void) +{ + pthread_mutex_lock(&_pammodutil_mutex); +} +static void _pammodutil_unlock(void) +{ + pthread_mutex_unlock(&_pammodutil_mutex); +} + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct passwd *_pammodutil_getpwnam(pam_handle_t *pamh, const char *user) +{ +#ifdef HAVE_GETPWNAM_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct passwd *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct passwd) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the user - so delete the memory */ + if (buffer) { + free(buffer); + } + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the pwd structure */ + errno = 0; + status = getpwnam_r(user, buffer, + sizeof(struct passwd) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getpwnam") + 1 + + strlen(user) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getpwnam_%s_%d", user, i); + _pammodutil_lock(); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, _pammodutil_cleanup); + } + _pammodutil_unlock(); + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= 2; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("pwd structure took %u bytes or so of memory", + length+sizeof(struct passwd))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETPWNAM_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getpwnam(). So, we use the standard libc function. + */ + + return getpwnam(user); + +#endif /* def HAVE_GETPWNAM_R */ +} diff --git a/Linux-PAM/modules/pammodutil/modutil_getpwuid.c b/Linux-PAM/modules/pammodutil/modutil_getpwuid.c new file mode 100644 index 00000000..8ba20d17 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/modutil_getpwuid.c @@ -0,0 +1,150 @@ +/* + * $Id: modutil_getpwuid.c,v 1.4 2005/03/30 14:59:41 kukuk Exp $ + * + * This function provides a thread safer version of getpwuid() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pammodutil.h" + +#include <errno.h> +#include <limits.h> +#include <pthread.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> + +static pthread_mutex_t _pammodutil_mutex = PTHREAD_MUTEX_INITIALIZER; +static void _pammodutil_lock(void) +{ + pthread_mutex_lock(&_pammodutil_mutex); +} +static void _pammodutil_unlock(void) +{ + pthread_mutex_unlock(&_pammodutil_mutex); +} + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +static int longlen(long number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct passwd *_pammodutil_getpwuid(pam_handle_t *pamh, uid_t uid) +{ +#ifdef HAVE_GETPWUID_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct passwd *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct passwd) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the user - so delete the memory */ + if (buffer) { + free(buffer); + } + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the pwd structure */ + errno = 0; + status = getpwuid_r(uid, buffer, + sizeof(struct passwd) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getpwuid") + 1 + + longlen((long) uid) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getpwuid_%ld_%d", + (long) uid, i); + _pammodutil_lock(); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, _pammodutil_cleanup); + } + _pammodutil_unlock(); + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= 2; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("pwd structure took %u bytes or so of memory", + length+sizeof(struct passwd))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETPWUID_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getpwuid(). So, we use the standard libc function. + */ + + return getpwuid(uid); + +#endif /* def HAVE_GETPWUID_R */ +} diff --git a/Linux-PAM/modules/pammodutil/modutil_getspnam.c b/Linux-PAM/modules/pammodutil/modutil_getspnam.c new file mode 100644 index 00000000..e966bb52 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/modutil_getspnam.c @@ -0,0 +1,139 @@ +/* + * $Id: modutil_getspnam.c,v 1.3 2005/03/30 14:59:41 kukuk Exp $ + * + * This function provides a thread safer version of getspnam() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pammodutil.h" + +#include <errno.h> +#include <limits.h> +#include <pthread.h> +#include <shadow.h> +#include <stdio.h> +#include <stdlib.h> + +static pthread_mutex_t _pammodutil_mutex = PTHREAD_MUTEX_INITIALIZER; +static void _pammodutil_lock(void) +{ + pthread_mutex_lock(&_pammodutil_mutex); +} +static void _pammodutil_unlock(void) +{ + pthread_mutex_unlock(&_pammodutil_mutex); +} + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct spwd *_pammodutil_getspnam(pam_handle_t *pamh, const char *user) +{ +#ifdef HAVE_GETSPNAM_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct spwd *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct spwd) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the user - so delete the memory */ + if (buffer) { + free(buffer); + } + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the spwd structure */ + errno = 0; + status = getspnam_r(user, buffer, + sizeof(struct spwd) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getspnam") + 1 + + strlen(user) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getspnam_%s_%d", user, i); + _pammodutil_lock(); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, _pammodutil_cleanup); + } + _pammodutil_unlock(); + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= 2; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("spwd structure took %u bytes or so of memory", + length+sizeof(struct spwd))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETSPNAM_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getspnam(). So, we use the standard libc function. + */ + + return getspnam(user); + +#endif /* def HAVE_GETSPNAM_R */ +} diff --git a/Linux-PAM/modules/pammodutil/modutil_ingroup.c b/Linux-PAM/modules/pammodutil/modutil_ingroup.c new file mode 100644 index 00000000..e1f88b81 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/modutil_ingroup.c @@ -0,0 +1,121 @@ +/* + * $Id: modutil_ingroup.c,v 1.1 2004/09/24 13:14:14 kukuk Exp $ + * + * This function provides common methods for checking if a user is in a + * specified group. + */ + +#include "pammodutil.h" +#include "include/security/_pam_modutil.h" +#include <pwd.h> +#include <grp.h> + +#ifdef HAVE_GETGROUPLIST +static int checkgrouplist(const char *user, gid_t primary, gid_t target) +{ + gid_t *grouplist = NULL; + int agroups, ngroups, i; + ngroups = agroups = 3; + do { + grouplist = malloc(sizeof(gid_t) * agroups); + if (grouplist == NULL) { + return 0; + } + ngroups = agroups; + i = getgrouplist(user, primary, grouplist, &ngroups); + if ((i < 0) || (ngroups < 1)) { + agroups *= 2; + free(grouplist); + } else { + for (i = 0; i < ngroups; i++) { + if (grouplist[i] == target) { + free(grouplist); + return 1; + } + } + free(grouplist); + } + } while (((i < 0) || (ngroups < 1)) && (agroups < 10000)); + return 0; +} +#endif + +static int _pammodutil_user_in_group_common(pam_handle_t *pamh, + struct passwd *pwd, + struct group *grp) +{ + int i; + + if (pwd == NULL) { + return 0; + } + if (grp == NULL) { + return 0; + } + + if (pwd->pw_gid == grp->gr_gid) { + return 1; + } + + for (i = 0; (grp->gr_mem != NULL) && (grp->gr_mem[i] != NULL); i++) { + if (strcmp(pwd->pw_name, grp->gr_mem[i]) == 0) { + return 1; + } + } + +#ifdef HAVE_GETGROUPLIST + if (checkgrouplist(pwd->pw_name, pwd->pw_gid, grp->gr_gid)) { + return 1; + } +#endif + + return 0; +} + +int _pammodutil_user_in_group_nam_nam(pam_handle_t *pamh, + const char *user, const char *group) +{ + struct passwd *pwd; + struct group *grp; + + pwd = _pammodutil_getpwnam(pamh, user); + grp = _pammodutil_getgrnam(pamh, group); + + return _pammodutil_user_in_group_common(pamh, pwd, grp); +} + +int _pammodutil_user_in_group_nam_gid(pam_handle_t *pamh, + const char *user, gid_t group) +{ + struct passwd *pwd; + struct group *grp; + + pwd = _pammodutil_getpwnam(pamh, user); + grp = _pammodutil_getgrgid(pamh, group); + + return _pammodutil_user_in_group_common(pamh, pwd, grp); +} + +int _pammodutil_user_in_group_uid_nam(pam_handle_t *pamh, + uid_t user, const char *group) +{ + struct passwd *pwd; + struct group *grp; + + pwd = _pammodutil_getpwuid(pamh, user); + grp = _pammodutil_getgrnam(pamh, group); + + return _pammodutil_user_in_group_common(pamh, pwd, grp); +} + +int _pammodutil_user_in_group_uid_gid(pam_handle_t *pamh, + uid_t user, gid_t group) +{ + struct passwd *pwd; + struct group *grp; + + pwd = _pammodutil_getpwuid(pamh, user); + grp = _pammodutil_getgrgid(pamh, group); + + return _pammodutil_user_in_group_common(pamh, pwd, grp); +} diff --git a/Linux-PAM/modules/pammodutil/modutil_ioloop.c b/Linux-PAM/modules/pammodutil/modutil_ioloop.c new file mode 100644 index 00000000..37ac76fc --- /dev/null +++ b/Linux-PAM/modules/pammodutil/modutil_ioloop.c @@ -0,0 +1,52 @@ +/* + * $Id: modutil_ioloop.c,v 1.2 2004/09/24 09:18:22 kukuk Exp $ + * + * These functions provides common methods for ensure a complete read or + * write occurs. It handles EINTR and partial read/write returns. + */ + +#include <unistd.h> +#include <errno.h> + +#include <security/pam_modules.h> +#include "include/security/_pam_modutil.h" + +int _pammodutil_read(int fd, char *buffer, int count) +{ + int block, offset = 0; + + while (count > 0) { + block = read(fd, &buffer[offset], count); + + if (block < 0) { + if (errno == EINTR) continue; + return block; + } + if (block == 0) return offset; + + offset += block; + count -= block; + } + + return offset; +} + +int _pammodutil_write(int fd, const char *buffer, int count) +{ + int block, offset = 0; + + while (count > 0) { + block = write(fd, &buffer[offset], count); + + if (block < 0) { + if (errno == EINTR) continue; + return block; + } + if (block == 0) return offset; + + offset += block; + count -= block; + } + + return offset; +} diff --git a/Linux-PAM/modules/pammodutil/pammodutil.h b/Linux-PAM/modules/pammodutil/pammodutil.h new file mode 100644 index 00000000..2b80c852 --- /dev/null +++ b/Linux-PAM/modules/pammodutil/pammodutil.h @@ -0,0 +1,22 @@ +#ifndef PAMMODUTIL_H +#define PAMMODUTIL_H + +/* + * $Id: pammodutil.h,v 1.2 2005/03/30 10:42:54 t8m Exp $ + * + * Copyright (c) 2001 Andrew Morgan <morgan@kernel.org> + */ + +#include <security/_pam_aconf.h> +#include <security/_pam_macros.h> +#include <security/pam_modules.h> +#include <security/_pam_modutil.h> + +#define PWD_INITIAL_LENGTH 0x100 +#define PWD_ABSURD_PWD_LENGTH 0x8000 + +/* This is a simple cleanup, it just free()s the 'data' memory */ +extern void _pammodutil_cleanup(pam_handle_t *pamh, void *data, + int error_status); + +#endif /* PAMMODUTIL_H */ diff --git a/Linux-PAM/modules/register_static b/Linux-PAM/modules/register_static new file mode 100755 index 00000000..f3aebb60 --- /dev/null +++ b/Linux-PAM/modules/register_static @@ -0,0 +1,49 @@ +#!/bin/sh + +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/Linux-PAM/pgp.keys.asc b/Linux-PAM/pgp.keys.asc new file mode 100644 index 00000000..583accd9 --- /dev/null +++ b/Linux-PAM/pgp.keys.asc @@ -0,0 +1,154 @@ + +pub 1024D/D41A6DF2 2002-09-23 Andrew G. Morgan <morgan@kernel.org> +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.0.6 (GNU/Linux) +Comment: For info see http://www.gnupg.org + +mQGiBD2PVCcRBADmR2dfKJIaGj120v0EjrGbnYic8nKCrDLUHmtiZyIlMeTNqnw/ +/Q2m057SIyFC5K5W7XV8LIsOcpEBAdIS5QLClwec/wqVj1FU5TLHNifR9fBq+DaI +tyMH+LX/HUo4xPaJ5KnE62/3M/SyUx/S69RURfRdDsC+9ucKZkW9mnRiUwCgm18E ++aUKNBKGyqHaNK+n50jxW0ED/2epmE6porj12MyTlTvmxHuq7SSqgzTP8KoNoOE4 +mtZnYAkopb8uksBo4yj4abfBZNiVXEttc+XpwhnRODfy576wVy/lmDMTFAz7CYw8 +5Qmsf9HZXyAS+XovscbYYAWq11BycqHzVNqOevMZPX4Om7+rHBcIPI6pCd3ouPlW +ObE5A/oDWXC35DcgFdmgVH2qr5COEvrDs9T5w4UdRXBMj2khE+32rdP0qqGYCW13 +by84+Cpoekmg+6/FqL/At0Xl2a87czur5xR9mrDd32iknws0DggEuf+zL3Twt9kA +ftnqgiGr33iaNeXwgqfgQYMbxruLvjaAOayKPhr+tgJU6bIpWbQkQW5kcmV3IEcu +IE1vcmdhbiA8bW9yZ2FuQGtlcm5lbC5vcmc+iFcEExECABcFAj2PVCcFCwcKAwQD +FQMCAxYCAQIXgAAKCRBCF4Sr1Bpt8u+VAJ958HbiLmhrpWjAauN9GrtKudijkACf +R2XasdjQs2ECc2qMX19QwNohDAc= +=7XTB +-----END PGP PUBLIC KEY BLOCK----- + + +The following is my DSA key 'D41A6DF2' signed with my old '2A398175' +RSA key. + +-----BEGIN PGP MESSAGE----- +Version: 2.6.3a + +owF9k82L42QAxmccVjFzUNjbHLQLHlbDTJq0aZuRAd+8+WgmaZqkTZtWWchHk6Zt +vpOmGcT1A1zm5EVZPXhQYW972IMHFVxQPK14UNjLICKiexBkEQQ9iI7zB/hcf4eH +B57f6dY7O5e2jt76ab/58Db7HPVasb3NvLvzT/5G44+H1uSpW0/fffDih3Dv9+uv +/ka/9Nh3n/h865FH0Qd1/f7Ny4/jg28/uPJ9tvvRjTdPrz1z+vVnyf4t50T95vmr +/gtninr/q7NP56+8/4Xb/tlJl9d/vXNj9+bnly6/vHX2p56urvwQ/3j3y92Pt/eO +3w57h3vO7O+/tn+5dw17HT555/bOe9YTQZR6ZrjPNHHQYjhi6zz7/4VmeUGuKbxS +U3RaEmBNZCc1WupD8QIjo1ma+VF4WOPDQuFra/ygftCqXeVlHZP8sNg8i8AoCGZh +fljjorTmh25Uy2az2jzP40MMK8vywAuL2Ds470eQQOV9miGUEbQ1GjCBRjiueCyY +/AIn6us6u0h5K5z4dicUYcpIejfI/WklrHqzoZyEJYZgKhHUyfZAqDhIiuS4bYw6 +kpD17ZilgSMMSFWCq3JmY2UyWuCcTg6lruy7GuXSCcqYApJXvS4qGVhXj5obxTwm +xZBtEVijhw0qfYMNWpSma67mMBlEqcIWp8sxFYSar5fQC/AOi6CmLsq0yFdJ15RF +NCTri824zjIYMYsDthVH6fmWXjVcDdfBplsk7cEg8U6GSkeM5KjPNpEgn4YTsIxi +q1MsMzpqVoumabn0VPZHBpvnNmrE5TzU+oxbke1WOaqwVcD0hhw4acNJ2UFINchc +qjs1KjBAjWid2dZkAsYJjtOVnXRPRnLSn617U8Vo9oM2mnZpW1CEVgydRlQoqzHS +t1gSYBEzNmCDZGyPcwJv1CWSlIR9dp0yGTUky6buaAbdWxDLOYs2iNRR6knCT+AY +byBW1WmiMI5my8BDWxiXSBjI68aKMDtt+6RIyY1GBSnjNAh/GZZZnfE8tnDRE6kx +LHNqCRA3DxPP59NGwzflmVF6ieupk561SQtpvTBB36xEZZ6iuXestywhHlvqUh2T +SzsYNQTWLhCBxdd24MwtH3SsMVVNCa5Q+XxlB+TKguQ5s1Gfs1l2w0JA2xxYXJyO +g6UtglJlEE7tQbCZQKAKhgeACDUacs1BitNx3inQETimyE7X8qVgnsbjBTALmeLT +XCwcf7EE0EU0wjAzZ6FmBAttIukZOKWWcjRngH2EHLWNIY1cGMTKzP/o9S8= +=cdkf +-----END PGP MESSAGE----- + +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----- |