pax_global_header00006660000000000000000000000064131343034740014514gustar00rootroot0000000000000052 comment=d0934138feabcc725fca4a4141df5dc03b4f8876 pam-MySQL-0.8.2/000077500000000000000000000000001313430347400132435ustar00rootroot00000000000000pam-MySQL-0.8.2/AUTHORS000066400000000000000000000025321313430347400143150ustar00rootroot00000000000000James O'Kane Original author and developer until (and including) Version 0.2 Steve Brown Took over from James at version 0.3 Tamas Szerb Mysql PASSWORD patch Matjaz Godec UNIX crypt() patch Gus Patch to deal with password() collection in one query Martin Edlman Combined crypt into an SQL statement (ENCRYPT) and fixed PASSWORD Shaun Clowes http://www.securereality.com.au Revealed significant security hole in authentication mechanism. See ChangeLog. ALL version prior to 0.4.7 are vulnerable. Asbjørn Sannes Patch for a potential bufferoverflow in db_checkpassword Kev Green Provided a missing bit of documentation that will likely save someone's time. Paul Bryan Provided a patch that implements the account management service. Fredrik Rambris Provided a spec file to build RPM. Moriyoshi Koizumi Set up automake build system. OpenSSL md5 support. Mega patch integration. Sergey Matveychuk Provided a patch to build pam-mysql with OpenPAM. Kees Cook Provided a patch that accomplishes to: - allow for []-style PAM option usage, arg clean up - use optional separate table for updates - use pam_set_data/pam_get_data instead of static vars - clean up syslog levels and debugging facility - clean up string handling/memory management pam-MySQL-0.8.2/COPYING000066400000000000000000000431331313430347400143020ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. pam-MySQL-0.8.2/ChangeLog000066400000000000000000000207101313430347400150150ustar00rootroot000000000000002006-01-09 Moriyoshi Koizumi * pam_mysql.c: PR #1325395 - add "disconnect_every_op" option that forces pam_mysql to disconnect from the database every operation. (pam_sm_chauthtok): PR #1338667 - stop using getuid() and use geteuid() instead. PR #1338672 - allow root to change the passwords of other users without their old password. * acinclude.m4: check PAM_AUTHTOK_RECOVERY_ERR availability, for Linux PAM's error. * README: note about disconnect_every_op. 2005-09-23 Moriyoshi Koizumi * pam_mysql.c: (pam_mysql_parse_args): change option parsing behaviour so "=" following each option name is not needed. (pam_sm_authenticate): fix try_first_pass behaviour. fix behaviour so that the password obtained in a conversation is always stored with pam_set_item(). 2005-09-21 Moriyoshi Koizumi * pam_mysql.c: add "logrhostcolumn" option. #define PAM_SM_* so that pam_mysql.c can be linked statically to libpam. static'ify some functions. s/pam_permit_modstruct/pam_mysql_modstruct/. (pam_mysql_stream_skip_spn): fix invalid assumption of an uninitialised variable. (pam_mysql_get_host_info): new function. (pam_mysql_destroy_ctx): add missing xfree(ctx->config_file) to avoid leak. * configure.in: checks for netdb functions / IPv6 availability. * acinclude.m4: ditto. * README: add description about logrhostcolumn. * NEWS: note about logrhostcolumn. 2005-09-20 Moriyoshi Koizumi * pam_mysql.c: prevent blocks obtained from pam_get_item() from being freed. (pam_mysql_check_passwd): do not escape the content of the "where" option. Fixes PR #1261484. (pam_mysql_sql_log): remove weird codes. Fixes PR #1256243. 2005-09-18 Moriyoshi Koizumi * README: add a note about the issue regarding official MySQL binary packages. * NEWS: prepare for the release. * pam_mysql.c: (pam_mysql_format_string): change column name handling to allow an expression for every XXXcolumn parameter. 2005-08-01 Moriyoshi Koizumi * acinclude.m4: (PAM_MYSQL_CHECK_LIBMYSQLCLIENT): remove redundant eval. Will fix PR #1245188. 2005-06-24 Moriyoshi Koizumi * pam_mysql.c: implement use_first_pass and try_first_pass options to follow the convention. make debug option an alias for verbose. (pam_sm_authenticate): return authentication token to the PAM implementation (by pam_set_item()). (pam_sm_chauthtok): return authentication token to the PAM implementation. (pam_mysql_check_passwd): don't update password column if crypt=md5 and non-crypt md5 is not implemented. use statically allocated buffer for make_scrambled_password(). * README: note about debug, use_first_pass and try_first_pass. * NEWS: use_first_pass and try_first_pass. 2005-06-23 Moriyoshi Koizumi * pam_mysql.c: (pam_mysql_check_passwd): fix buffer overflow spotted in verification of SHA1-encrypted password. 2005-06-19 Moriyoshi Koizumi * pam_mysql.c: add use_323_passwd option for compatibility with the old MySQL PASSWORD() implementation (inspired by Daniel Renaud. Thanks.) (pam_sm_acct_mgmt): add a missing piece of code to the account management callback. I don't know why I didn't notice this before the last release. * acinclude.m4: (PAM_MYSQL_CHECK_LIBMYSQLCLIENT) ditto. Fix compile failure on Solaris when --with-openssl is specified to configure. * .cvsignore: add pam_mysql.spec * README: note about statcolumn / use_323_passwd. 2005-06-17 Moriyoshi Koizumi * acinclude.m4: old mysql_config doesn"t accept --include option. this resulted in sed errors in config.status. Reported by Worlfgang Erlenkoetter. Thanks. * configure.in: a hyphen is not allowed for the "Version" section in a spec file. * .cvsignore: add .orig and .rej. * pam_mysql.spec.in: require OpenSSL by default. 2005-06-16 Moriyoshi Koizumi * acinclude.m4: fix library detection code: s/shrext/shrext_cmds/. use $libext instead of ".a" for library suffices. 2005-06-15 Moriyoshi Koizumi * README: fix description about config_file. add missing description for option "MD5". note about the GPL issue. * pam_mysql.c: SHA1 support (requires OpenSSL). * NEWS: entry for 0.7-pre2. * configure.in: add pam_mysql.spec to AC_OUTPUT. Noticed by Fredrik Rambris (boost at users dot sourceforge dot net). Thanks. * Makefile.am: add pam_mysql.spec and pkg.m4 to EXTRA_DIST. Notices by Fredrik Rambris (boost at users dot sourceforge dot net). * COPYING: replace by GPL terms, were accidentially LGPL ones. 2005-06-13 Moriyoshi Koizumi * pam_mysql.c: s/crypt/crypt_type/ due to possible namespace pollution. * acinclude.m4: (PAM_MYSQL_CHECK_LIBMYSQLCLIENT) check mysql_config availability for more accurate detection. * README: misc. documentation fixes. * INSTALL: ditto. 2005-06-12 Moriyoshi Koizumi * pam_mysql.c: - remove redundant #include that caused compatibility problem. - #define PAM_EXPORT / LOG_AUTHPRIV if undefined. - #include beforehand of #include as Sun's implementation doesn't really take inclusion order into account. - include crypt.h if available. (pam_sm_open_session): log session state, to fix PR# 1047213. (pam_sm_close_session): ditto. (pam_sm_authenticate): log authentication failure as well. * NEWS: note about above. * configure.in: add /usr/lib/security and /usr/lib/pam to module search path. * acinclude.m4: (PAM_MYSQL_CHECK_PAM): remove redundant checks for pam_misc.h. (PAM_MYSQL_CHECK_PROTOS): add for compatibility. 2005-06-07 Moriyoshi Koizumi * pam_mysql.c: initialize statcolumn to "1" (for the sake of backwards compatibility) on start and free on exit. This fixes the bug reported by Deniss from Latvia. Thanks. * configure.in: include pkg.m4 (from pkgconfig 0.17.2 which is covered by GPL) to deal with the case pkg-config devel package is not installed. * pkg.m4: added. * autogen.sh: run libtoolize prior to aclocal. * README: remove note about default values, as those ones no longer exist for string parameters. added notes for undocumented options (verbose, config_file). * NEWS: updates for expected releases. 2005-03-28 Moriyoshi Koizumi * pam_mysql.c: uncapitalize log message. fix log level in pam_mysql_set_option(). add missing PAM_MYSQL_LOG_PREFIX to syslog() params. add support for nss-mysql style external configuration file. * acinclude.m4: add PAM_MYSQL_CHECK_DEFINES macro. * configure.in: add checks for ELOOP and EOVERFLOW constants, which are used in pam_mysql_stream_open(). * Makefile.am: add install-sh, missing and mkinstalldirs to EXTRA_DISTS. 2005-03-27 Moriyoshi Koizumi * README: add CVS tags and note about unix socket / non-default port number support. * INSTALL: add CVS tags. * pam_mysql.c: add CVS tags. fix typo. * NEWS: reformat and add entry for 0.6. 2005-03-23 Moriyoshi Koizumi * README: add a notice about md5() crypt. 2005-03-22 Moriyoshi Koizumi * COPYING: this should have been there.. * pam_mysql.c: add missing license terms. * Makefile.am: ditto. * ChangeLog: fix indentation. 2005-03-12 Moriyoshi Koizumi * acinclude.m4: fix PAM_MYSQL_CHECK_OPENSSL to correctly check the library availability with the appropriate linker options. 2005-03-11 Moriyoshi Koizumi * acinclude.m4: add new macros -- PAM_MYSQL_CHECK_MD5_HEADERS, PAM_MYSQL_CHECK_CYRUS_SASL_V1, PAM_MYSQL_CHECK_CYRUS_SASL_V2 to deal with the "bogus" md5.h issue. * configure.in: add two new options --with-cyrus-sasl / --with-cyrus-sasl2. fix OpenSSL detection. bump package version. * pam_mysql.c: conditionally include if Cyrus SASL is available and enabled by --with-cyrus-sasl or --with-cyrus-sasl2. * NEWS: renamed from Changelog * ChangeLog: genuine ChangeLog :) * INSTALL: detailed installation instruction in there. * README: note about forking. * Makefile.am: correct typos, synchronize EXTRA_DISTS with new additions. * pam_mysql.spec.in: synchronization against new additions. pam-MySQL-0.8.2/INSTALL.pam-mysql000066400000000000000000000056541313430347400162250ustar00rootroot00000000000000Installation instructions. ========================== 1. Run "autoreconf -f -i". You can skip this step if you are building from a distributed tarball and thus "configure" is already present. 2. Run "./configure". The following parameters are accepted: --with-pam=[PAM_INSTALLATION_PREFIX] Specifies where the PAM headers required to build the package are installed. This may help when you are building a system in a different root. This can be omitted to turn on auto-detection. --with-pam-mods-dir=[MODULE_DIRECTORY] Specifies where to install the product (pam_mysql.so). This can be omitted also. --with-mysql=[MYSQL_INSTALLATION_PREFIX] Specifies where the required MySQL headers and libraries are installed. If not given, configure search to find them amongst the hard-coded prefixes in the following order: . /usr . /usr/local . /usr/mysql . /opt/mysql --with-openssl[=check|yes|no] Specifies whether to use the MD5 and SHA1 hashing implementations of libcrypto provided by OpenSSL. Uses pkg-config, thus automatic detection can be overridden by the openssl_CFLAGS and openssl_LIBS variables. --with-cyrus-sasl=[CYRUS_SASL_INSTALLATION_PREFIX] --with-cyrus-sasl2=[CYRUS_SASL2_INSTALLATION_PREFIX] Specifies where the Cyrus SASL headers and libraries, which will be used for MD5 calculation facility, are installed. If the option is supplied but no prefix is explicitly given, configure tries to search them amongst the hard-coded prefixes in the following order: . /usr . /usr/local . /opt/cyrus-sasl Note that these two options cannot be specified together, and it is discouraged to use this feature in order to avoid cross dependency and symbol conflict as PAM may be called from within a SASL client library. 3. Run "make" to build the module. 4. Run "make install" as root to install the module. 5. Make proper changes to an intended configuration file in /etc/pam.d/ or /etc/pam.conf. The following are historic instructions - I haven't checked they still work. Via RPM commands: 1. Copy the source archive (.tar.gz / .tar.bz2) to the "SOURCES" directory within the system dependent RPM build directory. That is typically /usr/src/redhat in Red Hat / Fedora Core distros. 2. Copy the spec file (pam_mysql.spec), that has been generated during configuration to the "SPECS" directory, that is also found in the same RPM build directory as explained in the previous step. 3. Chdir to the SPECS directory 4. Run rpmbuild. $ rpmbuild -ba pam_mysql.spec 5. Install the binary-rpm that was produced in the "RPMS" directory, ditto. (via ordinary make install) 6. Make proper changes to an intended configuration file in /etc/pam.d/ or /etc/pam.conf. pam-MySQL-0.8.2/Makefile.am000066400000000000000000000007271313430347400153050ustar00rootroot00000000000000# install this platform-dependent module by the 'install-exec' rules pamexecdir = @PAM_MODS_DIR@ pamexec_LTLIBRARIES = pam_mysql.la pam_mysql_la_SOURCES = pam_mysql.c crypto.c crypto-sha1.c crypto-md5.c pam_mysql_la_LDFLAGS = -module -avoid-version pam_mysql_la_CPPFLAGS = $(openssl_CFLAGS) pam_mysql_la_LIBADD = $(openssl_LIBS) -lpam EXTRA_DIST = INSTALL.pam-mysql ACLOCAL_AMFLAGS = -I m4 AM_DISTCHECK_CONFIGURE_FLAGS = --with-pam-mods-dir=\$${prefix}/lib/security pam-MySQL-0.8.2/NEWS000066400000000000000000000164371313430347400137550ustar00rootroot00000000000000Version 0.8 - 30 Nov 2016 - * This release is the first in a new fork of the project. Previous maintainers seem to have ceased activity not long after the last release. This version includes a number of patches that have been applied in the meantime. Version 0.7-RC1 2006/1/10 * Add a option "disconnect_every_op" option that forces pam_mysql to disconnect from the database every operation (PR #1325395). -moriyoshi * Use geteuid() instead of getuid() to check if the current user is authorized to change the password (PR #1338667). -moriyoshi * Allow root (uid=0) to change the passwords of other users without their old password. -moriyoshi Version 0.7-pre3 2005/9/29 * Changed handling of the "where" option to not escape meta characters (PR #1261484). -moriyoshi * Overhauled the SQL logging facility (PR #1256243). -moriyoshi * Added logrhostcolumn (log.rhost_column) option that enables you to log the value of the "rhost" item specified by the application. -moriyoshi * Fixed possible security flaw (though not considered to be severe). -moriyoshi * Fixed memory leaks spotted when "config_file" option is used. -moriyoshi * Fixed try_first_pass behaviour. -moriyoshi * Changed option parsing behaviour so "=" following each option name is not needed. -moriyoshi Version 0.7-pre2 2005/9/18 * Changed column name handling to not escape meta characters. Now you can specify an expression to every XXXcolumn variable like "CONCAT(a, b, c)". -moriyoshi * Supported SHA1 hash (PR #1117036). -moriyoshi, alexeen * Supported use_first_pass and try_first_pass options. -moriyoshi Version 0.7-pre1 2005/6/13 * Support for NSS-mysql style configuration file which is inspired by the Florian's work. -moriyoshi Version 0.6.2 2005/9/29 * Overhauled the SQL logging facility (PR #1256243). -moriyoshi * Fixed possible security flaw (though not considered to be severe). -moriyoshi Version 0.6.1 2005/9/18 * Added use_323_passwd option that allows you to use an encryption function used in the old MySQL versions (3.23.x). -moriyoshi, Daniel Renaud * Fixed account management code that wouldn't work at all :-p -moriyoshi * Included pam_mysql.spec to the tarball by default. This enables you to make a RPM with the following oneliner: (rpmbuild -tb pam_mysql.tar.gz). -moriyoshi * Fixed compile failure that occurs with the old mysql_config (< 4.0.16). -moriyoshi * Fixed compile failure on Solaris when --with-openssl is specified to the configure script. Version 0.6 2005/6/13 * Adopted autoconf / automake for build system. -moriyoshi * Portable MD5 support by using OpenSSL / Cyrus-SASL. -moriyoshi * MySQL library detection. -moriyoshi * Added RPM spec file. -moriyoshi * Tidied up the entire code for security and maintainability. -moriyoshi * Modified log output to be more verbose. -moriyoshi * Changed log facility type to LOG_AUTHPRIV as per the recommendation in the PAM documentation. -moriyoshi * Added support for unix socket and non-default ports. -moriyoshi * Added account management and authentication token alteration code. -moriyoshi * Remove default values for string parameters for the sake of performance. -moriyoshi * Enhanced SQL logging function to log session state as well. -moriyoshi * Solaris support. -moriyoshi Version 0.5 2002/11/20 * Added md5 support by default -ksmith * Added a makefile that works fon FreeBSD -ksmith * More buffer overflow related fixes -jo2y * Added -lz flag to link against the libz library -jo2y * Backport of sql logging into main branch -jo2y * Fixed a memoryleak with mysql_free_result() -jo2y * Fixed buffer overflow in parseArgs() -jo2y * Add askForPassword() for new passwords in pam_sm_chauthtok() -ksmith * All instances of syslog() now have a format string -ksmith * Many fixes from B J Black Version 0.4.7 2000/9/7 * URGENT! This release fixes a SERIOUS security hole in the authentication mechanism and is one I am deeply to ashamed to admit was there, but must. The SQL statement was never being escaped, so your users can effectively 'break out' of the query, add their own SQL and get authentication. Whichever version of PAM-MYSQL you are running, you should upgrade immediately to fix this problem. ANYONE can get authenticated on your system without needing to know the password of the user they are trying to be authenticated as. This means root too. And it is easy... Specify the username as root. Specify the password as; ' and user='SomeKnownUser' and whammo, you have root access to the machine because PAM authorised you. UPGRADE NOW! Thanks to Shaun Clowes at Secure Reality (http://www.securereality.com.au) for bringing this to my attention. Also, if you don't want users passwords displayed in your sql log, switch off logging for select statements! Version 0.4.6 2000/9/5 * ACK! Logfile spam from acct_mgmt() Removed it... Or rather, added it to the #ifdef Version 0.4.5 2000/9/5 * Applied patch from Martin "Edas" Edlman to fix PASSWORD() method and combine crypt() into one call.. * Changed the way PAM_MYSQL logs, removed _pam_log() and now just use syslog() instead of vsyslog() (Actually not sure why vsyslog was used anyway) which should hopefully fix another set of SEGV problems people have reported. * Removed debug logging. Compile with -DDEBUG if you want it. Most people won't though :) Version 0.4 2000/7/27 * Added the ability to have a where clause in addition to the username='blah'. Note though that spaces are NOT allowed in this where clause, sorry. * Fixed a nasty (and really stupid!) bug whereby user not existing would cause the sql string to be free'd twice, causing nastiness, lockups or segfaults. Version 0.3 2000/7/26 * This file started. * Merged patches for crypt() support and local mysql support from Tamas SZERB and Matjaz Godez. * Fixed potential buffer overrun in sql statement (username could be big, shouldn't be, but could be!). * Fixed potential buffer overrun in crypt password checks. Password provided could be long. * Combined queries into 1 when using internal MySQL password() crypt routine. * Changed Makefile to use staticly linked libmysqlclient, as dynamic causes a sigsegv when being unloaded. If anyone has any idea why that might be, please email me! * Patch to avoid second select submitted by Gus. Implemented with mods. * Changes to conversation function to make more generic. Hopefully its not broken anything! * Changes to better fit PAM spec. * Changes to explicitly close MySQL connection when finished. * Beginnings of ability to use use_first_pass (tied in with changes to conversation functions) * Implementation of stub functions for acct_mgmt, credential, chauthtok and session stuff. * If you are Tamas, Matjaz or Gus, please email me your contact details if you'd like to be in the CREDITS file :) ############################################################################# pam-MySQL-0.8.2/README000066400000000000000000000302441313430347400141260ustar00rootroot00000000000000pam_mysql - A PAM authentication module against MySQL database. Formerly maintained by Moriyoshi Koizumi at https://sourceforge.net/projects/pam-mysql/ Now taken care of by Nigel Cunningham at https://github.com/NigelCunningham/pam-MySQL The following is the original (and still valid) readme. ===================================================================== Introduction ------------ This is a successor of the "old" pam_mysql module, which comes with a more stable, secure and robust implementation. Prerequisites ------------- To try this module, you need the following stuff: - A *NIX (or similar) system, in which PAM facility is set up and working either system-wide or in a chroot jail. - A MySQL server, up and running. Installation instruction ------------------------ See INSTALL.pam-mysql file for detail. An example of the configuration file: --------------------------------------------------------------- auth optional pam_mysql.so user=root passwd=password account required pam_mysql.so user=root passwd=password --------------------------------------------------------------- Available options ----------------- The module options are listed below with default in ()s: verbose (0) If set to 1, produces logs with detailed messages that describes what PAM-MySQL is doing. May be useful for debugging. debug An alias for the verbose option. This is added in 0.7pre2. user The user name used to open the specified MySQL database. passwd The password used to open the specified MySQL database. host The host name or the absolute path to the unix socket where the MySQL server is listening. The following formats are accepted: 1. absolute path to the unix socket (e.g. "/tmp/mysql.sock") 2. host name (e.g. "somewhere.example.com") 3. host name + port number (e.g. "somewhere.example.com:3306") db The name of the database that contains a user-password table. table The name of table that maps unique login names to the passwords. This can be a combination of tables with full JOIN syntax if you need more control. For example: [table=Host LEFT JOIN HostUser ON HostUser.host_id=Host.id \ LEFT JOIN User ON HostUser.user_id=User.id] update_table The name of the table used for password alteration. If not defined, the value of the "table" option will be used instead. This is handy if you have a complex JOIN instead of a simple table in the "table" option above. usercolumn The name of the column that contains a unix login name. Should be in a fully qualified form. passwdcolumn The name of the column that contains a (encrypted) password string. Should be in a fully qualified form. statcolumn The name of the column or an SQL expression that indicates the status of the user. The status is expressed by the combination of two bitfields shown below: bit 0 (0x01): if flagged, pam_mysql deems the account to be expired and returns PAM_ACCT_EXPIRED. That is, the account is supposed to no longer be available. Note this doesn't mean that pam_mysql rejects further authentication operations. bit 1 (0x02): if flagged, pam_mysql deems the authentication token (password) to be expired and returns PAM_NEW_AUTHTOK_REQD. This ends up requiring that the user enter a new password. This option is available since 0.6. crypt (plain) The method to encrypt the user's password: 0 (or "plain") = No encryption. Passwords stored in plaintext. HIGHLY DISCOURAGED. 1 (or "Y") = Use crypt(3) function. 2 (or "mysql") = Use MySQL PASSWORD() function. It is possible that the encryption function used by PAM-MySQL is different from that of the MySQL server, as PAM-MySQL uses the function defined in MySQL's C-client API instead of using PASSWORD() SQL function in the query. 3 (or "md5") = Use plain hex MD5. 4 (or "sha1") = Use plain hex SHA1. 5 (or "drupal7") = Use Drupal7 salted passwords md5 (false) Use MD5 by default for crypt(3) hash. Only meaningful when crypt is set to "Y". use_323_passwd (false) Use MySQL version 3 style encryption function if available and the crypt option is set to "mysql". This is useful if you have a table migrated from the old MySQL database and it stores the old-style passwords. This option appeared since 0.7pre2 and 0.6.1. where Additional criteria for the query. For example: [where=Host.name="web" AND User.active=1] sqllog (false) If set to either "true" or "yes", SQL logging is enabled. logtable The name of the table to which logs are written. logmsgcolumn The name of the column in the log table to which the description of the performed operation is stored. logusercolumn The name of the column in the log table to which the name of the user being authenticated is stored. logpidcolumn The name of the column in the log table to which the pid of the process utilising the pam_mysql's authentication service is stored. loghostcolumn The name of the column in the log table to which the IP address of the machine performing the operation is stored. logrhostcolumn The name of the column in the log table to which the name of the remote host that initiates the session is stored. The value is supposed to be set by the PAM-aware application with pam_set_item(PAM_RHOST). Available since 0.7pre3. logtimecolumn The name of the column in the log table to which the timestamp of the log entry is stored. config_file Path to a NSS-MySQL style configuration file which enumerates the options per line. Acceptable option names and the counterparts in the PAM-MySQL are listed below: - users.host (host) - users.database (db) - users.db_user (user) - users.db_passwd (passwd) - users.where_clause (host) - users.table (table) - users.update_table (update_table) - users.user_column (usercolumn) - users.password_column (passwdcolumn) - users.status_column (statcolumn) - users.password_crypt (crypt) - users.use_323_password (use_323_passwd) - users.use_md5 (md5) - users.where_clause (where) - users.disconnect_every_operation (disconnect_every_op) *1 - verbose (verbose) - log.enabled (sqllog) - log.table (logtable) - log.message_column (logmsgcolumn) - log.pid_column (logpidcolumn) - log.user_column (logusercolumn) - log.host_column (loghostcolumn) - log.rhost_column (logrhostcolumn) *2 - log.time_column (logtimecolumn) A "#" in front of the line makes it a comment as in NSS-MySQL. This is available since 0.7pre1. (*1: added in 0.7RC1) (*2: added in 0.7pre3) use_first_pass (false) If true, pam_mysql doesn't prompt a password and uses the one provided given in a preceeding authentication module. If it is not given, authentication fails. This is available since 0.7pre2. try_first_pass (true) If true, pam_mysql first tries to authenticate with the password given in a preceeding authentication module. If it fails (because of either unavailableness of a password or simple authentication failure), then pam_mysql prompts a password for the following authentication. The semantics actually breaks the backwards compatibility, because authentication is not performed twice in the previous versions when the password given by the previous authentication module is wrong. This is available since 0.7pre2. disconnect_every_op (false) By default, pam_mysql keeps connection to the MySQL database until the session is closed. If this option is set to true it disconnects every time the PAM operation has finished. This option may be useful in case the session lasts quite long. BUGS ---- Beware that user names and clear text passwords may be syslogged if you explicitly configured PAM-MySQL to log select statements (verbose=1). (Not sure why you want to anyway, slows your system down badly!) Q&A --- Q. What on earth is PAM anyway? A. PAM is an acronym for Pluggable Authentication Modules. See http://www.kernel.org/pub/linux/libs/pam/whatispam.html for further information. Q. Are there any tools for changing passwords, etc. without updating tables directly through the command-line client program? A. You can use "passwd" program for that purpose. Note that pam-mysql doesn't permit password change without the root privilege (pid=0). Q. I need to retrieve misc. UNIX user information such as one's home directory stored in the account table. Can PAM-MySQL do this? A. No. As the name suggests, PAM is only involved in authentication that in principle has little to do with the account database itself. You need to use the nss-mysql module, which can be retrieved from here: http://savannah.nongnu.org/projects/nss-mysql Q. How can I quickly tell in which way a given password is encrypted, PASSWORD(), CRYPT()-ed, or md5()? A. Try using the following MySQL functions: ENCRYPT(), PASSWORD() and md5(), and compare the results with each other. SELECT ENCRYPT('mypass'), PASSWORD('mypass'), MD5('mypass'); Q. I set up saslauthd (of Cyrus-SASL) to use PAM-MySQL for authentication and noticed some authentication mechanisms such as CRAM-MD5 don't work. Why? A. CRAM-MD5 are DIGEST-MD5 are Challenge-Response authentication mechanisms (indeed CRAM is short for Challange-Response Authentication Mechanism), plain-text passwords have to be supplied to the instance that handles authentication communication with the user (that is, the SASL client library), rather than the authenticator (the server). Therefore, it is not possible to use PAM with these mechanisms and then you need to configure Cyrus-SASL to have "SQL" auxprop plugin with MySQL support and specify "auxprop" for the preferred password checking method. For instance, if you want to use it in conjunction with Postfix, the SASL configuration file "smtpd.conf", which is put in the Cyrus-SASL's plugin directory (or the location included in the SASL_PATH environment variable), would look like the following: pwcheck_method: auxprop mech_list: plain login cram-md5 digest-md5 sql_engine: mysql sql_database: sys sql_user: someuser sql_passwd: fubar sql_select: SELECT password FROM users WHERE name='%u' and domain='%r'; Note that passwords should be stored in plain-text in this case. Q. PAM-MySQL is licensed under GNU Public License and I heard that GPL requires the program that links to a GPL'ed shared binary object at runtime also being covered by GPL. Is it safe to use PAM-MYSQL from a program with a license that is incompatible with GPL? A. Our thought regarding this issue is that runtime dynamic linking itself is not an action to make a derivative work of anything that ends up in the physicial memory. No matter what GPL is like, and will be like, we exceptionally grant you a permanent and non-exclusive right to use a binary-formed derivative of PAM-MySQL in combination with any other programs. Q. I could not build pam-mysql on Solaris with the official MySQL binary package. How can I fix this? A. You apparently got a binary package built with the Forte C compiler, which requires a different set of command-line options than the compiler (most likely GCC) you are now trying to build pam_mysql with. There are two options to deal with this problem: 1. Get the Forte C compiler and build pam-mysql with it. 2. Build MySQL from the source with the same compiler as the one that should be used to build pam-mysql. LINKS ----- - MySQL http://www.mysql.com/ - NSS-MySQL: http://savannah.nongnu.org/projects/nss-mysql - OpenPAM http://www.openpam.org/ - PAM http://pam.sourceforge.net/ - sysauth-pgsql (the PostgreSQL counterpart of PAM-MySQL, accompanied by the nss module also) http://sourceforge.net/projects/sysauth-pgsql - Cyrus-SASL http://asg.web.cmu.edu/sasl/sasl-library.html - Sendmail-SQL: http://www.sourceforge.net/projects/sendmail-sql pam-MySQL-0.8.2/configure.ac000066400000000000000000000076501313430347400155410ustar00rootroot00000000000000m4_pattern_allow([AC_DEFINE]) AC_PREREQ([2.58]) AC_INIT(pam-mysql, 0.8) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([-Wall -Wno-extra-portability dist-xz no-dist-gzip]) AC_CONFIG_SRCDIR(pam_mysql.c) AC_CONFIG_HEADERS(config.h) AC_SUBST(PACKAGE_VERSION) AC_SUBST(PACKAGE_NAME) LT_INIT([disable-static]) AC_PROG_CC AC_PROG_INSTALL dnl For PureFTP crypto. AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_HEADERS([arpa/inet.h netinet/in.h netdb.h string.h strings.h sys/socket.h sys/types.h sys/stat.h sys/param.h fcntl.h syslog.h unistd.h stdarg.h errno.h crypt.h security/pam_appl.h]) AC_TYPE_SIZE_T AC_CHECK_DECLS([ELOOP, EOVERFLOW],,,[[#include ]]) AC_SEARCH_LIBS([socket],[socket],,[AC_MSG_ERROR([unable to find the socket() function])]) AC_CHECK_FUNCS([getaddrinfo]) PAM_MYSQL_CHECK_IPV6 PAM_MYSQL_CHECK_GETHOSTBYNAME_R AC_ARG_WITH([mysql], [ --with-mysql=PREFIX specify MySQL installation prefix], [ PAM_MYSQL_CHECK_LIBMYSQLCLIENT(["$withval"]) ], [ dnl try autodetection... PAM_MYSQL_CHECK_LIBMYSQLCLIENT([/usr /usr/local /usr/mysql /opt/mysql]) ]) AC_ARG_WITH([openssl], [AS_HELP_STRING([--with-openssl],[use the OpenSSL crypto provider @<:@default=check@:>@])],, [with_openssl=check]) AS_IF([test x"$with_openssl" != xno], [PKG_CHECK_MODULES([openssl],[libcrypto], [AC_DEFINE([HAVE_OPENSSL], [1], [Define to 1 if OpenSSL library is installed])], [AS_IF([test "x$with_openssl" != xcheck],[AC_MSG_ERROR([Unable to find OpenSSL])])])]) sasl_v2_avail= AC_ARG_WITH([cyrus-sasl2], [ --with-cyrus-sasl2[[=PREFIX]] specify Cyrus-SASL2 installation prefix], [ if test -z "$withval" -o "$withval" = "yes"; then dnl try autodetection... PAM_MYSQL_CHECK_CYRUS_SASL_V2([/usr /usr/local /opt/cyrus-sasl /opt/sasl], [ AC_DEFINE([HAVE_CYRUS_SASL_V2], [1], [Define to 1 if Cyrus-SASL Version 2 is installed]) sasl_v2_avail=1 ]) else PAM_MYSQL_CHECK_CYRUS_SASL_V2(["$withval"], [ AC_DEFINE([HAVE_CYRUS_SASL_V2], [1], [Define to 1 if Cyrus-SASL Version 2 is installed]) sasl_v2_avail=1 ], []) fi ], []) CFLAGS="$CFLAGS $sasl_v2_CFLAGS" LIBS="$LIBS $sasl_v2_LIBS" AC_ARG_WITH([cyrus-sasl], [ --with-cyrus-sasl[[=PREFIX]] specify Cyrus-SASL installation prefix], [ if test -z "$sasl_v2_avail"; then if test -z "$withval" -o "$withval" = "yes"; then dnl try autodetection... PAM_MYSQL_CHECK_CYRUS_SASL_V1([/usr /usr/local /opt/cyrus-sasl /opt/sasl], [ AC_DEFINE([HAVE_CYRUS_SASL_V1], [1], [Define to 1 if Cyrus-SASL Version 1 is installed]) ]) else PAM_MYSQL_CHECK_CYRUS_SASL_V1(["$withval"], [ AC_DEFINE([HAVE_CYRUS_SASL_V1], [1], [Define to 1 if Cyrus-SASL Version 1 is installed]) ], []) fi else AC_MSG_ERROR([--with-cyrus-sasl and --with-cyrus-sasl2 cannot be specified at the same time.]) fi ], []) CFLAGS="$CFLAGS $sasl_v1_CFLAGS" LIBS="$LIBS $sasl_v1_LIBS" AC_ARG_WITH([pam], [ --with-pam=PREFIX specify PAM installation prefix], [ PAM_MYSQL_CHECK_PAM(["$withval"]) ], [ dnl try autodetection... PAM_MYSQL_CHECK_PAM([/usr /usr/local]) ]) AC_ARG_WITH([pam_mods_dir], [ --with-pam-mods-dir=DIR specify PAM module installation path], [ PAM_MODS_DIR="$withval" ], [ PAM_MODS_DIR= for dir in "/lib/security" "/lib/pam" "/usr/lib/security" "/usr/lib/pam" \ "$libdir/pam" "$libdir/security"; do if test -d "$dir"; then PAM_MODS_DIR="$dir" fi done if test -z "$PAM_MODS_DIR"; then AC_MSG_ERROR([Your system doesn't appear to be configured to use PAM. Perhaps you need to specify the correct location where the PAM modules reside.]) fi ]) PAM_MYSQL_CHECK_MD5_HEADERS AC_SEARCH_LIBS([crypt],[crypt],,[AC_MSG_ERROR([unable to find the crypt() function])]) AC_SUBST(PAM_MODS_DIR) AC_CONFIG_FILES([Makefile pam_mysql.spec]) AC_SEARCH_LIBS([make_scrambled_password],[mysql],[AC_DEFINE([HAVE_MAKE_SCRAMBLED_PASSWORD], [1], [Build own SHA1 support.])]) AC_OUTPUT pam-MySQL-0.8.2/crypto-md5.c000066400000000000000000000246601313430347400154220ustar00rootroot00000000000000/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */ /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ #include #ifndef HAVE_MAKE_SCRAMBLED_PASSWORD #include #include "crypto.h" #include "crypto-md5.h" #ifdef WITH_DMALLOC # include #endif /* Constants for MD5Transform routine. */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 #define S21 5 #define S22 9 #define S23 14 #define S24 20 #define S31 4 #define S32 11 #define S33 16 #define S34 23 #define S41 6 #define S42 10 #define S43 15 #define S44 21 static void MD5Transform(crypto_uint4[4], const unsigned char[64]); #ifndef WORDS_BIGENDIAN # define Encode memcpy # define Decode memcpy #else static void Encode(unsigned char *, const crypto_uint4 *, size_t); static void Decode(crypto_uint4 *, const unsigned char *, size_t); #endif static unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* F, G, H and I are basic MD5 functions. */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits. */ #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is separate from addition to prevent recomputation. */ #define FF(a, b, c, d, x, s, ac) { \ (a) += F ((b), (c), (d)) + (x) + (crypto_uint4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) { \ (a) += G ((b), (c), (d)) + (x) + (crypto_uint4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) { \ (a) += H ((b), (c), (d)) + (x) + (crypto_uint4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) { \ (a) += I ((b), (c), (d)) + (x) + (crypto_uint4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } /* MD5 initialization. Begins an MD5 operation, writing a new context. */ void MD5Init(MD5_CTX * context) { context->count[0] = context->count[1] = 0; /* Load magic initialization constants. */ context->state[0] = 0x67452301; context->state[1] = 0xefcdab89; context->state[2] = 0x98badcfe; context->state[3] = 0x10325476; } /* MD5 block update operation. Continues an MD5 message-digest operation, processing another message block, and updating the context. */ void MD5Update(MD5_CTX * context, const unsigned char *input, size_t inputLen) { size_t i, index, partLen; /* Compute number of bytes mod 64 */ index = (size_t) ((context->count[0] >> 3) & 0x3F); /* Update number of bits */ if ((context->count[0] += ((crypto_uint4) inputLen << 3)) < ((crypto_uint4) inputLen << 3)) context->count[1]++; context->count[1] += ((crypto_uint4) inputLen >> 29); partLen = 64 - index; /* Transform as many times as possible. */ if (inputLen >= partLen) { memcpy ((void *) &context->buffer[index], (const void *) input, partLen); MD5Transform(context->state, context->buffer); for (i = partLen; i + 63 < inputLen; i += 64) MD5Transform(context->state, &input[i]); index = 0; } else i = 0; /* Buffer remaining input */ memcpy ((void *) &context->buffer[index], (const void *) &input[i], inputLen - i); } /* MD5 finalization. Ends an MD5 message-digest operation, writing the the message digest and zeroizing the context. */ void MD5Final(unsigned char digest[16], MD5_CTX * context) { unsigned char bits[8]; size_t index, padLen; /* Save number of bits */ Encode(bits, context->count, 8); /* Pad out to 56 mod 64. */ index = (size_t) ((context->count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); MD5Update(context, PADDING, padLen); /* Append length (before padding) */ MD5Update(context, bits, 8); if (digest != NULL) { /* Bill Simpson's padding */ /* store state in digest */ Encode(digest, context->state, 16); /* Zeroize sensitive information. */ memset(context, 0, sizeof (*context)); } } /* MD5 basic transformation. Transforms state based on block. */ static void MD5Transform(crypto_uint4 state[4], const unsigned char block[64]) { crypto_uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; Decode(x, block, 64); /* Round 1 */ FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; /* Zeroize sensitive information. */ memset(x, 0, sizeof x); } #ifdef WORDS_BIGENDIAN /* Encodes input (crypto_uint4) into output (unsigned char). Assumes len is a multiple of 4. */ static void Encode(unsigned char *output, const crypto_uint4 * input, size_t len) { size_t i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = (unsigned char) (input[i] & 0xff); output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff); output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff); output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff); } } /* Decodes input (unsigned char) into output (crypto_uint4). Assumes len is a multiple of 4. */ static void Decode(crypto_uint4 * output, const unsigned char *input, size_t len) { size_t i, j; for (i = 0, j = 0; j < len; i++, j += 4) output[i] = ((crypto_uint4) input[j]) | (((crypto_uint4) input[j + 1]) << 8) | (((crypto_uint4) input[j + 2]) << 16) | (((crypto_uint4) input[j + 3]) << 24); } #endif #else extern signed char v6ready; #endif pam-MySQL-0.8.2/crypto-md5.h000066400000000000000000000025611313430347400154230ustar00rootroot00000000000000/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ #ifndef __CRYPTO_MD5_H__ #define __CRYPTO_MD5_H__ 1 /* MD5 context. */ typedef struct MD5_CTX { crypto_uint4 state[4]; /* state (ABCD) */ crypto_uint4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } MD5_CTX; void MD5Init(MD5_CTX * context); void MD5Update(MD5_CTX * context, const unsigned char * data, size_t len); void MD5Final(unsigned char digest[16], MD5_CTX *context); #endif pam-MySQL-0.8.2/crypto-sha1.c000066400000000000000000000147541313430347400155740ustar00rootroot00000000000000/* * SHA-1 in C * By Steve Reid * 100% Public Domain * * Test Vectors (from FIPS PUB 180-1) * "abc" * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 * A million repetitions of "a" * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #include #ifndef HAVE_MAKE_SCRAMBLED_PASSWORD #include #include "crypto.h" #include "crypto-sha1.h" //#include "utils.h" #ifdef WITH_DMALLOC # include #endif /* #define SHA1HANDSOFF * Copies data before messing with it. */ #define SHA1HANDSOFF #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #ifndef WORDS_BIGENDIAN # define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #else # define blk0(i) block->l[i] #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ static void SHA1Transform(crypto_uint4 state[5], const unsigned char buffer[64]) { crypto_uint4 a, b, c, d, e; typedef union { unsigned char c[64]; crypto_uint4 l[16]; } CHAR64LONG16; CHAR64LONG16 *block; #ifdef SHA1HANDSOFF CHAR64LONG16 workspace; block = &workspace; memcpy(block, buffer, 64); #else block = (CHAR64LONG16 *) buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a, b, c, d, e, 0); R0(e, a, b, c, d, 1); R0(d, e, a, b, c, 2); R0(c, d, e, a, b, 3); R0(b, c, d, e, a, 4); R0(a, b, c, d, e, 5); R0(e, a, b, c, d, 6); R0(d, e, a, b, c, 7); R0(c, d, e, a, b, 8); R0(b, c, d, e, a, 9); R0(a, b, c, d, e, 10); R0(e, a, b, c, d, 11); R0(d, e, a, b, c, 12); R0(c, d, e, a, b, 13); R0(b, c, d, e, a, 14); R0(a, b, c, d, e, 15); R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19); R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23); R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27); R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31); R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35); R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39); R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43); R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47); R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51); R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55); R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59); R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63); R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67); R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71); R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75); R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } /* SHA1Init - Initialize new context */ void SHA1Init(SHA1_CTX * context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1Update(SHA1_CTX * context, const unsigned char *data, size_t len) { size_t i; size_t j; j = context->count[0]; if ((context->count[0] += len << 3) < j) context->count[1] += (len >> 29) + 1; j = (j >> 3) & 63; if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64 - j)); SHA1Transform(context->state, context->buffer); for (; i + 63 < len; i += 64) { SHA1Transform(context->state, &data[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } /* Add padding and return the message digest. */ void SHA1Final(unsigned char digest[20], SHA1_CTX * context) { size_t i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ } SHA1Update(context, (const unsigned char *) "\200", 1); while ((context->count[0] & 504) != 448) { SHA1Update(context, (const unsigned char *) "\0", 1); } SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ if (digest != NULL) { for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); } } /* Wipe variables */ memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(&finalcount, 0, 8); } #else extern signed char v6ready; #endif pam-MySQL-0.8.2/crypto-sha1.h000066400000000000000000000006511313430347400155700ustar00rootroot00000000000000/* * SHA-1 in C * By Steve Reid * 100% Public Domain */ #ifndef __SHA1_H__ #define __SHA1_H__ 1 typedef struct { crypto_uint4 state[5]; crypto_uint4 count[2]; unsigned char buffer[64]; } SHA1_CTX; void SHA1Init(SHA1_CTX * context); void SHA1Update(SHA1_CTX * context, const unsigned char * data, size_t len); void SHA1Final(unsigned char digest[20], SHA1_CTX * context); #endif pam-MySQL-0.8.2/crypto.c000066400000000000000000000257521313430347400147420ustar00rootroot00000000000000#include #ifndef HAVE_MAKE_SCRAMBLED_PASSWORD #include #include #include "crypto.h" #ifndef USE_SYSTEM_CRYPT_SHA1 # include "crypto-sha1.h" #else # include #endif #ifndef USE_SYSTEM_CRYPT_MD5 # include "crypto-md5.h" #else # include #endif #ifdef HAVE_LIBSODIUM # include #endif #ifdef WITH_DMALLOC # include #endif /* Convert a buffer to an hex string. * size_digest is the output length including the trailing \0 */ #ifdef HAVE_LIBSODIUM char *hexify(char * const result, const unsigned char *digest, const size_t size_result, size_t size_digest) { return sodium_bin2hex(result, size_result, digest, size_digest); } #else char *hexify(char * const result, const unsigned char *digest, const size_t size_result, size_t size_digest) { static const char * const hexchars = "0123456789ABCDEF"; char *result_pnt = result; if (size_digest <= (size_t) 0 || size_result <= (size_digest * (size_t) 2U)) { return NULL; } do { *result_pnt++ = hexchars[(*digest >> 4) & 0xf]; *result_pnt++ = hexchars[*digest & 0xf]; digest++; size_digest--; } while (size_digest > (size_t) 0U); *result_pnt = 0; return result; } #endif /* Encode a buffer to Base64 */ char *base64ify(char * const b64, const unsigned char *bin, size_t b64_maxlen, size_t bin_len) { #define B64_PAD '=' static const char b64chars[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char *b64_w = b64; if (b64_maxlen < (((bin_len + 2U) / 3U) * 4U + 1U)) { return NULL; } while (bin_len > (size_t) 2U) { const unsigned char t0 = (unsigned char) *bin++; const unsigned char t1 = (unsigned char) *bin++; const unsigned char t2 = (unsigned char) *bin++; *b64_w++ = b64chars[(t0 >> 2) & 63]; *b64_w++ = b64chars[((t0 << 4) & 48) | ((t1 >> 4) & 15)]; *b64_w++ = b64chars[((t1 << 2) & 60) | ((t2 >> 6) & 3)]; *b64_w++ = b64chars[t2 & 63]; bin_len -= (size_t) 3U; } if (bin_len > (size_t) 0U) { const unsigned char t0 = (unsigned char) bin[0]; *b64_w++ = b64chars[(t0 >> 2) & 63]; if (bin_len == 1U) { *b64_w++ = b64chars[((t0 << 4) & 48)]; *b64_w++ = B64_PAD; } else { const unsigned char t1 = (unsigned char) bin[1]; *b64_w++ = b64chars[((t0 << 4) & 48) | ((t1 >> 4) & 15)]; *b64_w++ = b64chars[((t1 << 2) & 60)]; } *b64_w++ = B64_PAD; } *b64_w = 0; return b64; } /* Decode a Base64 encoded string */ static unsigned char * debase64ify(unsigned char * const bin, const char *b64, size_t bin_maxlen, size_t b64_len, size_t * const bin_len_p) { #define REV64_EOT 128U #define REV64_NONE 64U #define REV64_PAD '=' static const unsigned char rev64chars[256] = { REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, 62U, REV64_NONE, REV64_NONE, REV64_NONE, 63U, 52U, 53U, 54U, 55U, 56U, 57U, 58U, 59U, 60U, 61U, REV64_NONE, REV64_NONE, REV64_NONE, REV64_EOT, REV64_NONE, REV64_NONE, REV64_NONE, 0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U, 12U, 13U, 14U, 15U, 16U, 17U, 18U, 19U, 20U, 21U, 22U, 23U, 24U, 25U, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, 26U, 27U, 28U, 29U, 30U, 31U, 32U, 33U, 34U, 35U, 36U, 37U, 38U, 39U, 40U, 41U, 42U, 43U, 44U, 45U, 46U, 47U, 48U, 49U, 50U, 51U, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE }; const unsigned char *b64_u = (const unsigned char *) b64; unsigned char *bin_w = bin; unsigned char mask; unsigned char t0, t1, t2, t3; uint32_t t; size_t i; if (b64_len % 4U != 0U || (i = b64_len / 4U) <= 0U || bin_maxlen < i * 3U - (b64_u[b64_len - 1U] == REV64_PAD) - (b64_u[b64_len - 2U] == REV64_PAD)) { return NULL; } while (i-- > 0U) { t0 = rev64chars[*b64++]; t1 = rev64chars[*b64++]; t2 = rev64chars[*b64++]; t3 = rev64chars[*b64++]; t = ((uint32_t) t3) | ((uint32_t) t2 << 6) | ((uint32_t) t1 << 12) | ((uint32_t) t0 << 18); mask = t0 | t1 | t2 | t3; if ((mask & (REV64_NONE | REV64_EOT)) != 0U) { if ((mask & REV64_NONE) != 0U || i > 0U) { return NULL; } break; } *bin_w++ = (unsigned char) (t >> 16); *bin_w++ = (unsigned char) (t >> 8); *bin_w++ = (unsigned char) t; } if ((mask & REV64_EOT) != 0U) { if (((t0 | t1) & REV64_EOT) != 0U || t3 != REV64_EOT) { return NULL; } *bin_w++ = (unsigned char) (t >> 16); if (t2 != REV64_EOT) { *bin_w++ = (unsigned char) (t >> 8); } } if (bin_len_p != NULL) { *bin_len_p = (size_t) (bin_w - bin); } return bin; } /* Compute a simple hex SHA1 digest of a C-string */ char *crypto_hash_sha1(const char *string, const int hex) { SHA1_CTX ctx; unsigned char digest[20]; static char result[41]; SHA1Init(&ctx); if (string != NULL && *string != 0) { SHA1Update(&ctx, (const unsigned char *) string, strlen(string)); } SHA1Final(digest, &ctx); if (hex == 0) { return base64ify(result, digest, sizeof result, sizeof digest); } return hexify(result, digest, sizeof result, sizeof digest); } /* Compute a simple hex MD5 digest of a C-string */ char *crypto_hash_md5(const char *string, const int hex) { MD5_CTX ctx; unsigned char digest[16]; static char result[33]; MD5Init(&ctx); if (string != NULL && *string != 0) { MD5Update(&ctx, (const unsigned char *) string, strlen(string)); } MD5Final(digest, &ctx); if (hex == 0) { return base64ify(result, digest, sizeof result, sizeof digest); } return hexify(result, digest, sizeof result, sizeof digest); } /* Compute a salted SHA1 digest of a C-string */ char *crypto_hash_ssha1(const char *string, const char *stored) { SHA1_CTX ctx; const char *salt; unsigned char digest[20]; size_t decoded_len; char *hash_and_salt; size_t sizeof_hash_and_salt; static char decoded[512]; if (debase64ify(decoded, stored, sizeof decoded, strlen(stored), &decoded_len) == NULL) { return NULL; /* huge salt, better abort */ } if (decoded_len < sizeof digest) { return NULL; /* corrupted hash result, abort */ } salt = decoded + sizeof digest; decoded_len -= sizeof digest; SHA1Init(&ctx); if (string != NULL && *string != 0) { SHA1Update(&ctx, (const unsigned char *) string, strlen(string)); } if (decoded_len > (size_t) 0U) { SHA1Update(&ctx, (const unsigned char *) salt, decoded_len); } SHA1Final(digest, &ctx); sizeof_hash_and_salt = sizeof digest + decoded_len; if ((hash_and_salt = malloc(sizeof_hash_and_salt)) == NULL) { return NULL; } memcpy(hash_and_salt, digest, sizeof digest); /* no possible overflow */ memcpy(hash_and_salt + sizeof digest, salt, decoded_len); /* no possible overflow */ if (base64ify(decoded, (const unsigned char *) hash_and_salt, sizeof decoded, sizeof_hash_and_salt) == NULL) { free(hash_and_salt); return NULL; } free(hash_and_salt); return decoded; } /* Compute a salted MD5 digest of a C-string */ char *crypto_hash_smd5(const char *string, const char *stored) { MD5_CTX ctx; const char *salt; unsigned char digest[20]; size_t decoded_len; char *hash_and_salt; size_t sizeof_hash_and_salt; static char decoded[512]; if (debase64ify(decoded, stored, sizeof decoded, strlen(stored), &decoded_len) == NULL) { return NULL; /* huge salt, better abort */ } if (decoded_len < sizeof digest) { return NULL; /* corrupted hash result, abort */ } salt = decoded + sizeof digest; decoded_len -= sizeof digest; MD5Init(&ctx); if (string != NULL && *string != 0) { MD5Update(&ctx, (const unsigned char *) string, strlen(string)); } if (decoded_len > (size_t) 0U) { MD5Update(&ctx, (const unsigned char *) salt, decoded_len); } MD5Final(digest, &ctx); sizeof_hash_and_salt = sizeof digest + decoded_len; if ((hash_and_salt = malloc(sizeof_hash_and_salt)) == NULL) { return NULL; } memcpy(hash_and_salt, digest, sizeof digest); /* no possible overflow */ memcpy(hash_and_salt + sizeof digest, salt, decoded_len); /* no possible overflow */ if (base64ify(decoded, (const unsigned char *) hash_and_salt, sizeof decoded, sizeof_hash_and_salt) == NULL) { free(hash_and_salt); return NULL; } free(hash_and_salt); return decoded; } #else extern signed char v6ready; #endif pam-MySQL-0.8.2/crypto.h000066400000000000000000000020101313430347400147250ustar00rootroot00000000000000#ifndef __CRYPTO_H__ #define __CRYPTO_H__ 1 #include #if SIZEOF_SHORT == 4 typedef short crypto_int4; typedef unsigned short crypto_uint4; #elif SIZEOF_INT == 4 typedef int crypto_int4; typedef unsigned int crypto_uint4; #elif SIZEOF_LONG == 4 typedef long crypto_int4; typedef unsigned long crypto_uint4; #elif SIZEOF_SHORT > 4 typedef short crypto_int4; typedef unsigned short crypto_uint4; #elif SIZEOF_INT > 4 typedef int crypto_int4; typedef unsigned int crypto_uint4; #elif SIZEOF_LONG > 4 typedef long crypto_int4; typedef unsigned long crypto_uint4; #else # error Please report your architecture and OS type to j at pureftpd dot org #endif char *crypto_hash_sha1(const char *string, const int hex); char *crypto_hash_ssha1(const char *string, const char *stored); char *crypto_hash_md5(const char *string, const int hex); char *crypto_hash_smd5(const char *string, const char *stored); char *hexify(char * const result, const unsigned char *digest, const size_t size_result, size_t size_digest); #endif pam-MySQL-0.8.2/m4/000077500000000000000000000000001313430347400135635ustar00rootroot00000000000000pam-MySQL-0.8.2/m4/pam-mysql.m4000066400000000000000000000307701313430347400157540ustar00rootroot00000000000000AC_DEFUN([PAM_MYSQL_CHECK_CONST], [ AC_CACHE_CHECK([$1 availability], [ac_cv_const_[]$1], [ AC_TRY_COMPILE([$4], [ int dummy = (int)$1; ], [ ac_cv_const_[]$1=yes ], [ ac_cv_const_[]$1=no ]) ]) if test "$ac_cv_const_[]$1" = "yes"; then ifelse([$2],[],[:],[$2]) else ifelse([$3],[],[:],[$3]) fi ]) AC_DEFUN([PAM_MYSQL_CHECK_PAM_PROTOS], [ ac_save_CFLAGS="$CFLAGS" CFLAGS="$INCLUDES -Werror $CFLAGS" AC_MSG_CHECKING([if the second argument of pam_get_user() takes const pointer]) AC_TRY_COMPILE([ #include #include ], [ int data = 0; pam_get_user((void *)&data, (const char **)&data, (void *)&data); ], [ AC_MSG_RESULT([yes]) AC_DEFINE([PAM_GET_USER_CONST], [const], [Define to `const' if the 2nd arg of pam_get_user() takes const pointer]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([PAM_GET_USER_CONST], [], [Define to `const' if the 2nd arg of pam_get_user() takes const pointer]) ]) AC_MSG_CHECKING([if the third argument of pam_get_data() takes const pointer]) AC_TRY_COMPILE([ #include #include ], [ int data = 0; pam_get_data((void *)&data, (void *)&data, (const void **)&data); ], [ AC_MSG_RESULT([yes]) AC_DEFINE([PAM_GET_DATA_CONST], [const], [Define to `const' if the 2nd arg of pam_get_data() takes const pointer]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([PAM_GET_DATA_CONST], [], [Define to `const' if the 2nd arg of pam_get_data() takes const pointer]) ]) AC_MSG_CHECKING([if the third argument of pam_get_item() takes const pointer]) AC_TRY_COMPILE([ #include #include ], [ int data = 0; pam_get_item((void *)&data, 0, (const void **)&data); ], [ AC_MSG_RESULT([yes]) AC_DEFINE([PAM_GET_ITEM_CONST], [const], [Define to `const' if the 2nd arg of pam_get_item() takes const pointer]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([PAM_GET_ITEM_CONST], [], [Define to `const' if the 2nd arg of pam_get_item() takes const pointer]) ]) AC_MSG_CHECKING([if the second argument of pam_conv.conv() takes const pointer]) AC_TRY_COMPILE([ #include #include ], [ int (*conv)(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) = 0; struct pam_conv c = { conv, 0 }; c.conv = 0; ], [ AC_MSG_RESULT(yes) AC_DEFINE([PAM_CONV_CONST], [const], [Define to `const' if the 2nd arg of pam_conv.conv takes const pointer.]) ], [ AC_MSG_RESULT(no) AC_DEFINE([PAM_CONV_CONST], [], [Define to `const' if the 2nd arg of pam_conv.conv takes const pointer.]) ]) CFLAGS="$ac_save_CFLAGS" ]) AC_DEFUN([PAM_MYSQL_CHECK_PAM_CONSTS], [ ac_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$INCLUDES $CPPFLAGS" PAM_MYSQL_CHECK_CONST([PAM_CONV_AGAIN], [ AC_DEFINE([HAVE_PAM_CONV_AGAIN], [1], [Define to 1 if PAM defines PAM_CONV_AGAIN constant.]) ], [], [ #include #include ]) PAM_MYSQL_CHECK_CONST([PAM_INCOMPLETE], [ AC_DEFINE([HAVE_PAM_INCOMPLETE], [1], [Define to 1 if PAM defines PAM_INCOMPLETE constant.]) ], [], [ #include #include ]) PAM_MYSQL_CHECK_CONST([PAM_NEW_AUTHTOK_REQD], [ AC_DEFINE([HAVE_PAM_NEW_AUTHTOK_REQD], [1], [Define to 1 if PAM defines PAM_NEW_AUTHTOK_REQD constant.]) ], [], [ #include #include ]) PAM_MYSQL_CHECK_CONST([PAM_AUTHTOK_RECOVERY_ERR], [], [ PAM_MYSQL_CHECK_CONST([PAM_AUTHTOK_RECOVER_ERR], [ AC_DEFINE([LINUX_PAM_CONST_BUG], [1], [Define to 1 if the implementation does not define PAM_AUTHTOK_RECOVER_ERR]) ], [], [ #include #include ]) ], [ #include #include ]) CPPFLAGS="$ac_save_CPPFLAGS" ]) AC_DEFUN([PAM_MYSQL_CHECK_PAM], [ pam_include_path= pam_prefix= for _pfx in $1; do for dir in "$_pfx/include" "$_pfx/include/security" "$_pfx/include/pam"; do if test -e "$dir/pam_modules.h"; then ac_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I$_pfx/include -I$dir" AC_CHECK_HEADERS([pam_appl.h], [ AC_MSG_CHECKING([pam_modules.h usability]) AC_TRY_COMPILE([ #include #include ], [], [ AC_MSG_RESULT([yes]) pam_prefix="$_pfx" pam_include_path="$dir" break ], [ AC_MSG_RESULT([no]) ]) ]) CPPFLAGS="$ac_save_CPPFLAGS" fi done done if test -z "$pam_include_path"; then AC_MSG_ERROR([Cannot find pam headers. Please check if your system is ready for pam module development.]) fi INCLUDES="$INCLUDES -I$pam_include_path -I$pam_prefix/include" PAM_MYSQL_CHECK_PAM_CONSTS PAM_MYSQL_CHECK_PAM_PROTOS ]) AC_DEFUN([PAM_MYSQL_CHECK_LIBMYSQLCLIENT], [ AC_MSG_CHECKING([if] $1 [is a mysql_config script]) _cfg="$1" if test -x "$_cfg" -a -r "$_cfg" -a -f "$_cfg"; then dnl $1 may be a path to mysql_config AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_MYSQL_H], [1], [Define to `1' if you have the header file.]) mysql_config="$1" else AC_MSG_RESULT([no]) mysql_lib_path= mysql_include_path= mysql_lib_name=mysqlclient for _pfx in $1; do _cfg="$_pfx/bin/mysql_config" AC_MSG_CHECKING([mysql_config availability in $_pfx/bin]) if test -x "$_cfg" -a -r "$_cfg" -a -f "$_cfg"; then AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_MYSQL_H], [1], [Define to `1' if you have the header file.]) mysql_config="$_cfg" break else AC_MSG_RESULT([no]) fi for dir in "$_pfx/lib" "$_pfx/lib/mysql"; do AC_MSG_CHECKING([$mysql_lib_name availability in $dir]) name="$mysql_lib_name" if eval test -e "$dir/$libname_spec$shrext_cmds" -o -e "$dir/$libname_spec.$libext"; then AC_MSG_RESULT([yes]) AC_MSG_CHECKING([$dir/$name usability]) ac_save_LIBS="$LIBS" LIBS="$LIBS -L$dir" AC_CHECK_LIB([$mysql_lib_name], [mysql_init], [ AC_MSG_RESULT([yes]) mysql_lib_path="$dir" ], [ AC_MSG_RESULT([no]) ]) LIBS="$ac_save_LIBS" if test ! -z "$mysql_lib_path"; then break fi else AC_MSG_RESULT([no]) fi done for dir in "$_pfx/include" "$_pfx/include/mysql"; do AC_MSG_CHECKING([mysql headers availability in $dir]) if test -e "$dir/mysql.h"; then AC_MSG_RESULT([yes]) AC_MSG_CHECKING([mysql headers usability]) ac_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I$dir" AC_CHECK_HEADER([mysql.h], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_MYSQL_H], [1], [Define to `1' if you have the header file.]) mysql_include_path="$dir" ], [ AC_MSG_RESULT([no]) ]) CPPFLAGS="$ac_save_CPPFLAGS" if test ! -z "$mysql_include_path"; then break fi else AC_MSG_RESULT([no]) fi done done fi if test -z "$mysql_config"; then if test -z "$mysql_lib_path" -o -z "$mysql_include_path"; then AC_MSG_ERROR([Cannot locate mysql client library. Please check your mysql installation.]) fi INCLUDES="$INCLUDES -I$mysql_include_path" LIBS="$LIBS -L$mysql_lib_path -l$mysql_lib_name" else CFLAGS="$CFLAGS `\"$mysql_config\" --cflags`" LIBS="$LIBS `\"$mysql_config\" --libs`" fi ac_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $INCLUDES" AC_CHECK_FUNCS([mysql_real_query mysql_real_escape_string make_scrambled_password_323], [], []) CPPFLAGS="$ac_save_CPPFLAGS" ]) AC_DEFUN([PAM_MYSQL_CHECK_CYRUS_SASL_V1], [ sasl_v1_CFLAGS= sasl_v1_LIBS= sasl_v1_lib_name="sasl" for _pfx in $1; do for dir in "$_pfx/include"; do if test -e "$dir/sasl.h" -a -z "$sasl_CFLAGS"; then ac_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I$dir" AC_MSG_CHECKING([if sasl.h is one of Cyrus SASL version 1]) AC_TRY_RUN([ #include int main() { return (SASL_VERSION_MAJOR == 1 ? 0: 1); } ], [ AC_MSG_RESULT([yes]) sasl_v1_CFLAGS="-I$dir" ], [ AC_MSG_RESULT([no]) ]) CPPFLAGS="$ac_save_CPPFLAGS" fi done for dir in "$_pfx/lib"; do if test -z "$sasl_v1_LIBS"; then ac_save_LIBS="$LIBS" LIBS="$LIBS -L$dir" name="$sasl_v1_lib_name" if eval test -e "$dir/$libname_spec$shrext_cmds" -o -e "$dir/$libname_spec.$libext"; then AC_CHECK_LIB([$sasl_v1_lib_name], [sasl_client_init], [ sasl_v1_LIBS="-L$dir -l$sasl_v1_lib_name" ],[]) fi LIBS="$ac_save_LIBS" fi done done if test -z "$sasl_v1_CFLAGS" -o -z "$sasl_v1_LIBS"; then ifelse([$3],[],[:],[$3]) else ifelse([$2],[],[:],[$2]) fi ]) AC_DEFUN([PAM_MYSQL_CHECK_CYRUS_SASL_V2], [ sasl_v2_CFLAGS= sasl_v2_LIBS= sasl_v2_lib_name="sasl2" for _pfx in $1; do for dir in "$_pfx/include/sasl_v1" "$_pfx/include/sasl_v2" "$_pfx/include"; do if test -e "$dir/sasl.h" -a -z "$sasl_v2_CFLAGS"; then ac_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I$dir" AC_MSG_CHECKING([if sasl.h is one of Cyrus SASL version 2]) AC_TRY_RUN([ #include int main() { return (SASL_VERSION_MAJOR == 2 ? 0: 1); } ], [ AC_MSG_RESULT([yes]) sasl_v2_CFLAGS="-I$dir" ], [ AC_MSG_RESULT([no]) ]) CPPFLAGS="$ac_save_CPPFLAGS" fi done for dir in "$_pfx/lib"; do if test -z "$sasl_v2_LIBS"; then ac_save_LIBS="$LIBS" LIBS="$LIBS -L$dir" name="$sasl_v2_lib_name" if eval test -e "$dir/$libname_spec$shrext_cmds" -o -e "$dir/$libname_spec.$libext"; then AC_CHECK_LIB([$sasl_v2_lib_name], [sasl_v2_client_init], [ sasl_v2_LIBS="-L$dir -l$sasl_v2_lib_name" ],[]) fi LIBS="$ac_save_LIBS" fi done done if test -z "$sasl_v2_CFLAGS" -o -z "$sasl_v2_LIBS"; then ifelse([$3],[],[:],[$3]) else ifelse([$2],[],[:],[$2]) fi ]) AC_DEFUN([PAM_MYSQL_CHECK_MD5_HEADERS], [ AC_MSG_CHECKING([if md5.h is derived from Cyrus SASL Version 1]) AC_TRY_COMPILE([ #include #include ], [ MD5_CTX ctx; _sasl_MD5Init(&ctx); ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_SASL_MD5_H], [1], [Define to 1 if md5.h in the include path is derived from cyrus-sasl_v1 package]) ], [ AC_MSG_RESULT([no]) AC_CHECK_HEADERS([md5.h]) ]) AC_MSG_CHECKING([if md5.h is Solaris's]) AC_TRY_COMPILE([ #include ], [ md5_calc(0, 0, 0); ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_SOLARIS_MD5_H], [1], [Define to 1 if md5.h in the include path is Solaris's]) AC_CHECK_LIB([md5], [md5_calc], [ AC_DEFINE([HAVE_SOLARIS_LIBMD5], [1], [Define to 1 if Solaris's libmd5 is available]) LIBS="$LIBS -lmd5" ]) ], [ AC_MSG_RESULT([no]) AC_CHECK_HEADERS([md5.h]) AC_CHECK_FUNCS([MD5Data]) ]) ]) AC_DEFUN([PAM_MYSQL_CHECK_IPV6], [ ac_save_CFLAGS="$CFLAGS" CFLAGS="$INCLUDES $CFLAGS" PAM_MYSQL_CHECK_CONST([PF_INET6], [ AC_CHECK_TYPES([struct sockaddr_in6,struct in6_addr], [ AC_DEFINE([HAVE_IPV6], [1], [Define to 1 if IPv6 is available.]) ], [], [ #include #include #include ]) ], [], [ #include #include ]) CFLAGS="$ac_save_CFLAGS" ]) AC_DEFUN([PAM_MYSQL_CHECK_GETHOSTBYNAME_R], [ ac_save_CFLAGS="$CFLAGS" CFLAGS="$INCLUDES $CFLAGS" AC_CHECK_FUNCS([gethostbyname_r], [ AC_MSG_CHECKING([if gethostbyname_r() is part of glibc]) AC_TRY_COMPILE([ #include ], [ int x = gethostbyname_r("", (struct hostent *)0, (char *)0, 0, (struct hostent **)0, (int *)0); ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_GNU_GETHOSTBYNAME_R], [1], [Define to 1 if gethostbyname_r() is part of glibc]) ], [ AC_MSG_RESULT([no]) AC_MSG_CHECKING([if gethostbyname_r() is part of SUN libc]) AC_TRY_COMPILE([ #include ], [ struct hostent *x = gethostbyname_r("", (struct hostent *)0, (char *)0, 0, (int *)0); ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_SUNOS_GETHOSTBYNAME_R], [1], [Define to 1 if gethostbyname_r() is part of SUN libc]) ], [ AC_MSG_RESULT([no]) ]) ]) ], []) CFLAGS="$ac_save_CFLAGS" ]) pam-MySQL-0.8.2/pam_mysql.c000066400000000000000000004751751313430347400154340ustar00rootroot00000000000000/* * PAM module for MySQL * * Copyright (C) 1998-2005 Gunay Arslan and the contributors. * Copyright (C) 2015-2017 Nigel Cunningham and contributors. * All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Original Version written by: Gunay ARSLAN * This version by: James O'Kane * Modifications by Steve Brown, * B.J. Black, * Kyle Smith, * Patch integration by Moriyoshi Koizumi, , * based on the patches by the following contributors: * Paul Bryan (check_acct_mgmt service support) * Kev Green (Documentation, UNIX socket option) * Kees Cook (Misc. clean-ups and more) * Fredrik Rambris (RPM spec file) * Peter E. Stokke (chauthtok service support) * Sergey Matveychuk (OpenPAM support) * Package unmaintained for some years; now taken care of by Nigel Cunningham * https://github.com/NigelCunningham/pam-MySQL */ #define _GNU_SOURCE #define PAM_SM_AUTH #define PAM_SM_ACCOUNT #define PAM_SM_SESSION #define PAM_SM_PASSWORD #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYSLOG_H #include #endif #ifdef HAVE_STDARG_H #include #endif #ifdef HAVE_ALLOCA_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STRINGS_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #include #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_CRYPT_H #include #endif #ifndef HAVE_OPENSSL #ifdef HAVE_MD5_H #include #endif #if defined(HAVE_SASL_MD5_H) && (defined(HAVE_CYRUS_SASL_V1) || defined(HAVE_CYRUS_SASL_V2)) #define USE_SASL_MD5 #include #include #endif #endif #ifdef HAVE_OPENSSL #include #include #include #include #include #include #endif #ifdef HAVE_MYSQL_H #include #endif /* * 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 #include #ifndef PAM_EXTERN #define PAM_EXTERN #endif #ifndef LOG_AUTHPRIV #define LOG_AUTHPRIV LOG_AUTH #endif #ifdef LINUX_PAM_CONST_BUG #define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR #endif #ifndef HAVE_MAKE_SCRAMBLED_PASSWORD #include "crypto.h" #include "crypto-sha1.h" /** * Implementation of make_scrambled_password. * * Taken from commit 2db6b50c7b7c638104bd9639994f0574e8f4813c in Pure-ftp * source. * * @param char scrambled_password * The buffer into which the password should be saved. * @param char password * The password to be scrambled. */ void make_scrambled_password(char scrambled_password[42], const char password[255]) { SHA1_CTX ctx; unsigned char h0[20], h1[20]; SHA1Init(&ctx); SHA1Update(&ctx, password, strlen(password)); SHA1Final(h0, &ctx); SHA1Init(&ctx); SHA1Update(&ctx, h0, sizeof h0); #ifdef HAVE_EXPLICIT_BZERO explicit_bzero(h0, strlen(password)); #else volatile unsigned char *pnt_ = (volatile unsigned char *) h0; size_t i = (size_t) 0U; while (i < strlen(password)) { pnt_[i++] = 0U; } #endif SHA1Final(h1, &ctx); *scrambled_password = '*'; hexify(scrambled_password + 1U, h1, 42, sizeof h1); } #endif #define PAM_MODULE_NAME "pam_mysql" #define PAM_MYSQL_LOG_PREFIX PAM_MODULE_NAME " - " #define PLEASE_ENTER_PASSWORD "Password:" #define PLEASE_ENTER_OLD_PASSWORD "Current Password:" #define PLEASE_ENTER_NEW_PASSWORD "New Password:" #define PLEASE_REENTER_NEW_PASSWORD "Retype New Password:" enum _pam_mysql_err_t { PAM_MYSQL_ERR_SUCCESS = 0, PAM_MYSQL_ERR_UNKNOWN = -1, PAM_MYSQL_ERR_NO_ENTRY = 1, PAM_MYSQL_ERR_ALLOC = 2, PAM_MYSQL_ERR_INVAL = 3, PAM_MYSQL_ERR_BUSY = 4, PAM_MYSQL_ERR_DB = 5, PAM_MYSQL_ERR_MISMATCH = 6, PAM_MYSQL_ERR_IO = 7, PAM_MYSQL_ERR_SYNTAX = 8, PAM_MYSQL_ERR_EOF = 9, PAM_MYSQL_ERR_NOTIMPL = 10 }; enum _pam_mysql_config_token_t { PAM_MYSQL_CONFIG_TOKEN_EQUAL = 0, PAM_MYSQL_CONFIG_TOKEN_NEWLINE, PAM_MYSQL_CONFIG_TOKEN_STRING, PAM_MYSQL_CONFIG_TOKEN_SEMICOLON, PAM_MYSQL_CONFIG_TOKEN_COMMENT, PAM_MYSQL_CONFIG_TOKEN__LAST }; #define PAM_MYSQL_USER_STAT_EXPIRED 0x0001 #define PAM_MYSQL_USER_STAT_AUTHTOK_EXPIRED 0x0002 #define PAM_MYSQL_USER_STAT_NULL_PASSWD 0x0004 #define PAM_MYSQL_CAP_CHAUTHTOK_SELF 0x0001 #define PAM_MYSQL_CAP_CHAUTHTOK_OTHERS 0x0002 typedef struct _pam_mysql_ctx_t { MYSQL *mysql_hdl; char *host; char *where; char *db; char *user; char *passwd; char *table; char *update_table; char *usercolumn; char *passwdcolumn; char *statcolumn; int crypt_type; int use_323_passwd; int md5; int sqllog; int verbose; int use_first_pass; int try_first_pass; int disconnect_every_op; char *logtable; char *logmsgcolumn; char *logpidcolumn; char *logusercolumn; char *loghostcolumn; char *logrhostcolumn; char *logtimecolumn; char *config_file; char *my_host_info; } pam_mysql_ctx_t; /*Max length for most MySQL fields is 16 */ typedef enum _pam_mysql_err_t pam_mysql_err_t; typedef enum _pam_mysql_config_token_t pam_mysql_config_token_t; typedef int(*pam_mysql_option_getter_t)(void *val, const char **pretval, int *to_release); typedef int(*pam_mysql_option_setter_t)(void *val, const char *newval_str); typedef struct _pam_mysql_option_accessor_t { pam_mysql_option_getter_t get_op; pam_mysql_option_setter_t set_op; } pam_mysql_option_accessor_t; typedef struct _pam_mysql_option_t { const char *name; size_t name_len; size_t offset; pam_mysql_option_accessor_t *accessor; } pam_mysql_option_t; typedef struct _pam_mysql_str_t { char *p; size_t len; size_t alloc_size; int mangle; } pam_mysql_str_t; struct _pam_mysql_entry_handler_t; typedef pam_mysql_err_t (*pam_mysql_handle_entry_fn_t)( struct _pam_mysql_entry_handler_t *, int, const char *, size_t, const char *, size_t); typedef struct _pam_mysql_entry_handler_t { pam_mysql_ctx_t *ctx; pam_mysql_handle_entry_fn_t handle_entry_fn; pam_mysql_option_t *options; } pam_mysql_entry_handler_t; typedef struct _pam_mysql_config_parser_t { pam_mysql_ctx_t *ctx; pam_mysql_entry_handler_t *hdlr; } pam_mysql_config_parser_t; typedef struct _pam_mysql_stream_t { int fd; unsigned char buf[2][2048]; unsigned char *buf_start; unsigned char *buf_ptr; unsigned char *buf_end; unsigned char *pushback; size_t buf_in_use; int eof; } pam_mysql_stream_t; typedef struct _pam_mysql_config_scanner_t { pam_mysql_ctx_t *ctx; pam_mysql_str_t image; pam_mysql_config_token_t token; pam_mysql_stream_t *stream; int state; } pam_mysql_config_scanner_t; /* prototypes */ /* General PAM Prototypes */ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv); PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv); PAM_EXTERN int pam_sm_acct_mgmt(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); 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); /* static prototypes */ static void pam_mysql_cleanup_hdlr(pam_handle_t *pamh, void * voiddata, int status); /* pam_mysql methods */ static pam_mysql_err_t pam_mysql_retrieve_ctx(pam_mysql_ctx_t **pretval, pam_handle_t *pamh); static pam_mysql_err_t pam_mysql_init_ctx(pam_mysql_ctx_t *); static void pam_mysql_destroy_ctx(pam_mysql_ctx_t *); static void pam_mysql_saltify(pam_mysql_ctx_t *, char *salt, const char *salter ); static pam_mysql_err_t pam_mysql_parse_args(pam_mysql_ctx_t *, int argc, const char **argv); static pam_mysql_err_t pam_mysql_open_db(pam_mysql_ctx_t *); static void pam_mysql_close_db(pam_mysql_ctx_t *); static pam_mysql_err_t pam_mysql_check_passwd(pam_mysql_ctx_t *ctx, const char *user, const char *passwd, int null_inhibited); static pam_mysql_err_t pam_mysql_update_passwd(pam_mysql_ctx_t *, const char *user, const char *new_passwd); static pam_mysql_err_t pam_mysql_query_user_stat(pam_mysql_ctx_t *, int *pretval, const char *user); static pam_mysql_err_t pam_mysql_query_user_caps(pam_mysql_ctx_t *, int *pretval, const char *user); static pam_mysql_err_t pam_mysql_sql_log(pam_mysql_ctx_t *, const char *msg, const char *user, const char *host); static pam_mysql_err_t pam_mysql_get_host_info(pam_mysql_ctx_t *, const char **pretval); static size_t strnncpy(char *dest, size_t dest_size, const char *src, size_t src_len); static void *xcalloc(size_t nmemb, size_t size); static char *xstrdup(const char *ptr); static void xfree(void *ptr); static void xfree_overwrite(char *ptr); /** * Local strnncpy. * * @param char* dest * Pointer to a string into which the string should be copied. * @param size_t dest_size * The size of the destination buffer. * @param char *src * Pointer to the source string. * @param size_t src_len * The length of the source string. * * @return size_t * The length of the copied data. */ static size_t strnncpy(char *dest, size_t dest_size, const char *src, size_t src_len) { size_t cpy_len; dest_size--; cpy_len = (dest_size < src_len ? dest_size: src_len); memcpy(dest, src, cpy_len); dest[cpy_len] = '\0'; return cpy_len; } /** * Allocate space for an array of elements of equal size. * * The product of the size and number of items must not exceed the capacity * of a signed int (if I've understood the check correctly!) * * Memory is cleared. * * @param size_t nmemb * The number of array elements. * @param size_t size * The size of each element. * * @return mixed * NULL if no allocation is made, or a cleared array of the requested size. */ static void *xcalloc(size_t nmemb, size_t size) { void *retval; double v = ((double)size) * (int)(nmemb & (((size_t)-1) >> 1)); if (v != nmemb * size) { return NULL; } retval = calloc(nmemb, size); return retval; } /** * Adjust the amount of space allocated for an array of elements of equal size. * * The product of the size and number of items must not exceed the capacity * of a signed int. * * @param size_t nmemb * The number of array elements. * @param size_t size * The size of each element. * * @return mixed * NULL if no allocation is made, or a cleared array of the requested size. */ static void *xrealloc(void *ptr, size_t nmemb, size_t size) { void *retval; size_t total = nmemb * size; if (((double)size) * (int)(nmemb & (((size_t)-1) >> 1)) != total) { return NULL; } retval = realloc(ptr, total); return retval; } /** * Duplicate a string. * * @param const char *ptr * The string to be duplicated. * * @return mixed. * A copy of the string or NULL if memory allocation failed. */ static char *xstrdup(const char *ptr) { size_t len = strlen(ptr) + sizeof(char); char *retval = xcalloc(sizeof(char), len); if (retval == NULL) { return NULL; } memcpy(retval, ptr, len); return retval; } /** * Free memory allocated with malloc. * * @param void *ptr * A pointer to the allocation. */ static void xfree(void *ptr) { if (ptr != NULL) { free(ptr); } } /** * Clear memory and free it. * * @param char *ptr * The memory to be cleared and freed. */ static void xfree_overwrite(char *ptr) { if (ptr != NULL) { char *p; for (p = ptr; *p != '\0'; p++) { *p = '\0'; } free(ptr); } } /** * Skip instances of a list of delimiters in an input buffer. * * @param void *buf * The memory * @param size_t buf_len * The length of the input buffer. * @param const unsigned char *delims * A pointer to an array of single character delimiters. * @param size_t ndelims * The number of delimiters provided. * * @return unsigned char * A pointer to the first delimiter found, or the end of the input buffer. */ static void *memspn(void *buf, size_t buf_len, const unsigned char *delims, size_t ndelims) { unsigned char *buf_end = ((unsigned char *)buf) + buf_len; unsigned char *p; switch (ndelims) { case 0: return buf_end; case 1: { unsigned char c = delims[0]; for (p = (unsigned char *)buf; p < buf_end; p++) { if (*p != c) { return (void *)p; } } } break; case 2: { unsigned char c1 = delims[0], c2 = delims[1]; for (p = (unsigned char *)buf; p < buf_end; p++) { if (*p != c1 && *p != c2) { return (void *)p; } } } break; default: { const unsigned char *delims_end = delims + ndelims; unsigned char and_mask = ~0, or_mask = 0; const unsigned char *q; for (q = delims; q < delims_end; q++) { and_mask &= *q; or_mask |= *q; } for (p = (unsigned char *)buf; p < buf_end; p++) { if ((*p & and_mask) == and_mask && (*p & or_mask) != 0) { for (q = delims; *p != *q; q++) { if (q >= delims_end) { return (void *)p; } } } } } break; } return NULL; } /** * Locate a delimiter or delimiters within a string. * * Scan for the earliest instance of a list of delimiters. * * @param void *buf * The input buffer. * @param size_t buf_len * The size of the input buffer. * @param const unsigned char *delims * An array of single character delimiters. * @param size_t ndelims * The number of delimiters. * * @return mixed * The location of the first matching delimiter or NULL. */ static void *memcspn(void *buf, size_t buf_len, const unsigned char *delims, size_t ndelims) { if (ndelims == 1) { return memchr(buf, delims[0], buf_len); } else { unsigned char *buf_end = ((unsigned char *)buf) + buf_len; const unsigned char *delims_end = delims + ndelims; unsigned char *p; for (p = (unsigned char *)buf; p < buf_end; p++) { const unsigned char *q; for (q = delims; q < delims_end; q++) { if (*p == *q) { return (void *)p; } } } return NULL; } } /** * pam_mysql_md5_data * * AFAIK, only FreeBSD has MD5Data() defined in md5.h * better MD5 support will appear in 0.5 */ #ifdef HAVE_MD5DATA #define HAVE_PAM_MYSQL_MD5_DATA #define pam_mysql_md5_data MD5Data #elif defined(HAVE_OPENSSL) || (defined(HAVE_SASL_MD5_H) && defined(USE_SASL_MD5)) || (!defined(HAVE_OPENSSL) && defined(HAVE_SOLARIS_MD5)) #if defined(USE_SASL_MD5) /** * Get the MD5 hash of a string. * * @param const unsigned char *d * The string for which a MD5 sum is to be computed. * @param unsigned int n * The length of the input. * @param unsigned char *md * The buffer into which the MD5 sum will be placed. * * @return unsigned char * * The output buffer location. */ static unsigned char *MD5(const unsigned char *d, unsigned int n, unsigned char *md) { MD5_CTX ctx; _sasl_MD5Init(&ctx); _sasl_MD5Update(&ctx, (unsigned char *)d, n); _sasl_MD5Final(md, &ctx); return md; } #elif defined(USE_SOLARIS_MD5) #define MD5(d, n, md) md5_calc(d, md, n) #endif #define HAVE_PAM_MYSQL_MD5_DATA /** * Calculate a MD5 sum and return as a hex string. * * @param const unsigned char *d * The input buffer. * @param unsigned int sz * The size of the input. * @param char *md * A pointer to the output buffer (NULL or at least 33 bytes). * * @return char * * A pointer to the output buffer. */ static char *pam_mysql_md5_data(const unsigned char *d, unsigned int sz, char *md) { size_t i, j; unsigned char buf[16]; if (md == NULL) { if ((md = xcalloc(32 + 1, sizeof(char))) == NULL) { return NULL; } } MD5(d, (unsigned long)sz, buf); for (i = 0, j = 0; i < 16; i++, j += 2) { md[j + 0] = "0123456789abcdef"[(int)(buf[i] >> 4)]; md[j + 1] = "0123456789abcdef"[(int)(buf[i] & 0x0f)]; } md[j] = '\0'; return md; } #endif #if defined(HAVE_OPENSSL) #define HAVE_PAM_MYSQL_SHA1_DATA /** * Implementation of calcDecodeLength * * Calculates the length of a decoded string * Copyright (c) 2013 Barry Steyn * https://gist.github.com/barrysteyn/7308212 * * @param char* b64input * The buffer of the encoded string. * * @return size_t * The length of the decoded string */ static size_t calcDecodeLength(const char* b64input) { size_t len = strlen(b64input), padding = 0; if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are = padding = 2; else if (b64input[len-1] == '=') //last char is = padding = 1; return (len*3)/4 - padding; } /** * Implementation of Base64Encode * * Encodes a binary safe base 64 string * Copyright (c) 2013 Barry Steyn * https://gist.github.com/barrysteyn/7308212 * * @param unsigned char* buffer * The buffer of the "normal" string as input. * @param size_t length * The length of the "normal" string. * @param char** b64text * The buffer of the encoded string. * * @return int * 0 = success */ int Base64Encode(const unsigned char* buffer, size_t length, char** b64text) { BIO *bio, *b64; BUF_MEM *bufferPtr; size_t b64len; b64 = BIO_new(BIO_f_base64()); bio = BIO_new(BIO_s_mem()); bio = BIO_push(b64, bio); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line BIO_set_close(bio, BIO_CLOSE); BIO_write(bio, buffer, length); BIO_flush(bio); BIO_get_mem_ptr(bio, &bufferPtr); b64len = bufferPtr->length; (*b64text) = (char *) malloc((b64len + 1) * sizeof(char)); memcpy(*b64text, bufferPtr->data, b64len); (*b64text)[b64len] = '\0'; BIO_free_all(bio); return (0); //success } /** * Implementation of Base64Decode * * Decodes a base64 encoded string * Copyright (c) 2013 Barry Steyn * https://gist.github.com/barrysteyn/7308212 * * @param unsigned char* b64message * The buffer of the Base64 encoded string as input. * @param char** buffer * The buffer of the decoded string. * @param size_t length * The length of the decoded string. * * @return int * 0 = success */ static int Base64Decode(char* b64message, unsigned char** buffer, size_t* length) { BIO *bio, *b64; int decodeLen = calcDecodeLength(b64message); *buffer = (unsigned char*)malloc(decodeLen + 1); (*buffer)[decodeLen] = '\0'; bio = BIO_new_mem_buf(b64message, -1); b64 = BIO_new(BIO_f_base64()); bio = BIO_push(b64, bio); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer *length = BIO_read(bio, *buffer, strlen(b64message)); assert(*length == decodeLen); //length should equal decodeLen, else something went horribly wrong BIO_free_all(bio); return (0); //success } /** * Calculate the SHA1 hash of input and return as a hex string. * * @param const unsigned char *d * The input buffer location. * @param unsigned int sz * The size of the input. * @param char *md * A pointer to the output buffer (NULL or at least 41 bytes). * * @return char * * A pointer to the output buffer. */ static char *pam_mysql_sha1_data(const unsigned char *d, unsigned int sz, char *md) { size_t i, j; unsigned char buf[20]; if (md == NULL) { if ((md = xcalloc(40 + 1, sizeof(char))) == NULL) { return NULL; } } SHA1(d, (unsigned long)sz, buf); for (i = 0, j = 0; i < 20; i++, j += 2) { md[j + 0] = "0123456789abcdef"[(int)(buf[i] >> 4)]; md[j + 1] = "0123456789abcdef"[(int)(buf[i] & 0x0f)]; } md[j] = '\0'; return md; } /** * Calculate the SHA256 hash of input and return as a hex string. * * @param const unsigned char *d * The input buffer location. * @param unsigned int sz * The size of the input. * @param char *md * A pointer to the output buffer (NULL or at least 65 bytes). * * @return char * * A pointer to the output buffer. */ static char *pam_mysql_sha256_data(const unsigned char *d, unsigned int sz, char *md) { size_t i, j; unsigned char buf[32]; if (md == NULL) { if ((md = xcalloc(64 + 1, sizeof(char))) == NULL) { return NULL; } } SHA256(d, (unsigned long)sz, buf); for (i = 0, j = 0; i < 32; i++, j += 2) { md[j + 0] = "0123456789abcdef"[(int)(buf[i] >> 4)]; md[j + 1] = "0123456789abcdef"[(int)(buf[i] & 0x0f)]; } md[j] = '\0'; return md; } #define HAVE_PAM_MYSQL_SHA256_DATA /** * Calculate the SHA512 hash of input and return as a hex string. * * @param const unsigned char *d * The input buffer location. * @param unsigned int sz * The size of the input. * @param char *md * A pointer to the output buffer (NULL or at least 129 bytes). * * @return char * * A pointer to the output buffer. */ static char *pam_mysql_sha512_data(const unsigned char *d, unsigned int sz, char *md) { size_t i, j; unsigned char buf[64]; if (md == NULL) { if ((md = xcalloc(128 + 1, sizeof(char))) == NULL) { return NULL; } } SHA512(d, (unsigned long)sz, buf); for (i = 0, j = 0; i < 64; i++, j += 2) { md[j + 0] = "0123456789abcdef"[(int)(buf[i] >> 4)]; md[j + 1] = "0123456789abcdef"[(int)(buf[i] & 0x0f)]; } md[j] = '\0'; return md; } #define HAVE_PAM_MYSQL_SHA512_DATA /** * Calculate the salted SHA hash and return as a base64 string. * * @param const unsigned char *d * The input buffer. * @param unsigned int sz * The size of the input. * @param char *salt * A pointer to the salt string. * @param size_t salt_length * The size of the salt string. * @param char *md * A pointer to the output buffer (NULL or at least 33 bytes). * * @return char * * A pointer to the output buffer. */ static char *pam_mysql_ssha_data(const unsigned char *d, size_t sz, char *salt, size_t salt_length, char *md) { if (md == NULL) { if ((md = xcalloc(40 + 1, sizeof(char))) == NULL) { return NULL; } } unsigned char sha_hash_data[sz + salt_length]; memcpy(sha_hash_data, d, sz); memcpy(&(sha_hash_data[sz]), salt, salt_length); unsigned char buf[20]; SHA1(sha_hash_data, sz + salt_length, buf); unsigned char b64_hash_data[20 + salt_length]; memcpy(b64_hash_data, buf, 20); memcpy(&(b64_hash_data[20]), salt, salt_length); char* base64EncodeOutput; Base64Encode(b64_hash_data, 20 + salt_length, &base64EncodeOutput); memcpy(md, base64EncodeOutput, strlen(base64EncodeOutput) + 1); return md; } #endif #if defined(HAVE_PAM_MYSQL_SHA1_DATA) && defined(HAVE_PAM_MYSQL_MD5_DATA) #define HAVE_PAM_MYSQL_DRUPAL7 /** * The standard log2 number of iterations for password stretching. This should * increase by 1 every Drupal version in order to counteract increases in the * speed and power of computers available to crack the hashes. */ #define DRUPAL_HASH_COUNT 14 /** * The minimum allowed log2 number of iterations for password stretching. */ #define DRUPAL_MIN_HASH_COUNT 7 /** * The maximum allowed log2 number of iterations for password stretching. */ #define DRUPAL_MAX_HASH_COUNT 30 /** * The expected (and maximum) number of characters in a hashed password. */ #define DRUPAL_HASH_LENGTH 55 /** * Returns a string for mapping an int to the corresponding base 64 character. * * @return char * * The string for use in mapping ints to base 64 characters. */ static char * _password_itoa64(void) { return "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; } /** * Parse the log2 iteration count from a stored hash or setting string. * * @param char *setting * The Drupal 7 password setting string. * * @return mixed * -1 if the character wasn't found or the log2 iteration count. */ static int d7_password_get_count_log2(char *setting) { char *itoa64 = _password_itoa64(); int i; for (i = 0; i < strlen(itoa64); i++) { if (itoa64[i] == setting[3]) return i; } return -1; } /** * Base64 encode a password. * * @param unsigned char *input * The unencoded password. * @param int count * The length of the input. * @param char *output * The buffer for the encoded password. * * @return char * * The location of the NULL terminating the output buffer. */ static char * _password_base64_encode(unsigned char *input, int count, char *output) { int i = 0, off = 0; char *itoa64 = _password_itoa64(); unsigned long value; do { value = (unsigned long) input[i++]; output[off++] = itoa64[value & 0x3f]; if (i < count) { value |= (((unsigned long) input[i]) << 8); } output[off++] = itoa64[(value >> 6) & 0x3f]; if (i++ >= count) { break; } if (i < count) { value |= (((unsigned long) input[i]) << 16); } output[off++] = itoa64[(value >> 12) & 0x3f]; if (i++ >= count) { break; } output[off++] = itoa64[(value >> 18) & 0x3f]; } while (i < count); output[off] = 0; return output; } /** * Calculate the Drupal 7 hash for a password. * * Strings may be binary and contain \0, so we can't use strlen. * * @param int use_md5 * Whether to use MD5 (non zero) or SHA512. * @param char *string1 * The first string (setting string) * @param int len1 * The length of the first string. * @param char *string2 * The second string (password) * @param int len2 * The length of the second string. * * @return mixed * The D7 hash or NULL if memory could not be allocated. */ static char *d7_hash(int use_md5, char *string1, int len1, char *string2, int len2) { int len = len1 + len2; char *combined = xcalloc(len, sizeof(char)), *output = xcalloc(129, sizeof(char)); if (!combined) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "hash: Failed to allocate memory for combined value."); return NULL; } memcpy(combined, string1, len1); memcpy(combined + len1, string2, len2); if (use_md5) MD5(combined, (unsigned long)len, output); else SHA512(combined, (unsigned long)len, output); xfree(combined); return output; } /** * Encrypt a Drupal 7 password. * * The first 12 characters of an existing hash are its setting string. * * @param int use_md5 * Whether to use MD5 or SHA512. * @param char *password * The unencryped password. * @param char *setting * The setting string. * * @return mixed * NULL if memory allocation failed or inputs were invalid, else a ponter to * the encrypted password. */ static char * d7_password_crypt(int use_md5, char *password, char *setting) { char salt[9], *old, *new, *final; int expected, count, count_log2 = d7_password_get_count_log2(setting); int len; // Hashes may be imported from elsewhere, so we allow != DRUPAL_HASH_COUNT if (count_log2 < DRUPAL_MIN_HASH_COUNT || count_log2 > DRUPAL_MAX_HASH_COUNT) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "_password_crypt: count_log2 outside of range."); return NULL; } strncpy(salt, &setting[4], 8); salt[8] = '\0'; if (strlen(salt) != 8) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "_password_crypt: Salt length is not 8."); return NULL; } // Convert the base 2 logarithm into an integer. count = 1 << count_log2; old = d7_hash(use_md5, salt , 8, password, strlen(password)); do { new = d7_hash(use_md5, old , use_md5 ? 16 : 64, password, strlen(password)); xfree(old); if (!new) return NULL; old = new; } while (--count); len = use_md5 ? 16 : 64; new = xcalloc(129, sizeof(char)); memcpy(new, setting, 12); _password_base64_encode(old, len, &new[12]); xfree(old); // _password_base64_encode() of a 16 byte MD5 will always be 22 characters. // _password_base64_encode() of a 64 byte sha512 will always be 86 characters. expected = 12 + ((8 * len + 5) / 6); if (strlen(new) != expected) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "_password_crypt: Hash length not as expected."); xfree(new); return NULL; } final = xcalloc(DRUPAL_HASH_LENGTH + 1, sizeof(char)); strncpy(final, new, DRUPAL_HASH_LENGTH); xfree(new); return final; } /** * Encrypt a provided password, using the same method applied to the password in * the database. * * @param const unsigned char *pwd * The unencrypted password. * @param unsigned int sz * The size of the password. * @param char *md * The hashed, crypted password. * @param char *db_pwd * The password stored in the DB. * * @return char * * The encrypted password. */ static char *pam_mysql_drupal7_data(const unsigned char *pwd, unsigned int sz, char *md, char *db_pwd) { char *stored_hash = db_pwd, *pwd_ptr = (char *) pwd, *hashed; int match = 0, offset = 0; // Algorithm taken from user_check_password in includes/password.c in D7.0. if (db_pwd[0] == 'U' && db_pwd[1] == '$') { // This may be an updated password from user_update_7000(). Such hashes // have 'U' added as the first character and need an extra md5(). stored_hash = &db_pwd[1]; offset++; pwd_ptr = pam_mysql_md5_data(pwd, (unsigned long)sz, md); } else stored_hash = &db_pwd[0]; if (stored_hash[0] == '$' && stored_hash[2] == '$') { switch (stored_hash[1]) { case 'S': match = 1; hashed = d7_password_crypt(0, pwd_ptr, stored_hash); break; case 'H': // phpBB3 uses "$H$" for the same thing as "$P$". case 'P': match = 1; hashed = d7_password_crypt(1, pwd_ptr, stored_hash); break; } } if (!match || !hashed) { md[0] = db_pwd[0] + 1; return NULL; } memcpy(md, hashed, strlen(hashed)); xfree(hashed); return md; } #endif /* Option handlers */ /** * Getter for a string option. * * @param void *val * The pointer to be set to the address of the option. * @param const char **pretval. * Pointer to the start of the requested string. * @param int *to_release. * Pointer to a flag indicating whether the caller should free val. * * @return pam_mysql_err_t * Indication of whether the operation succeeded. */ static pam_mysql_err_t pam_mysql_string_opt_getter(void *val, const char **pretval, int *to_release) { *pretval = *(char **)val; *to_release = 0; return PAM_MYSQL_ERR_SUCCESS; } /** * Setter for a string option. * * @param void *val * A pointer to the string. Any existing value will be freed. * @param const char *newval_str * Pointer to the new string value. * * @return pam_mysql_err_t * Indication of whether the operation succeeded. */ static pam_mysql_err_t pam_mysql_string_opt_setter(void *val, const char *newval_str) { if (*(char **)val != NULL) { xfree(*(char **)val); } if (NULL == (*(char **)val = xstrdup(newval_str))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_ALLOC; } return PAM_MYSQL_ERR_SUCCESS; } /** * Getter for a boolean option. * * @param void *val * The pointer to be set to the address of the option. * @param const char **pretval. * Pointer to the start of the requested option. * @param int *to_release. * Pointer to a flag indicating whether the caller should free val. * * @return pam_mysql_err_t * Indication of whether the operation succeeded. */ static pam_mysql_err_t pam_mysql_boolean_opt_getter(void *val, const char **pretval, int *to_release) { *pretval = (*(int *)val ? "true": "false"); *to_release = 0; return PAM_MYSQL_ERR_SUCCESS; } /** * Setter for a boolean option. * * @param void *val * A pointer to the boolean. * @param const char *newval_str * Pointer to the new value. * * @return int * Indication of whether the operation succeeded. */ static pam_mysql_err_t pam_mysql_boolean_opt_setter(void *val, const char *newval_str) { *(int *)val = (strcmp(newval_str, "0") != 0 && strcasecmp(newval_str, "N") != 0 && strcasecmp(newval_str, "false") != 0 && strcasecmp(newval_str, "no") != 0); return PAM_MYSQL_ERR_SUCCESS; } /** * Get the name matching a numeric key for a crypt method. * * @param void *val * The index of the method. * @param const char **pretval * A pointer that should be set to the address of the matching string. * @param int * * Pointer to an integer indicating whether the caller should free val. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_crypt_opt_getter(void *val, const char **pretval, int *to_release) { switch (*(int *)val) { case 0: *pretval = "plain"; break; case 1: *pretval = "Y"; break; case 2: *pretval = "mysql"; break; case 3: *pretval = "md5"; break; case 4: *pretval = "sha1"; break; case 5: *pretval = "drupal7"; break; case 6: *pretval = "joomla15"; break; case 7: *pretval = "ssha"; break; case 8: *pretval = "sha512"; break; case 9: *pretval = "sha256"; break; default: *pretval = NULL; } *to_release = 0; return PAM_MYSQL_ERR_SUCCESS; } /* pam_mysql_crypt_opt_setter */ /** * Get the number matching a crypt method name. * * @param void *val * Pointer to the integer value to be returned. * @param const char *newval_str * Pointer to a string to be matched against method names. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_crypt_opt_setter(void *val, const char *newval_str) { if (strcmp(newval_str, "0") == 0 || strcasecmp(newval_str, "plain") == 0) { *(int *)val = 0; return PAM_MYSQL_ERR_SUCCESS; } if (strcmp(newval_str, "1") == 0 || strcasecmp(newval_str, "Y") == 0) { *(int *)val = 1; return PAM_MYSQL_ERR_SUCCESS; } if (strcmp(newval_str, "2") == 0 || strcasecmp(newval_str, "mysql") == 0) { *(int *)val = 2; return PAM_MYSQL_ERR_SUCCESS; } if (strcmp(newval_str, "3") == 0 || strcasecmp(newval_str, "md5") == 0) { *(int *)val = 3; return PAM_MYSQL_ERR_SUCCESS; } if (strcmp(newval_str, "4") == 0 || strcasecmp(newval_str, "sha1") == 0) { *(int *)val = 4; return PAM_MYSQL_ERR_SUCCESS; } if (strcmp(newval_str, "5") == 0 || strcasecmp(newval_str, "drupal7") == 0) { *(int *)val = 5; return PAM_MYSQL_ERR_SUCCESS; } if (strcmp(newval_str, "6") == 0 || strcasecmp(newval_str, "joomla15") == 0) { *(int *)val = 6; return PAM_MYSQL_ERR_SUCCESS; } if (strcmp(newval_str, "7") == 0 || strcasecmp(newval_str, "ssha") == 0) { *(int *)val = 7; return PAM_MYSQL_ERR_SUCCESS; } if (strcmp(newval_str, "8") == 0 || strcasecmp(newval_str, "sha512") == 0) { *(int *)val = 8; return PAM_MYSQL_ERR_SUCCESS; } if (strcmp(newval_str, "9") == 0 || strcasecmp(newval_str, "sha256") == 0) { *(int *)val = 9; return PAM_MYSQL_ERR_SUCCESS; } *(int *)val = 0; return PAM_MYSQL_ERR_INVAL; } /* option definitions */ #define PAM_MYSQL_OFFSETOF(type, x) ((size_t)&((type *)0)->x) #define PAM_MYSQL_DEF_OPTION(name, accr) PAM_MYSQL_DEF_OPTION2(name, name, accr) #define PAM_MYSQL_DEF_OPTION2(name, sname, accr) \ { #name, sizeof(#name) - 1, PAM_MYSQL_OFFSETOF(pam_mysql_ctx_t, sname), accr } static pam_mysql_option_accessor_t pam_mysql_string_opt_accr = { pam_mysql_string_opt_getter, pam_mysql_string_opt_setter }; static pam_mysql_option_accessor_t pam_mysql_boolean_opt_accr = { pam_mysql_boolean_opt_getter, pam_mysql_boolean_opt_setter }; static pam_mysql_option_accessor_t pam_mysql_crypt_opt_accr = { pam_mysql_crypt_opt_getter, pam_mysql_crypt_opt_setter }; static pam_mysql_option_t options[] = { PAM_MYSQL_DEF_OPTION(host, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(where, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(db, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(user, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(passwd, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(table, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(update_table, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(usercolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(passwdcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(statcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(crypt, crypt_type, &pam_mysql_crypt_opt_accr), PAM_MYSQL_DEF_OPTION(md5, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION(sqllog, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION(verbose, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION(logtable, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(logmsgcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(logpidcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(logusercolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(loghostcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(logrhostcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(logtimecolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(config_file, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION(use_323_passwd, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION(use_first_pass, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION(try_first_pass, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION(disconnect_every_op, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION2(debug, verbose, &pam_mysql_boolean_opt_accr), { NULL, 0, 0, NULL } }; /* string functions */ /** * String initialisor. * * @param pam_mysql_str_t *str * Pointer to the string to be initialised. * @param int mangle * Value for the mangle attribute. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_str_init(pam_mysql_str_t *str, int mangle) { str->p = ""; str->len = 0; str->alloc_size = 0; str->mangle = mangle; return PAM_MYSQL_ERR_SUCCESS; } /** * String destructor. Clears memory if mangle is set. * * @param pam_mysql_str_t *str * Pointer to the string to be freed. */ static void pam_mysql_str_destroy(pam_mysql_str_t *str) { if (str->alloc_size > 0) { if (str->mangle) { memset(str->p, 0, str->len); } xfree(str->p); } } /** * Modify a string structure to allow for a longer string. * * @param pam_mysql_str_t *str * Pointer to the structure. * @param size_t len * The amount of additional space required. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_str_reserve(pam_mysql_str_t *str, size_t len) { size_t len_req; { len_req = str->len + len; if (len_req < str->len) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "integer overflow at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_INVAL; } len_req += sizeof(char); // space for a string terminator } if (len_req >= str->alloc_size) { size_t cv = 0; size_t new_size = (str->alloc_size == 0 ? 1: str->alloc_size); char *new_buf; do { new_size *= 2; if (cv > new_size) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_ALLOC; } cv = new_size; } while (new_size < len_req); if (str->mangle) { if (NULL == (new_buf = xcalloc(new_size, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_ALLOC; } memcpy(new_buf, str->p, str->len); memset(str->p, 0, str->len); if (str->alloc_size > 0) { xfree(str->p); } } else { if (str->alloc_size == 0) { if (NULL == (new_buf = xcalloc(new_size, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_ALLOC; } } else { if (NULL == (new_buf = xrealloc(str->p, new_size, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_ALLOC; } } } str->p = new_buf; str->alloc_size = new_size; } return PAM_MYSQL_ERR_SUCCESS; } /** * Append new content to an existing string. * * @param pam_mysql_str_t *str * Pointer to the structure. * @param const char *s * The string to be appended. * @param size_t len * The length of the new string. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_str_append(pam_mysql_str_t *str, const char *s, size_t len) { pam_mysql_err_t err; if ((err = pam_mysql_str_reserve(str, len))) { return err; } memcpy(str->p + str->len, s, len); str->len += len; str->p[str->len] = '\0'; return PAM_MYSQL_ERR_SUCCESS; } /** * Append a single character to a string. * * @param pam_mysql_str_t *str * Pointer to the structure. * @param char c * The character to be appended. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_str_append_char(pam_mysql_str_t *str, char c) { return pam_mysql_str_append(str, &c, sizeof(c)); } /** * Truncate a string. * * @param pam_mysql_str_t *str * Pointer to the structure. * @param size_t len * The new string length. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_str_truncate(pam_mysql_str_t *str, size_t len) { if (len > str->len) { return PAM_MYSQL_ERR_INVAL; } str->len = len; if (str->alloc_size != 0) { str->p[len] = '\0'; } return PAM_MYSQL_ERR_SUCCESS; } /* Stream functions */ /** * Open a stream. * * @param pam_mysql_stream_t *stream * Pointer to the stream structure. * @param pam_mysql_ctx_t *ctx * Pointer to the context data structure. * @param const char *file * Pointer to the file name. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_stream_open(pam_mysql_stream_t *stream, pam_mysql_ctx_t *ctx, const char *file) { stream->buf_end = stream->buf_start = stream->buf_ptr = stream->buf[0]; stream->pushback = NULL; stream->buf_in_use = 0; stream->eof = 0; if ((stream->fd = open(file, O_RDONLY)) == -1) { if (ctx->verbose) { switch (errno) { case EACCES: case EPERM: syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "access to %s not permitted", file); break; case EISDIR: syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "%s is directory", file); break; #if HAVE_DECL_ELOOP case ELOOP: syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "%s refers to an inresolvable symbolic link", file); break; #endif case EMFILE: syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "too many opened files"); break; case ENFILE: syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "too many opened files within this system"); break; case ENOENT: syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "%s does not exist", file); break; case ENOMEM: syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "kernel resource exhausted"); break; #if HAVE_DECL_EOVERFLOW case EOVERFLOW: syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "%s is too big", file); break; #endif default: syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "unknown error while opening %s", file); break; } } return PAM_MYSQL_ERR_IO; } return PAM_MYSQL_ERR_SUCCESS; } /** * Close a stream. * * @param pam_mysql_stream_t *stream * Pointer to the stream data structure. */ static void pam_mysql_stream_close(pam_mysql_stream_t *stream) { if (stream->fd != -1) { close(stream->fd); } } /** * Get a single character from a stream. * * @param pam_mysql_stream_t *stream * Pointer to the stream data structure. * @param int *retval * Pointer to the int where the index should be stored. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_stream_getc(pam_mysql_stream_t *stream, int *retval) { if (stream->buf_ptr >= stream->buf_end) { ssize_t new_buf_len; unsigned char *new_buf = stream->buf[1 - stream->buf_in_use]; if (stream->pushback != NULL) { stream->buf_end = stream->pushback; stream->pushback = NULL; } else { if (stream->eof) { return PAM_MYSQL_ERR_EOF; } if ((new_buf_len = read(stream->fd, new_buf, sizeof(stream->buf[0]))) == -1) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "I/O error"); return PAM_MYSQL_ERR_IO; } if (new_buf_len == 0) { stream->eof = 1; return PAM_MYSQL_ERR_EOF; } stream->buf_end = new_buf + new_buf_len; } stream->buf_start = stream->buf_ptr = new_buf; stream->buf_in_use = 1 - stream->buf_in_use; } *retval = *(stream->buf_ptr++); return PAM_MYSQL_ERR_SUCCESS; } /** * Put back a character to the stream. * * @param pam_mysql_stream_t *stream * Pointer to the stream structure. * @param int c * The character to 'return' to the stream. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_stream_ungetc(pam_mysql_stream_t *stream, int c) { if (stream->buf_ptr == stream->buf_start) { if (stream->pushback != NULL) { return PAM_MYSQL_ERR_IO; } stream->pushback = stream->buf_end; stream->buf_in_use = 1 - stream->buf_in_use; stream->buf_start = stream->buf[stream->buf_in_use]; stream->buf_ptr = stream->buf_start + sizeof(stream->buf[0]); } *(--stream->buf_ptr) = c; return PAM_MYSQL_ERR_SUCCESS; } /* pam_mysql_stream_skip_spn */ /** * Skip instances of a list of characters in the input stream. * * @param pam_mysql_stream_t *stream * A pointer to the stream data structure. * @param const char *accept_cset * A pointer to the set of characters to be skipped. * @param size_t naccepts * The length of the list of characters to skip. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_stream_skip_spn(pam_mysql_stream_t *stream, const char *accept_cset, size_t naccepts) { unsigned char *p; if (stream->eof) { return PAM_MYSQL_ERR_EOF; } if ((p = (unsigned char *)memspn(stream->buf_ptr, stream->buf_end - stream->buf_ptr, (const unsigned char *)accept_cset, naccepts)) != NULL) { stream->buf_ptr = p; return PAM_MYSQL_ERR_SUCCESS; } if (stream->pushback != NULL) { stream->buf_in_use = 1 - stream->buf_in_use; stream->buf_start = stream->buf_ptr = stream->buf[stream->buf_in_use]; stream->buf_end = stream->pushback; stream->pushback = NULL; if ((p = (unsigned char *)memspn(stream->buf_ptr, stream->buf_end - stream->buf_ptr, (const unsigned char *)accept_cset, naccepts)) != NULL) { stream->buf_ptr = p; return PAM_MYSQL_ERR_SUCCESS; } } for (;;) { ssize_t new_buf_len; if ((new_buf_len = read(stream->fd, stream->buf_start, sizeof(stream->buf[0]))) == -1) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "I/O error"); return PAM_MYSQL_ERR_IO; } if (new_buf_len == 0) { stream->eof = 1; return PAM_MYSQL_ERR_EOF; } stream->buf_end = stream->buf_start + new_buf_len; if ((p = (unsigned char *)memspn(stream->buf_start, new_buf_len, (const unsigned char *)accept_cset, naccepts)) != NULL) { stream->buf_ptr = p; break; } } return PAM_MYSQL_ERR_SUCCESS; } /** * Read a stream until a character in the list of delimiters is found, and * append the substr to an existing string. * * @param pam_mysql_stream_t *stream * A pointer to the stream data structure. * @param pam_mysql_str_t *append_to * A pointer to the string to which data should be appended. * @param int *found_delim * A pointer to the delimiter that was found. * @param const char *delims * A pointer to the list of delimiters to match. * @param size_t ndelims * The number of items in the delims list. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_stream_read_cspn(pam_mysql_stream_t *stream, pam_mysql_str_t *append_to, int *found_delim, const char *delims, size_t ndelims) { pam_mysql_err_t err; unsigned char *p; ssize_t len; size_t rem_len; if (stream->eof) { return PAM_MYSQL_ERR_EOF; } if ((p = (unsigned char *)memcspn(stream->buf_ptr, stream->buf_end - stream->buf_ptr, (const unsigned char *)delims, ndelims)) != NULL) { if ((err = pam_mysql_str_append(append_to, (char *)stream->buf_ptr, p - stream->buf_ptr))) { return err; } *found_delim = *p; stream->buf_ptr = p; return PAM_MYSQL_ERR_SUCCESS; } if ((err = pam_mysql_str_append(append_to, (char *)stream->buf_ptr, stream->buf_end - stream->buf_ptr))) { return err; } if (stream->pushback != NULL) { stream->buf_in_use = 1 - stream->buf_in_use; stream->buf_start = stream->buf_ptr = stream->buf[stream->buf_in_use]; stream->buf_end = stream->pushback; stream->pushback = NULL; if ((p = (unsigned char *)memcspn(stream->buf_ptr, stream->buf_end - stream->buf_ptr, (const unsigned char *)delims, ndelims)) != NULL) { if ((err = pam_mysql_str_append(append_to, (char *)stream->buf_ptr, p - stream->buf_ptr))) { return err; } *found_delim = *p; stream->buf_ptr = p; return PAM_MYSQL_ERR_SUCCESS; } if ((err = pam_mysql_str_append(append_to, (char *)stream->buf_ptr, stream->buf_end - stream->buf_ptr))) { return err; } } rem_len = 0; for (;;) { unsigned char *block; if ((err = pam_mysql_str_reserve(append_to, sizeof(stream->buf[0]) - rem_len))) { return err; } block = (unsigned char*)append_to->p + append_to->len; if ((len = read(stream->fd, block, sizeof(stream->buf[0]))) == -1) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "I/O error"); return PAM_MYSQL_ERR_IO; } if (len == 0) { stream->eof = 1; return PAM_MYSQL_ERR_EOF; } if ((p = (unsigned char *)memcspn(block, len, (const unsigned char *)delims, ndelims)) != NULL) { size_t new_buf_len; append_to->len += p - block; new_buf_len = len - (p - block); memcpy(stream->buf_start, p, new_buf_len); stream->buf_ptr = stream->buf_start; stream->buf_end = stream->buf_start + new_buf_len; break; } append_to->len += len; rem_len = sizeof(stream->buf[0]) - len; } *found_delim = *p; append_to->p[append_to->len] = '\0'; return PAM_MYSQL_ERR_SUCCESS; } /* config file scanner / parser */ static const char * pam_mysql_config_token_name[] = { "=", "", "", ";", "", NULL }; /** * Initialise the scanner data structure. * * @param pam_mysql_config_scanner_t *scanner * A pointer to the scanner data structure. * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param pam_mysql_stream_t *stream * A pointer to the stream data structure. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_config_scanner_init( pam_mysql_config_scanner_t *scanner, pam_mysql_ctx_t *ctx, pam_mysql_stream_t *stream) { pam_mysql_err_t err; if ((err = pam_mysql_str_init(&scanner->image, 1))) { return err; } scanner->ctx = ctx; scanner->stream = stream; scanner->state = 0; return PAM_MYSQL_ERR_SUCCESS; } /** * Destroy a scanner data structure. * * @param pam_mysql_config_scanner_t *scanner * A pointer to the scanner data structure. */ static void pam_mysql_config_scanner_destroy( pam_mysql_config_scanner_t *scanner) { pam_mysql_str_destroy(&scanner->image); } /** * Get the next token in a stream. * * @param pam_mysql_config_scanner_t *scanner * A pointer to the scanner data structure. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_config_scanner_next_token( pam_mysql_config_scanner_t *scanner) { pam_mysql_err_t err; int c; switch (scanner->state) { case 0: if ((err = pam_mysql_str_truncate(&scanner->image, 0))) { return err; } if ((err = pam_mysql_stream_getc(scanner->stream, &c))) { return err; } if (c == '#') { if ((err = pam_mysql_str_append_char(&scanner->image, c))) { return err; } if ((err = pam_mysql_stream_read_cspn(scanner->stream, &scanner->image, &c, "\n\r", sizeof("\n\r") - 1))) { return err; } scanner->token = PAM_MYSQL_CONFIG_TOKEN_COMMENT; if ((err = pam_mysql_stream_getc(scanner->stream, &c))) { return err; } if (c == '\r') { if ((err = pam_mysql_stream_getc(scanner->stream, &c))) { return err; } if (c != '\n') { if ((err = pam_mysql_stream_ungetc(scanner->stream, c))) { return err; } } } break; } else { if ((err = pam_mysql_stream_ungetc(scanner->stream, c))) { return err; } } case 1: if ((err = pam_mysql_stream_skip_spn(scanner->stream, " \t", sizeof(" \t") - 1))) { return err; } if ((err = pam_mysql_str_truncate(&scanner->image, 0))) { return err; } if ((err = pam_mysql_stream_getc(scanner->stream, &c))) { return err; } if ((err = pam_mysql_str_append_char(&scanner->image, c))) { return err; } switch (scanner->image.p[0]) { case '=': scanner->token = PAM_MYSQL_CONFIG_TOKEN_EQUAL; scanner->state = 2; break; case ';': scanner->token = PAM_MYSQL_CONFIG_TOKEN_SEMICOLON; scanner->state = 1; break; default: if ((err = pam_mysql_stream_read_cspn(scanner->stream, &scanner->image, &c, "=; \t\n\r", sizeof("=; \t\n\r") - 1))) { return err; } scanner->token = PAM_MYSQL_CONFIG_TOKEN_STRING; scanner->state = 1; break; case '\r': if ((err = pam_mysql_stream_getc(scanner->stream, &c))) { return err; } if (c != '\n') { if ((err = pam_mysql_stream_ungetc(scanner->stream, c))) { return err; } } else { if ((err = pam_mysql_str_append_char(&scanner->image, c))) { return err; } } scanner->token = PAM_MYSQL_CONFIG_TOKEN_NEWLINE; scanner->state = 0; break; case '\n': scanner->token = PAM_MYSQL_CONFIG_TOKEN_NEWLINE; scanner->state = 0; break; } break; case 2: if ((err = pam_mysql_stream_skip_spn(scanner->stream, " \t", sizeof(" \t") - 1))) { return err; } if ((err = pam_mysql_str_truncate(&scanner->image, 0))) { return err; } if ((err = pam_mysql_stream_getc(scanner->stream, &c))) { return err; } if ((err = pam_mysql_str_append_char(&scanner->image, c))) { return err; } switch (scanner->image.p[0]) { case ';': scanner->token = PAM_MYSQL_CONFIG_TOKEN_SEMICOLON; scanner->state = 1; break; default: if ((err = pam_mysql_stream_read_cspn(scanner->stream, &scanner->image, &c, ";\n\r", sizeof(";\n\r") - 1))) { return err; } scanner->token = PAM_MYSQL_CONFIG_TOKEN_STRING; break; case '\r': if ((err = pam_mysql_stream_getc(scanner->stream, &c))) { return err; } if (c != '\n') { if ((err = pam_mysql_stream_ungetc(scanner->stream, c))) { return err; } } else { if ((err = pam_mysql_str_append_char(&scanner->image, c))) { return err; } } scanner->token = PAM_MYSQL_CONFIG_TOKEN_NEWLINE; scanner->state = 0; break; case '\n': scanner->token = PAM_MYSQL_CONFIG_TOKEN_NEWLINE; scanner->state = 0; break; } break; } return PAM_MYSQL_ERR_SUCCESS; } /** * Initialise the config parser data structure. * * pam_mysql_config_parser_t *parser * A pointer to the parser data structure. * pam_mysql_ctx_t *ctx * A pointer to the context data structure. * pam_mysql_entry_handler_t *hdlr * A pointer to the handler data structure. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_config_parser_init( pam_mysql_config_parser_t *parser, pam_mysql_ctx_t *ctx, pam_mysql_entry_handler_t *hdlr) { parser->ctx = ctx; parser->hdlr = hdlr; return PAM_MYSQL_ERR_SUCCESS; } /** * Destroy the config parser data structure. * * @param pam_mysql_config_parser_t *parser * A pointer to the parser data structure. */ static void pam_mysql_config_parser_destroy(pam_mysql_config_parser_t *parser) { // Do nothing as it's a stack variable in the caller. } /** * Parse a configuration file. * * @param pam_mysql_config_parser_t *parser * A pointer to the parser data structure. * @param pam_mysql_stream_t *stream * A pointer to the stream data structure. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_config_parser_parse( pam_mysql_config_parser_t *parser, pam_mysql_stream_t *stream) { pam_mysql_err_t err; pam_mysql_config_scanner_t scanner; char *name = NULL; size_t name_len = 0; char *value = NULL; size_t value_len = 0; int state = 0; int line_num = 1; if ((err = pam_mysql_config_scanner_init(&scanner, parser->ctx, stream))) { return err; } while (!(err = pam_mysql_config_scanner_next_token(&scanner))) { switch (state) { case 0: switch (scanner.token) { case PAM_MYSQL_CONFIG_TOKEN_STRING: if (name == NULL || name_len < scanner.image.len) { char *new_buf; if (NULL == (new_buf = xrealloc(name, scanner.image.len + 1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } name = new_buf; } memcpy(name, scanner.image.p, scanner.image.len + 1); name_len = scanner.image.len; state = 1; break; case PAM_MYSQL_CONFIG_TOKEN_NEWLINE: line_num++; break; case PAM_MYSQL_CONFIG_TOKEN_COMMENT: line_num++; break; default: err = PAM_MYSQL_ERR_SYNTAX; goto out; } break; case 1: switch (scanner.token) { case PAM_MYSQL_CONFIG_TOKEN_EQUAL: state = 2; break; default: err = PAM_MYSQL_ERR_SYNTAX; goto out; } break; case 2: switch (scanner.token) { case PAM_MYSQL_CONFIG_TOKEN_SEMICOLON: if (parser->hdlr && (err = parser->hdlr->handle_entry_fn( parser->hdlr, line_num, name, name_len, "", 0))) { goto out; } state = 4; break; case PAM_MYSQL_CONFIG_TOKEN_NEWLINE: if (parser->hdlr && (err = parser->hdlr->handle_entry_fn( parser->hdlr, line_num, name, name_len, "", 0))) { goto out; } line_num++; state = 0; break; case PAM_MYSQL_CONFIG_TOKEN_STRING: if (value == NULL || value_len < scanner.image.len) { char *new_buf; if (NULL == (new_buf = xrealloc(value, scanner.image.len + 1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } value = new_buf; } memcpy(value, scanner.image.p, scanner.image.len + 1); value_len = scanner.image.len; if (parser->hdlr && (err = parser->hdlr->handle_entry_fn( parser->hdlr, line_num, name, name_len, value, value_len))) { goto out; } state = 3; break; default: err = PAM_MYSQL_ERR_SYNTAX; goto out; } break; case 3: switch (scanner.token) { case PAM_MYSQL_CONFIG_TOKEN_SEMICOLON: state = 4; break; case PAM_MYSQL_CONFIG_TOKEN_NEWLINE: line_num++; state = 0; break; default: err = PAM_MYSQL_ERR_SYNTAX; goto out; } break; case 4: switch (scanner.token) { case PAM_MYSQL_CONFIG_TOKEN_NEWLINE: line_num++; state = 0; break; default: err = PAM_MYSQL_ERR_SYNTAX; goto out; } break; } } if (err == PAM_MYSQL_ERR_EOF) { err = PAM_MYSQL_ERR_SUCCESS; } out: if (err == PAM_MYSQL_ERR_SYNTAX) { if (parser->ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "unexpected token %s on line %d\n", pam_mysql_config_token_name[scanner.token], line_num); } } if (name != NULL) { xfree(name); } if (value != NULL) { xfree(value); } pam_mysql_config_scanner_destroy(&scanner); return err; } /** * Find an option with the specified name. * * @param pam_mysql_option_t *options * The list of defined options. * @param const char *name * A pointer to the string being sought. * @param size_t name_len * The length of the string being matched. * * @return mixed * A pointer to the option data structure or NULL if no match is found. */ pam_mysql_option_t *pam_mysql_find_option(pam_mysql_option_t *options, const char *name, size_t name_len) { /* set the various ctx */ pam_mysql_option_t *retval; for (retval = options; retval->name != NULL; retval++) { if (retval->name_len == name_len && memcmp(retval->name, name, name_len) == 0) { return retval; } } return NULL; } /* entry handler */ static pam_mysql_option_t pam_mysql_entry_handler_options[] = { PAM_MYSQL_DEF_OPTION2(users.host, host, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.database, db, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.db_user, user, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.db_passwd, passwd, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.where_clause, where, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.table, table, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.update_table, update_table, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.user_column, usercolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.password_column, passwdcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.status_column, statcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.password_crypt, crypt_type, &pam_mysql_crypt_opt_accr), PAM_MYSQL_DEF_OPTION2(users.use_md5, md5, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION2(verbose, verbose, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION2(log.enabled, sqllog, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION2(log.table, logtable, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(log.message_column, logmsgcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(log.pid_column, logpidcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(log.user_column, logusercolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(log.host_column, loghostcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(log.rhost_column, logrhostcolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(log.time_column, logtimecolumn, &pam_mysql_string_opt_accr), PAM_MYSQL_DEF_OPTION2(users.use_323_password, use_323_passwd, &pam_mysql_boolean_opt_accr), PAM_MYSQL_DEF_OPTION2(users.disconnect_every_operation, disconnect_every_op, &pam_mysql_boolean_opt_accr), { NULL, 0, 0, NULL } }; /** * Handle one option in the configuration file. * * @param pam_mysql_entry_handler_t *hdlr * A pointer to the handler data structure. * @param int line_num * The current line number in the configuration file. * @param const char *name * The name of the option being sought. * @param size_t name_len * The length of the name string. * @param const char *value * A pointer to the value being given for the option. * @param size_t value_len * The length of the value string. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_handle_entry( pam_mysql_entry_handler_t *hdlr, int line_num, const char *name, size_t name_len, const char *value, size_t value_len) { pam_mysql_err_t err; pam_mysql_option_t *opt = pam_mysql_find_option(hdlr->options, name, name_len); if (opt == NULL) { if (hdlr->ctx->verbose) { char buf[1024]; strnncpy(buf, sizeof(buf), name, name_len); syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "unknown option %s on line %d", buf, line_num); } return PAM_MYSQL_ERR_SUCCESS; } err = opt->accessor->set_op((void*)((char *)hdlr->ctx + opt->offset), value); if (!err && hdlr->ctx->verbose) { char buf[1024]; strnncpy(buf, sizeof(buf), name, name_len); syslog(LOG_AUTHPRIV | LOG_INFO, PAM_MYSQL_LOG_PREFIX "option %s is set to \"%s\"", buf, value); } return err; } /** * Initialise the handler data structure. * * @param pam_mysql_entry_handler_t *hdlr * A pointer to the handler data structure. * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_entry_handler_init( pam_mysql_entry_handler_t *hdlr, pam_mysql_ctx_t *ctx) { hdlr->handle_entry_fn = pam_mysql_handle_entry; hdlr->ctx = ctx; hdlr->options = pam_mysql_entry_handler_options; return PAM_MYSQL_ERR_SUCCESS; } /** * Destroy the handler data structure. * * @param pam_mysql_entry_handler_t *hdlr * A pointer to the handler data structure. */ static void pam_mysql_entry_handler_destroy( pam_mysql_entry_handler_t *hdlr) { // Do nothing - stack variable. } /** * Convert a host name to an IP address. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param const char **pretval * A pointer to the address of a string. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_get_host_info(pam_mysql_ctx_t *ctx, const char **pretval) { char hostname[MAXHOSTNAMELEN + 1]; char *retval; if (ctx->my_host_info) { *pretval = ctx->my_host_info; return PAM_MYSQL_ERR_SUCCESS; } if (gethostname(hostname, sizeof(hostname))) { return PAM_MYSQL_ERR_UNKNOWN; } #ifdef HAVE_GETADDRINFO { struct addrinfo *ainfo = NULL; static const struct addrinfo hint = { AI_CANONNAME, PF_INET, 0, 0, 0, NULL, NULL, NULL }; switch (getaddrinfo(hostname, NULL, &hint, &ainfo)) { case 0: break; case EAI_MEMORY: return PAM_MYSQL_ERR_ALLOC; default: return PAM_MYSQL_ERR_UNKNOWN; } switch (ainfo->ai_family) { case AF_INET: if (NULL == (retval = xcalloc(INET_ADDRSTRLEN, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); freeaddrinfo(ainfo); return PAM_MYSQL_ERR_ALLOC; } if (!inet_ntop(AF_INET, &((struct sockaddr_in *)ainfo->ai_addr)->sin_addr, retval, INET_ADDRSTRLEN)) { xfree(retval); freeaddrinfo(ainfo); return PAM_MYSQL_ERR_UNKNOWN; } break; #ifdef HAVE_IPV6 case AF_INET6: if (NULL == (retval = xcalloc(INET6_ADDRSTRLEN,sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); freeaddrinfo(ainfo); return PAM_MYSQL_ERR_ALLOC; } if (!inet_ntop(AF_INET6, &((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_addr, retval, INET6_ADDRSTRLEN)) { xfree(retval); freeaddrinfo(ainfo); return PAM_MYSQL_ERR_UNKNOWN; } break; #endif /* HAVE_IPV6 */ default: freeaddrinfo(ainfo); return PAM_MYSQL_ERR_NOTIMPL; } freeaddrinfo(ainfo); } #else { struct hostent *hent = NULL; struct hostent *tmp; size_t hent_size = sizeof(struct hostent) + 64; int herr = 0; #if defined(HAVE_SUNOS_GETHOSTBYNAME_R) for (;;) { if (NULL == (tmp = xrealloc(hent, hent_size, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); if (hent != NULL) { xfree(hent); } return PAM_MYSQL_ERR_ALLOC; } hent = tmp; if (gethostbyname_r(hostname, hent, (char *)hent + sizeof(struct hostent), hent_size - sizeof(struct hostent), &herr) != NULL) { break; } if (errno != ERANGE) { xfree(hent); return PAM_MYSQL_ERR_UNKNOWN; } if (hent_size + 32 < hent_size) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); xfree(hent); return PAM_MYSQL_ERR_ALLOC; } hent_size += 32; } #elif defined(HAVE_GNU_GETHOSTBYNAME_R) for (;;) { if (NULL == (tmp = xrealloc(hent, hent_size, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); if (hent != NULL) { xfree(hent); } return PAM_MYSQL_ERR_ALLOC; } hent = tmp; if (!gethostbyname_r(hostname, hent, (char *)hent + sizeof(struct hostent), hent_size - sizeof(struct hostent), &tmp, &herr)) { break; } if (errno != ERANGE) { xfree(hent); return PAM_MYSQL_ERR_UNKNOWN; } if (hent_size + 32 < hent_size) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); xfree(hent); return PAM_MYSQL_ERR_ALLOC; } hent_size += 32; } #else return PAM_MYSQL_ERR_NOTIMPL; #endif switch (hent->h_addrtype) { case AF_INET: if (NULL == (retval = xcalloc(INET_ADDRSTRLEN, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); xfree(hent); return PAM_MYSQL_ERR_ALLOC; } if (!inet_ntop(AF_INET, hent->h_addr_list[0], retval, INET_ADDRSTRLEN)) { xfree(retval); xfree(hent); return PAM_MYSQL_ERR_UNKNOWN; } break; #ifdef HAVE_IPV6 case AF_INET6: if (NULL == (retval = xcalloc(INET6_ADDRSTRLEN,sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); xfree(hent); return PAM_MYSQL_ERR_ALLOC; } if (!inet_ntop(AF_INET6, hent->h_addr_list[0], retval, INET6_ADDRSTRLEN)) { xfree(retval); xfree(hent); return PAM_MYSQL_ERR_UNKNOWN; } break; #endif /* HAVE_IPV6 */ default: xfree(hent); return PAM_MYSQL_ERR_NOTIMPL; } xfree(hent); } #endif /* HAVE_GETADDRINFO */ *pretval = ctx->my_host_info = retval; return PAM_MYSQL_ERR_SUCCESS; } /** * Initialise the context data structure. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_init_ctx(pam_mysql_ctx_t *ctx) { ctx->mysql_hdl = NULL; ctx->host = NULL; ctx->where = NULL; ctx->db = NULL; ctx->user = NULL; ctx->passwd = NULL; ctx->table = NULL; ctx->update_table =NULL; ctx->usercolumn = NULL; ctx->passwdcolumn = NULL; ctx->statcolumn = xstrdup("0"); ctx->crypt_type = 0; ctx->use_323_passwd = 0; ctx->md5 = -1; ctx->sqllog = 0; ctx->verbose = 0; ctx->use_first_pass = 0; ctx->try_first_pass = 1; ctx->disconnect_every_op = 0; ctx->logtable = NULL; ctx->logmsgcolumn = NULL; ctx->logpidcolumn = NULL; ctx->logusercolumn = NULL; ctx->loghostcolumn = NULL; ctx->logrhostcolumn = NULL; ctx->logtimecolumn = NULL; ctx->config_file = NULL; ctx->my_host_info = NULL; return PAM_MYSQL_ERR_SUCCESS; } /** * Destroy the context data structure. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. */ static void pam_mysql_destroy_ctx(pam_mysql_ctx_t *ctx) { if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_destroy_ctx() called."); } pam_mysql_close_db(ctx); xfree(ctx->host); ctx->host = NULL; xfree(ctx->where); ctx->where = NULL; xfree(ctx->db); ctx->db = NULL; xfree(ctx->user); ctx->user = NULL; xfree(ctx->passwd); ctx->passwd = NULL; xfree(ctx->table); ctx->table = NULL; xfree(ctx->update_table); ctx->update_table = NULL; xfree(ctx->usercolumn); ctx->usercolumn = NULL; xfree(ctx->passwdcolumn); ctx->passwdcolumn = NULL; xfree(ctx->statcolumn); ctx->statcolumn = NULL; xfree(ctx->logtable); ctx->logtable = NULL; xfree(ctx->logmsgcolumn); ctx->logmsgcolumn = NULL; xfree(ctx->logpidcolumn); ctx->logpidcolumn = NULL; xfree(ctx->logusercolumn); ctx->logusercolumn = NULL; xfree(ctx->loghostcolumn); ctx->loghostcolumn = NULL; xfree(ctx->logrhostcolumn); ctx->logrhostcolumn = NULL; xfree(ctx->logtimecolumn); ctx->logtimecolumn = NULL; xfree(ctx->config_file); ctx->config_file = NULL; xfree(ctx->my_host_info); ctx->my_host_info = NULL; } /** * Free a context data structure. * * @param pam_mysql_ctx_t *ctx * A pointer to a context data structure. */ static void pam_mysql_release_ctx(pam_mysql_ctx_t *ctx) { if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_release_ctx() called."); } if (ctx != NULL) { pam_mysql_destroy_ctx(ctx); xfree(ctx); } } /** * Cleanup everything. * * @param pam_handle_t *pamh * A pointer to the pam_handle_t data structure. * @param void *voiddata * A pointer to our context data structure. * @param int status * The unused status value from PAM. */ static void pam_mysql_cleanup_hdlr(pam_handle_t *pamh, void * voiddata, int status) { pam_mysql_release_ctx((pam_mysql_ctx_t*)voiddata); } /** * Retrieve context information from PAM. * * @param pam_mysql_ctx_t **pretval * A pointer to a data provided by PAM. * @param pam_handle_t *pamh * A pointer to the pam data structure handle. * * @return pam_mysql_err_t * Indication of success or failure. */ pam_mysql_err_t pam_mysql_retrieve_ctx(pam_mysql_ctx_t **pretval, pam_handle_t *pamh) { pam_mysql_err_t err; switch (pam_get_data(pamh, PAM_MODULE_NAME, (PAM_GET_DATA_CONST void**)pretval)) { case PAM_NO_MODULE_DATA: *pretval = NULL; break; case PAM_SUCCESS: break; default: return PAM_MYSQL_ERR_UNKNOWN; } if (*pretval == NULL) { /* allocate global data space */ if (NULL == (*pretval = (pam_mysql_ctx_t*)xcalloc(1, sizeof(pam_mysql_ctx_t)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_ALLOC; } /* give the data back to PAM for management */ if (pam_set_data(pamh, PAM_MODULE_NAME, (void*)*pretval, pam_mysql_cleanup_hdlr)) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "failed to set context to PAM at " __FILE__ ":%d", __LINE__); xfree(*pretval); *pretval = NULL; return PAM_MYSQL_ERR_UNKNOWN; } if ((err = pam_mysql_init_ctx(*pretval))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "cannot initialize context at " __FILE__ ":%d", __LINE__); pam_mysql_destroy_ctx(*pretval); xfree(*pretval); *pretval = NULL; return err; } } return PAM_MYSQL_ERR_SUCCESS; } /** * Set an option. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param const char *name * A pointer to the name of the option being set. * @param size_t name_len * The length of the name being set. * @param const char *val * A pointer to the new value of the option. * * @return pam_mysql_err_t * Indication of success or failure. */ pam_mysql_err_t pam_mysql_set_option(pam_mysql_ctx_t *ctx, const char *name, size_t name_len, const char *val) { pam_mysql_option_t *opt = pam_mysql_find_option(options, name, name_len); if (opt == NULL) { if (ctx->verbose) { char buf[1024]; strnncpy(buf, sizeof(buf), name, name_len); syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "unknown option: %s", buf); } return PAM_MYSQL_ERR_NO_ENTRY; } return opt->accessor->set_op((void *)((char *)ctx + opt->offset), val); } /** * Get an option. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param const char **pretval * A pointer to the string (will be updated to point to the value) * @param int *to_release * A pointer to an int controlling whether the caller releases *pretval. * @param const char *name * A pointer to the name of the option being set. * @param size_t name_len * The length of the name being set. * * @return pam_mysql_err_t * Indication of success or failure. */ pam_mysql_err_t pam_mysql_get_option(pam_mysql_ctx_t *ctx, const char **pretval, int *to_release, const char *name, size_t name_len) { pam_mysql_option_t *opt = pam_mysql_find_option(options, name, name_len); if (opt == NULL) { if (ctx->verbose) { char buf[1024]; strnncpy(buf, sizeof(buf), name, name_len); syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "unknown option: %s", buf); } return PAM_MYSQL_ERR_NO_ENTRY; } return opt->accessor->get_op((void *)((char *)ctx + opt->offset), pretval, to_release); } /** * Parse arguments. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param int argc * The number of arguments. * @param const char **argv * A pointer to the string containing arguments. * * @return pam_mysql_err_t * Indication of success or failure. */ pam_mysql_err_t pam_mysql_parse_args(pam_mysql_ctx_t *ctx, int argc, const char **argv) { pam_mysql_err_t err; int param_changed = 0; char *value = NULL; int i; /* process all the arguments */ for (i = 0; i < argc; i++) { const char *name = argv[i]; size_t name_len; if ((value = strchr(name, '=')) != NULL) { name_len = (size_t)(value - name); value++; /* get past the '=' */ } else { name_len = strlen(name); value = ""; } err = pam_mysql_set_option(ctx, name, name_len, value); if (err == PAM_MYSQL_ERR_NO_ENTRY) { continue; } else if (err) { return err; } param_changed = 1; if (ctx->verbose) { char buf[1024]; strnncpy(buf, sizeof(buf), name, name_len); syslog(LOG_AUTHPRIV | LOG_INFO, PAM_MYSQL_LOG_PREFIX "option %s is set to \"%s\"", buf, value); } } /* close the database in case we get new args */ if (param_changed) { pam_mysql_close_db(ctx); } return PAM_MYSQL_ERR_SUCCESS; } /** * Read a configuration file. * * @param pam_mysql_ctx_t *ctx * A pointer to a context data structure. * @param const char *path * The path to the configuration file. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_read_config_file(pam_mysql_ctx_t *ctx, const char *path) { pam_mysql_err_t err; pam_mysql_entry_handler_t handler; pam_mysql_config_parser_t parser; pam_mysql_stream_t stream; if ((err = pam_mysql_entry_handler_init(&handler, ctx))) { return err; } if ((err = pam_mysql_stream_open(&stream, ctx, path))) { pam_mysql_entry_handler_destroy(&handler); return err; } if ((err = pam_mysql_config_parser_init(&parser, ctx, &handler))) { pam_mysql_stream_close(&stream); pam_mysql_entry_handler_destroy(&handler); return err; } err = pam_mysql_config_parser_parse(&parser, &stream); pam_mysql_config_parser_destroy(&parser); pam_mysql_stream_close(&stream); pam_mysql_entry_handler_destroy(&handler); return err; } /** * Attempt to open a connection to the database server. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_open_db(pam_mysql_ctx_t *ctx) { pam_mysql_err_t err; char *host = NULL; char *socket = NULL; int port = 0; if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_open_db() called."); } if (ctx->mysql_hdl != NULL) { return PAM_MYSQL_ERR_BUSY; } if (NULL == (ctx->mysql_hdl = xcalloc(1, sizeof(MYSQL)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_ALLOC; } if (ctx->user == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "required option \"user\" is not set"); return PAM_MYSQL_ERR_INVAL; } if (ctx->db == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "required option \"db\" is not set"); return PAM_MYSQL_ERR_INVAL; } if (ctx->host != NULL) { if (ctx->host[0] == '/') { host = NULL; socket = ctx->host; } else { char *p; if ((p = strchr(ctx->host, ':')) != NULL) { size_t len = (size_t)(p - ctx->host); if (NULL == (host = xcalloc(len + 1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_ALLOC; } memcpy(host, ctx->host, len); host[len] = '\0'; port = strtol(p + 1, NULL, 10); } else { host = ctx->host; } socket = NULL; } } if (NULL == mysql_init(ctx->mysql_hdl)) { err = PAM_MYSQL_ERR_ALLOC; goto out; } if (NULL == mysql_real_connect(ctx->mysql_hdl, host, ctx->user, (ctx->passwd == NULL ? "": ctx->passwd), ctx->db, port, socket, 0)) { err = PAM_MYSQL_ERR_DB; goto out; } if (mysql_select_db(ctx->mysql_hdl, ctx->db)) { err = PAM_MYSQL_ERR_DB; goto out; } err = PAM_MYSQL_ERR_SUCCESS; out: if (err == PAM_MYSQL_ERR_DB) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "MySQL error (%s)\n", mysql_error(ctx->mysql_hdl)); } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_open_db() returning %d.", err); } if (host != ctx->host) { xfree(host); } return err; } /** * Close a connection to the database. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. */ static void pam_mysql_close_db(pam_mysql_ctx_t *ctx) { if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_close_db() called."); } if (ctx->mysql_hdl == NULL) { return; /* closed already */ } mysql_close(ctx->mysql_hdl); mysql_library_end(); xfree(ctx->mysql_hdl); ctx->mysql_hdl = NULL; } /** * Escape a string and append it to another. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param pam_mysql_str_t *append_to * The string to which the escaped text should be appended. * @param const char *val * The string to be escaped and appended. * @param size_t val_len * The length of the string to be escaped and appended. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_quick_escape(pam_mysql_ctx_t *ctx, pam_mysql_str_t *append_to, const char *val, size_t val_len) { size_t len; if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_quick_escape() called."); } if (val_len >= (((size_t)-1)>>1) || pam_mysql_str_reserve(append_to, val_len * 2)) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); return PAM_MYSQL_ERR_ALLOC; } #ifdef HAVE_MYSQL_REAL_ESCAPE_STRING len = mysql_real_escape_string(ctx->mysql_hdl, &append_to->p[append_to->len], val, val_len); #else len = mysql_escape_string(&append_to->p[append_to->len], val, val_len); #endif append_to->p[append_to->len += len] = '\0'; return PAM_MYSQL_ERR_SUCCESS; } /** * Format a string. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param pam_mysql_str_t *pretval * A pointer to the output string. * @param const char *template * The template to which arguments should be applied. * @param int mangle * Unused parameter - va_start just wants to know where args start. * @param mixed * Additional parameters used to replace % macros in the template. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_format_string(pam_mysql_ctx_t *ctx, pam_mysql_str_t *pretval, const char *template, int mangle, ...) { pam_mysql_err_t err = PAM_MYSQL_ERR_SUCCESS; const char *p; const char *name = NULL; const char *commit_ptr; int state; va_list ap; if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_format_string() called"); } va_start(ap, mangle); state = 0; for (commit_ptr = p = template; *p != '\0'; p++) { switch (state) { case 0: if (*p == '%') { if ((err = pam_mysql_str_append(pretval, commit_ptr, (size_t)(p - commit_ptr)))) { goto out; } commit_ptr = p; state = 1; } break; case 1: switch (*p) { case '{': state = 2; break; case '[': state = 4; break; case 's': { const char *val = va_arg(ap, char *); if ((err = pam_mysql_quick_escape(ctx, pretval, val, strlen(val)))) { goto out; } state = 0; commit_ptr = p + 1; } break; case 'S': { const char *val = va_arg(ap, char *); if ((err = pam_mysql_str_append(pretval, val, strlen(val)))) { goto out; } state = 0; commit_ptr = p + 1; } break; case 'u': { char buf[128]; unsigned int val = va_arg(ap, unsigned int); char *q = buf + sizeof(buf); while (--q >= buf) { *q = "0123456789"[val % 10]; val /= 10; if (val == 0) break; } if ((err = pam_mysql_str_append(pretval, q, sizeof(buf) - (size_t)(q - buf)))) { goto out; } state = 0; commit_ptr = p + 1; } break; default: if ((err = pam_mysql_str_append_char(pretval, '%'))) { goto out; } if ((err = pam_mysql_str_append_char(pretval, *p))) { goto out; } state = 0; commit_ptr = p + 1; break; } break; case 2: name = p; state = 3; break; case 3: if (*p == '}') { const char *val; int to_release; if ((err = pam_mysql_get_option(ctx, &val, &to_release, name, (size_t)(p - name)))) { goto out; } if (val == NULL) { val = xstrdup(""); } if ((err = pam_mysql_quick_escape(ctx, pretval, val, strlen(val)))) { if (to_release) { xfree((char *)val); } goto out; } if (to_release) { xfree((char *)val); } state = 0; commit_ptr = p + 1; } break; case 4: name = p; state = 5; break; case 5: if (*p == ']') { const char *val; int to_release; if ((err = pam_mysql_get_option(ctx, &val, &to_release, name, (size_t)(p - name)))) { goto out; } if (val == NULL) { val = xstrdup(""); } if ((err = pam_mysql_str_append(pretval, val, strlen(val)))) { if (to_release) { xfree((char *)val); } goto out; } if (to_release) { xfree((char *)val); } state = 0; commit_ptr = p + 1; } break; } } if (commit_ptr < p) { if ((err = pam_mysql_str_append(pretval, commit_ptr, (size_t)(p - commit_ptr)))) { goto out; } } out: if (err) { pam_mysql_str_destroy(pretval); } va_end(ap); return err; } /** * Check a password. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @ param const char *user * A pointer to the user name string. * @param const char *password * A pointer to the unencrypted password string. * @param int null_inhibited * Whether null authentication tokens should be disallowed. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_check_passwd(pam_mysql_ctx_t *ctx, const char *user, const char *passwd, int null_inhibited) { pam_mysql_err_t err; pam_mysql_str_t query; MYSQL_RES *result = NULL; MYSQL_ROW row; int vresult; if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_check_passwd() called."); } /* To avoid putting a plain password in the MySQL log file and on * the wire more than needed we will request the encrypted password * from MySQL. We will check encrypt the passed password against the * one returned from MySQL. */ if ((err = pam_mysql_str_init(&query, 1))) { return err; } err = pam_mysql_format_string(ctx, &query, (ctx->where == NULL ? "SELECT %[passwdcolumn] FROM %[table] WHERE %[usercolumn] = '%s'": "SELECT %[passwdcolumn] FROM %[table] WHERE %[usercolumn] = '%s' AND (%S)"), 1, user, ctx->where); if (err) { goto out; } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "%s", query.p); } #ifdef HAVE_MYSQL_REAL_QUERY if (mysql_real_query(ctx->mysql_hdl, query.p, query.len)) { #else if (mysql_query(ctx->mysql_hdl, query.p)) { #endif err = PAM_MYSQL_ERR_DB; goto out; } if (NULL == (result = mysql_store_result(ctx->mysql_hdl))) { err = PAM_MYSQL_ERR_DB; goto out; } switch (mysql_num_rows(result)) { case 0: syslog(LOG_AUTHPRIV | LOG_ERR, "%s", PAM_MYSQL_LOG_PREFIX "SELECT returned no result."); err = PAM_MYSQL_ERR_NO_ENTRY; goto out; case 1: break; case 2: syslog(LOG_AUTHPRIV | LOG_ERR, "%s", PAM_MYSQL_LOG_PREFIX "SELECT returned an indetermined result."); err = PAM_MYSQL_ERR_UNKNOWN; goto out; } /* Grab the password from RESULT_SET. */ if (NULL == (row = mysql_fetch_row(result))) { err = PAM_MYSQL_ERR_DB; goto out; } vresult = -1; if (row[0] != NULL) { if (passwd != NULL) { char *crypted_password = NULL; switch (ctx->crypt_type) { /* PLAIN */ case 0: vresult = strcmp(row[0], passwd); break; /* ENCRYPT */ case 1: crypted_password = crypt(passwd, row[0]); if (crypted_password == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "something went wrong when invoking crypt() - %s", strerror(errno)); vresult = 1; // fail } else { vresult = strcmp(row[0], crypted_password); } break; /* PASSWORD */ case 2: { char buf[42]; #ifdef HAVE_MAKE_SCRAMBLED_PASSWORD_323 if (ctx->use_323_passwd) { make_scrambled_password_323(buf, passwd); } else { make_scrambled_password(buf, passwd); } #else make_scrambled_password(buf, passwd); #endif vresult = strcmp(row[0], buf); { char *p = buf - 1; while (*(++p)) *p = '\0'; } } break; /* MD5 hash (not MD5 crypt()) */ case 3: { #ifdef HAVE_PAM_MYSQL_MD5_DATA char buf[33]; pam_mysql_md5_data((unsigned char*)passwd, strlen(passwd), buf); vresult = strcmp(row[0], buf); { char *p = buf - 1; while (*(++p)) *p = '\0'; } #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish MD5 hash is not supported in this build."); #endif } break; case 4: { #ifdef HAVE_PAM_MYSQL_SHA1_DATA char buf[41]; pam_mysql_sha1_data((unsigned char*)passwd, strlen(passwd), buf); vresult = strcmp(row[0], buf); { char *p = buf - 1; while (*(++p)) *p = '\0'; } #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish SHA1 hash is not supported in this build."); #endif } break; case 5: { #if defined(HAVE_PAM_MYSQL_MD5_DATA) && defined(HAVE_PAM_MYSQL_SHA1_DATA) char buf[128]; memset(buf, 0, 128); pam_mysql_drupal7_data((unsigned char*)passwd, strlen(passwd), buf, row[0]); vresult = strcmp(row[0], buf); { char *p = buf - 1; while (*(++p)) *p = '\0'; } #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish MD5 hash or SHA support lacking in this build."); #endif } break; case 6: { /* Joomla 1.5 like password */ #ifdef HAVE_PAM_MYSQL_MD5_DATA char buf[33]; buf[32]=0; char *salt = row[0]; char *hash = strsep(&salt,":"); int len = strlen(passwd)+strlen(salt); char *tmp; if (NULL == (tmp = xcalloc(len+1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } strcat(tmp,passwd); strcat(tmp,salt); pam_mysql_md5_data((unsigned char*)tmp, len, buf); vresult = strcmp(hash, buf); { char *p = buf - 1; while (*(++p)) *p = '\0'; } xfree(tmp); #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish MD5 hash is not supported in this build."); #endif } break; case 7: { /* Salted SHA */ #ifdef HAVE_PAM_MYSQL_SHA1_DATA unsigned char* hash; size_t sha1_size; Base64Decode(row[0], &hash, &sha1_size); size_t salt_length = sha1_size - 20; unsigned char salt[salt_length]; memcpy(salt, &(hash[20]), salt_length); char buf[41]; pam_mysql_ssha_data((unsigned char*)passwd, strlen(passwd), salt, salt_length, buf); vresult = strcmp(row[0], buf); { char *p = buf - 1; while (*(++p)) *p = '\0'; } #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish SSHA hash is not supported in this build."); #endif } break; case 8: { #ifdef HAVE_PAM_MYSQL_SHA512_DATA char buf[128]; pam_mysql_sha512_data((unsigned char*)passwd, strlen(passwd), buf); vresult = strcmp(row[0], buf); { char *p = buf - 1; while (*(++p)) *p = '\0'; } #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish SHA512 hash is not supported in this build."); #endif } break; case 9: { #ifdef HAVE_PAM_MYSQL_SHA256_DATA char buf[64]; pam_mysql_sha256_data((unsigned char*)passwd, strlen(passwd), buf); vresult = strcmp(row[0], buf); { char *p = buf - 1; while (*(++p)) *p = '\0'; } #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish SHA256 hash is not supported in this build."); #endif } break; default: { } } } } else { vresult = null_inhibited; } if (vresult == 0) { err = PAM_MYSQL_ERR_SUCCESS; } else { err = PAM_MYSQL_ERR_MISMATCH; } out: if (err == PAM_MYSQL_ERR_DB) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "MySQL error(%s)", mysql_error(ctx->mysql_hdl)); } if (result != NULL) { mysql_free_result(result); } pam_mysql_str_destroy(&query); if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_check_passwd() returning %i.", err); } return err; } /** * Create a random salt for use with CRYPT() when changing passwords. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param char *salt * A pointer to the string to be filled. * @param const char *salter * A pointer to the string used to generate the salt. (NB: Pointless if * HAVE_GETTIMEOFDAY is defined). */ static void pam_mysql_saltify(pam_mysql_ctx_t *ctx, char *salt, const char *salter) { unsigned int i = 0; char *q; unsigned int seed = 0; static const char saltstr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./"; if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "saltify called."); } if (salter != NULL) { const char *p; for (p = salter; *p != '\0'; p++) { seed += salter[i]; } } #ifdef HAVE_GETTIMEOFDAY { struct timeval *tv; if (gettimeofday(&tv, NULL)) { tv.tv_usec = 0; } seed = (unsigned int)((tv.tv_usec + j) % 65535); } #endif q = salt; if (ctx->md5) { strcpy(salt, "$1$"); q += sizeof("$1$") - 1; i = 8; } else { i = 2; } while (i-- > 0) { *(q++) = saltstr[seed % 64]; seed = (((seed ^ 0x967e3c5a) << 3) ^ (~(seed >> 2) + i)); } if (ctx->md5) { *(q++) = '$'; } *q = '\0'; if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_saltify() returning salt = %s.", salt); } } /** * Update the password in MySQL. * * To reduce the number of calls to the DB, I'm now assuming that the old * password has been verified elsewhere, so I only check for null/not null * and is_root. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param const char *user * A pointer to the string containing the username. * @param const char *new_passwd * A pointer to the string containing the new password. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_update_passwd(pam_mysql_ctx_t *ctx, const char *user, const char *new_passwd) { pam_mysql_err_t err = PAM_MYSQL_ERR_SUCCESS; pam_mysql_str_t query; char *encrypted_passwd = NULL; if ((err = pam_mysql_str_init(&query, 1))) { return err; } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_update_passwd() called."); } if (user == NULL) { if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "user is NULL."); } syslog(LOG_NOTICE, PAM_MYSQL_LOG_PREFIX "unable to change password"); return PAM_MYSQL_ERR_INVAL; } if (new_passwd != NULL) { switch (ctx->crypt_type) { case 0: if (NULL == (encrypted_passwd = xstrdup(new_passwd))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } break; case 1: { char salt[18]; pam_mysql_saltify(ctx, salt, new_passwd); if (NULL == (encrypted_passwd = xstrdup(crypt(new_passwd, salt)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } } break; case 2: if (NULL == (encrypted_passwd = xcalloc(41 + 1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } #ifdef HAVE_MAKE_SCRAMBLED_PASSWORD_323 if (ctx->use_323_passwd) { make_scrambled_password_323(encrypted_passwd, new_passwd); } else { make_scrambled_password(encrypted_passwd, new_passwd); } #else make_scrambled_password(encrypted_passwd, new_passwd); #endif break; case 3: #ifdef HAVE_PAM_MYSQL_MD5_DATA if (NULL == (encrypted_passwd = xcalloc(32 + 1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } pam_mysql_md5_data((unsigned char*)new_passwd, strlen(new_passwd), encrypted_passwd); #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish MD5 hash is not supported in this build."); err = PAM_MYSQL_ERR_NOTIMPL; goto out; #endif break; case 4: #ifdef HAVE_PAM_MYSQL_SHA1_DATA if (NULL == (encrypted_passwd = xcalloc(40 + 1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } pam_mysql_sha1_data((unsigned char*)new_passwd, strlen(new_passwd), encrypted_passwd); #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish SHA1 hash is not supported in this build."); err = PAM_MYSQL_ERR_NOTIMPL; goto out; #endif break; case 6: { #ifdef HAVE_PAM_MYSQL_MD5_DATA if (NULL == (encrypted_passwd = xcalloc(32 + 1+ 32 +1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } int len=strlen(new_passwd)+32; char *tmp; if (NULL == (tmp = xcalloc(len+1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } char salt[33]; salt[32]=0; srandom(time(NULL)); int i; for(i=0;i<32; i++) salt[i]=(char)((random()/(double)RAND_MAX * 93.0) +33.0); strcat(tmp,new_passwd); strcat(tmp,salt); pam_mysql_md5_data((unsigned char*)tmp, len, encrypted_passwd); xfree(tmp); strcat(encrypted_passwd,":"); strcat(encrypted_passwd,salt); #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish MD5 hash is not supported in this build."); err = PAM_MYSQL_ERR_NOTIMPL; goto out; #endif break; } case 8: { #ifdef HAVE_PAM_MYSQL_SHA512_DATA if (NULL == (encrypted_passwd = xcalloc(128 + 1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } pam_mysql_sha512_data((unsigned char*)new_passwd, strlen(new_passwd), encrypted_passwd); #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish SHA512 hash is not supported in this build."); err = PAM_MYSQL_ERR_NOTIMPL; goto out; #endif break; } case 9: { #ifdef HAVE_PAM_MYSQL_SHA256_DATA if (NULL == (encrypted_passwd = xcalloc(64 + 1, sizeof(char)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } pam_mysql_sha256_data((unsigned char*)new_passwd, strlen(new_passwd), encrypted_passwd); #else syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "non-crypt()ish SHA256 hash is not supported in this build."); err = PAM_MYSQL_ERR_NOTIMPL; goto out; #endif break; } default: encrypted_passwd = NULL; break; } } err = pam_mysql_format_string(ctx, &query, (ctx->where == NULL ? "UPDATE %[table] SET %[passwdcolumn] = '%s' WHERE %[usercolumn] = '%s'": "UPDATE %[table] SET %[passwdcolumn] = '%s' WHERE %[usercolumn] = '%s' AND (%S)"), 1, (encrypted_passwd == NULL ? "": encrypted_passwd), user, ctx->where); if (err) { goto out; } #ifdef HAVE_MYSQL_REAL_QUERY if (mysql_real_query(ctx->mysql_hdl, query.p, query.len)) { #else if (mysql_query(ctx->mysql_hdl, query.p)) { #endif err = PAM_MYSQL_ERR_DB; goto out; } out: if (err == PAM_MYSQL_ERR_DB) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "MySQL error (%s)", mysql_error(ctx->mysql_hdl)); } if (encrypted_passwd != NULL) { char *p; for (p = encrypted_passwd; *p != '\0'; p++) { *p = '\0'; } xfree(encrypted_passwd); } pam_mysql_str_destroy(&query); if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_update_passwd() returning %i.", err); } return err; } /** * Detemine whether a username is known. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param int pretval * A pointer to the result. * @param const char *user * A pointer to the username string to be checked. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_query_user_stat(pam_mysql_ctx_t *ctx, int *pretval, const char *user) { pam_mysql_err_t err = PAM_MYSQL_ERR_SUCCESS; pam_mysql_str_t query; MYSQL_RES *result = NULL; MYSQL_ROW row; if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_query_user_stat() called."); } if ((err = pam_mysql_str_init(&query, 0))) { return err; } err = pam_mysql_format_string(ctx, &query, (ctx->where == NULL ? "SELECT %[statcolumn], %[passwdcolumn] FROM %[table] WHERE %[usercolumn] = '%s'": "SELECT %[statcolumn], %[passwdcolumn] FROM %[table] WHERE %[usercolumn] = '%s' AND (%S)"), 1, user, ctx->where); if (err) { goto out; } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "%s", query.p); } #ifdef HAVE_MYSQL_REAL_QUERY if (mysql_real_query(ctx->mysql_hdl, query.p, query.len)) { #else if (mysql_query(ctx->mysql_hdl, query.p)) { #endif err = PAM_MYSQL_ERR_DB; goto out; } if (NULL == (result = mysql_store_result(ctx->mysql_hdl))) { err = PAM_MYSQL_ERR_DB; goto out; } switch (mysql_num_rows(result)) { case 0: syslog(LOG_AUTHPRIV | LOG_ERR, "%s", PAM_MYSQL_LOG_PREFIX "SELECT returned no result."); err = PAM_MYSQL_ERR_NO_ENTRY; goto out; case 1: break; case 2: syslog(LOG_AUTHPRIV | LOG_ERR, "%s", PAM_MYSQL_LOG_PREFIX "SELECT returned an indetermined result."); err = PAM_MYSQL_ERR_UNKNOWN; goto out; } if (NULL == (row = mysql_fetch_row(result))) { err = PAM_MYSQL_ERR_DB; goto out; } if (row[0] == NULL) { *pretval = PAM_MYSQL_USER_STAT_EXPIRED; } else { *pretval = strtol(row[0], NULL, 10) & ~PAM_MYSQL_USER_STAT_NULL_PASSWD; } if (row[1] == NULL) { *pretval |= PAM_MYSQL_USER_STAT_NULL_PASSWD; } out: if (err == PAM_MYSQL_ERR_DB) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "MySQL error (%s)", mysql_error(ctx->mysql_hdl)); } if (result != NULL) { mysql_free_result(result); } pam_mysql_str_destroy(&query); if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_query_user_stat() returning %i.", err); } return err; } /** * Log a message. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param const char *msg * A pointer to the message to be logged. * @param const char *user * A pointer to the string containing the relevant user name. * @param const char *rhost * A pointer to a string containing the name of the remote host. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_sql_log(pam_mysql_ctx_t *ctx, const char *msg, const char *user, const char *rhost) { pam_mysql_err_t err; pam_mysql_str_t query; const char *host; if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_sql_log() called."); } if ((err = pam_mysql_str_init(&query, 1))) { return err; } if (!ctx->sqllog) { err = PAM_MYSQL_ERR_SUCCESS; goto out; } if (pam_mysql_get_host_info(ctx, &host)) { host = "(unknown)"; } if (ctx->logtable == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, "%s", PAM_MYSQL_LOG_PREFIX "sqllog set but logtable not set"); return PAM_MYSQL_ERR_INVAL; } if (ctx->logmsgcolumn == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, "%s", PAM_MYSQL_LOG_PREFIX "sqllog set but logmsgcolumn not set"); return PAM_MYSQL_ERR_INVAL; } if (ctx->logusercolumn == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, "%s", PAM_MYSQL_LOG_PREFIX "sqllog set but logusercolumn not set"); return PAM_MYSQL_ERR_INVAL; } if (ctx->loghostcolumn == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, "%s", PAM_MYSQL_LOG_PREFIX "sqllog set but loghostcolumn not set"); return PAM_MYSQL_ERR_INVAL; } if (ctx->logtimecolumn == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, "%s", PAM_MYSQL_LOG_PREFIX "sqllog set but logtimecolumn not set"); return PAM_MYSQL_ERR_INVAL; } if (ctx->logrhostcolumn) { err = pam_mysql_format_string(ctx, &query, "INSERT INTO %[logtable] (%[logmsgcolumn], %[logusercolumn], %[loghostcolumn], %[logrhostcolumn], %[logpidcolumn], %[logtimecolumn]) VALUES ('%s', '%s', '%s', '%s', '%u', NOW())", 1, msg, user, host, rhost == NULL ? "(unknown)": rhost, getpid()); } else { err = pam_mysql_format_string(ctx, &query, "INSERT INTO %[logtable] (%[logmsgcolumn], %[logusercolumn], %[loghostcolumn], %[logpidcolumn], %[logtimecolumn]) VALUES ('%s', '%s', '%s', '%u', NOW())", 1, msg, user, host, getpid()); } if (err) { goto out; } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "%s", query.p); } #ifdef HAVE_MYSQL_REAL_QUERY if (mysql_real_query(ctx->mysql_hdl, query.p, query.len)) { #else if (mysql_query(ctx->mysql_hdl, query.p)) { #endif err = PAM_MYSQL_ERR_DB; goto out; } err = PAM_MYSQL_ERR_SUCCESS; out: if (err == PAM_MYSQL_ERR_DB) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "MySQL error (%s)", mysql_error(ctx->mysql_hdl)); } pam_mysql_str_destroy(&query); if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_sql_log() returning %d.", err); } return err; } /** * Have a conversation with an application via PAM. * * (This is not the PAM conversation callback). * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param char **pretval * The address of a pointer to the return value. * @param pam_handle_t *pamh * A pointer to the PAM handle. * @param size_t nargs * The number of messages to be sent. * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_converse(pam_mysql_ctx_t *ctx, char ***pretval, pam_handle_t *pamh, size_t nargs, ...) { pam_mysql_err_t err = PAM_MYSQL_ERR_SUCCESS; int perr; struct pam_message **msgs = NULL; struct pam_message *bulk_msg_buf = NULL; struct pam_response *resps = NULL; struct pam_conv *conv = NULL; va_list ap; size_t i; char **retval = NULL; if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_mysql_converse() called."); } va_start(ap, nargs); /* obtain conversation interface */ if ((perr = pam_get_item(pamh, PAM_CONV, (PAM_GET_ITEM_CONST void **)&conv))) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "could not obtain coversation interface (reason: %s)", pam_strerror(pamh, perr)); err = PAM_MYSQL_ERR_UNKNOWN; goto out; } /* build message array */ if (NULL == (msgs = xcalloc(nargs, sizeof(struct pam_message *)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } for (i = 0; i < nargs; i++) { msgs[i] = NULL; } if (NULL == (bulk_msg_buf = xcalloc(nargs, sizeof(struct pam_message)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } for (i = 0; i < nargs; i++) { msgs[i] = &bulk_msg_buf[i]; msgs[i]->msg_style = va_arg(ap, int); msgs[i]->msg = va_arg(ap, char *); } if (NULL == (retval = xcalloc(nargs + 1, sizeof(char **)))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } for (i = 0; i < nargs; i++) { retval[i] = NULL; } switch ((perr = conv->conv(nargs, (PAM_CONV_CONST struct pam_message **)msgs, &resps, conv->appdata_ptr))) { case PAM_SUCCESS: break; #ifdef HAVE_PAM_CONV_AGAIN case PAM_CONV_AGAIN: break; #endif default: syslog(LOG_DEBUG, PAM_MYSQL_LOG_PREFIX "conversation failure (reason: %s)", pam_strerror(pamh, perr)); err = PAM_MYSQL_ERR_UNKNOWN; goto out; } for (i = 0; i < nargs; i++) { if (resps && resps[i].resp != NULL && NULL == (retval[i] = xstrdup(resps[i].resp))) { syslog(LOG_AUTHPRIV | LOG_CRIT, PAM_MYSQL_LOG_PREFIX "allocation failure at " __FILE__ ":%d", __LINE__); err = PAM_MYSQL_ERR_ALLOC; goto out; } } retval[i] = NULL; out: if (resps != NULL) { size_t i; for (i = 0; i < nargs; i++) { xfree_overwrite(resps[i].resp); } xfree(resps); } if (bulk_msg_buf != NULL) { memset(bulk_msg_buf, 0, sizeof(*bulk_msg_buf) * nargs); xfree(bulk_msg_buf); } xfree(msgs); if (err) { if (retval != NULL) { for (i = 0; i < nargs; i++) { xfree_overwrite(retval[i]); retval[i] = NULL; } xfree(retval); } } else { *pretval = retval; } va_end(ap); return err; } /** * Query the capabilities of a user. * * @param pam_mysql_ctx_t *ctx * A pointer to the context data structure. * @param int *pretval * A pointer to the integer where the result should be stored. * @param const char *user * A pointer to the username (unused). * * @return pam_mysql_err_t * Indication of success or failure. */ static pam_mysql_err_t pam_mysql_query_user_caps(pam_mysql_ctx_t *ctx, int *pretval, const char *user) { *pretval = 0; if (geteuid() == 0) { *pretval |= PAM_MYSQL_CAP_CHAUTHTOK_SELF; if (getuid() == 0) { *pretval |= PAM_MYSQL_CAP_CHAUTHTOK_OTHERS; } } return PAM_MYSQL_ERR_SUCCESS; } /* PAM Authentication services */ /** * Authenticate a user. * * @param pam_handle_t *pamh * A pointer to the PAM handle. * @param int flags * Flags indicating desired behaviour. * @param int argc * The number of arguments provided. * @param const char **argv * An array of arguments. * * @return pam_mysql_err_t * Indication of success or failure. */ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval; int err; const char *user; const char *rhost; char *passwd = NULL; pam_mysql_ctx_t *ctx = NULL; char **resps = NULL; int passwd_is_local = 0; switch (pam_mysql_retrieve_ctx(&ctx, pamh)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: return PAM_BUF_ERR; default: return PAM_SERVICE_ERR; } switch (pam_mysql_parse_args(ctx, argc, argv)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } if (ctx->config_file != NULL) { switch (pam_mysql_read_config_file(ctx, ctx->config_file)) { case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: break; } } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_authenticate() called."); } /* Get User */ if ((retval = pam_get_user(pamh, (PAM_GET_USER_CONST char **)&user, NULL))) { goto out; } if (user == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "no user specified."); retval = PAM_USER_UNKNOWN; goto out; } switch (pam_get_item(pamh, PAM_RHOST, (PAM_GET_ITEM_CONST void **)&rhost)) { case PAM_SUCCESS: break; default: rhost = NULL; } if (ctx->use_first_pass || ctx->try_first_pass) { retval = pam_get_item(pamh, PAM_AUTHTOK, (PAM_GET_ITEM_CONST void **)&passwd); switch (retval) { case PAM_SUCCESS: break; case PAM_NO_MODULE_DATA: passwd = NULL; goto askpass; default: retval = PAM_AUTH_ERR; goto out; } switch (pam_mysql_open_db(ctx)) { case PAM_MYSQL_ERR_BUSY: case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; case PAM_MYSQL_ERR_DB: retval = PAM_AUTHINFO_UNAVAIL; goto out; default: retval = PAM_SERVICE_ERR; goto out; } err = pam_mysql_check_passwd(ctx, user, passwd, !(flags & PAM_DISALLOW_NULL_AUTHTOK)); if (err == PAM_MYSQL_ERR_SUCCESS) { pam_mysql_sql_log(ctx, "AUTHENTICATION SUCCESS (FIRST_PASS)", user, rhost); } else { pam_mysql_sql_log(ctx, "AUTHENTICATION FALURE (FIRST_PASS)", user, rhost); } switch (err) { case PAM_MYSQL_ERR_SUCCESS: if (ctx->use_first_pass || ctx->try_first_pass) { retval = PAM_SUCCESS; goto out; } break; case PAM_MYSQL_ERR_NO_ENTRY: if (ctx->use_first_pass) { retval = PAM_USER_UNKNOWN; goto out; } break; case PAM_MYSQL_ERR_MISMATCH: if (ctx->use_first_pass) { retval = PAM_AUTH_ERR; goto out; } break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } } askpass: switch (pam_mysql_converse(ctx, &resps, pamh, 1, PAM_PROMPT_ECHO_OFF, PLEASE_ENTER_PASSWORD)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } passwd = resps[0]; passwd_is_local = 1; resps[0] = NULL; xfree(resps); if (passwd == NULL) { if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "failed to retrieve authentication token."); } retval = PAM_AUTH_ERR; goto out; } if (passwd_is_local) { (void) pam_set_item(pamh, PAM_AUTHTOK, passwd); } switch (pam_mysql_open_db(ctx)) { case PAM_MYSQL_ERR_BUSY: case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; case PAM_MYSQL_ERR_DB: retval = PAM_AUTHINFO_UNAVAIL; goto out; default: retval = PAM_SERVICE_ERR; goto out; } err = pam_mysql_check_passwd(ctx, user, passwd, !(flags & PAM_DISALLOW_NULL_AUTHTOK)); if (err == PAM_MYSQL_ERR_SUCCESS) { pam_mysql_sql_log(ctx, "AUTHENTICATION SUCCESS", user, rhost); } else { pam_mysql_sql_log(ctx, "AUTHENTICATION FAILURE", user, rhost); } switch (err) { case PAM_MYSQL_ERR_SUCCESS: retval = PAM_SUCCESS; break; case PAM_MYSQL_ERR_NO_ENTRY: retval = PAM_USER_UNKNOWN; goto out; case PAM_MYSQL_ERR_MISMATCH: retval = PAM_AUTH_ERR; goto out; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } out: if (ctx->disconnect_every_op) { pam_mysql_close_db(ctx); } if (passwd != NULL && passwd_is_local) { xfree_overwrite(passwd); } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_authenticate() returning %d.", retval); } return retval; } /** * Get the status of a user account. * * @param pam_handle_t *pamh * A pointer to the PAM handle. * @param int flags * An integer indicating desired behaviour. * @param int argc * The number of arguments provided. * @param const char **argv * An array of arguments. * * @return pam_mysql_err_t * Indication of success or failure. */ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval; int err; int stat; const char *user; const char *rhost; pam_mysql_ctx_t *ctx = NULL; switch (pam_mysql_retrieve_ctx(&ctx, pamh)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: return PAM_BUF_ERR; default: return PAM_SERVICE_ERR; } switch (pam_mysql_parse_args(ctx, argc, argv)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } if (ctx->config_file != NULL) { switch (pam_mysql_read_config_file(ctx, ctx->config_file)) { case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: break; } } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_acct_mgmt() called."); } /* Get User */ if ((retval = pam_get_user(pamh, (PAM_GET_USER_CONST char **)&user, NULL))) { goto out; } if (user == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "no user specified."); retval = PAM_USER_UNKNOWN; goto out; } switch (pam_get_item(pamh, PAM_RHOST, (PAM_GET_ITEM_CONST void **)&rhost)) { case PAM_SUCCESS: break; default: rhost = NULL; } switch (pam_mysql_open_db(ctx)) { case PAM_MYSQL_ERR_BUSY: case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; case PAM_MYSQL_ERR_DB: retval = PAM_AUTHINFO_UNAVAIL; goto out; default: retval = PAM_SERVICE_ERR; goto out; } err = pam_mysql_query_user_stat(ctx, &stat, user); if (err == PAM_MYSQL_ERR_SUCCESS) { pam_mysql_sql_log(ctx, "QUERYING SUCCESS", user, rhost); } else { pam_mysql_sql_log(ctx, "QUERYING FAILURE", user, rhost); } switch (err) { case PAM_MYSQL_ERR_SUCCESS: retval = PAM_SUCCESS; break; case PAM_MYSQL_ERR_NO_ENTRY: retval = PAM_USER_UNKNOWN; goto out; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } if (stat & PAM_MYSQL_USER_STAT_EXPIRED) { retval = PAM_ACCT_EXPIRED; } else if (stat & PAM_MYSQL_USER_STAT_AUTHTOK_EXPIRED) { if (stat & PAM_MYSQL_USER_STAT_NULL_PASSWD) { #if defined(HAVE_PAM_NEW_AUTHTOK_REQD) retval = PAM_NEW_AUTHTOK_REQD; #else retval = PAM_AUTHTOK_EXPIRED; #endif } else { retval = PAM_AUTHTOK_EXPIRED; } } out: if (ctx->disconnect_every_op) { pam_mysql_close_db(ctx); } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_acct_mgmt() returning %i.",retval); } return retval; } /** * Set a user's credentials. * * @param pam_handle_t *pamh * A pointer to the PAM handle. * @param int flags * An integer indicating desired behaviour. * @param int argc * The number of arguments provided. * @param const char **argv * An array of arguments. * * @return pam_mysql_err_t * Indication of success or failure. */ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc, const char **argv) { #ifdef DEBUG syslog(LOG_INFO, "%s", PAM_MYSQL_LOG_PREFIX "setcred called but not implemented."); #endif return PAM_SUCCESS; } /** * Check a user's credentials, possibly force a password change. * * @param pam_handle_t *pamh * A pointer to the PAM handle. * @param int flags * An integer indicating desired behaviour. * @param int argc * The number of arguments provided. * @param const char **argv * An array of arguments. * * @return pam_mysql_err_t * Indication of success or failure. */ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc, const char **argv) { int retval; int err; const char *user; const char *rhost; char *old_passwd = NULL; char *first_enter = NULL; char *new_passwd = NULL; int old_passwd_should_be_freed = 0; int new_passwd_is_local = 0; int caps = 0; int stat = 0; pam_mysql_ctx_t *ctx = NULL; switch (pam_mysql_retrieve_ctx(&ctx, pamh)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: return PAM_BUF_ERR; default: return PAM_SERVICE_ERR; } switch (pam_mysql_parse_args(ctx, argc, argv)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } if (ctx->config_file != NULL) { switch (pam_mysql_read_config_file(ctx, ctx->config_file)) { case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: break; } } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_chauthtok() called."); } /* Get User */ if ((retval = pam_get_user(pamh, (PAM_GET_USER_CONST char **)&user, NULL))) { goto out; } if (user == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "no user specified."); retval = PAM_USER_UNKNOWN; goto out; } switch (pam_get_item(pamh, PAM_RHOST, (PAM_GET_ITEM_CONST void **)&rhost)) { case PAM_SUCCESS: break; default: rhost = NULL; } err = pam_mysql_open_db(ctx); if (flags & PAM_PRELIM_CHECK) { switch (err) { case PAM_MYSQL_ERR_BUSY: case PAM_MYSQL_ERR_SUCCESS: break; default: retval = PAM_TRY_AGAIN; goto out; } } else { switch (err) { case PAM_MYSQL_ERR_BUSY: case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; case PAM_MYSQL_ERR_DB: retval = PAM_PERM_DENIED; goto out; default: retval = PAM_SERVICE_ERR; goto out; } } if (!(flags & PAM_UPDATE_AUTHTOK)) { goto out; } err = pam_mysql_query_user_caps(ctx, &caps, user); switch (err) { case PAM_MYSQL_ERR_SUCCESS: retval = PAM_SUCCESS; break; case PAM_MYSQL_ERR_NO_ENTRY: retval = PAM_SUCCESS; caps = 0; break; default: retval = PAM_PERM_DENIED; goto out; } if (!(caps & (PAM_MYSQL_CAP_CHAUTHTOK_SELF | PAM_MYSQL_CAP_CHAUTHTOK_OTHERS))) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "User is not allowed to change the authentication token."); retval = PAM_PERM_DENIED; goto out; } err = pam_mysql_query_user_stat(ctx, &stat, user); if (err == PAM_MYSQL_ERR_SUCCESS) { pam_mysql_sql_log(ctx, "QUERYING SUCCESS", user, rhost); } else { pam_mysql_sql_log(ctx, "QUERYING FAILURE", user, rhost); } switch (err) { case PAM_MYSQL_ERR_SUCCESS: retval = PAM_SUCCESS; break; default: retval = PAM_PERM_DENIED; goto out; } if (!(flags & PAM_CHANGE_EXPIRED_AUTHTOK) && (stat & PAM_MYSQL_USER_STAT_EXPIRED)) { retval = PAM_AUTHTOK_LOCK_BUSY; goto out; } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "update authentication token"); } if (!(caps & PAM_MYSQL_CAP_CHAUTHTOK_OTHERS) && !(stat & PAM_MYSQL_USER_STAT_NULL_PASSWD)) { if (ctx->use_first_pass || ctx->try_first_pass) { retval = pam_get_item(pamh, PAM_OLDAUTHTOK, (PAM_GET_ITEM_CONST void **)&old_passwd); switch (retval) { case PAM_SUCCESS: break; case PAM_NO_MODULE_DATA: old_passwd = NULL; break; default: retval = PAM_AUTHTOK_ERR; goto out; } if (old_passwd != NULL) { switch (pam_mysql_check_passwd(ctx, user, old_passwd, 0)) { case PAM_MYSQL_ERR_SUCCESS: retval = PAM_SUCCESS; break; case PAM_MYSQL_ERR_NO_ENTRY: retval = PAM_USER_UNKNOWN; goto out; case PAM_MYSQL_ERR_MISMATCH: if (ctx->use_first_pass) { retval = PAM_AUTH_ERR; goto out; } retval = PAM_SUCCESS; break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } } } if (!ctx->use_first_pass) { char **resps; if (flags & PAM_SILENT) { retval = PAM_AUTHTOK_RECOVERY_ERR; goto out; } switch (pam_mysql_converse(ctx, &resps, pamh, 1, PAM_PROMPT_ECHO_OFF, PLEASE_ENTER_OLD_PASSWORD)) { case PAM_MYSQL_ERR_SUCCESS: break; default: retval = PAM_SERVICE_ERR; goto out; } old_passwd = resps[0]; old_passwd_should_be_freed = 1; resps[0] = NULL; xfree(resps); if (old_passwd == NULL) { retval = PAM_AUTHTOK_RECOVERY_ERR; goto out; } switch (pam_mysql_check_passwd(ctx, user, old_passwd, 0)) { case PAM_MYSQL_ERR_SUCCESS: retval = PAM_SUCCESS; break; case PAM_MYSQL_ERR_NO_ENTRY: retval = PAM_USER_UNKNOWN; goto out; case PAM_MYSQL_ERR_MISMATCH: retval = PAM_AUTH_ERR; goto out; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } if ((retval = pam_set_item(pamh, PAM_OLDAUTHTOK, old_passwd)) != PAM_SUCCESS) { goto out; } } } retval = pam_get_item(pamh, PAM_AUTHTOK, (PAM_GET_ITEM_CONST void **)&new_passwd); switch (retval) { case PAM_SUCCESS: break; case PAM_NO_MODULE_DATA: new_passwd = NULL; break; default: retval = PAM_AUTHTOK_ERR; goto out; } if (new_passwd == NULL) { char **resps; if (ctx->use_first_pass) { retval = PAM_AUTHTOK_RECOVERY_ERR; goto out; } if (flags & PAM_SILENT) { retval = PAM_AUTHTOK_RECOVERY_ERR; goto out; } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, "Asking for new password (1)"); } switch (pam_mysql_converse(ctx, &resps, pamh, 1, PAM_PROMPT_ECHO_OFF, PLEASE_ENTER_NEW_PASSWORD)) { case PAM_MYSQL_ERR_SUCCESS: break; default: retval = PAM_SERVICE_ERR; goto out; } first_enter = resps[0]; resps[0] = NULL; xfree(resps); switch (pam_mysql_converse(ctx, &resps, pamh, 1, PAM_PROMPT_ECHO_OFF, PLEASE_REENTER_NEW_PASSWORD)) { case PAM_MYSQL_ERR_SUCCESS: break; default: retval = PAM_SERVICE_ERR; goto out; } new_passwd = resps[0]; new_passwd_is_local = 1; resps[0] = NULL; xfree(resps); if (new_passwd == NULL || strcmp(first_enter, new_passwd) != 0) { retval = PAM_AUTHTOK_RECOVERY_ERR; goto out; } } switch (pam_mysql_update_passwd(ctx, user, new_passwd)) { case PAM_MYSQL_ERR_SUCCESS: if (new_passwd_is_local) { (void) pam_set_item(pamh, PAM_AUTHTOK, new_passwd); } retval = PAM_SUCCESS; break; default: retval = PAM_AUTHTOK_ERR; break; } if (retval == PAM_SUCCESS) { pam_mysql_sql_log(ctx, "ALTERATION SUCCESS", user, rhost); } else { pam_mysql_sql_log(ctx, "ALTERATION FAILURE", user, rhost); } out: if (ctx->disconnect_every_op) { pam_mysql_close_db(ctx); } if (new_passwd != NULL && new_passwd_is_local) { xfree_overwrite(new_passwd); } if (first_enter != NULL) { xfree_overwrite(first_enter); } if (old_passwd != NULL && old_passwd_should_be_freed) { xfree_overwrite(old_passwd); } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_chauthtok() returning %d.", retval); } return retval; } /** * Open a database connection. * * @param pam_handle_t *pamh * A pointer to the PAM handle. * @param int flags * An integer indicating desired behaviour. * @param int argc * The number of arguments provided. * @param const char **argv * An array of arguments. * * @return pam_mysql_err_t * Indication of success or failure. */ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { int retval; pam_mysql_ctx_t *ctx = NULL; const char *user; const char *rhost; switch (pam_mysql_retrieve_ctx(&ctx, pamh)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: return PAM_BUF_ERR; default: return PAM_SERVICE_ERR; } switch (pam_mysql_parse_args(ctx, argc, argv)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } if (ctx->config_file != NULL) { switch (pam_mysql_read_config_file(ctx, ctx->config_file)) { case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: break; } } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_open_session() called."); } /* Get User */ if ((retval = pam_get_user(pamh, (PAM_GET_USER_CONST char **)&user, NULL))) { goto out; } if (user == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "no user specified."); retval = PAM_USER_UNKNOWN; goto out; } switch (pam_get_item(pamh, PAM_RHOST, (PAM_GET_ITEM_CONST void **)&rhost)) { case PAM_SUCCESS: break; default: rhost = NULL; } switch (pam_mysql_open_db(ctx)) { case PAM_MYSQL_ERR_BUSY: case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; case PAM_MYSQL_ERR_DB: retval = PAM_AUTHINFO_UNAVAIL; goto out; default: retval = PAM_SERVICE_ERR; goto out; } pam_mysql_sql_log(ctx, "OPEN SESSION", user, rhost); out: if (ctx->disconnect_every_op) { pam_mysql_close_db(ctx); } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_open_session() returning %i.", retval); } return retval; } /** * Close a session. * * The content seems rather pointless to me, but I'll confirm that later. * * @param pam_handle_t *pamh * A pointer to the PAM handle. * @param int flags * An integer indicating desired behaviour. * @param int argc * The number of arguments provided. * @param const char **argv * An array of arguments. * * @return pam_mysql_err_t * Indication of success or failure. */ PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { int retval; pam_mysql_ctx_t *ctx = NULL; const char *user; const char *rhost; switch (pam_mysql_retrieve_ctx(&ctx, pamh)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: return PAM_BUF_ERR; default: return PAM_SERVICE_ERR; } switch (pam_mysql_parse_args(ctx, argc, argv)) { case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: retval = PAM_SERVICE_ERR; goto out; } if (ctx->config_file != NULL) { switch (pam_mysql_read_config_file(ctx, ctx->config_file)) { case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; default: break; } } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_close_session() called."); } /* Get User */ if ((retval = pam_get_user(pamh, (PAM_GET_USER_CONST char **)&user, NULL))) { goto out; } if (user == NULL) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "no user specified."); retval = PAM_USER_UNKNOWN; goto out; } switch (pam_get_item(pamh, PAM_RHOST, (PAM_GET_ITEM_CONST void **)&rhost)) { case PAM_SUCCESS: break; default: rhost = NULL; } switch (pam_mysql_open_db(ctx)) { case PAM_MYSQL_ERR_BUSY: case PAM_MYSQL_ERR_SUCCESS: break; case PAM_MYSQL_ERR_ALLOC: retval = PAM_BUF_ERR; goto out; case PAM_MYSQL_ERR_DB: retval = PAM_AUTHINFO_UNAVAIL; goto out; default: retval = PAM_SERVICE_ERR; goto out; } pam_mysql_sql_log(ctx, "CLOSE SESSION", user, rhost); out: if (ctx->disconnect_every_op) { pam_mysql_close_db(ctx); } if (ctx->verbose) { syslog(LOG_AUTHPRIV | LOG_ERR, PAM_MYSQL_LOG_PREFIX "pam_sm_close_session() returning %i.", retval); } return retval; } /* end of module definition */ #ifdef PAM_STATIC /* static module data */ struct pam_module _pam_mysql_modstruct = { PAM_MODULE_NAME, pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, pam_sm_open_session, pam_sm_close_session, pam_sm_chauthtok }; #endif /* vim: set expandtab sw=4 ts=4 textwidth=80 : */ pam-MySQL-0.8.2/pam_mysql.spec.in000066400000000000000000000015661313430347400165360ustar00rootroot00000000000000Summary: A PAM-module for authentication against MySQL Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ Release: 1 Group: System Environment/Base URL: http://github.com/victor73/pam-MySQL/ License: GPLv2 Source: %{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-root Requires: pam BuildPrereq: mysql-devel pam-devel %description A PAM-module for authentication against MySQL %define security_dir /lib/security/ %prep %setup -n %{name}-%{version} %build %configure --prefix=${_prefix} --with-pam-mods-dir=%{security_dir} %{__make} %install [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT" %{__make} install DESTDIR="$RPM_BUILD_ROOT" rm -f "$RPM_BUILD_ROOT"%{security_dir}/*.la %clean [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT" %{__make} clean %files %defattr(-,root,root) /lib/security/pam_mysql.so %doc NEWS README ChangeLog INSTALL CREDITS