pam_radius-1.3.17/0000755000201400020140000000000010611133750012630 5ustar alandalandpam_radius-1.3.17/Changelog0000644000201400020140000000366110601655643014461 0ustar alandaland1.3.17 ------ Allow any number of retries, instead of only up to 3. Add ruser option, to authenticate as PAM_RUSER instead of PAM_USER, to allow applications such as 'su' to authenticate as the real user. Patch from David Mitchell. Add 'localifdown' option. 1.3.16 ------ Memory handling fixes, which caused the module to not work on RH9.0 Added dummy pam_sm_acct_mgmt() function, which is needed by pppd 2.4 Increase the allowed length of user names 1.3.15 ------ Bug fix: don't try to free() static storage when using skip_passwd. Implement retry option. 1.3.14 ------ Solaris 8 changed their header files for PAM. Bug fix to work on HURD: Don't use PATH_MAX. 1.3.13 ------ Fix a bug where *no* module options would prevent it from finding the configuration file. Jon Nelson 1.3.12 ------ Solaris helpfully passes argc==1 and argv==NULL to the function pam_private_session(). So we've got to check for that ridiculous condition. Based on comments from "David Black" Check that the response packet ID matches the request ID. Based on a patch from Leon Vernikov Use Calling-Station-Id (string), instead of Login-IP-Host (IP address) for the name of the host the user is logging in from. Comments from Mike Smith Fix for a buffer overflow from Vesselin Atanasov miscellanous bug fixes. Don't add password to accounting requests; log more errors; add NAS-Port and NAS-Port-Type attributes to ALL packets. Some patches based on input from Grzegorz Paszka 1.3.11 ------ Bug fixes from Jon Nelson Bug fixes from robert.hendrickx@smals-mvm.be More debugging messages. 1.3.10 ------ If no password is given, then add a blank password to the outgoing request. This change ensures that the outgoing packet is RFC compliant. pam_radius-1.3.17/INSTALL0000644000201400020140000000652607132362065013701 0ustar alandaland********************************************************************** Redhat Linux 4.2 (PAM 0.54) ********************************************************************** make. Copy 'pam_radius_auth.so' to /lib/security/pam_radius_auth.so In /etc/pam.conf, add the line: login auth sufficient /lib/security/pam_radius_auth.so AFTER login auth required /lib/security/pam_securetty.so and BEFORE login auth required /lib/security/pam_unix_auth.so i.e. login auth required /lib/security/pam_securetty.so login auth sufficient /lib/security/pam_radius_auth.so login auth required /lib/security/pam_unix_auth.so ********************************************************************** Redhat Linux > 5.0 ********************************************************************** make. Copy 'pam_radius_auth.so' to /lib/security/pam_radius_auth.so In the per-application configuration (/etc/pam.d/application) add: auth sufficient /lib/security/pam_radius_auth.so AFTER auth required /lib/security/pam_securetty.so and BEFORE auth required /lib/security/pam_unix_auth.so i.e. auth required /lib/security/pam_securetty.so auth sufficient /lib/security/pam_radius_auth.so auth required /lib/security/pam_unix_auth.so ********************************************************************** Solaris 2.6 ********************************************************************** make. Copy 'pam_radius_auth.so' to /usr/lib/security/pam_radius_auth.so.1 in /etc/pam.conf, add the line: login auth sufficient /usr/lib/security/pam_radius_auth.so.1 BEFORE login auth required /usr/lib/security/pam_unix_auth.so.1 You will probably also have to add the lines: telnet auth sufficient /usr/lib/security/pam_radius_auth.so.1 telnet auth required /usr/lib/security/pam_unix.so.1 in order to perform network logins. ---------------------------------------------------------------------- Password change requests are pretty much the same. Add a line like: passwd password sufficient /lib/security/pam_radius_auth.so And you're set. Note that password change requests will NOT work for RADIUS users using challenge-response authentication. ---------------------------------------------------------------------- If you're familiar with PAM, configuring RADIUS authentication for other applications should be straightforward. Note that you should be *very* careful when configuring users who use RADIUS challenge-response. They should *not* have a Unix password defined, or the challenge-response token card may become meaningless. Users who have have a RADIUS challenge-response configuration must enter an initial password, unless 'skip_passwd' (see below) is defined. The password they enter may not be blank or empty. ---------------------------------------------------------------------- You will need a server configuration file. An example is given in the file pam_radius_auth.conf. You will need to copy this file to /etc/raddb/server. The file MUST be secure! i.e. chown root /etc/raddb chmod go-rwx /etc/raddb chmod go-rwx /etc/raddb/server See 'USAGE' for details of the configuration file. ---------------------------------------------------------------------- pam_radius-1.3.17/LICENSE0000644000201400020140000003543307501663125013655 0ustar alandaland GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, 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 pam_radius-1.3.17/Makefile0000644000201400020140000000435210601645163014301 0ustar alandaland###################################################################### # # A minimal 'Makefile', by Alan DeKok # # $Id: Makefile,v 1.13 2007/03/26 04:22:11 fcusack Exp $ # ############################################################################# VERSION=1.3.17 ###################################################################### # # If we're really paranoid, use these flags #CFLAGS = -Wall -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Waggregate-return # # If you're not using GCC, then you'll have to change the CFLAGS. # CFLAGS = -Wall -fPIC # # On Irix, use this with MIPSPRo C Compiler, and don't forget to export CC=cc # gcc on Irix does not work yet for pam_radius # Also, use gmake instead of make # Then copy pam_radius_auth.so to /usr/freeware/lib32/security (PAM dir) # CFLAGS = ###################################################################### # # The default rule to build everything. # all: pam_radius_auth.so ###################################################################### # # Build the object file from the C source. # pam_radius_auth.o: pam_radius_auth.c pam_radius_auth.h $(CC) $(CFLAGS) -c pam_radius_auth.c -o pam_radius_auth.o # # This is what should work on Irix: #pam_radius_auth.so: pam_radius_auth.o md5.o # ld -shared pam_radius_auth.o md5.o -L/usr/freeware/lib32 -lpam -lc -o pam_radius_auth.so ###################################################################### # # Build the shared library. # # The -Bshareable flag *should* work on *most* operating systems. # # On Solaris, you might try using '-G', instead. # # On systems with a newer GCC, you will need to do: # # gcc -shared pam_radius_auth.o md5.o -lpam -lc -o pam_radius_auth.so # pam_radius_auth.so: pam_radius_auth.o md5.o ld -Bshareable pam_radius_auth.o md5.o -lpam -o pam_radius_auth.so ###################################################################### # # Check a distribution out of the source tree, and make a tar file. # dist: cvs export -D now -d pam_radius-${VERSION} pam_radius tar -cf pam_radius-${VERSION}.tar pam_radius-${VERSION} rm -rf pam_radius-${VERSION} ###################################################################### # # Clean up everything # clean: @rm -f *~ *.so *.o pam_radius-1.3.17/README0000644000201400020140000000407507261153665013534 0ustar alandaland pam_radius_auth.c =================== This is the PAM to RADIUS authentication module. It allows any Linux or Solaris machine to become a RADIUS client for authentication and password change requests. You will need to supply your own RADIUS server to perform the actual authentication. The latest version has a simple merger of the original pam_radius session accounting code which will work *only* on Linux. See INSTALL for instructions on building and installing this module. I have successfully used it for RADIUS authentication on RedHat 4.2, RedHat 5.x, RedHat 6.x, and Solaris 2.6. A number of options are supported by this module. See USAGE for more details. Care should be taken when configuring RADIUS authentication. Your RADIUS server should have a minimal set of machines in it's 'clients' file. The server should NOT be visible to the world at large, but should be contained behind a firewall. If your RADIUS server is visible from the Internet, a number of attacks become possible. Any additional questions can be directed to: Alan DeKok (aland@freeradius.org) For the latest version and updates, see the main web or ftp site: http://www.freeradius.org/ ftp://ftp.freeradius.org/pub/radius/ The pam_radius_auth module based on an old version of Cristian Gafton's pam_radius.c, and on an Apache module I wrote a while back. (mod_auth_radius.c, also on ftp://ftp.freeradius.org/pub/radius/). The source contains a full suite of RADIUS functions, instead of using libpwdb. It makes sense, because we want it to compile out of the box on Linux and Solaris 2.6. I also wasn't able to find much documentation for RADIUS authentication support in libpwdb, so I rolled my own. There are minimal restrictions on using the code, as set out in the disclaimer and copyright notice in pam_radius_auth.c. Building it is straightforward: use GNU make, and type 'make'. If you've got some other weird make, you'll have to edit the Makefile to remove the GNU make directives 'ifeq', 'else', etc. Alan DeKok pam_radius-1.3.17/TODO0000644000201400020140000000121606757001366013335 0ustar alandaland------------------------------------------------------------ Note that root won't be able to change anyone's passwords via this method, as RADIUS doesn't support the notion of root. ------------------------------------------------------------ Add in pam_set_data && pam_get_data to keep track of which RADIUS server we were talking to, and what the session_time was. Oddly enough, the session_time information seems to be happy to be a 'static', but the radius_server_t *live_server doesn't. It works for login, is re-used for open_session, but is ignored for close_session. ------------------------------------------------------------ pam_radius-1.3.17/USAGE0000644000201400020140000001022510601655643013430 0ustar alandaland The module takes a number of configuration options. Password changing is not implemented, as the RADIUS protocol does not support it. The pam configuration can be: ... auth sufficient /lib/security/pam_radius_auth.so [options] ... account sufficient /lib/security/pam_radius_auth.so --------------------------------------------------------------------------- The 'options' section is optional, and can contain one or more of the following strings. Note that not all of these options are relevant in for all uses of the module. debug - print out extensive debugging information via pam_log. These messages generally end up being handled by sylog(), and go to /var/log/messages. Depending on your host operating system, the log messages may be elsewhere. You should generally use the debug option when first trying to install the module, as it will help enormously in tracking down problems. use_first_pass - Instead of prompting the user for a password, retrieve the password from the previous authentication module. If the password does not exist, return failure. If the password exists, try it, returning success/failure as appropriate. try_first_pass - Instead of prompting the user for a password, retrieve the password from the previous authentication module. If the password exists, try it, and return success if it passes. If there was no previous password, or the previous password fails authentication, prompt the user with "Enter RADIUS password: ", and ask for another password. Try this password, and return success/failure as appropriate. This is the default for authentication. skip_passwd - Do not prompt for a password, even if there was none retrieved from the previous layer. Send the previous one (if it exists), or else send a NULL password. If this fails, exit. If an Access-Challenge is returned, display the challenge message, and ask the user for the response. Return success/failure as appropriate. The password sent to the next authentication module will NOT be the response to the challenge. If a password from a previous authentication module exists, it is passed on. Otherwise, no password is sent to the next module. conf=foo - set the configuration filename to 'foo'. Default is /etc/raddb/server client_id=bar - send a NAS-Identifier RADIUS attribute with string 'bar'. If the client_id is not specified, the PAM_SERVICE type is used instead. ('login', 'su', 'passwd', etc.) This feature may be disabled by using 'client_id='. i.e. A blank client ID. retry = # - allow a number of retries before continuing to the next authentication module use_authtok - force the use of a previously entered password. This is needed for pluggable password strength checking i.e. try cracklib to be sure it's secure, then go update the RADIUS server. ruser - If PAM_USER is root, Use the value of PAM_RUSER instead of PAM_USER to determine the username to authenticate via RADIUS. This is to allow 'su' to act like 'sudo'. localifdown - This option tells pam_radius to return PAM_IGNORE instead of PAM_AUTHINFO_UNAVAIL if RADIUS auth failed due to network unavailability. PAM_IGNORE tells the pam stack to continue down the stack regardless of the control flag. accounting_bug - When used, the accounting response vector is NOT validated. This option will probably only be necessary on REALLY OLD (i.e. Livingston 1.16) servers. --------------------------------------------------------------------------- pam_radius-1.3.17/index.html0000644000201400020140000000271407732613133014641 0ustar alandaland <code>pam_radius_auth</CODE>: The PAM RADIUS authentication module

pam_radius_auth: The PAM RADIUS authentication module

This is the PAM to RADIUS authentication module. It allows any PAM-capable machine to become a RADIUS client for authentication and accounting requests. You will need a RADIUS server to perform the actual authentication.


Files included with the module

README Introduction and documentation
INSTALL Installation instructions
USAGE Module configuration and usage documentation
Changelog What's changed
pam_radius_auth.conf Sample configuration file for telling the client the location of the RADIUS server.
pam_radius_auth.c C source file
pam_radius_auth.h C header file


Updates

For the latest version and updates, see the main web or ftp site:

http://www.freeradius.org/pam_radius_auth/
ftp://ftp.freeradius.org/pub/radius/


$Id: index.html,v 1.5 2003/09/19 14:44:43 aland Exp $ pam_radius-1.3.17/md5.c0000644000201400020140000002133710601645063013473 0ustar alandaland/* $Id: md5.c,v 1.3 2007/03/26 04:21:07 fcusack Exp $ * * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. * * $Log: md5.c,v $ * Revision 1.3 2007/03/26 04:21:07 fcusack * use uint32_t (C99) not u_int32_t * * Revision 1.2 2002/06/28 06:29:21 fcusack * change HIGHFIRST #ifdef from 'sun' to __sparc, and add __mips * * Revision 1.1.1.1 1999/08/19 13:13:26 aland * Start of the pam_radius module * * Revision 1.2 1998/04/03 20:19:21 aland * now builds cleanly on Solaris 2.6 * * Revision 1.1 1998/04/03 19:36:59 aland * oh yeah, do MD5 stuff, too * * Revision 1.1 1996/12/01 03:06:54 morgan * Initial revision * * Revision 1.1 1996/09/05 06:43:31 morgan * Initial revision * */ #include #include "md5.h" #if defined(__sparc) || defined(__mips) #define HIGHFIRST #endif #ifndef HIGHFIRST #define byteReverse(buf, len) /* Nothing */ #else void byteReverse(unsigned char *buf, unsigned longs); #ifndef ASM_MD5 /* * Note: this code is harmless on little-endian machines. */ void byteReverse(unsigned char *buf, unsigned longs) { uint32_t t; do { t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsigned) buf[1] << 8 | buf[0]); *(uint32_t *) buf = t; buf += 4; } while (--longs); } #endif #endif /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301U; ctx->buf[1] = 0xefcdab89U; ctx->buf[2] = 0x98badcfeU; ctx->buf[3] = 0x10325476U; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void MD5Update(struct MD5Context *ctx, unsigned const char *buf, unsigned len) { uint32_t t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if (t) { unsigned char *p = (unsigned char *) ctx->in + t; t = 64 - t; if (len < t) { memcpy(p, buf, len); return; } memcpy(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32_t *) ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32_t *) ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned count; unsigned char *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ memset(p, 0, count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32_t *) ctx->in); /* Now fill the next block with 56 bytes */ memset(ctx->in, 0, 56); } else { /* Pad block to 56 bytes */ memset(p, 0, count - 8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ ((uint32_t *) ctx->in)[14] = ctx->bits[0]; ((uint32_t *) ctx->in)[15] = ctx->bits[1]; MD5Transform(ctx->buf, (uint32_t *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ } #ifndef ASM_MD5 /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { register uint32_t a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } #endif pam_radius-1.3.17/md5.h0000644000201400020140000000174010601645064013475 0ustar alandaland#ifndef MD5_H #define MD5_H /* * Some operating systems MAY resolve the MD5* functions to * secret functions in one of their libraries. These OS supplied * MD5 functions almost always blow up, and cause problems. * To get around the issue, we re-define the MD5 function names * so that we're sure that our module uses our tested and working * MD5 functions. */ #define MD5Init pra_MD5Init #define MD5Update pra_MD5Update #define MD5Final pra_MD5Final #define MD5Transform pra_MD5Transform #include struct MD5Context { uint32_t buf[4]; uint32_t bits[2]; unsigned char in[64]; }; void MD5Init(struct MD5Context *); void MD5Update(struct MD5Context *, unsigned const char *, unsigned); void MD5Final(unsigned char digest[16], struct MD5Context *); void MD5Transform(uint32_t buf[4], uint32_t const in[16]); /* * This is needed to make RSAREF happy on some MS-DOS compilers. */ typedef struct MD5Context MD5_CTX; #endif /* MD5_H */ pam_radius-1.3.17/pam_radius_auth.c0000644000201400020140000013470510601712015016147 0ustar alandaland/* * $Id: pam_radius_auth.c,v 1.39 2007/03/26 05:35:31 fcusack Exp $ * pam_radius_auth * Authenticate a user via a RADIUS session * * 0.9.0 - Didn't compile quite right. * 0.9.1 - Hands off passwords properly. Solaris still isn't completely happy * 0.9.2 - Solaris now does challenge-response. Added configuration file * handling, and skip_passwd field * 1.0.0 - Added handling of port name/number, and continue on select * 1.1.0 - more options, password change requests work now, too. * 1.1.1 - Added client_id=foo (NAS-Identifier), defaulting to PAM_SERVICE * 1.1.2 - multi-server capability. * 1.2.0 - ugly merger of pam_radius.c to get full RADIUS capability * 1.3.0 - added my own accounting code. Simple, clean, and neat. * 1.3.1 - Supports accounting port (oops!), and do accounting authentication * 1.3.2 - added support again for 'skip_passwd' control flag. * 1.3.10 - ALWAYS add Password attribute, to make packets RFC compliant. * 1.3.11 - Bug fixes by Jon Nelson * 1.3.12 - miscellanous bug fixes. Don't add password to accounting * requests; log more errors; add NAS-Port and NAS-Port-Type * attributes to ALL packets. Some patches based on input from * Grzegorz Paszka * 1.3.13 - Always update the configuration file, even if we're given * no options. Patch from Jon Nelson * 1.3.14 - Don't use PATH_MAX, so it builds on GNU Hurd. * 1.3.15 - Implement retry option, miscellanous bug fixes. * 1.3.16 - Miscellaneous fixes (see CVS for history) * 1.3.17 - Security fixes * * * 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 * * The original pam_radius.c code is copyright (c) Cristian Gafton, 1996, * * * Some challenge-response code is copyright (c) CRYPTOCard Inc, 1998. * All rights reserved. */ #define PAM_SM_AUTH #define PAM_SM_PASSWORD #define PAM_SM_SESSION #include #include #ifdef sun #include #endif #include #include "pam_radius_auth.h" #define DPRINT if (ctrl & PAM_DEBUG_ARG) _pam_log /* internal data */ static CONST char *pam_module_name = "pam_radius_auth"; static char conf_file[BUFFER_SIZE]; /* configuration file */ /* we need to save these from open_session to close_session, since * when close_session will be called we won't be root anymore and * won't be able to access again the radius server configuration file * -- cristiang */ static radius_server_t *live_server = NULL; static time_t session_time; /* logging */ static void _pam_log(int err, CONST char *format, ...) { va_list args; char buffer[BUFFER_SIZE]; va_start(args, format); vsprintf(buffer, format, args); /* don't do openlog or closelog, but put our name in to be friendly */ syslog(err, "%s: %s", pam_module_name, buffer); va_end(args); } /* argument parsing */ static int _pam_parse(int argc, CONST char **argv, radius_conf_t *conf) { int ctrl=0; memset(conf, 0, sizeof(radius_conf_t)); /* ensure it's initialized */ strcpy(conf_file, CONF_FILE); /* * If either is not there, then we can't parse anything. */ if ((argc == 0) || (argv == NULL)) { return ctrl; } /* step through arguments */ for (ctrl=0; argc-- > 0; ++argv) { /* generic options */ if (!strncmp(*argv,"conf=",5)) { strcpy(conf_file,*argv+5); } else if (!strcmp(*argv, "use_first_pass")) { ctrl |= PAM_USE_FIRST_PASS; } else if (!strcmp(*argv, "try_first_pass")) { ctrl |= PAM_TRY_FIRST_PASS; } else if (!strcmp(*argv, "skip_passwd")) { ctrl |= PAM_SKIP_PASSWD; } else if (!strncmp(*argv, "retry=", 6)) { conf->retries = atoi(*argv+6); } else if (!strcmp(*argv, "localifdown")) { conf->localifdown = 1; } else if (!strncmp(*argv, "client_id=", 10)) { if (conf->client_id) { _pam_log(LOG_WARNING, "ignoring duplicate '%s'", *argv); } else { conf->client_id = (char *) *argv+10; /* point to the client-id */ } } else if (!strcmp(*argv, "accounting_bug")) { conf->accounting_bug = TRUE; } else if (!strcmp(*argv, "ruser")) { ctrl |= PAM_RUSER_ARG; } else if (!strcmp(*argv, "debug")) { ctrl |= PAM_DEBUG_ARG; conf->debug = 1; } else { _pam_log(LOG_WARNING, "unrecognized option '%s'", *argv); } } return ctrl; } /* Callback function used to free the saved return value for pam_setcred. */ void _int_free( pam_handle_t * pamh, void *x, int error_status ) { free(x); } /************************************************************************* * SMALL HELPER FUNCTIONS *************************************************************************/ /* * Return an IP address in host long notation from * one supplied in standard dot notation. */ static UINT4 ipstr2long(char *ip_str) { char buf[6]; char *ptr; int i; int count; UINT4 ipaddr; int cur_byte; ipaddr = (UINT4)0; for(i = 0;i < 4;i++) { ptr = buf; count = 0; *ptr = '\0'; while(*ip_str != '.' && *ip_str != '\0' && count < 4) { if(!isdigit(*ip_str)) { return((UINT4)0); } *ptr++ = *ip_str++; count++; } if(count >= 4 || count == 0) { return((UINT4)0); } *ptr = '\0'; cur_byte = atoi(buf); if(cur_byte < 0 || cur_byte > 255) { return ((UINT4)0); } ip_str++; ipaddr = ipaddr << 8 | (UINT4)cur_byte; } return(ipaddr); } /* * Check for valid IP address in standard dot notation. */ static int good_ipaddr(char *addr) { int dot_count; int digit_count; dot_count = 0; digit_count = 0; while(*addr != '\0' && *addr != ' ') { if(*addr == '.') { dot_count++; digit_count = 0; } else if(!isdigit(*addr)) { dot_count = 5; } else { digit_count++; if(digit_count > 3) { dot_count = 5; } } addr++; } if(dot_count != 3) { return(-1); } else { return(0); } } /* * Return an IP address in host long notation from a host * name or address in dot notation. */ static UINT4 get_ipaddr(char *host) { struct hostent *hp; if(good_ipaddr(host) == 0) { return(ipstr2long(host)); } else if((hp = gethostbyname(host)) == (struct hostent *)NULL) { return((UINT4)0); } return(ntohl(*(UINT4 *)hp->h_addr)); } /* * take server->hostname, and convert it to server->ip and server->port */ static int host2server(radius_server_t *server) { char *p; int ctrl = 1; /* for DPRINT */ if ((p = strchr(server->hostname, ':')) != NULL) { *(p++) = '\0'; /* split the port off from the host name */ } if ((server->ip.s_addr = get_ipaddr(server->hostname)) == ((UINT4)0)) { DPRINT(LOG_DEBUG, "DEBUG: get_ipaddr(%s) returned 0.\n", server->hostname); return PAM_AUTHINFO_UNAVAIL; } /* * If the server port hasn't already been defined, go get it. */ if (!server->port) { if (p && isdigit(*p)) { /* the port looks like it's a number */ unsigned int i = atoi(p) & 0xffff; if (!server->accounting) { server->port = htons((u_short) i); } else { server->port = htons((u_short) (i + 1)); } } else { /* the port looks like it's a name */ struct servent *svp; if (p) { /* maybe it's not "radius" */ svp = getservbyname (p, "udp"); /* quotes allow distinction from above, lest p be radius or radacct */ DPRINT(LOG_DEBUG, "DEBUG: getservbyname('%s', udp) returned %d.\n", p, svp); *(--p) = ':'; /* be sure to put the delimiter back */ } else { if (!server->accounting) { svp = getservbyname ("radius", "udp"); DPRINT(LOG_DEBUG, "DEBUG: getservbyname(radius, udp) returned %d.\n", svp); } else { svp = getservbyname ("radacct", "udp"); DPRINT(LOG_DEBUG, "DEBUG: getservbyname(radacct, udp) returned %d.\n", svp); } } if (svp == (struct servent *) 0) { /* debugging above... */ return PAM_AUTHINFO_UNAVAIL; } server->port = svp->s_port; } } return PAM_SUCCESS; } /* * Do XOR of two buffers. */ static unsigned char * xor(unsigned char *p, unsigned char *q, int length) { int i; unsigned char *retval = p; for (i = 0; i < length; i++) { *(p++) ^= *(q++); } return retval; } /************************************************************************** * MID-LEVEL RADIUS CODE **************************************************************************/ /* * get a pseudo-random vector. */ static void get_random_vector(unsigned char *vector) { #ifdef linux int fd = open("/dev/urandom",O_RDONLY); /* Linux: get *real* random numbers */ int total = 0; if (fd >= 0) { while (total < AUTH_VECTOR_LEN) { int bytes = read(fd, vector + total, AUTH_VECTOR_LEN - total); if (bytes <= 0) break; /* oops! Error */ total += bytes; } close(fd); } if (total != AUTH_VECTOR_LEN) #endif { /* do this *always* on other platforms */ MD5_CTX my_md5; struct timeval tv; struct timezone tz; static unsigned int session = 0; /* make the number harder to guess */ /* Use the time of day with the best resolution the system can give us -- often close to microsecond accuracy. */ gettimeofday(&tv,&tz); if (session == 0) { session = getppid(); /* (possibly) hard to guess information */ } tv.tv_sec ^= getpid() * session++; /* Hash things to get maybe cryptographically strong pseudo-random numbers */ MD5Init(&my_md5); MD5Update(&my_md5, (unsigned char *) &tv, sizeof(tv)); MD5Update(&my_md5, (unsigned char *) &tz, sizeof(tz)); MD5Final(vector, &my_md5); /* set the final vector */ } } /* * RFC 2139 says to do generate the accounting request vector this way. * However, the Livingston 1.16 server doesn't check it. The Cistron * server (http://home.cistron.nl/~miquels/radius/) does, and this code * seems to work with it. It also works with Funk's Steel-Belted RADIUS. */ static void get_accounting_vector(AUTH_HDR *request, radius_server_t *server) { MD5_CTX my_md5; int secretlen = strlen(server->secret); int len = ntohs(request->length); memset(request->vector, 0, AUTH_VECTOR_LEN); MD5Init(&my_md5); memcpy(((char *)request) + len, server->secret, secretlen); MD5Update(&my_md5, (unsigned char *)request, len + secretlen); MD5Final(request->vector, &my_md5); /* set the final vector */ } /* * Verify the response from the server */ static int verify_packet(char *secret, AUTH_HDR *response, AUTH_HDR *request) { MD5_CTX my_md5; unsigned char calculated[AUTH_VECTOR_LEN]; unsigned char reply[AUTH_VECTOR_LEN]; /* * We could dispense with the memcpy, and do MD5's of the packet * + vector piece by piece. This is easier understand, and maybe faster. */ memcpy(reply, response->vector, AUTH_VECTOR_LEN); /* save the reply */ memcpy(response->vector, request->vector, AUTH_VECTOR_LEN); /* sent vector */ /* MD5(response packet header + vector + response packet data + secret) */ MD5Init(&my_md5); MD5Update(&my_md5, (unsigned char *) response, ntohs(response->length)); /* * This next bit is necessary because of a bug in the original Livingston * RADIUS server. The authentication vector is *supposed* to be MD5'd * with the old password (as the secret) for password changes. * However, the old password isn't used. The "authentication" vector * for the server reply packet is simply the MD5 of the reply packet. * Odd, the code is 99% there, but the old password is never copied * to the secret! */ if (*secret) { MD5Update(&my_md5, (unsigned char *) secret, strlen(secret)); } MD5Final(calculated, &my_md5); /* set the final vector */ /* Did he use the same random vector + shared secret? */ if (memcmp(calculated, reply, AUTH_VECTOR_LEN) != 0) { return FALSE; } return TRUE; } /* * Find an attribute in a RADIUS packet. Note that the packet length * is *always* kept in network byte order. */ static attribute_t * find_attribute(AUTH_HDR *response, unsigned char type) { attribute_t *attr = (attribute_t *) &response->data; int len = ntohs(response->length) - AUTH_HDR_LEN; while (attr->attribute != type) { if ((len -= attr->length) <= 0) { return NULL; /* not found */ } attr = (attribute_t *) ((char *) attr + attr->length); } return attr; } /* * Add an attribute to a RADIUS packet. */ static void add_attribute(AUTH_HDR *request, unsigned char type, CONST unsigned char *data, int length) { attribute_t *p; p = (attribute_t *) ((unsigned char *)request + ntohs(request->length)); p->attribute = type; p->length = length + 2; /* the total size of the attribute */ request->length = htons(ntohs(request->length) + p->length); memcpy(p->data, data, length); } /* * Add an integer attribute to a RADIUS packet. */ static void add_int_attribute(AUTH_HDR *request, unsigned char type, int data) { int value = htonl(data); add_attribute(request, type, (unsigned char *) &value, sizeof(int)); } /* * Add a RADIUS password attribute to the packet. Some magic is done here. * * If it's an PW_OLD_PASSWORD attribute, it's encrypted using the encrypted * PW_PASSWORD attribute as the initialization vector. * * If the password attribute already exists, it's over-written. This allows * us to simply call add_password to update the password for different * servers. */ static void add_password(AUTH_HDR *request, unsigned char type, CONST char *password, char *secret) { MD5_CTX md5_secret, my_md5; unsigned char misc[AUTH_VECTOR_LEN]; int i; int length = strlen(password); unsigned char hashed[256 + AUTH_PASS_LEN]; /* can't be longer than this */ unsigned char *vector; attribute_t *attr; if (length > MAXPASS) { /* shorten the password for now */ length = MAXPASS; } if (length == 0) { length = AUTH_PASS_LEN; /* 0 maps to 16 */ } if ((length & (AUTH_PASS_LEN - 1)) != 0) { length += (AUTH_PASS_LEN - 1); /* round it up */ length &= ~(AUTH_PASS_LEN - 1); /* chop it off */ } /* 16*N maps to itself */ memset(hashed, 0, length); memcpy(hashed, password, strlen(password)); attr = find_attribute(request, PW_PASSWORD); if (type == PW_PASSWORD) { vector = request->vector; } else { vector = attr->data; /* attr CANNOT be NULL here. */ } /* ************************************************************ */ /* encrypt the password */ /* password : e[0] = p[0] ^ MD5(secret + vector) */ MD5Init(&md5_secret); MD5Update(&md5_secret, (unsigned char *) secret, strlen(secret)); my_md5 = md5_secret; /* so we won't re-do the hash later */ MD5Update(&my_md5, vector, AUTH_VECTOR_LEN); MD5Final(misc, &my_md5); /* set the final vector */ xor(hashed, misc, AUTH_PASS_LEN); /* For each step through, e[i] = p[i] ^ MD5(secret + e[i-1]) */ for (i = 1; i < (length >> 4); i++) { my_md5 = md5_secret; /* grab old value of the hash */ MD5Update(&my_md5, &hashed[(i-1) * AUTH_PASS_LEN], AUTH_PASS_LEN); MD5Final(misc, &my_md5); /* set the final vector */ xor(&hashed[i * AUTH_PASS_LEN], misc, AUTH_PASS_LEN); } if (type == PW_OLD_PASSWORD) { attr = find_attribute(request, PW_OLD_PASSWORD); } if (!attr) { add_attribute(request, type, hashed, length); } else { memcpy(attr->data, hashed, length); /* overwrite the packet */ } } static void cleanup(radius_server_t *server) { radius_server_t *next; while (server) { next = server->next; _pam_drop(server->hostname); _pam_forget(server->secret); _pam_drop(server); server = next; } } /* * allocate and open a local port for communication with the RADIUS * server */ static int initialize(radius_conf_t *conf, int accounting) { struct sockaddr salocal; u_short local_port; char hostname[BUFFER_SIZE]; char secret[BUFFER_SIZE]; char buffer[BUFFER_SIZE]; char *p; FILE *fserver; radius_server_t *server = NULL; struct sockaddr_in * s_in; int timeout; int line = 0; /* the first time around, read the configuration file */ if ((fserver = fopen (conf_file, "r")) == (FILE*)NULL) { _pam_log(LOG_ERR, "Could not open configuration file %s: %s\n", conf_file, strerror(errno)); return PAM_ABORT; } while (!feof(fserver) && (fgets (buffer, sizeof(buffer), fserver) != (char*) NULL) && (!ferror(fserver))) { line++; p = buffer; /* * Skip blank lines and whitespace */ while (*p && ((*p == ' ') || (*p == '\t') || (*p == '\r') || (*p == '\n'))) p++; /* * Nothing, or just a comment. Ignore the line. */ if ((!*p) || (*p == '#')) { continue; } timeout = 3; if (sscanf(p, "%s %s %d", hostname, secret, &timeout) < 2) { _pam_log(LOG_ERR, "ERROR reading %s, line %d: Could not read hostname or secret\n", conf_file, line); continue; /* invalid line */ } else { /* read it in and save the data */ radius_server_t *tmp; tmp = malloc(sizeof(radius_server_t)); if (server) { server->next = tmp; server = server->next; } else { conf->server = tmp; server= tmp; /* first time */ } /* sometime later do memory checks here */ server->hostname = strdup(hostname); server->secret = strdup(secret); server->accounting = accounting; server->port = 0; if ((timeout < 1) || (timeout > 60)) { server->timeout = 3; } else { server->timeout = timeout; } server->next = NULL; } } fclose(fserver); if (!server) { /* no server found, die a horrible death */ _pam_log(LOG_ERR, "No RADIUS server found in configuration file %s\n", conf_file); return PAM_AUTHINFO_UNAVAIL; } /* open a socket. Dies if it fails */ conf->sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (conf->sockfd < 0) { _pam_log(LOG_ERR, "Failed to open RADIUS socket: %s\n", strerror(errno)); return PAM_AUTHINFO_UNAVAIL; } /* set up the local end of the socket communications */ s_in = (struct sockaddr_in *) &salocal; memset ((char *) s_in, '\0', sizeof(struct sockaddr)); s_in->sin_family = AF_INET; s_in->sin_addr.s_addr = INADDR_ANY; /* * Use our process ID as a local port for RADIUS. */ local_port = (getpid() & 0x7fff) + 1024; do { local_port++; s_in->sin_port = htons(local_port); } while ((bind(conf->sockfd, &salocal, sizeof (struct sockaddr_in)) < 0) && (local_port < 64000)); if (local_port >= 64000) { close(conf->sockfd); _pam_log(LOG_ERR, "No open port we could bind to."); return PAM_AUTHINFO_UNAVAIL; } return PAM_SUCCESS; } /* * Helper function for building a radius packet. * It initializes *some* of the header, and adds common attributes. */ static void build_radius_packet(AUTH_HDR *request, CONST char *user, CONST char *password, radius_conf_t *conf) { char hostname[256]; UINT4 ipaddr; hostname[0] = '\0'; gethostname(hostname, sizeof(hostname) - 1); request->length = htons(AUTH_HDR_LEN); if (password) { /* make a random authentication req vector */ get_random_vector(request->vector); } add_attribute(request, PW_USER_NAME, (unsigned char *) user, strlen(user)); /* * Add a password, if given. */ if (password) { add_password(request, PW_PASSWORD, password, conf->server->secret); /* * Add a NULL password to non-accounting requests. */ } else if (request->code != PW_ACCOUNTING_REQUEST) { add_password(request, PW_PASSWORD, "", conf->server->secret); } /* the packet is from localhost if on localhost, to make configs easier */ if ((conf->server->ip.s_addr == ntohl(0x7f000001)) || (!hostname[0])) { ipaddr = 0x7f000001; } else { struct hostent *hp; if ((hp = gethostbyname(hostname)) == (struct hostent *) NULL) { ipaddr = 0x00000000; /* no client IP address */ } else { ipaddr = ntohl(*(UINT4 *) hp->h_addr); /* use the first one available */ } } /* If we can't find an IP address, then don't add one */ if (ipaddr) { add_int_attribute(request, PW_NAS_IP_ADDRESS, ipaddr); } /* There's always a NAS identifier */ if (conf->client_id && *conf->client_id) { add_attribute(request, PW_NAS_IDENTIFIER, (unsigned char *) conf->client_id, strlen(conf->client_id)); } /* * Add in the port (pid) and port type (virtual). * * We might want to give the TTY name here, too. */ add_int_attribute(request, PW_NAS_PORT_ID, getpid()); add_int_attribute(request, PW_NAS_PORT_TYPE, PW_NAS_PORT_TYPE_VIRTUAL); } /* * Talk RADIUS to a server. * Send a packet and get the response */ static int talk_radius(radius_conf_t *conf, AUTH_HDR *request, AUTH_HDR *response, char *password, char *old_password, int tries) { int salen, total_length; fd_set set; struct timeval tv; time_t now, end; int rcode; struct sockaddr saremote; struct sockaddr_in *s_in = (struct sockaddr_in *) &saremote; radius_server_t *server = conf->server; int ok; int server_tries; int retval; /* ************************************************************ */ /* Now that we're done building the request, we can send it */ /* Hmm... on password change requests, all of the found server information could be saved with a pam_set_data(), which means even the radius_conf_t information will have to be malloc'd at some point On the other hand, we could just try all of the servers again in sequence, on the off chance that one may have ended up fixing itself. */ /* loop over all available servers */ while (server != NULL) { /* only look up IP information as necessary */ if ((retval = host2server(server)) != PAM_SUCCESS) { _pam_log(LOG_ERR, "Failed looking up IP address for RADIUS server %s (errcode=%d)", server->hostname, retval); ok = FALSE; goto next; /* skip to the next server */ } /* set up per-server IP && port configuration */ memset ((char *) s_in, '\0', sizeof(struct sockaddr)); s_in->sin_family = AF_INET; s_in->sin_addr.s_addr = htonl(server->ip.s_addr); s_in->sin_port = server->port; total_length = ntohs(request->length); if (!password) { /* make an RFC 2139 p6 request authenticator */ get_accounting_vector(request, server); } server_tries = tries; send: /* send the packet */ if (sendto(conf->sockfd, (char *) request, total_length, 0, &saremote, sizeof(struct sockaddr_in)) < 0) { _pam_log(LOG_ERR, "Error sending RADIUS packet to server %s: %s", server->hostname, strerror(errno)); ok = FALSE; goto next; /* skip to the next server */ } /* ************************************************************ */ /* Wait for the response, and verify it. */ salen = sizeof(struct sockaddr); tv.tv_sec = server->timeout; /* wait for the specified time */ tv.tv_usec = 0; FD_ZERO(&set); /* clear out the set */ FD_SET(conf->sockfd, &set); /* wait only for the RADIUS UDP socket */ time(&now); end = now + tv.tv_sec; /* loop, waiting for the select to return data */ ok = TRUE; while (ok) { rcode = select(conf->sockfd + 1, &set, NULL, NULL, &tv); /* select timed out */ if (rcode == 0) { _pam_log(LOG_ERR, "RADIUS server %s failed to respond", server->hostname); if (--server_tries) goto send; ok = FALSE; break; /* exit from the select loop */ } else if (rcode < 0) { /* select had an error */ if (errno == EINTR) { /* we were interrupted */ time(&now); if (now > end) { _pam_log(LOG_ERR, "RADIUS server %s failed to respond", server->hostname); if (--server_tries) goto send; ok = FALSE; break; /* exit from the select loop */ } tv.tv_sec = end - now; if (tv.tv_sec == 0) { /* keep waiting */ tv.tv_sec = 1; } } else { /* not an interrupt, it was a real error */ _pam_log(LOG_ERR, "Error waiting for response from RADIUS server %s: %s", server->hostname, strerror(errno)); ok = FALSE; break; } /* the select returned OK */ } else if (FD_ISSET(conf->sockfd, &set)) { /* try to receive some data */ if ((total_length = recvfrom(conf->sockfd, (char *) response, BUFFER_SIZE, 0, &saremote, &salen)) < 0) { _pam_log(LOG_ERR, "error reading RADIUS packet from server %s: %s", server->hostname, strerror(errno)); ok = FALSE; break; /* there's data, see if it's valid */ } else { char *p = server->secret; if ((ntohs(response->length) != total_length) || (ntohs(response->length) > BUFFER_SIZE)) { _pam_log(LOG_ERR, "RADIUS packet from server %s is corrupted", server->hostname); ok = FALSE; break; } /* Check if we have the data OK. We should also check request->id */ if (password) { if (old_password) { #ifdef LIVINGSTON_PASSWORD_VERIFY_BUG_FIXED p = old_password; /* what it should be */ #else p = ""; /* what it really is */ #endif } /* * RFC 2139 p.6 says not do do this, but the Livingston 1.16 * server disagrees. If the user says he wants the bug, give in. */ } else { /* authentication request */ if (conf->accounting_bug) { p = ""; } } if (!verify_packet(p, response, request)) { _pam_log(LOG_ERR, "packet from RADIUS server %s fails verification: The shared secret is probably incorrect.", server->hostname); ok = FALSE; break; } /* * Check that the response ID matches the request ID. */ if (response->id != request->id) { _pam_log(LOG_WARNING, "Response packet ID %d does not match the request packet ID %d: verification of packet fails", response->id, request->id); ok = FALSE; break; } } /* * Whew! The select is done. It hasn't timed out, or errored out. * It's our descriptor. We've got some data. It's the right size. * The packet is valid. * NOW, we can skip out of the select loop, and process the packet */ break; } /* otherwise, we've got data on another descriptor, keep select'ing */ } /* go to the next server if this one didn't respond */ next: if (!ok) { radius_server_t *old; /* forget about this server */ old = server; server = server->next; conf->server = server; _pam_forget(old->secret); free(old->hostname); free(old); if (server) { /* if there's more servers to check */ /* get a new authentication vector, and update the passwords */ get_random_vector(request->vector); request->id = request->vector[0]; /* update passwords, as appropriate */ if (password) { get_random_vector(request->vector); if (old_password) { /* password change request */ add_password(request, PW_PASSWORD, password, old_password); add_password(request, PW_OLD_PASSWORD, old_password, old_password); } else { /* authentication request */ add_password(request, PW_PASSWORD, password, server->secret); } } } continue; } else { /* we've found one that does respond, forget about the other servers */ cleanup(server->next); server->next = NULL; live_server = server; /* we've got a live one! */ break; } } if (!server) { _pam_log(LOG_ERR, "All RADIUS servers failed to respond."); if (conf->localifdown) retval = PAM_IGNORE; else retval = PAM_AUTHINFO_UNAVAIL; } else { retval = PAM_SUCCESS; } return retval; } /************************************************************************** * MIDLEVEL PAM CODE **************************************************************************/ /* this is our front-end for module-application conversations */ #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { return retval; } static int rad_converse(pam_handle_t *pamh, int msg_style, char *message, char **password) { CONST struct pam_conv *conv; struct pam_message resp_msg; CONST struct pam_message *msg[1]; struct pam_response *resp = NULL; int retval; resp_msg.msg_style = msg_style; resp_msg.msg = message; msg[0] = &resp_msg; /* grab the password */ retval = pam_get_item(pamh, PAM_CONV, (CONST void **) &conv); PAM_FAIL_CHECK; retval = conv->conv(1, msg, &resp,conv->appdata_ptr); PAM_FAIL_CHECK; if (password) { /* assume msg.type needs a response */ /* I'm not sure if this next bit is necessary on Linux */ #ifdef sun /* NULL response, fail authentication */ if ((resp == NULL) || (resp->resp == NULL)) { return PAM_SYSTEM_ERR; } #endif *password = resp->resp; free(resp); } return PAM_SUCCESS; } /************************************************************************** * GENERAL CODE **************************************************************************/ #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { \ int *pret = malloc( sizeof(int) ); \ *pret = retval; \ pam_set_data( pamh, "rad_setcred_return" \ , (void *) pret, _int_free ); \ return retval; } PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,CONST char **argv) { CONST char *user; CONST char **userinfo; char *password = NULL; CONST char *rhost; char *resp2challenge = NULL; int ctrl; int retval = PAM_AUTH_ERR; char recv_buffer[4096]; char send_buffer[4096]; AUTH_HDR *request = (AUTH_HDR *) send_buffer; AUTH_HDR *response = (AUTH_HDR *) recv_buffer; radius_conf_t config; ctrl = _pam_parse(argc, argv, &config); /* grab the user name */ retval = pam_get_user(pamh, &user, NULL); PAM_FAIL_CHECK; /* check that they've entered something, and not too long, either */ if ((user == NULL) || (strlen(user) > MAXPWNAM)) { int *pret = malloc( sizeof(int) ); *pret = PAM_USER_UNKNOWN; pam_set_data( pamh, "rad_setcred_return", (void *) pret, _int_free ); DPRINT(LOG_DEBUG, "User name was NULL, or too long"); return PAM_USER_UNKNOWN; } DPRINT(LOG_DEBUG, "Got user name %s", user); if (ctrl & PAM_RUSER_ARG) { retval = pam_get_item(pamh, PAM_RUSER, (CONST void **) &userinfo); PAM_FAIL_CHECK; DPRINT(LOG_DEBUG, "Got PAM_RUSER name %s", userinfo); if (!strcmp("root", user)) { user = userinfo; DPRINT(LOG_DEBUG, "Username now %s from ruser", user); } else { DPRINT(LOG_DEBUG, "Skipping ruser for non-root auth"); }; }; /* * Get the IP address of the authentication server * Then, open a socket, and bind it to a port */ retval = initialize(&config, FALSE); PAM_FAIL_CHECK; /* * If there's no client id specified, use the service type, to help * keep track of which service is doing the authentication. */ if (!config.client_id) { retval = pam_get_item(pamh, PAM_SERVICE, (CONST void **) &config.client_id); PAM_FAIL_CHECK; } /* now we've got a socket open, so we've got to clean it up on error */ #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {goto error; } /* build and initialize the RADIUS packet */ request->code = PW_AUTHENTICATION_REQUEST; get_random_vector(request->vector); request->id = request->vector[0]; /* this should be evenly distributed */ /* grab the password (if any) from the previous authentication layer */ retval = pam_get_item(pamh, PAM_AUTHTOK, (CONST void **) &password); PAM_FAIL_CHECK; if(password) { password = strdup(password); DPRINT(LOG_DEBUG, "Got password %s", password); } /* no previous password: maybe get one from the user */ if (!password) { if (ctrl & PAM_USE_FIRST_PASS) { retval = PAM_AUTH_ERR; /* use one pass only, stopping if it fails */ goto error; } /* check to see if we send a NULL password the first time around */ if (!(ctrl & PAM_SKIP_PASSWD)) { retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, "Password: ", &password); PAM_FAIL_CHECK; } } /* end of password == NULL */ build_radius_packet(request, user, password, &config); /* not all servers understand this service type, but some do */ add_int_attribute(request, PW_USER_SERVICE_TYPE, PW_AUTHENTICATE_ONLY); /* * Tell the server which host the user is coming from. * * Note that this is NOT the IP address of the machine running PAM! * It's the IP address of the client. */ retval = pam_get_item(pamh, PAM_RHOST, (CONST void **) &rhost); PAM_FAIL_CHECK; if (rhost) { add_attribute(request, PW_CALLING_STATION_ID, (unsigned char *) rhost, strlen(rhost)); } DPRINT(LOG_DEBUG, "Sending RADIUS request code %d", request->code); retval = talk_radius(&config, request, response, password, NULL, config.retries + 1); PAM_FAIL_CHECK; DPRINT(LOG_DEBUG, "Got RADIUS response code %d", response->code); /* * If we get an authentication failure, and we sent a NULL password, * ask the user for one and continue. * * If we get an access challenge, then do a response, for as many * challenges as we receive. */ while (response->code == PW_ACCESS_CHALLENGE) { attribute_t *a_state, *a_reply; char challenge[BUFFER_SIZE]; /* Now we do a bit more work: challenge the user, and get a response */ if (((a_state = find_attribute(response, PW_STATE)) == NULL) || ((a_reply = find_attribute(response, PW_REPLY_MESSAGE)) == NULL)) { /* Actually, State isn't required. */ _pam_log(LOG_ERR, "RADIUS Access-Challenge received with State or Reply-Message missing"); retval = PAM_AUTHINFO_UNAVAIL; goto error; } /* * Security fixes. */ if ((a_state->length <= 2) || (a_reply->length <= 2)) { _pam_log(LOG_ERR, "RADIUS Access-Challenge received with invalid State or Reply-Message"); retval = PAM_AUTHINFO_UNAVAIL; goto error; } memcpy(challenge, a_reply->data, a_reply->length - 2); challenge[a_reply->length - 2] = 0; /* It's full challenge-response, we should have echo on */ retval = rad_converse(pamh, PAM_PROMPT_ECHO_ON, challenge, &resp2challenge); /* now that we've got a response, build a new radius packet */ build_radius_packet(request, user, resp2challenge, &config); /* request->code is already PW_AUTHENTICATION_REQUEST */ request->id++; /* one up from the request */ /* copy the state over from the servers response */ add_attribute(request, PW_STATE, a_state->data, a_state->length - 2); retval = talk_radius(&config, request, response, resp2challenge, NULL, 1); PAM_FAIL_CHECK; DPRINT(LOG_DEBUG, "Got response to challenge code %d", response->code); } /* Whew! Done the pasword checks, look for an authentication acknowledge */ if (response->code == PW_AUTHENTICATION_ACK) { retval = PAM_SUCCESS; } else { retval = PAM_AUTH_ERR; /* authentication failure */ error: /* If there was a password pass it to the next layer */ if (password && *password) { pam_set_item(pamh, PAM_AUTHTOK, password); } } if (ctrl & PAM_DEBUG_ARG) { _pam_log(LOG_DEBUG, "authentication %s" , retval==PAM_SUCCESS ? "succeeded":"failed" ); } close(config.sockfd); cleanup(config.server); _pam_forget(password); _pam_forget(resp2challenge); { int *pret = malloc( sizeof(int) ); *pret = retval; pam_set_data( pamh, "rad_setcred_return", (void *) pret, _int_free ); } return retval; } /* * Return a value matching the return value of pam_sm_authenticate, for * greatest compatibility. * (Always returning PAM_SUCCESS breaks other authentication modules; * always returning PAM_IGNORE breaks PAM when we're the only module.) */ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,CONST char **argv) { int retval, *pret; retval = PAM_SUCCESS; pret = &retval; pam_get_data( pamh, "rad_setcred_return", (CONST void **) &pret ); return *pret; } #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { return PAM_SESSION_ERR; } static int pam_private_session(pam_handle_t *pamh, int flags, int argc, CONST char **argv, int status) { CONST char *user; int ctrl; int retval = PAM_AUTH_ERR; char recv_buffer[4096]; char send_buffer[4096]; AUTH_HDR *request = (AUTH_HDR *) send_buffer; AUTH_HDR *response = (AUTH_HDR *) recv_buffer; radius_conf_t config; ctrl = _pam_parse(argc, argv, &config); /* grab the user name */ retval = pam_get_user(pamh, &user, NULL); PAM_FAIL_CHECK; /* check that they've entered something, and not too long, either */ if ((user == NULL) || (strlen(user) > MAXPWNAM)) { return PAM_USER_UNKNOWN; } /* * Get the IP address of the authentication server * Then, open a socket, and bind it to a port */ retval = initialize(&config, TRUE); PAM_FAIL_CHECK; /* * If there's no client id specified, use the service type, to help * keep track of which service is doing the authentication. */ if (!config.client_id) { retval = pam_get_item(pamh, PAM_SERVICE, (CONST void **) &config.client_id); PAM_FAIL_CHECK; } /* now we've got a socket open, so we've got to clean it up on error */ #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {goto error; } /* build and initialize the RADIUS packet */ request->code = PW_ACCOUNTING_REQUEST; get_random_vector(request->vector); request->id = request->vector[0]; /* this should be evenly distributed */ build_radius_packet(request, user, NULL, &config); add_int_attribute(request, PW_ACCT_STATUS_TYPE, status); sprintf(recv_buffer, "%08d", (int) getpid()); add_attribute(request, PW_ACCT_SESSION_ID, (unsigned char *) recv_buffer, strlen(recv_buffer)); add_int_attribute(request, PW_ACCT_AUTHENTIC, PW_AUTH_RADIUS); if (status == PW_STATUS_START) { session_time = time(NULL); } else { add_int_attribute(request, PW_ACCT_SESSION_TIME, time(NULL) - session_time); } retval = talk_radius(&config, request, response, NULL, NULL, 1); PAM_FAIL_CHECK; /* oops! They don't have the right password. Complain and die. */ if (response->code != PW_ACCOUNTING_RESPONSE) { retval = PAM_PERM_DENIED; goto error; } retval = PAM_SUCCESS; error: close(config.sockfd); cleanup(config.server); return retval; } PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, CONST char **argv) { return pam_private_session(pamh, flags, argc, argv, PW_STATUS_START); } PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, CONST char **argv) { return pam_private_session(pamh, flags, argc, argv, PW_STATUS_STOP); } #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {return retval; } #define MAX_PASSWD_TRIES 3 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, CONST char **argv) { CONST char *user; char *password = NULL; char *new_password = NULL; char *check_password = NULL; int ctrl; int retval = PAM_AUTHTOK_ERR; int attempts; char recv_buffer[4096]; char send_buffer[4096]; AUTH_HDR *request = (AUTH_HDR *) send_buffer; AUTH_HDR *response = (AUTH_HDR *) recv_buffer; radius_conf_t config; ctrl = _pam_parse(argc, argv, &config); /* grab the user name */ retval = pam_get_user(pamh, &user, NULL); PAM_FAIL_CHECK; /* check that they've entered something, and not too long, either */ if ((user == NULL) || (strlen(user) > MAXPWNAM)) { return PAM_USER_UNKNOWN; } /* * Get the IP address of the authentication server * Then, open a socket, and bind it to a port */ retval = initialize(&config, FALSE); PAM_FAIL_CHECK; /* * If there's no client id specified, use the service type, to help * keep track of which service is doing the authentication. */ if (!config.client_id) { retval = pam_get_item(pamh, PAM_SERVICE, (CONST void **) &config.client_id); PAM_FAIL_CHECK; } /* now we've got a socket open, so we've got to clean it up on error */ #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {goto error; } /* grab the old password (if any) from the previous password layer */ retval = pam_get_item(pamh, PAM_OLDAUTHTOK, (CONST void **) &password); PAM_FAIL_CHECK; if(password) password = strdup(password); /* grab the new password (if any) from the previous password layer */ retval = pam_get_item(pamh, PAM_AUTHTOK, (CONST void **) &new_password); PAM_FAIL_CHECK; if(new_password) new_password = strdup(new_password); /* preliminary password change checks. */ if (flags & PAM_PRELIM_CHECK) { if (!password) { /* no previous password: ask for one */ retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, "Password: ", &password); PAM_FAIL_CHECK; } /* * We now check the password to see if it's the right one. * If it isn't, we let the user try again. * Note that RADIUS doesn't have any concept of 'root'. The only way * that root can change someone's password is to log into the RADIUS * server, and and change it there. */ /* build and initialize the access request RADIUS packet */ request->code = PW_AUTHENTICATION_REQUEST; get_random_vector(request->vector); request->id = request->vector[0]; /* this should be evenly distributed */ build_radius_packet(request, user, password, &config); add_int_attribute(request, PW_USER_SERVICE_TYPE, PW_AUTHENTICATE_ONLY); retval = talk_radius(&config, request, response, password, NULL, 1); PAM_FAIL_CHECK; /* oops! They don't have the right password. Complain and die. */ if (response->code != PW_AUTHENTICATION_ACK) { _pam_forget(password); retval = PAM_PERM_DENIED; goto error; } /* * We're now sure it's the right user. * Ask for their new password, if appropriate */ if (!new_password) { /* not found yet: ask for it */ int new_attempts; attempts = 0; /* loop, trying to get matching new passwords */ while (attempts++ < 3) { /* loop, trying to get a new password */ new_attempts = 0; while (new_attempts++ < 3) { retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, "New password: ", &new_password); PAM_FAIL_CHECK; /* the old password may be short. Check it, first. */ if (strcmp(password, new_password) == 0) { /* are they the same? */ rad_converse(pamh, PAM_ERROR_MSG, "You must choose a new password.", NULL); _pam_forget(new_password); continue; } else if (strlen(new_password) < 6) { rad_converse(pamh, PAM_ERROR_MSG, "it's WAY too short", NULL); _pam_forget(new_password); continue; } /* insert crypt password checking here */ break; /* the new password is OK */ } if (new_attempts >= 3) { /* too many new password attempts: die */ retval = PAM_AUTHTOK_ERR; goto error; } /* make sure of the password by asking for verification */ retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, "New password (again): ", &check_password); PAM_FAIL_CHECK; retval = strcmp(new_password, check_password); _pam_forget(check_password); /* if they don't match, don't pass them to the next module */ if (retval != 0) { _pam_forget(new_password); rad_converse(pamh, PAM_ERROR_MSG, "You must enter the same password twice.", NULL); retval = PAM_AUTHTOK_ERR; goto error; /* ??? maybe this should be a 'continue' ??? */ } break; /* everything's fine */ } /* loop, trying to get matching new passwords */ if (attempts >= 3) { /* too many new password attempts: die */ retval = PAM_AUTHTOK_ERR; goto error; } } /* now we have a new password which passes all of our tests */ /* * Solaris 2.6 calls pam_sm_chauthtok only ONCE, with PAM_PRELIM_CHECK * set. */ #ifndef sun /* If told to update the authentication token, do so. */ } else if (flags & PAM_UPDATE_AUTHTOK) { #endif if (!password || !new_password) { /* ensure we've got passwords */ retval = PAM_AUTHTOK_ERR; goto error; } /* build and initialize the password change request RADIUS packet */ request->code = PW_PASSWORD_REQUEST; get_random_vector(request->vector); request->id = request->vector[0]; /* this should be evenly distributed */ /* the secret here can not be know to the user, so it's the new password */ _pam_forget(config.server->secret); config.server->secret = strdup(password); /* it's free'd later */ build_radius_packet(request, user, new_password, &config); add_password(request, PW_OLD_PASSWORD, password, password); retval = talk_radius(&config, request, response, new_password, password, 1); PAM_FAIL_CHECK; /* Whew! Done password changing, check for password acknowledge */ if (response->code != PW_PASSWORD_ACK) { retval = PAM_AUTHTOK_ERR; goto error; } } /* * Send the passwords to the next stage if preliminary checks fail, * or if the password change request fails. */ if ((flags & PAM_PRELIM_CHECK) || (retval != PAM_SUCCESS)) { error: /* If there was a password pass it to the next layer */ if (password && *password) { pam_set_item(pamh, PAM_OLDAUTHTOK, password); } if (new_password && *new_password) { pam_set_item(pamh, PAM_AUTHTOK, new_password); } } if (ctrl & PAM_DEBUG_ARG) { _pam_log(LOG_DEBUG, "password change %s" , retval==PAM_SUCCESS ? "succeeded":"failed" ); } close(config.sockfd); cleanup(config.server); _pam_forget(password); _pam_forget(new_password); return retval; } /* * Do nothing for account management. This is apparently needed by * some programs. */ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc,CONST char **argv) { int retval; retval = PAM_SUCCESS; return retval; } #ifdef PAM_STATIC /* static module data */ struct pam_module _pam_radius_modstruct = { "pam_radius_auth", pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, pam_sm_open_session, pam_sm_close_session, pam_sm_chauthtok, }; #endif pam_radius-1.3.17/pam_radius_auth.conf0000644000201400020140000000242007132420622016643 0ustar alandaland# pam_radius_auth configuration file. Copy to: /etc/raddb/server # # For proper security, this file SHOULD have permissions 0600, # that is readable by root, and NO ONE else. If anyone other than # root can read this file, then they can spoof responses from the server! # # There are 3 fields per line in this file. There may be multiple # lines. Blank lines or lines beginning with '#' are treated as # comments, and are ignored. The fields are: # # server[:port] secret [timeout] # # the port name or number is optional. The default port name is # "radius", and is looked up from /etc/services The timeout field is # optional. The default timeout is 3 seconds. # # If multiple RADIUS server lines exist, they are tried in order. The # first server to return success or failure causes the module to return # success or failure. Only if a server fails to response is it skipped, # and the next server in turn is used. # # The timeout field controls how many seconds the module waits before # deciding that the server has failed to respond. # # server[:port] shared_secret timeout (s) 127.0.0.1 secret 1 other-server other-secret 3 # # having localhost in your radius configuration is a Good Thing. # # See the INSTALL file for pam.conf hints. pam_radius-1.3.17/pam_radius_auth.h0000644000201400020140000000511210601655643016156 0ustar alandaland#ifndef PAM_RADIUS_H #define PAM_RADIUS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "radius.h" #include "md5.h" /************************************************************************* * Additional RADIUS definitions *************************************************************************/ /* Per-attribute structure */ typedef struct attribute_t { unsigned char attribute; unsigned char length; unsigned char data[1]; } attribute_t; typedef struct radius_server_t { struct radius_server_t *next; struct in_addr ip; u_short port; char *hostname; char *secret; int timeout; int accounting; } radius_server_t; typedef struct radius_conf_t { radius_server_t *server; int retries; int localifdown; char *client_id; int accounting_bug; int sockfd; int debug; } radius_conf_t; /************************************************************************* * Platform specific defines *************************************************************************/ #ifdef sun #define PAM_EXTERN extern /* * On older versions of Solaris, you may have to change this to: * #define CONST */ #define CONST const #else #define CONST const #endif /************************************************************************* * Useful macros and defines *************************************************************************/ #define _pam_forget(X) if (X) {memset(X, 0, strlen(X));free(X);X = NULL;} #ifndef _pam_drop #define _pam_drop(X) if (X) {free(X);X = NULL;} #endif #define PAM_DEBUG_ARG 1 #define PAM_SKIP_PASSWD 2 #define PAM_USE_FIRST_PASS 4 #define PAM_TRY_FIRST_PASS 8 #define PAM_RUSER_ARG 16 /* Module defines */ #ifndef BUFFER_SIZE #define BUFFER_SIZE 1024 #endif /* BUFFER_SIZE */ #define MAXPWNAM 253 /* maximum user name length. Server dependent, * this is the default value */ #define MAXPASS 128 /* max password length. Again, depends on server * compiled in. This is the default. */ #ifndef CONF_FILE /* the configuration file holding the server secret */ #define CONF_FILE "/etc/raddb/server" #endif /* CONF_FILE */ #ifndef FALSE #define FALSE 0 #undef TRUE #define TRUE !FALSE #endif #endif /* PAM_RADIUS_H */ pam_radius-1.3.17/pam_radius_auth.spec0000644000201400020140000000253607477744414016704 0ustar alandaland%define name pam_radius_auth %define version 1.3.15 %define release 0 Name: %{name} Summary: PAM Module for RADIUS Authentication Version: %{version} Release: %{release} Source: ftp://ftp.freeradius.org/pub/radius/pam_radius_auth-%{version}.tar URL: http://www.freeradius.org/pam_radius_auth/ Group: System Environment/Libraries BuildRoot: %{_tmppath}/%{name}-buildroot License: BSD-like or GNU GPL Requires: pam %description This is the PAM to RADIUS authentication module. It allows any PAM-capable machine to become a RADIUS client for authentication and accounting requests. You will need a RADIUS server to perform the actual authentication. %prep %setup -q -n pam_radius-%{version} %build make %install mkdir -p %{buildroot}/lib/security cp -p pam_radius_auth.so %{buildroot}/lib/security mkdir -p %{buildroot}/etc/raddb [ -f %{buildroot}/etc/raddb/server ] || cp -p pam_radius_auth.conf %{buildroot}/etc/raddb/server chown root %{buildroot}/etc/raddb/server chgrp root %{buildroot}/etc/raddb/server chmod 0600 %{buildroot}/etc/raddb/server %clean [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT %postun rmdir /etc/raddb || true %files %defattr(-,root,root,0755) %doc README INSTALL USAGE Changelog %config /etc/raddb/server /lib/security/pam_radius_auth.so %changelog * Mon Jun 03 2002 Richie Laager 1.3.15-0 - Inital RPM Version pam_radius-1.3.17/radius.h0000644000201400020140000001370207150515477014311 0ustar alandaland/* * * RADIUS * Remote Authentication Dial In User Service * * * Livingston Enterprises, Inc. * 6920 Koll Center Parkway * Pleasanton, CA 94566 * * Copyright 1992 Livingston Enterprises, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose and without fee is hereby granted, provided that this * copyright and permission notice appear on all copies and supporting * documentation, the name of Livingston Enterprises, Inc. not be used * in advertising or publicity pertaining to distribution of the * program without specific prior permission, and notice be given * in supporting documentation that copying and distribution is by * permission of Livingston Enterprises, Inc. * * Livingston Enterprises, Inc. makes no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty. * */ /* * @(#)radius.h 1.9 11/14/94 */ #ifndef RADIUS_H #define RADIUS_H #define AUTH_VECTOR_LEN 16 #define AUTH_PASS_LEN 16 #define AUTH_STRING_LEN 128 /* maximum of 254 */ #ifndef UINT4 typedef unsigned long UINT4; #endif typedef struct pw_auth_hdr { u_char code; u_char id; u_short length; u_char vector[AUTH_VECTOR_LEN]; u_char data[2]; } AUTH_HDR; #define AUTH_HDR_LEN 20 #define CHAP_VALUE_LENGTH 16 #define PW_AUTH_UDP_PORT 1645 #define PW_ACCT_UDP_PORT 1646 #define PW_TYPE_STRING 0 #define PW_TYPE_INTEGER 1 #define PW_TYPE_IPADDR 2 #define PW_TYPE_DATE 3 #define PW_AUTHENTICATION_REQUEST 1 #define PW_AUTHENTICATION_ACK 2 #define PW_AUTHENTICATION_REJECT 3 #define PW_ACCOUNTING_REQUEST 4 #define PW_ACCOUNTING_RESPONSE 5 #define PW_ACCOUNTING_STATUS 6 #define PW_PASSWORD_REQUEST 7 #define PW_PASSWORD_ACK 8 #define PW_PASSWORD_REJECT 9 #define PW_ACCOUNTING_MESSAGE 10 #define PW_ACCESS_CHALLENGE 11 #define PW_USER_NAME 1 #define PW_PASSWORD 2 #define PW_CHAP_PASSWORD 3 #define PW_NAS_IP_ADDRESS 4 #define PW_NAS_PORT_ID 5 #define PW_USER_SERVICE_TYPE 6 #define PW_FRAMED_PROTOCOL 7 #define PW_FRAMED_ADDRESS 8 #define PW_FRAMED_NETMASK 9 #define PW_FRAMED_ROUTING 10 #define PW_FRAMED_FILTER_ID 11 #define PW_FRAMED_MTU 12 #define PW_FRAMED_COMPRESSION 13 #define PW_LOGIN_HOST 14 #define PW_LOGIN_SERVICE 15 #define PW_LOGIN_TCP_PORT 16 #define PW_OLD_PASSWORD 17 #define PW_REPLY_MESSAGE 18 #define PW_CALLBACK_NUMBER 19 #define PW_CALLBACK_ID 20 #define PW_EXPIRATION 21 #define PW_FRAMED_ROUTE 22 #define PW_FRAMED_IPXNET 23 #define PW_STATE 24 #define PW_CLASS 25 /* string */ #define PW_VENDOR_SPECIFIC 26 /* vendor */ #define PW_SESSION_TIMEOUT 27 /* integer */ #define PW_IDLE_TIMEOUT 28 /* integer */ #define PW_TERMINATION_ACTION 29 /* integer */ #define PW_CALLED_STATION_ID 30 /* string */ #define PW_CALLING_STATION_ID 31 /* string */ #define PW_NAS_IDENTIFIER 32 /* string */ #define PW_PROXY_STATE 33 /* string */ #define PW_LOGIN_LAT_SERVICE 34 /* string */ #define PW_LOGIN_LAT_NODE 35 /* string */ #define PW_LOGIN_LAT_GROUP 36 /* string */ #define PW_FRAMED_APPLETALK_LINK 37 /* integer */ #define PW_FRAMED_APPLETALK_NETWORK 38 /* integer */ #define PW_FRAMED_APPLETALK_ZONE 39 /* string */ #define PW_ACCT_STATUS_TYPE 40 #define PW_ACCT_DELAY_TIME 41 #define PW_ACCT_INPUT_OCTETS 42 #define PW_ACCT_OUTPUT_OCTETS 43 #define PW_ACCT_SESSION_ID 44 #define PW_ACCT_AUTHENTIC 45 #define PW_ACCT_SESSION_TIME 46 #define PW_CHAP_CHALLENGE 60 /* string */ #define PW_NAS_PORT_TYPE 61 /* integer */ #define PW_PORT_LIMIT 62 /* integer */ #define PW_LOGIN_LAT_PORT 63 /* string */ #define PW_PROMPT 64 /* integer */ /* * INTEGER TRANSLATIONS */ /* USER TYPES */ #define PW_LOGIN_USER 1 #define PW_FRAMED_USER 2 #define PW_DIALBACK_LOGIN_USER 3 #define PW_DIALBACK_FRAMED_USER 4 #define PW_OUTBOUND_USER 5 #define PW_SHELL_USER 6 /* FRAMED PROTOCOLS */ #define PW_PPP 1 #define PW_SLIP 2 /* FRAMED ROUTING VALUES */ #define PW_NONE 0 #define PW_BROADCAST 1 #define PW_LISTEN 2 #define PW_BROADCAST_LISTEN 3 /* NAS PORT TYPES */ #define PW_NAS_PORT_TYPE_VIRTUAL 5 /* FRAMED COMPRESSION TYPES */ #define PW_VAN_JACOBSEN_TCP_IP 1 /* LOGIN SERVICES */ #define PW_TELNET 0 #define PW_RLOGIN 1 #define PW_TCP_CLEAR 2 #define PW_PORTMASTER 3 #define PW_AUTHENTICATE_ONLY 8 /* AUTHENTICATION LEVEL */ #define PW_AUTH_NONE 0 #define PW_AUTH_RADIUS 1 #define PW_AUTH_LOCAL 2 /* STATUS TYPES */ #define PW_STATUS_START 1 #define PW_STATUS_STOP 2 #define PW_STATUS_ALIVE 3 /* Default Database File Names */ #define RADIUS_DIR "/etc/raddb" #define RADACCT_DIR "/usr/adm/radacct" #define RADIUS_DICTIONARY "dictionary" #define RADIUS_CLIENTS "clients" #define RADIUS_USERS "users" #define RADIUS_HOLD "holdusers" #define RADIUS_LOG "logfile" /* Server data structures */ typedef struct dict_attr { char name[32]; int value; int type; struct dict_attr *next; } DICT_ATTR; typedef struct dict_value { char attrname[32]; char name[32]; int value; struct dict_value *next; } DICT_VALUE; typedef struct value_pair { char name[32]; int attribute; int type; UINT4 lvalue; char strvalue[AUTH_STRING_LEN]; struct value_pair *next; } VALUE_PAIR; typedef struct auth_req { UINT4 ipaddr; u_short udp_port; u_char id; u_char code; u_char vector[16]; u_char secret[16]; VALUE_PAIR *request; int child_pid; /* Process ID of child */ UINT4 timestamp; struct auth_req *next; /* Next active request */ } AUTH_REQ; #define SECONDS_PER_DAY 86400 #define MAX_REQUEST_TIME 30 #define CLEANUP_DELAY 5 #define MAX_REQUESTS 100 #endif /* RADIUS_H */