From 384d380eb33fef2432385c8a9607e7ed8526cabf Mon Sep 17 00:00:00 2001 From: Adrian Lang Date: Thu, 16 Feb 2012 10:35:45 +0100 Subject: [PATCH] Init with upstream 0.8.9 --- docs/CHANGELOG | 64 ++ docs/FAQ | 36 + docs/INSTALL | 1 + docs/LICENSE | 341 ++++++ docs/README | 447 ++++++++ docs/htaccess | 22 + engine.php | 2013 +++++++++++++++++++++++++++++++++ images/logo.gif | Bin 0 -> 4253 bytes images/openid-logo.gif | Bin 0 -> 3978 bytes images/openid.gif | Bin 0 -> 237 bytes images/openid.ico | Bin 0 -> 1406 bytes images/openid.png | Bin 0 -> 797 bytes images/seatbelt/icon-gray.png | Bin 0 -> 193 bytes images/seatbelt/icon-high.png | Bin 0 -> 193 bytes images/seatbelt/icon-logo.png | Bin 0 -> 1517 bytes images/user.gif | Bin 0 -> 164 bytes index.php | 84 ++ ldap.php | 172 +++ showme.php | 40 + style.css | 27 + 20 files changed, 3247 insertions(+) create mode 100644 docs/CHANGELOG create mode 100644 docs/FAQ create mode 100644 docs/INSTALL create mode 100644 docs/LICENSE create mode 100644 docs/README create mode 100644 docs/htaccess create mode 100644 engine.php create mode 100644 images/logo.gif create mode 100644 images/openid-logo.gif create mode 100644 images/openid.gif create mode 100644 images/openid.ico create mode 100644 images/openid.png create mode 100644 images/seatbelt/icon-gray.png create mode 100644 images/seatbelt/icon-high.png create mode 100644 images/seatbelt/icon-logo.png create mode 100644 images/user.gif create mode 100644 index.php create mode 100644 ldap.php create mode 100644 showme.php create mode 100644 style.css diff --git a/docs/CHANGELOG b/docs/CHANGELOG new file mode 100644 index 0000000..4c4daf4 --- /dev/null +++ b/docs/CHANGELOG @@ -0,0 +1,64 @@ +OpenID-LDAP-PHP +*************** +An open source PHP-based OpenID IdP package using LDAP as backend. + + +By Zdravko Stoychev aka Dako. +Copyright 1996-2011 by 5Group & Co. http://www.5group.com/ +See LICENSE file for more details. + +********** +CHANGE LOG +********** + +Version 0.8.9 +- Maintenance and security update. + +Version 0.8.8 +- Added Auto-DN Patch by Barry Stewart , Colon + patch by Kozlov Ilya , and SREG default server + values (not obtained from AD). + + +Version 0.8.7 +- Fixed Authorization mode failure. Added mandatory SSL connection + requirement. Fixed SSL LDAP server connection on test phase. Added + VeriSign's SeatBelt Firefox plugin support (credits go to Kaspars). + +Version 0.8.6 +- Added Login form field styling. Added ActiveDirectory connect flag and + CN attribute lookup after search flag. Added some Windows usage notes. + Added favorite icon html head code. Integrated pre-0.9 phpMyID changes. + +Version 0.8.5 +- Added LDAP protocol setting. Fixed Cancel URL Accept mode with missing + openid.mode param. Fixed invalid trust root dots counter. Updated the + mod_rewrite section in README file. Changed logo.gif image. + +Version 0.8.4 +- Modified username field on home page usage; javascript was replaced + with post form and new page was added as post target. Added new mode + for confirming SREG transfers. + +Version 0.8.3 +- Merged with all phpMyID version 0.8 changes. Added SREG to LDAP field + mappings for user's fullname array support (first name + last name). + Updated is the README file. + +Version 0.8.2 +- Added style.css file. Added username field on home page for faster + and intuitive navigation to user pages. Added two image files. Note + all images and icons were relocated into "images" subfolder, and all + documents were relocated into "docs" one. + +Version 0.8.1 +- Fixed login page as title should be in a new paragraph. Replaced all + misused "RP" with correct "Server" and "IdP" ones. Various HTML text + templates moved into index.html file. + +Version 0.8 +- Initial Release, based on phpMyID version 0.7 with some improvements, + fixes and multi-user support with LDAP backend. + + +EOF diff --git a/docs/FAQ b/docs/FAQ new file mode 100644 index 0000000..97fd10a --- /dev/null +++ b/docs/FAQ @@ -0,0 +1,36 @@ +OpenID-LDAP-PHP +*************** +An open source PHP-based OpenID IdP package using LDAP as backend. + + +By Zdravko Stoychev aka Dako. +Copyright 1996-2011 by 5Group & Co. http://www.5group.com/ +See LICENSE file for more details. + +************************** +FREQUENTLY ASKED QUESTIONS +************************** + +WHY ISN'T THERE A WEB BASED CONFIG GUI? + +OpenID-LDAP-PHP is, more or less, "security" software. That being the case, I +find it essential that I do my best to ensure my users employ good security +practices, and the bottom line is that web based config tools are not secure +enough. You have to allow your web server write access to a config file, and +you have to send your credentials "over the wire" in a non-secure way to +perform the setup at all. + + +WHY IS GMP NOT ON BY (OR EVEN USED BY) DEFAULT? + +From the GMP home page (gmplib.org): + + GMP is very often miscompiled! We are seeing ever increasing + problems with miscompilations of the GMP code. It has now come + to the point where a compiler should be assumed to miscompile GMP. + +That about says it all as far as I'm concerned. If you know you have a good GMP +library, you can switch it on. + + +EOF diff --git a/docs/INSTALL b/docs/INSTALL new file mode 100644 index 0000000..c80d322 --- /dev/null +++ b/docs/INSTALL @@ -0,0 +1 @@ +Please read the README file. \ No newline at end of file diff --git a/docs/LICENSE b/docs/LICENSE new file mode 100644 index 0000000..9bb6545 --- /dev/null +++ b/docs/LICENSE @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + +EOF diff --git a/docs/README b/docs/README new file mode 100644 index 0000000..5395a3d --- /dev/null +++ b/docs/README @@ -0,0 +1,447 @@ +OpenID-LDAP-PHP +*************** +An open source PHP-based OpenID IdP package using LDAP as backend. + + +By Zdravko Stoychev aka Dako. +Copyright 1996-2011 by 5Group & Co. http://www.5group.com/ +See LICENSE file for more details. + +This work is based on CJ Niemira's phpMyID - A standalone, single user, OpenID +Identity Provider, version 0.8, home page: http://siege.org/projects/phpMyID. + +OpenID-LDAP-PHP is a small, fairly lightweight, standalone, multi user Identity +Provider for OpenID authentication. It comprises a few PHP scripts that can be +used by one individual to run their own personal OpenID IdP. + +This program requires no external libraries, and has very minimal requirements. +It should run on any PHP server (v4.2+), and can support OpenID in 'Smart Mode.' +This program caches all data using built-in PHP session handling, so it requires +no database, and no explicit write access to the file system. + +OpenID-LDAP-PHP is NOT compatible with Suhosin or other hardened PHP systems +(Debian users take note). + +NOTE: User authentication is done using HTTP Basic authentication, so your + password is transmitted over the wire in plain text, base64 encoded. Thus + we STRONGLY RECOMMEND using SSL for your hosting (https://yourdomain.com) + + In fact, if you are not using SSL the Authorization OpenID mode *will* + fail with an error message. Since OpenID-LDAP-PHP version 0.8.7 you are + required to use SSL connection. + + 'Smart Mode' OpenID authentication requires the use of "big math" + functions. If you do not have bcmath or GMP available, you can use a built + in pure-PHP "big math" library, but it is not very efficient. You are + encouraged to use bcmath if at all possible. + + No MySQL or any other database is required. This software does not use a + backend database by design! It uses a LDAP server tho, to fetch users data. + Currently OpenID-LDAP-PHP does not provide any way for creating or + editing user accounts. You have to use some other way to perform this. + + There's no reason that OpenID-LDAP-PHP should not work under IIS. + + It is proved that OpenID-LDAP-PHP works fine with Microsoft Windows Server + and Active Directory user accounts. + + +FOR MORE INFORMATION ON OpenID, PLEASE SEE http://openid.net/ + + +************ +INSTALLATION +************ + +OpenID-LDAP-PHP can be installed on just about any PHP server. It is recommended +that you use a server that you own and control. + + +1) Decide how you want to install OpenID-LDAP-PHP. It requires at least few files, + and can be installed in a number of ways. The files you will be uploading are + + - installation root folder: + + engine.php The application library + + showme.php Username to URL redirection library + + index.php This is the file you'll be visiting in your browser + + ldap.php The LDAP-link library, contains creds, and options + + style.css Some CSS formating definitions + + - into images directory: + + logo.gif The OpenID logo and text, plus LDAP, on white background + + openid.ico A small OpenID logo icon (can be placed in root folder + and renamed to "favicon.ico", see * notes bellow) + + openid.gif The OpenID logo on white background + + openid.png The OpenID logo with transparent background + + openid-logo.gif The OpenID logo and text on white background + + user.gif A small person image + + Please keep the directory structure while uploading files. Do not store all + files in one location unless you want to edit source files respectively. + + +2) Upload the files. That's right, just upload 'em. Put 'em wherever you want. + I suggest your root URL, but you can do whatever you need to make them web + accessable, as long as you can figure out what the URL should be. + + +3) Visit your upload location in a web browser. You should see a message that + says "This is the OpenID provider home page." You should also see + some welcome message and help links. + + If you don't see all of these things, proceed to the Troubleshooting section + of this document. + + +4) If your "Realm" is anything other than the string 'OpenID' (like say, if it + has a number after it) then make note of the value. This means that PHP is + running in "safe mode," and while I disagree for the reasons they change the + realm, there's nothing I can do about it. + + Edit your config file, and change the key "auth_realm" to reflect the "Realm" + value displayed in your browser. The default is 'OpenID' and it only needs + to be changed if the string you see in your browser is something different. + + If you're going to want to change this to some custom value just edit the + "auth_realm" key to read whatever you want. Remember, however, that you need + to double check your realm value by visiting MyID.config.php in a browser after + you make the change (and upload it). + + +5) Now you get to configure your LDAP connection settings. Edit ldap.php file + and set up your Primary and secondary (Fallback) server names or their IP + addresses. Also provide BindDN and Password to connect to servers. + + SearchDN is a root where users shall be looked up. The entire subtree will + be searched using Filter setting. Note that it contains '%s'; that's where + the username will be substituted before performing the lookup. For more + information about LDAP search filters, go to + http://confluence.atlassian.com/display/DEV/How+to+write+a+LDAP+search+filter + + And finally, TestDB setting is similar to Filter one, except it describes + the DN of the user which tryes to login. It is needed in order to validate + provided password. The '%s' will be replaced with provided username. + + Fill names matching between OpenID Simple Registration and LDAP fields. + + +6) Upload modified files again, replacing the ones that were already there. + + Visit upload location in a browser again, or refresh the current page. The + output shouldn't change, you're just looking to be sure there are no errors. + + Be certain that the 'Realm' listed exactly matches the value you used when + you created your password hash in step 5. + + Enter your LDAP username into text-edit field and click "Enter" button. For + example, the URL shall now be "https://yourdomain.com/some/path/username". + + You should see a welcome message saying your fullname and your OpenID URL. + If an error message is displayed or "User not found in our database, sorry." + text is shown, then go back and edit your ldap.php file again. + + If you still can't get trought it, proceed to the Troubleshooting section. + + Click 'Login' - you should be redirected a couple of times and then presented + with a login dialogue box. Enter your username and password and click ok. + Again, you should be bounced around for a sec, then get a message which says + you're logged in as whoever your username is. + + If you can't log in, if you get an error, or if doesn't work in some other + way, proceed to the Troubleshooting section. + + +7) Now, you are logged in, click "Continue" to return to your Identity Provider. + This is the URL you must use to authenticate as to any OpenID-enabled site, + as this is your personal OpenID. + + If you wish to use another URL (owned by you), instead of this one, then use + the above link as your openid.server and the new URL as openid.delegate. + + The preferred way of setting this up is to determine the URL you wish to + authenticate as (for example "http://myblog.org/"), and add the following + to the HTML section for that document: + + + + + You may now use your URL http://myblog.org/ with OpenID-enabled sites. + + +8) Remember, you have to run this package under SSL in order to protect user + passwords. OpenID-LDAP-PHP generates OpenID for each LDAP user in form: + + https://yourdomain.com/some/path/username + + This requires some changes in you Apache configuration. We'll use mod_rewrite + features and put these settings into your ssl.conf file - in general host + section, or in some virtual host one, depending of your configuration. + + First, you might want (highly recommended) to redirect all non-SSL requests + via SSL. Add this to your "httpd.conf" file: + + RewriteEngine On + + RewriteRule ^/dir$ https://host/dir/ [R=permanent,L] + RewriteRule ^/dir/$ https://host/dir/ [R=permanent,L] + RewriteRule ^/dir/(.*)$ https://host/dir/$1 [R=permanent,L] + + Next, add this to your "ssl.conf" file to proxy usernames to internally + processed requests to index.php script: + + SSLProxyEngine On + RewriteEngine On + + RewriteCond %{REQUEST_URI} !^/(.+)\.php(.*)$ + RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /dir/([A-Za-z0-9]+)\?(.*)\ HTTP/ + RewriteRule ^/dir/(.*)$ https://host/dir/index.php?user=%1&%2 [P] + + RewriteCond %{REQUEST_URI} !^/(.+)\.php(.*)$ + RewriteRule ^/dir/([A-Za-z0-9]+)$ https://host/dir/index.php?user=$1 [P] + + Make sure you replace 'host' and 'dir' above with corresponding values from + your setup (i.e. 'yourdomain.com' and 'some/path', for example). + + The SSL host definition must be using the IP and not the hostname. It + should look like: . If you fail to do so + you may spend quite some time why mod_rewrite is doing nothing. + + Don't forget to change in httpd.conf: "AllowOverride None" to + "AllowOverride All" (it may appear several times). + +9) Set right permissions to ldap.php file because it contains your LDAP password + + $ cd /var/www/ssl/some/path + $ chown root.apache * + $ chmod 644 * + $ chmod 640 ldap.php + + +*) Additional notes: + + - Using favicon.ico in your webserver root folder could require a change to + your apache.conf file. Add this line to it, if its not there already: + + AddType image/x-icon .ico + + then restart the httpd service and reload the page in your browser. + + - Running PHP under Windows, by default, stores sessions in a directory called + "sessions" in the PHP directory. However, this directory doesn't exist in the + distribution, and it won't be created, so sessions fail and OpenID-LDAP-PHP + won't allow logins. So for phpmyid to work, an empty directory called "sessions" + needs to be created in the php installation directory. + + - Connecting to Active Directory is enabled by default and could be disabled by + setting $ldap['isad'] setting to 'false'. + + +##### +USAGE +##### + +There isn't much to using OpenID-LDAP-PHP other than pointing at it, and logging +in. If you wish, you can log out by visiting: + + http://yourdomain.com/some/path/username?openid.mode=logout + +There is also a 'log in' mode that will prompt you for credentials without +having to be referred from a client site. + + http://yourdomain.com/some/path/username?openid.mode=login + + +#################################### +SIMPLE REGISTRATION EXTENSION (SREG) +#################################### + +OpenID features something called 'sreg' which is a way to supply commonly +requested personal information to any site which you log into. Typically, a +client site uses this to create your profile the first time you log in. This +'sreg' information comprises such things as your nickname, email address, time +zone preference, etc... + +All of the SREG keys are optional. To enable the use of any of them, simply +edit ldap.php file and supply name matching between SREG and LDAP attribute, +corresponding to that key. If you don't feel like supplying a particular detail, +just leave it commented out, or empty. Note that these settings are common for +all LDAP users. + + +#################### +ADVANCED CONFIG KEYS +#################### + +Several other configuration keys exist and can be set in the 'profile' array: + +'allow_test' If set to true, this key will allow OpenID-LDAP-PHP to run a special + test mode. Set the key to true, and visit your IdP url. You + should see a "Test" link that was not previously available. + By clicking it, OpenID-LDAP-PHP will conduct a series of tests designed + to validate the functionality of the internal signature and + math functions. This information is useful for troubleshooting. + +'auth_realm' You can configure the name of the realm you are prompted to log + into during authentication. Remember, if you change this value, + you will have to adjust your 'auth_password.' + +'debug' If set to true, OpenID-LDAP-PHP will do perform debug logging. If you + turn this on, you are strongly encouraged to also set the key + 'logfile' to explicitly set the path to your debug log. If not, + OpenID-LDAP-PHP will attempt to automatically ascertain the correct + location to put a 'OpenID-LDAP-PHP.debug.log' file, and will cause an + internal error if it cannot write to that location. + +'idp_url' This key defines the identity that OpenID-LDAP-PHP will allow + you to claim. It is almost always set correctly by default. + +'logfile' When the 'debug' key is true, this key is used to define the + absolute path to a debug log. + +'force_ssl' When writing openid.server and openid.delegate into HTML + section, always use SSL (https) protocol, if this key is 'true'. + + +###################### +# OPTIONAL CONFIG KEYS +###################### + +'microid' If you would like OpenID-LDAP-PHP to assert your ownership via + MicroID tags, you can configure this key (either as a string or an + array). Any values must be complete URI to the asserted owner. + One suggestion is to enter the value of the URL you are + delegating from (installation step 7). You can also use your + email address. In either case, be sure to prefix your URI + correctly with "mailto:" or "http://" etc... + +'pavatar' If you would like OpenID-LDAP-PHP to assert your ownership of a + Pavatar by embedding an optional image tag into its HTML document + header, you can configure this key. The value set should be a + complete URL to your Pavatar image. See http://pavatar.com + for more information. + + +########################## +# EXPERIMENTAL CONFIG KEYS +########################## + +'paranoid' Part of the OpenID specification requires clients to ask the + identity servers to trust a specific root URL for all identity + transactions. OpenID-LDAP-PHP ignores this requirement by default + in favor of a speedier and more fluid login for users, however, + you can a "paranoid" mode that will allow you to receive prompts + to trust each client as you log in to them. + + +########################### +REALLY ADVANCED CONFIG KEYS +########################### + +'allow_gmp' When set to true, the GMP (Gnu MP Bignum library) extension, + if available, can be used for big math calculations (ie + encryption, or "Smart Mode"). + +'auth_domain' This is the domain value used in the HTTP Basic authentication. + It defaults to your idp_url (see above). + +'force_bigmath' If set to true, the internal pure-PHP big math library will be + used to ensure "Smart Mode" is always available, even if neither + bcmath nor gmp extensions can be used. Note that using this may + result in a severe performance degredation for your system. You + should only switch this on if you *really* need to use "Smart + Mode," and cannot otherwise get bcmath or gmp installed on your + system. + +'lifetime' This key defines how long an OpenID session is valid for. The + default value takes into consideration both the internal PHP + session lifespan as well as the default frequency of garbage + collection on your system. + + +############### +TROUBLESHOOTING +############### + +*) Can't log in while running OpenID-LDAP-PHP under IIS + + By default, PHP for Windows ships in such a way that sessions do not work + correctly out of the box. You will need to create a folder called "sessions" + in your PHP installation directory. + +*) Received error: "This site is not compatible with 'suhosin'" + + Suhosin is a security add-on for PHP which, amongst other things, removes + PHP's ability to open and access multiple sessions at one time. Simply put, + OpenID-LDAP-PHP is reliant upon this feature, and will therefore not work with + a hardened PHP. + + Some users have reported successfully using OpenID-LDAP-PHP with suhosin, + either by modifying the script, or using an alternate webserver (such as + lighttpd). These solutions are unsupported. There is currently no official + support for OpenID-LDAP-PHP with suhosin. + + +*) Received error: "Missing expected authorization header." + + OpenID-LDAP-PHP must be able to read http request headers which are only + available if PHP is running as a webserver module. If you are using PHP + in CGI mode, you must convert the HTTP 'Authorization' header into an + environment variable ("PHP_AUTH_DIGEST") or query parameter ("auth") that + can then be used to perform the authorization. + + If you are using Apache, the included 'htaccess' file contains three examples + of how you can use mod_rewrite or mod_setenvif directives to set the + necessary variable. If you need to use this technique, it is recommended + that you place OpenID-LDAP-PHP in its own directory, isolated from the rest + of your web site. + + +*) Received error: "User not found..." no matter what name I provide. + + Check your ldap.php connection settings, again. Also try it out at the + command prompt, if it works? + + $ ldapsearch -s sub -x -h -W -D -b + + You could try lookup your own user account. Note that the returned result + should be only 1. If for some reasons the results are more that one, it + wan't work. + + +*) Why does/doesn't my Server string have "www." in it, my URL does/dosen't? + + If your ServerName is set to one value (in your webserver configuration) + and you regularly access it with something else (say, an alias or w / w/o a + www on the front of it), OpenID-LDAP-PHP may get confused. + + The easiest way to remedy this situation is to explicitly set the "idp_url" + key in your profile array. The value should be the exact, complete URL, to + your installation of OpenID-LDAP-PHP. + + $GLOBALS['profile'] = array( + ... + 'idp_url' => 'http://yoursite.com/index.php', + ... + ); + + +*) Still can't figure it out? + + Sometimes the unexpected happens. Sometimes bugs creep into the code. For + all these times, check if you're using the latest version at: + + http://www.openid-ldap.org/ + + +EOF diff --git a/docs/htaccess b/docs/htaccess new file mode 100644 index 0000000..a4fae55 --- /dev/null +++ b/docs/htaccess @@ -0,0 +1,22 @@ +# If you are running PHP in CGI mode you will need to add one set of these +# directives to your .htaccess file, or your virtualhost configuration. +# +# The easiest way to do so is to simply rename this file ".htaccess" and +# to uncomment one of the options bellow. + +# Option 1, mod_rewrite (req) +#RewriteEngine on +#RewriteCond %{HTTP:Authorization} !^$ +#RewriteCond %{QUERY_STRING} openid.mode=authorize +#RewriteCond %{QUERY_STRING} !auth= +#RewriteCond %{REQUEST_METHOD} =GET +#RewriteRule (.*) %{REQUEST_URI}?%{QUERY_STRING}&auth=%{HTTP:Authorization} [L] + + +# Option 2, mod_rewrite (env) +#RewriteEngine on +#RewriteRule \.php$ - [E=PHP_AUTH_DIGEST:%{HTTP:Authorization},L] + + +# Option 3, mod_setenvif +#SetEnvIf Authorization "(.*)" PHP_AUTH_DIGEST=$1 \ No newline at end of file diff --git a/engine.php b/engine.php new file mode 100644 index 0000000..6b06a94 --- /dev/null +++ b/engine.php @@ -0,0 +1,2013 @@ + aka Dako. + * Copyright 1996-2011 by 5Group & Co. http://www.5group.com/ + * See LICENSE file for more details. + */ + + +/** + * Set a constant to indicate that phpMyID is running + */ +define('PHPMYID_STARTED', true); + +/** + * List the known types and modes + * @name $known + * @global array $GLOBALS['known'] + */ +$GLOBALS['known'] = array( + 'assoc_types' => array('HMAC-SHA1'), + + 'openid_modes' => array('accept', + 'associate', + 'authorize', + 'cancel', + 'checkid_immediate', + 'checkid_setup', + 'check_authentication', + 'error', + 'id_res', + 'login', + 'logout', + 'sendsreg', + 'sb_status', + 'sb_config', + 'test'), + + 'session_types' => array('', + 'DH-SHA1'), + + 'bigmath_types' => array('DH-SHA1'), +); + +/** + * Defined by OpenID spec + * @name $g + * @global integer $GLOBALS['g'] + */ +$GLOBALS['g'] = 2; + +/** + * Defined by OpenID spec + * @name $p + * @global integer $GLOBALS['p'] + */ +$GLOBALS['p'] = '155172898181473697471232257763715539915724801966915404479707' . +'7953140576293785419175806512274236981889937278161526466314385615958256881888' . +'8995127215884267541995034125870655654980358010487053768147672651325574704076' . +'5857479291291572334510643245094715007229621094194349783925984760375594985848' . +'253359305585439638443'; + + +// Runmode functions + +/** + * Allow the user to accept trust on a URL + * @global array $profile + */ +function accept_mode () { + global $profile; + + // this is a user session + user_session(); + + // the user needs refresh urls in their session to access this mode + if (! isset($_SESSION['post_accept_url']) || ! isset($_SESSION['cancel_accept_url']) || ! isset($_SESSION['unaccepted_url'])) + error_500('You may not access this mode directly.'); + + // has the user accepted the trust_root? + $accepted = @strlen($_REQUEST['accepted']) + ? $_REQUEST['accepted'] + : null; + + // if so, refresh back to post_accept_url + if ($accepted === 'yes') { + $_SESSION['accepted_url'] = $_SESSION['unaccepted_url']; + wrap_refresh($_SESSION['post_accept_url']); + + // if they rejected it, return to the client + } elseif ($accepted === 'no') { + wrap_refresh($_SESSION['cancel_accept_url']); + } + + // if neither, offer the trust request + $yes = wrap_param($profile['req_url'],'accepted=yes'); + $no = wrap_param($profile['req_url'],'accepted=no'); + + wrap_html('The client site you are attempting to log into has requested that you trust the following URL:
' . $_SESSION['unaccepted_url'] . '

Do you wish to continue?
Yes | No'); +} + +/** + * Allow the user to accept sending of a SREG + * @global array $profile + */ +function sendsreg_mode () { + global $profile, $sreg; + + // this is a user session + user_session(); + + // the user needs refresh urls in their session to access this mode + if (! isset($_SESSION['post_accept_url']) || ! isset($_SESSION['cancel_accept_url']) || ! isset($_SESSION['unaccepted_sreg'])) + error_500('You may not access this mode directly.'); + + // has the user accepted the trust_root? + $accepted = @strlen($_REQUEST['allowed']) + ? $_REQUEST['allowed'] + : null; + + // if so, refresh back to post_accept_url + if ($accepted === 'yes') { + $_SESSION['accepted_sreg'] = $_SESSION['unaccepted_sreg']; + wrap_refresh($_SESSION['post_accept_url']); + + // if they rejected it, return to the client + } elseif ($accepted === 'no') { + wrap_refresh($_SESSION['cancel_accept_url']); + } + + // if neither, offer the trust request + $yes = wrap_param($profile['req_url'],'allowed=yes'); + $no = wrap_param($profile['req_url'],'allowed=no'); + + $sregs = $_SESSION['unaccepted_sreg']; + + // Add the sreg stuff, if we've got it + $tokens = ''; + foreach (explode(',', $sregs) as $key) { + if (! isset($sreg[$key])) + continue; + + $tokens .= sprintf("%s:%s", $key, $sreg[$key]); + } + if ($tokens != "") + $tokens = sprintf('%s
Registration info details
',$tokens); + + wrap_html('The client site you just have logged into has requested that you provide a registration info:
' . $_SESSION['unaccepted_url'] . '
' . $tokens . '
Do you allow the transfer?
Yes | No'); +} + +/** + * Produce SeatBelt Login State XML if they are logged in or not + * @global array $profile + */ +function sb_status_mode () { + global $profile; + + user_session(); + + if ($profile['authorized']) { + // output XML with active username + wrap_seatbelt_status($_SESSION['auth_username']); + } else { + // output XML as no user has logged in + wrap_seatbelt_status(); + } +} + + +/** + * Return SeatBelt configuration XML + * @global array $profile + */ +function sb_config_mode () { + wrap_seatbelt_config(); +} + + +/** + * Perform an association with a consumer + * @global array $known + * @global array $profile + * @global integer $g + * @global integer $p + */ +function associate_mode () { + global $g, $known, $p, $profile; + + // Validate the request + if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'associate') + error_400(); + + // Get the request options, using defaults as necessary + $assoc_type = (@strlen($_REQUEST['openid_assoc_type']) + && in_array($_REQUEST['openid_assoc_type'], $known['assoc_types'])) + ? $_REQUEST['openid_assoc_type'] + : 'HMAC-SHA1'; + + $session_type = (@strlen($_REQUEST['openid_session_type']) + && in_array($_REQUEST['openid_session_type'], $known['session_types'])) + ? $_REQUEST['openid_session_type'] + : ''; + + $dh_modulus = (@strlen($_REQUEST['openid_dh_modulus'])) + ? long(base64_decode($_REQUEST['openid_dh_modulus'])) + : ($session_type == 'DH-SHA1' + ? $p + : null); + + $dh_gen = (@strlen($_REQUEST['openid_dh_gen'])) + ? long(base64_decode($_REQUEST['openid_dh_gen'])) + : ($session_type == 'DH-SHA1' + ? $g + : null); + + $dh_consumer_public = (@strlen($_REQUEST['openid_dh_consumer_public'])) + ? $_REQUEST['openid_dh_consumer_public'] + : ($session_type == 'DH-SHA1' + ? error_post('dh_consumer_public was not specified') + : null); + + $lifetime = time() + $profile['lifetime']; + + // Create standard keys + $keys = array( + 'assoc_type' => $assoc_type, + 'expires_in' => $profile['lifetime'] + ); + + // If I can't handle bigmath, default to plaintext sessions + if (in_array($session_type, $known['bigmath_types']) && $profile['use_bigmath'] == false) + $session_type = null; + + // Add response keys based on the session type + switch ($session_type) { + case 'DH-SHA1': + // Create the associate id and shared secret now + list ($assoc_handle, $shared_secret) = new_assoc($lifetime); + + // Compute the Diffie-Hellman stuff + $private_key = random($dh_modulus); + $public_key = bmpowmod($dh_gen, $private_key, $dh_modulus); + $remote_key = long(base64_decode($dh_consumer_public)); + $ss = bmpowmod($remote_key, $private_key, $dh_modulus); + + $keys['assoc_handle'] = $assoc_handle; + $keys['session_type'] = $session_type; + $keys['dh_server_public'] = base64_encode(bin($public_key)); + $keys['enc_mac_key'] = base64_encode(x_or(sha1_20(bin($ss)), $shared_secret)); + + break; + + default: + // Create the associate id and shared secret now + list ($assoc_handle, $shared_secret) = new_assoc($lifetime); + + $keys['assoc_handle'] = $assoc_handle; + $keys['mac_key'] = base64_encode($shared_secret); + } + + // Return the keys + wrap_kv($keys); +} + + +/** + * Perform a user authorization + * @global array $profile + */ +function authorize_mode () { + global $profile, $proto; + + // this is a user session + user_session(); + + // the user needs refresh urls in their session to access this mode + if (! isset($_SESSION['post_auth_url']) || ! isset($_SESSION['cancel_auth_url'])) + error_500('You may not access this mode directly.'); + + if (! $profile['user_found']) { + $c = wrap_param($_SESSION['cancel_auth_url'],'openid.mode=cancel'); + wrap_html('This user does not exists! | Cancel'); + } + + // try to get the auth headers + if (function_exists('apache_request_headers') && ini_get('safe_mode') == false) { + $arh = apache_request_headers(); + $hdr = isset($arh['Authorization']) ? $arh['Authorization'] : null; + + } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) { + $hdr = $_SERVER['PHP_AUTH_DIGEST']; + + } elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) { + $hdr = $_SERVER['HTTP_AUTHORIZATION']; + + } elseif (isset($_ENV['PHP_AUTH_DIGEST'])) { + $hdr = $_ENV['PHP_AUTH_DIGEST']; + + } elseif (isset($_SERVER['Authorization'])) { + $hdr = $_SERVER['Authorization']; + + } elseif (isset($_REQUEST['auth'])) { + $hdr = stripslashes(urldecode($_REQUEST['auth'])); + + } else { + $hdr = null; + } + + debug('Authorization header: ' . $hdr); + $myauth = substr($hdr,0,6) == 'Basic ' + ? substr($hdr, strpos($hdr, ' ') + 1) + : $hdr; + + // is the user trying to log in? + if ($proto == "https" && ! is_null($myauth) && $profile['authorized'] == false) { + debug('Basic headers: ' . $myauth); + $hdr = array(); + + // decode the Basic authorization headers + // base64-encoded concatenation of the username, a colon, and the password + $myauth = base64_decode($myauth); + $hdr['username'] = substr($myauth, 0, strpos($myauth, ':')); + $hdr['password'] = substr($myauth, strpos($myauth, ':') + 1); + + if ( isset($_SERVER["REMOTE_ADDR"]) ) { + $hdr['clientip'] = $_SERVER["REMOTE_ADDR"]; + } elseif ( isset($_SERVER["HTTP_X_FORWARDED_FOR"]) ) { + $hdr['clientip'] = $_SERVER["HTTP_X_FORWARDED_FOR"]; + } elseif ( isset($_SERVER["HTTP_CLIENT_IP"]) ) { + $hdr['clientip'] = $_SERVER["HTTP_CLIENT_IP"]; + } + + debug($hdr, 'Parsed basic auth headers:'); + + if (! isset($_SESSION['failures'])) + $_SESSION['failures'] = 0; + + if (($profile['auth_username'] == $hdr['username']) && ($hdr['password'] != '')) { + + $ok = test_ldap($profile['auth_cn'],$hdr['password']); + if ($ok == "ok") { + $hdr['response'] = $ok; + debug('Ldap bind is OK'); + } else { + $hdr['response'] = "failed"; + debug('Ldap bind FAILED'); + } + + // successful login! + if ($hdr['response'] == $ok) { + debug('Authentication successful for ' . $hdr['username']); + debug('User session is: ' . session_id()); + $_SESSION['auth_username'] = $profile['auth_username']; + $_SESSION['auth_url'] = $profile['idp_url']; + $profile['authorized'] = true; + + // return to the refresh url if they get in + wrap_refresh($_SESSION['post_auth_url']); + authlog('Login passed for ' . $hdr['username'] . ' at ' . $hdr['clientip']); + + // failed login + } else { + $_SESSION['failures']++; + debug('Login failed for ' . $hdr['username']); + debug('Fail count: ' . $_SESSION['failures']); + authlog('Login FAILED for ' . $hdr['username'] . ' at ' . $hdr['clientip']); + } + } elseif ($profile['auth_username'] == $hdr['username']) { + $_SESSION['failures']++; + debug('Empty password for ' . $hdr['username']); + debug('Fail count: ' . $_SESSION['failures']); + authlog('Login FAILED for ' . $hdr['username'] . ' at ' . $hdr['clientip']); + } else { + $_SESSION['failures']++; + debug('Bad username: ' . $hdr['username']); + debug('Fail count: ' . $_SESSION['failures']); + authlog('Login FAILED for ' . $hdr['username'] . ' at ' . $hdr['clientip']); + } + + // does this make too many failures? + if ($_SESSION['failures'] > 4) { + debug('Too many password failures'); + error_get($_SESSION['cancel_auth_url'], 'Too many password failures. Double check your authorization realm. You must restart your browser to try again.'); + } + + } elseif ($proto == "http") { + error_500('You have to use secure connection (https) in order to login.'); + } + + // if we get this far the user is not authorized, so send the headers + debug('Prompting user to log in.'); + header('HTTP/1.0 401 Unauthorized'); + header(sprintf('WWW-Authenticate: Basic realm="%s"', $profile['auth_realm'])); + wrap_refresh(wrap_param($_SESSION['cancel_auth_url'],'openid.mode=cancel')); +} + + +/** + * Handle a consumer's request for cancellation. + */ +function cancel_mode () { + wrap_html('Request cancelled.'); +} + + +/** + * Handle a consumer's request to see if the user is authenticated + */ +function check_authentication_mode () { + // Validate the request + if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'check_authentication') + error_400(); + + $assoc_handle = @strlen($_REQUEST['openid_assoc_handle']) + ? $_REQUEST['openid_assoc_handle'] + : error_post('Missing assoc_handle'); + + $sig = @strlen($_REQUEST['openid_sig']) + ? $_REQUEST['openid_sig'] + : error_post('Missing sig'); + + $signed = @strlen($_REQUEST['openid_signed']) + ? $_REQUEST['openid_signed'] + : error_post('Missing signed'); + + // Prepare the return keys + $keys = array( + 'openid.mode' => 'id_res' + ); + + // Invalidate the assoc handle if we need to + if (@strlen($_REQUEST['openid_invalidate_handle'])) { + destroy_assoc_handle($_REQUEST['openid_invalidate_handle']); + + $keys['invalidate_handle'] = $_REQUEST['openid_invalidate_handle']; + } + + // Validate the sig by recreating the kv pair and signing + $_REQUEST['openid_mode'] = 'id_res'; + $tokens = ''; + foreach (explode(',', $signed) as $param) { + $post = preg_replace('/\./', '_', $param); + $tokens .= sprintf("%s:%s\n", $param, $_REQUEST['openid_' . $post]); + } + + // Add the sreg stuff, if we've got it + if (isset($sreg_required)) { + foreach (explode(',', $sreg_required) as $key) { + if (! isset($sreg[$key])) + continue; + $skey = 'sreg.' . $key; + + $tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]); + $keys[$skey] = $sreg[$key]; + $fields[] = $skey; + } + } + + // Look up the consumer's shared_secret and timeout + list ($shared_secret, $expires) = secret($assoc_handle); + + // if I can't verify the assoc_handle, or if it's expired + if ($shared_secret == false || (is_numeric($expires) && $expires < time())) { + $keys['is_valid'] = 'false'; + + } else { + $ok = base64_encode(hmac($shared_secret, $tokens)); + $keys['is_valid'] = ($sig == $ok) ? 'true' : 'false'; + } + + // Return the keys + wrap_kv($keys); +} + + +/** + * Handle a consumer's request to see if the end user is logged in + * @global array $known + * @global array $profile + * @global array $sreg + */ +function checkid ( $wait ) { + debug("checkid: wait? $wait"); + global $known, $profile, $sreg; + + // This is a user session + user_session(); + + // Get the options, use defaults as necessary + $return_to = @strlen($_REQUEST['openid_return_to']) + ? $_REQUEST['openid_return_to'] + : error_400('Missing return_to'); + + $identity = @strlen($_REQUEST['openid_identity']) + ? $_REQUEST['openid_identity'] + : error_get($return_to, 'Missing identity'); + + $assoc_handle = @strlen($_REQUEST['openid_assoc_handle']) + ? $_REQUEST['openid_assoc_handle'] + : null; + + $trust_root = @strlen($_REQUEST['openid_trust_root']) + ? $_REQUEST['openid_trust_root'] + : $return_to; + + $sreg_required = @strlen($_REQUEST['openid_sreg_required']) + ? $_REQUEST['openid_sreg_required'] + : ''; + + $sreg_optional = @strlen($_REQUEST['openid_sreg_optional']) + ? $_REQUEST['openid_sreg_optional'] + : ''; + + // determine the cancel url + $cancel_url = wrap_param($return_to,'openid.mode=cancel'); + + // required and optional make no difference to us + $sreg_required .= ',' . $sreg_optional; + + // do the trust_root analysis + if ($trust_root != $return_to) { + // the urls are not the same, be sure return decends from trust + if (! url_descends($return_to, $trust_root)) + error_500('Invalid trust_root: "' . $trust_root . '"'); + + } + + // transfer the user to the url accept mode if they're paranoid + if ($wait == 1 && isset($profile['paranoid']) && $profile['paranoid'] == true && (! session_is_registered('accepted_url') || $_SESSION['accepted_url'] != $trust_root)) { + $_SESSION['cancel_accept_url'] = $cancel_url; + $_SESSION['post_accept_url'] = $profile['req_url']; + $_SESSION['unaccepted_url'] = $trust_root; + + debug('Transferring to acceptance mode.'); + debug('Cancel URL: ' . $_SESSION['cancel_accept_url']); + debug('Post URL: ' . $_SESSION['post_accept_url']); + + wrap_refresh(wrap_param($profile['idp_url'],'openid.mode=accept')); + } + + // make sure i am this identifier + if ($identity != $profile['idp_url']) { + debug("Invalid identity: $identity"); + debug("IdP URL: " . $profile['idp_url']); + error_get($return_to, "Invalid identity: '$identity'"); + } + + // begin setting up return keys + $keys = array( + 'mode' => 'id_res' + ); + + // if the user is not logged in, transfer to the authorization mode + if ($profile['authorized'] == false || $identity != $_SESSION['auth_url']) { + // users can only be logged in to one url at a time + $_SESSION['auth_username'] = null; + $_SESSION['auth_url'] = null; + + if ($wait) { + $_SESSION['cancel_auth_url'] = $cancel_url; + $_SESSION['post_auth_url'] = $profile['req_url']; + + debug('Transferring to authorization mode.'); + debug('Cancel URL: ' . $_SESSION['cancel_auth_url']); + debug('Post URL: ' . $_SESSION['post_auth_url']); + + wrap_refresh(wrap_param($profile['idp_url'],'openid.mode=authorize')); + } else { + $keys['user_setup_url'] = $profile['idp_url']; + } + + // the user is logged in + } else { + // transfer the user to the sreg accept mode if they're paranoid + if ($profile['paranoid'] == true && $sreg_required != "," && (! session_is_registered('accepted_sreg') || $_SESSION['accepted_sreg'] != $sreg_required)) { + $_SESSION['cancel_accept_url'] = wrap_param($return_to,'openid.mode=cancel'); + $_SESSION['post_accept_url'] = $profile['req_url']; + $_SESSION['unaccepted_sreg'] = $sreg_required; + + debug('Transferring to sreg acceptance mode.'); + debug('Cancel URL: ' . $_SESSION['cancel_accept_url']); + debug('Post URL: ' . $_SESSION['post_accept_url']); + debug('SREG fields: ' . $_SESSION['unaccepted_sreg']); + + wrap_refresh(wrap_param($profile['idp_url'],'openid.mode=sendsreg')); + } + + // remove the refresh URLs if set + unset($_SESSION['cancel_auth_url']); + unset($_SESSION['post_auth_url']); + + // check the assoc handle + list($shared_secret, $expires) = secret($assoc_handle); + + // if I can't verify the assoc_handle, or if it's expired + if ($shared_secret == false || (is_numeric($expires) && $expires < time())) { + debug("Session expired or missing key: $expires < " . time()); + if ($assoc_handle != null) { + $keys['invalidate_handle'] = $assoc_handle; + destroy_assoc_handle($assoc_handle); + } + + $lifetime = time() + $profile['lifetime']; + list ($assoc_handle, $shared_secret) = new_assoc($lifetime); + } + + $keys['identity'] = $profile['idp_url']; + $keys['assoc_handle'] = $assoc_handle; + $keys['return_to'] = $return_to; + + $fields = array_keys($keys); + $tokens = ''; + foreach ($fields as $key) + $tokens .= sprintf("%s:%s\n", $key, $keys[$key]); + + // add sreg keys + foreach (explode(',', $sreg_required) as $key) { + if (! isset($sreg[$key])) + continue; + $skey = 'sreg.' . $key; + + $tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]); + $keys[$skey] = $sreg[$key]; + $fields[] = $skey; + } + + $keys['signed'] = implode(',', $fields); + $keys['sig'] = base64_encode(hmac($shared_secret, $tokens)); + } + + wrap_location($return_to, $keys); +} + + +/** + * Handle a consumer's request to see if the user is already logged in + */ +function checkid_immediate_mode () { + if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_immediate') + error_500(); + + checkid(false); +} + + +/** + * Handle a consumer's request to see if the user is logged in, but be willing + * to wait for them to perform a login if they're not + */ +function checkid_setup_mode () { + if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_setup') + error_500(); + + checkid(true); +} + + +/** + * Handle errors + */ +function error_mode () { + isset($_REQUEST['openid_error']) + ? wrap_html($_REQUEST['openid_error']) + : error_500(); +} + + +/** + * Show a user if they are logged in or not + * @global array $profile + */ +function id_res_mode () { + global $profile; + + user_session(); + + if ($profile['authorized']) + wrap_html('You are logged in as ' . $_SESSION['auth_username'] . ' | Continue'); + + wrap_html('You are not logged in | Login'); +} + + +/** + * Allow a user to perform a static login + * @global array $profile + */ +function login_mode () { + global $profile; + + user_session(); + + if ($profile['authorized']) + id_res_mode(); + + $keys = array( + 'mode' => 'checkid_setup', + 'identity' => $profile['idp_url'], + 'return_to' => $profile['idp_url'] + ); + + wrap_location($profile['idp_url'], $keys); +} + + +/** + * Allow a user to perform a static logout + * @global array $profile + */ +function logout_mode () { + global $profile; + + user_session(); + + if (! $profile['authorized']) + wrap_html('You were not logged in'); + + $_SESSION = array(); + session_destroy(); + debug('User session destroyed.'); + + header('HTTP/1.0 401 Unauthorized'); + wrap_refresh($profile['idp_url']); +} + + +/** + * The default information screen + * @global array $profile + */ +function no_mode () { + global $profile, $sreg, $proto, $html, $reldir; + + user_session(); + + $demo_name = $proto . '://' . $_SERVER['SERVER_NAME'] . $reldir . 'joe'; + $user_name = ($sreg['fullname'] == '') ? $sreg['nickname'] : $sreg['fullname']; + + $stat_html = ($profile['authorized'] == true ? ' (logged in)' : null); + $auth_html = ($profile['authorized'] == false ? ' | Login' : null); + $test_html = ($profile['allow_test'] == true ? ' | Test' : null); + + $menu_html = 'Home' . $auth_html . $test_html . ' | Help'; + + $name_html = ($profile['user_found'] == true ? 'Welcome, ' . $user_name . '! Your OpenID is:
' . $profile['idp_url'] . '' . $stat_html . '

Your current options are: ' . $menu_html : '' . sprintf($html['user_not_found'],$profile['auth_username']) . ''); + + $sign_html = ($profile['auth_username'] != "" ? $name_html : 'Realm: ' . $profile['auth_realm'] . '

' . sprintf($html['welcome_text'],$demo_name) . '

' . $html['welcome_help']); + + wrap_html($sign_html); +} + + +/** + * Testing for setup + * @global array $profile + */ +function test_mode () { + global $profile, $p, $g; + + if ($profile['allow_test'] != true) + error_403(); + + @ini_set('max_execution_time', 180); + + $test_expire = time() + 120; + $test_ss_enc = 'W7hvmld2yEYdDb0fHfSkKhQX+PM='; + $test_ss = base64_decode($test_ss_enc); + $test_token = "alpha:bravo\ncharlie:delta\necho:foxtrot"; + $test_server_private = '11263846781670293092494395517924811173145217135753406847875706165886322533899689335716152496005807017390233667003995430954419468996805220211293016296351031812246187748601293733816011832462964410766956326501185504714561648498549481477143603650090931135412673422192550825523386522507656442905243832471167330268'; + $test_client_public = base64_decode('AL63zqI5a5p8HdXZF5hFu8p+P9GOb816HcHuvNOhqrgkKdA3fO4XEzmldlb37nv3+xqMBgWj6gxT7vfuFerEZLBvuWyVvR7IOGZmx0BAByoq3fxYd3Fpe2Coxngs015vK37otmH8e83YyyGo5Qua/NAf13yz1PVuJ5Ctk7E+YdVc'); + + $res = array(); + + // bcmath + $res['bcmath'] = extension_loaded('bcmath') + ? 'pass' : 'warn - not loaded'; + + // gmp + if ($profile['allow_gmp']) { + $res['gmp'] = extension_loaded('gmp') + ? 'pass' : 'warn - not loaded'; + } else { + $res['gmp'] = 'pass - n/a'; + } + + // sys_get_temp_dir + $res['logfile'] = is_writable($profile['logfile']) + ? 'pass' : "warn - log is not writable"; + + // session & new_assoc + user_session(); + list($test_assoc, $test_new_ss) = new_assoc($test_expire); + $res['session'] = ($test_assoc != session_id()) + ? 'pass' : 'fail'; + + // secret + @session_unregister('shared_secret'); + list($check, $check2) = secret($test_assoc); + $res['secret'] = ($check == $test_new_ss) + ? 'pass' : 'fail'; + + // expire + $res['expire'] = ($check2 <= $test_expire) + ? 'pass' : 'fail'; + + // base64 + $res['base64'] = (base64_encode($test_ss) == $test_ss_enc) + ? 'pass' : 'fail'; + + // hmac + $test_sig = base64_decode('/VXgHvZAOdoz/OTa5+XJXzSGhjs='); + $check = hmac($test_ss, $test_token); + $res['hmac'] = ($check == $test_sig) + ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); + + if ($profile['use_bigmath']) { + // bigmath powmod + $test_server_public = '102773334773637418574009974502372885384288396853657336911033649141556441102566075470916498748591002884433213640712303846640842555822818660704173387461364443541327856226098159843042567251113889701110175072389560896826887426539315893475252988846151505416694218615764823146765717947374855806613410142231092856731'; + $check = bmpowmod($g, $test_server_private, $p); + $res['bmpowmod-1'] = ($check == $test_server_public) + ? 'pass' : sprintf("fail - '%s'", $check); + + // long + $test_client_long = '133926731803116519408547886573524294471756220428015419404483437186057383311250738749035616354107518232016420809434801736658109316293127101479053449990587221774635063166689561125137927607200322073086097478667514042144489248048756916881344442393090205172004842481037581607299263456852036730858519133859409417564'; + $res['long'] = (long($test_client_public) == $test_client_long) + ? 'pass' : 'fail'; + + // bigmath powmod 2 + $test_client_share = '19333275433742428703546496981182797556056709274486796259858099992516081822015362253491867310832140733686713353304595602619444380387600756677924791671971324290032515367930532292542300647858206600215875069588627551090223949962823532134061941805446571307168890255137575975911397744471376862555181588554632928402'; + $check = bmpowmod($test_client_long, $test_server_private, $p); + $res['bmpowmod-2'] = ($check == $test_client_share) + ? 'pass' : sprintf("fail - '%s'", $check); + + // bin + $test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI='); + $check = bin($test_client_share); + $res['bin'] = ($check == $test_client_mac_s1) + ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); + + } else { + $res['bigmath'] = 'fail - big math functions are not available.'; + } + + // sha1_20 + $test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI='); + $test_client_mac_s2 = base64_decode('0Mb2t9d/HvAZyuhbARJPYdx3+v4='); + $check = sha1_20($test_client_mac_s1); + $res['sha1_20'] = ($check == $test_client_mac_s2) + ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); + + // x_or + $test_client_mac_s3 = base64_decode('i36ZLYAJ1rYEx1VEHObrS8hgAg0='); + $check = x_or($test_client_mac_s2, $test_ss); + $res['x_or'] = ($check == $test_client_mac_s3) + ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); + + $out = "\n"; + foreach ($res as $test => $stat) { + $code = substr($stat, 0, 4); + $color = ($code == 'pass') ? '#9f9' + : (($code == 'warn') ? '#ff9' : '#f99'); + $out .= sprintf("\n", $test, $color, $stat); + } + $out .= "
%s%s
"; + + wrap_html($out); +} + + +// Support functions + +/** + * Prefix the keys of an array with 'openid.' + * @param array $array + * @return array + */ +function append_openid ($array) { + $keys = array_keys($array); + $vals = array_values($array); + + $r = array(); + for ($i=0; $i $rl ) { + $r = str_repeat("0", $ll-$rl) . $r; + $o = $ll; + + } else { + $o = $ll; + } + + $v = ''; + $carry = 0; + + for ($i = $o-1; $i >= 0; $i--) { + $d = (int)$l[$i] + (int)$r[$i] + $carry; + if ($d <= 9) { + $carry = 0; + + } else { + $carry = 1; + $d -= 10; + } + $v = (string) $d . $v; + } + + if ($carry > 0) + $v = "1" . $v; + + return $v; +} + +/** + * Create a big math comparison function + * @param string $l + * @param string $r + * @return string + */ +function bmcomp($l, $r) { + if (function_exists('bccomp')) + return bccomp($l, $r); + + global $profile; + if ($profile['use_gmp']) + return gmp_strval(gmp_cmp($l, $r)); + + $l = strval($l); $r = strval($r); + $ll = strlen($l); $lr = strlen($r); + if ($ll != $lr) + return ($ll > $lr) ? 1 : -1; + + return strcmp($l, $r); +} + +/** + * Create a big math division function + * @param string $l + * @param string $r + * @param int $z + * @return string + * @url http://www.icosaedro.it/bigint Inspired by + */ +function bmdiv($l, $r, $z = 0) { + if (function_exists('bcdiv')) + return ($z == 0) ? bcdiv($l, $r) : bcmod($l, $r); + + global $profile; + if ($profile['use_gmp']) + return gmp_strval(($z == 0) ? gmp_div_q($l, $r) : gmp_mod($l, $r)); + + $l = strval($l); $r = strval($r); + $v = '0'; + + while (true) { + if( bmcomp($l, $r) < 0 ) + break; + + $delta = strlen($l) - strlen($r); + if ($delta >= 1) { + $zeroes = str_repeat("0", $delta); + $r2 = $r . $zeroes; + + if (strcmp($l, $r2) >= 0) { + $v = bmadd($v, "1" . $zeroes); + $l = bmsub($l, $r2); + + } else { + $zeroes = str_repeat("0", $delta - 1); + $v = bmadd($v, "1" . $zeroes); + $l = bmsub($l, $r . $zeroes); + } + + } else { + $l = bmsub($l, $r); + $v = bmadd($v, "1"); + } + } + + return ($z == 0) ? $v : $l; +} + +/** + * Create a big math multiplication function + * @param string $l + * @param string $r + * @return string + * @url http://www.icosaedro.it/bigint Inspired by + */ +function bmmul($l, $r) { + if (function_exists('bcmul')) + return bcmul($l, $r); + + global $profile; + if ($profile['use_gmp']) + return gmp_strval(gmp_mul($l, $r)); + + $l = strval($l); $r = strval($r); + + $v = '0'; + $z = ''; + + for( $i = strlen($r)-1; $i >= 0; $i-- ){ + $bd = (int) $r[$i]; + $carry = 0; + $p = ""; + for( $j = strlen($l)-1; $j >= 0; $j-- ){ + $ad = (int) $l[$j]; + $pd = $ad * $bd + $carry; + if( $pd <= 9 ){ + $carry = 0; + } else { + $carry = (int) ($pd / 10); + $pd = $pd % 10; + } + $p = (string) $pd . $p; + } + if( $carry > 0 ) + $p = (string) $carry . $p; + $p = $p . $z; + $z .= "0"; + $v = bmadd($v, $p); + } + + return $v; +} + +/** + * Create a big math modulus function + * @param string $value + * @param string $mod + * @return string + */ +function bmmod( $value, $mod ) { + if (function_exists('bcmod')) + return bcmod($value, $mod); + + global $profile; + if ($profile['use_gmp']) + return gmp_strval(gmp_mod($value, $mod)); + + $r = bmdiv($value, $mod, 1); + return $r; +} + +/** + * Create a big math power function + * @param string $value + * @param string $exponent + * @return string + */ +function bmpow ($value, $exponent) { + if (function_exists('bcpow')) + return bcpow($value, $exponent); + + global $profile; + if ($profile['use_gmp']) + return gmp_strval(gmp_pow($value, $exponent)); + + $r = '1'; + while ($exponent) { + $r = bmmul($r, $value, 100); + $exponent--; + } + return (string)rtrim($r, '0.'); +} + +/** + * Create a big math 'powmod' function + * @param string $value + * @param string $exponent + * @param string $mod + * @return string + * @url http://php.net/manual/en/function.bcpowmod.php#72704 Borrowed from + */ +function bmpowmod ($value, $exponent, $mod) { + if (function_exists('bcpowmod')) + return bcpowmod($value, $exponent, $mod); + + global $profile; + if ($profile['use_gmp']) + return gmp_strval(gmp_powm($value, $exponent, $mod)); + + $r = ''; + while ($exponent != '0') { + $t = bmmod($exponent, '4096'); + $r = substr("000000000000" . decbin(intval($t)), -12) . $r; + $exponent = bmdiv($exponent, '4096'); + } + + $r = preg_replace("!^0+!","",$r); + + if ($r == '') + $r = '0'; + $value = bmmod($value, $mod); + $erb = strrev($r); + $q = '1'; + $a[0] = $value; + + for ($i = 1; $i < strlen($erb); $i++) { + $a[$i] = bmmod( bmmul($a[$i-1], $a[$i-1]), $mod ); + } + + for ($i = 0; $i < strlen($erb); $i++) { + if ($erb[$i] == "1") { + $q = bmmod( bmmul($q, $a[$i]), $mod ); + } + } + + return($q); +} + +/** + * Create a big math subtraction function + * @param string $l + * @param string $r + * @return string + * @url http://www.icosaedro.it/bigint Inspired by + */ +function bmsub($l, $r) { + if (function_exists('bcsub')) + return bcsub($l, $r); + + global $profile; + if ($profile['use_gmp']) + return gmp_strval(gmp_sub($l, $r)); + + + $l = strval($l); $r = strval($r); + $ll = strlen($l); $rl = strlen($r); + + if ($ll < $rl) { + $l = str_repeat("0", $rl-$ll) . $l; + $o = $rl; + } elseif ( $ll > $rl ) { + $r = str_repeat("0", $ll-$rl) . (string)$r; + $o = $ll; + } else { + $o = $ll; + } + + if (strcmp($l, $r) >= 0) { + $sign = ''; + } else { + $x = $l; $l = $r; $r = $x; + $sign = '-'; + } + + $v = ''; + $carry = 0; + + for ($i = $o-1; $i >= 0; $i--) { + $d = ($l[$i] - $r[$i]) - $carry; + if ($d < 0) { + $carry = 1; + $d += 10; + } else { + $carry = 0; + } + $v = (string) $d . $v; + } + + return $sign . ltrim($v, '0'); +} + + +/** + * Get a binary value + * @param integer $n + * @return string + * @url http://openidenabled.com Borrowed from PHP-OpenID + */ +function bin ($n) { + $bytes = array(); + while (bmcomp($n, 0) > 0) { + array_unshift($bytes, bmmod($n, 256)); + $n = bmdiv($n, bmpow(2,8)); + } + + if ($bytes && ($bytes[0] > 127)) + array_unshift($bytes, 0); + + $b = ''; + foreach ($bytes as $byte) + $b .= pack('C', $byte); + + return $b; +} + + +/** + * Debug logging + * @param mixed $x + * @param string $m + */ +function debug ($x, $m = null) { + global $profile; + + if (! isset($profile['debug']) || $profile['debug'] == false) + return true; + + if (! is_writable(dirname($profile['logfile'])) &! is_writable($profile['logfile'])) + error_500('Cannot write to log file: ' . $profile['logfile']); + + if (is_array($x)) { + ob_start(); + print_r($x); + $x = $m . ($m != null ? "\n" : '') . ob_get_clean(); + + } else { + $x .= "\n"; + } + + error_log($x . "\n", 3, $profile['logfile']); +} + + +/** + * Auth logging + * @param string $m + */ +function authlog ($m) { + global $profile; + + if (! isset($profile['authlog']) || $profile['authlog'] == false) + return true; + + if (! is_writable(dirname($profile['logfile'])) &! is_writable($profile['logfile'])) + error_500('Cannot write to log file: ' . $profile['logfile']); + + error_log($m . "\n\n", 3, $profile['logfile']); +} + + +/** + * Destroy a consumer's assoc handle + * @param string $id + */ +function destroy_assoc_handle ( $id ) { + debug("Destroying session: $id"); + + $sid = session_id(); + session_write_close(); + + session_id($id); + session_start(); + session_destroy(); + + session_id($sid); + session_start(); +} + + +/** + * Return an error message to the user + * @param string $message + */ +function error_400 ( $message = 'Bad Request' ) { + header("HTTP/1.1 400 Bad Request"); + wrap_html($message); +} + + +/** + * Return an error message to the user + * @param string $message + */ +function error_403 ( $message = 'Forbidden' ) { + header("HTTP/1.1 403 Forbidden"); + wrap_html($message); +} + + +/** + * Return an error message to the user + * @param string $message + */ +function error_500 ( $message = 'Internal Server Error' ) { + header("HTTP/1.1 500 Internal Server Error"); + wrap_html($message); +} + + +/** + * Return an error message to the consumer + * @param string $message + */ +function error_get ( $url, $message = 'Bad Request') { + wrap_location($url, array('mode' => 'error', 'error' => $message)); +} + + +/** + * Return an error message to the consumer + * @param string $message + */ +function error_post ( $message = 'Bad Request' ) { + header("HTTP/1.1 400 Bad Request"); + echo ('error:' . $message); + exit(0); +} + + +/** + * Do an HMAC + * @param string $key + * @param string $data + * @param string $hash + * @return string + * @url http://php.net/manual/en/function.sha1.php#39492 Borrowed from + */ +function hmac($key, $data, $hash = 'sha1_20') { + $blocksize=64; + + if (strlen($key) > $blocksize) + $key = $hash($key); + + $key = str_pad($key, $blocksize,chr(0x00)); + $ipad = str_repeat(chr(0x36),$blocksize); + $opad = str_repeat(chr(0x5c),$blocksize); + + $h1 = $hash(($key ^ $ipad) . $data); + $hmac = $hash(($key ^ $opad) . $h1); + return $hmac; +} + + +if (! function_exists('http_build_query')) { +/** + * Create function if missing + * @param array $array + * @return string + */ +function http_build_query ($array) { + $r = array(); + foreach ($array as $key => $val) + $r[] = sprintf('%s=%s', urlencode($key), urlencode($val)); + return implode('&', $r); +}} + + +/** + * Turn a binary back into a long + * @param string $b + * @return integer + * @url http://openidenabled.com Borrowed from PHP-OpenID + */ +function long($b) { + $bytes = array_merge(unpack('C*', $b)); + $n = 0; + foreach ($bytes as $byte) { + $n = bmmul($n, bmpow(2,8)); + $n = bmadd($n, $byte); + } + return $n; +} + + +/** + * Create a new consumer association + * @param integer $expiration + * @return array + */ +function new_assoc ( $expiration ) { + if (isset($_SESSION) && is_array($_SESSION)) { + $sid = session_id(); + $dat = session_encode(); + session_write_close(); + } + + session_start(); + session_regenerate_id('false'); + + $id = session_id(); + $shared_secret = new_secret(); + debug('Started new assoc session: ' . $id); + + $_SESSION = array(); + $_SESSION['expiration'] = $expiration; + $_SESSION['shared_secret'] = base64_encode($shared_secret); + + session_write_close(); + + if (isset($sid)) { + session_id($sid); + session_start(); + $_SESSION = array(); + session_decode($dat); + } + + return array($id, $shared_secret); +} + + +/** + * Create a new shared secret + * @return string + */ +function new_secret () { + $r = ''; + for($i=0; $i<20; $i++) + $r .= chr(mt_rand(0, 255)); + + debug("Generated new key: hash = '" . md5($r) . "', length = '" . strlen($r) . "'"); + return $r; +} + + +/** + * Random number generation + * @param integer max + * @return integer + */ +function random ( $max ) { + if (strlen($max) < 4) + return mt_rand(1, $max - 1); + + $r = ''; + for($i=1; $i= 0 && ($pr_host[$break] != '*' || substr_count(substr($pr_host, 0, $break), '.') < 2)) + return false; + + // now compare the paths + $break = str_diff_at($parts['child']['path'], $parts['parent']['path']); + if ($break >= 0 + && ($break < strlen($parts['parent']['path']) && $parts['parent']['path'][$break] != '*') + || ($break > strlen($parts['child']['path']))) + return false; + + return true; +} + + +/** + * Create a user session + * @global array $profile + * @global array $proto + */ +function user_session () { + global $proto, $profile; + + session_name('OpenIDLdap_Server'); + @session_start(); + + $profile['authorized'] = (isset($_SESSION['auth_username']) + && $_SESSION['auth_username'] == $profile['auth_username']) + ? true + : false; + + debug('Started user session: ' . session_id() . ' Auth? ' . $profile['authorized']); +} + + +/** + * Return HTML + * @global string $charset + * @param string $message + */ +function wrap_html ($message) { + global $charset, $profile, $html; + + header('Content-Type: text/html; charset=' . $charset); + echo ' + + +' . $html['page_title'] . ' + + + +' . implode("\n", $profile['opt_headers']) . ' + + +' . $html['page_head'] . ' + + +

' . $html['page_header'] . '

+

' . $message . '

+ + +'; + + exit(0); +} + + +/** + * Return a key-value pair in plain text + * @global string $charset + * @param array $keys + */ +function wrap_kv ($keys) { + global $charset; + + debug($keys, 'Wrapped key/vals'); + header('Content-Type: text/plain; charset=' . $charset); + foreach ($keys as $key => $value) + printf("%s:%s\n", $key, $value); + + exit(0); +} + + +/** + * Return an HTML refresh, with OpenID keys + * @param string $url + * @param array @keys + */ +function wrap_location ($url, $keys) { + $keys = append_openid($keys); + debug($keys, 'Location keys'); + + $c = wrap_param($url,http_build_query($keys)); + header('Location: ' . $c); + debug('Location: ' . $c); + exit(0); +} + + +/** + * Return an HTML with new parameret added + * @param string $url + * $param string $param + */ +function wrap_param ($url, $param) { + $q = strpos($url, '?') ? '&' : '?'; + return $url . $q . $param; +} + + +/** + * Return an HTML refresh + * @global string $charset + * @param string $url + */ +function wrap_refresh ($url) { + global $charset, $sreg, $html, $profile; + + $pos = strpos($url, 'openid.mode=authorize'); + + if ($pos == false) { + $site = ''; + } else { + $user_name = ($sreg['fullname'] == '') ? $sreg['nickname'] : $sreg['fullname']; + $party_url = ($profile['paranoid'] == true ? $_SESSION['accepted_url'] : $_SESSION['cancel_auth_url']); + $site = '

Hello, ' . $user_name . '! You are going to confirm your identity to a client site with URL:
' . $party_url . '

'; + } + + header('Content-Type: text/html; charset=' . $charset); + echo ' + + +' . $html['page_title'] . ' + +' . $html['page_head'] . ' + + +

' . $html['page_header'] . '

' . $site . ' +

Redirecting to ' . $url . '

+ + +'; + + debug('Refresh: ' . $url); + exit(0); +} + + +/** + * Implement binary x_or + * @param string $a + * @param string $b + * @return string + */ +function x_or ($a, $b) { + $r = ""; + + for ($i = 0; $i < strlen($b); $i++) + $r .= $a[$i] ^ $b[$i]; + debug("Xor size: " . strlen($r)); + return $r; +} + + +/** + * Return SeatBelt state XML + * @global string $charset + * @param string $name + */ +function wrap_seatbelt_status ( $name = false ) { + global $charset, $profile; + + header('Content-Type: application/xml; charset=' . $charset); + echo ''; + + if ($name != false) { + echo '' . $profile['idp_url'] . ''; + } + + echo ''; + exit(0); +} + + +/** + * Return SeatBelt configuration XML file + * @global string $charset + * @global array $profile + */ +function wrap_seatbelt_config () { + global $charset, $profile, $proto, $reldir, $html; + + $current_url = $proto . '://' . $_SERVER['SERVER_NAME'] . $reldir; + + $out = ' + + + ' . $html['page_title'] . ' + ' . $html['seatbelt_text'] . ' + ' . $profile["idp_url"] . '?openid.mode=login + ' . $profile["idp_url"] . ' + ' . $profile["idp_url"] . '?openid.mode=sb_status + + + + ' . $current_url . 'images/seatbelt/icon-logo.png + ' . $current_url . 'images/seatbelt/icon-gray.png + ' . $current_url . 'images/seatbelt/icon-high.png + #D6D6D6 + #EEEEEE + #74D174 + #7C7C7C + #009900 + #2B802B + #666666 + #009900 + #FFFFFF +'; + + header('Content-Type: application/xml; charset=' . $charset); + echo $out; + exit(0); +} + + +/* + * App Initialization + */ +// Determine the charset to use +$GLOBALS['charset'] = 'iso-8859-1'; + +// Set the internal encoding +if (function_exists('mb_internal_encoding')) + mb_internal_encoding($charset); + +// Avoid problems with non-default arg_separator.output settings +// Credit for this goes to user 'prelog' on the forums +ini_set('arg_separator.output', '&'); + +// Do a check to be sure everything is set up correctly +self_check(); + + +/** + * Determine the HTTP request port + * @name $port + * @global integer $GLOBALS['port'] + */ +$GLOBALS['port'] = ((isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' && $_SERVER['SERVER_PORT'] == 443) + || ($_SERVER['SERVER_PORT'] == 80) || ($_SERVER['SERVER_PORT'] == 443)) // Omit default ports + ? '' + : ':' . $_SERVER['SERVER_PORT']; +debug("Port: " . $_SERVER['SERVER_PORT'] . ' => ' . $GLOBALS['port']); + +/** + * Determine the HTTP request protocol + * @name $proto + * @global string $GLOBALS['proto'] + */ +$GLOBALS['proto'] = (($profile['force_ssl'] == true) || ($_SERVER['SERVER_PORT'] == 443) || (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on')) ? 'https' : 'http'; +debug("Proto: " . $GLOBALS['proto']); + +/** + * Determine the relative server path, i.e. where the package is installed + * @name $reldir + * @global string $GLOBALS['reldir'] + */ +$GLOBALS['reldir'] = substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/') + 1); +debug("Path: " . $GLOBALS['reldir']); + +// Set the authorization state - DO NOT OVERRIDE +$profile['authorized'] = false; + +// Set a default log file +if (! array_key_exists('logfile', $profile)) + $profile['logfile'] = '/var/log/openid'; + +// Set a default IDP URL +if (! array_key_exists('idp_url', $profile)) + $profile['idp_url'] = sprintf("%s://%s%s%s", + $proto, + $_SERVER['SERVER_NAME'], + $port, + ($profile['auth_username'] != "") ? $GLOBALS['reldir'] . $profile['auth_username'] : $_SERVER['PHP_SELF']); + +// Determine the requested URL - DO NOT OVERRIDE +$profile['req_url'] = sprintf("%s://%s%s%s", + $proto, + $_SERVER['HTTP_HOST'], + $port, + $_SERVER["REQUEST_URI"]); + +// Set the default allowance for testing +if (! array_key_exists('allow_test', $profile)) + $profile['allow_test'] = false; + +// Set the default allowance for gmp +if (! array_key_exists('allow_gmp', $profile)) + $profile['allow_gmp'] = false; + +// Set the default force bigmath - BAD IDEA to override this +if (! array_key_exists('force_bigmath', $profile)) + $profile['force_bigmath'] = false; + +// Determine if GMP is usable +$profile['use_gmp'] = (extension_loaded('gmp') && $profile['allow_gmp']) ? true : false; + +// Determine if I can perform big math functions +$profile['use_bigmath'] = (extension_loaded('bcmath') || $profile['use_gmp'] || $profile['force_bigmath']) ? true : false; + +// Set a default authentication domain +if (! array_key_exists('auth_domain', $profile)) + $profile['auth_domain'] = $profile['req_url'] . ' ' . $profile['idp_url']; + +// Set a default authentication realm +if (! array_key_exists('auth_realm', $profile)) + $profile['auth_realm'] = 'OpenID'; + +// Determine the realm for authentication - DO NOT OVERRIDE +$profile['php_realm'] = $profile['auth_realm'] . (ini_get('safe_mode') ? '-' . getmyuid() : ''); + +// Set a default lifetime - the lesser of GC and cache time +if (! array_key_exists('lifetime', $profile)) { + $sce = session_cache_expire() * 60; + $gcm = ini_get('session.gc_maxlifetime'); + $profile['lifetime'] = $sce < $gcm ? $sce : $gcm; +} + + +/* + * Optional Initialization + */ +// Setup optional headers +$profile['opt_headers'] = array(); + +// Determine if I should add microid stuff +if (array_key_exists('microid', $profile)) { + $hash = sha1($profile['idp_url']); + $values = is_array($profile['microid']) ? $profile['microid'] : array($profile['microid']); + + foreach ($values as $microid) { + preg_match('/^([a-z]+)/i', $microid, $mtx); + $profile['opt_headers'][] = sprintf('', $mtx[1], $proto, sha1(sha1($microid) . $hash)); + } +} + +// Determine if I should add pavatar stuff +if (array_key_exists('pavatar', $profile)) + $profile['opt_headers'][] = sprintf('', $profile['pavatar']); + + +/* + * Do it + */ +// Decide which runmode, based on user request or default +$run_mode = (isset($_REQUEST['openid_mode']) + && in_array($_REQUEST['openid_mode'], $known['openid_modes'])) + ? $_REQUEST['openid_mode'] + : 'no'; + +// Run in the determined runmode +debug("Run mode: $run_mode at: " . time()); +debug($_REQUEST, 'Request params'); +eval($run_mode . '_mode();'); +?> diff --git a/images/logo.gif b/images/logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..1b5c40b01bb37e2a6e084d0442fc804c36bb3303 GIT binary patch literal 4253 zcmYk2cRbbq<40fb_Z_ZlbnWZfd!$?|qH*n!j3yE{JEUubq^k zVnUE=D){{olWT1xZ?hIE96RKmir}{{34}>MeLX z@%Qt*AVO5I^hU7yLvS}=&|fYXWsi(c3VwbS6lVJ6%>6eXKSeVFh2AebS^D<<5HI!%|K-Y$zXFF} zym$LUE|;{P$4Ow5P1wx76T%uTM?Tb74a7>Cuxh4wjS*Y8L|GU)R&txoP~K5|5>s#NXfN$tbdv7r|9GA)rM+0(q*$D>v=nnn+8OQ znh2C?pLQ@o3nBKw4wulPvgm0MId-T|qWnM%?cSCL!;V>hH$D4wA-&ApaF_A&U)~FF6o|jWdWb`s9=7n5nU)G(nMg&1 z%{}&zPp4J z#W0rCRGen#1FaHJx+;-XZrn`;XSpA=a>V?OJr+`(*0aXzl!FxgcpDmmNoJX1F>y8& zO`(n%y=AsJgKv#eerGv|m;FFZK^gZ@ZNz1JIIw-VJ~6!HveGZT;D)Fs5>&zker1QK z`5TYaK`kGFai$zP5DQ~#p!;OWNV{526{Yole9Qpmylf;{Sm&{KC|-@m8Kf$I@URiz z_JM-pTKYcnyJV7ORTf-=@hIB0yJEZ_=9I{`Ja}BS2P3Ym%gi!XGo=J&>K@?tYqKZ_ z_N?%45wf^qoO&N*a)9}LLgqRd0Sbx)_}+3QjnZz%F)-q)f8YQy;V|D|u#oh}H!19@ z{fcec4ZZ^EXnS%LYd^KWNnbOJ5t--x=rDvC0Th_jJI^-+_askrV3lHuZ(=c;1)>lw0jk)RkOVU%3u!=eZDmG4Gg=(1(Q69CL zrt^@_fW`94Ty?fS-k|XqR=9&<3u`BO42%@-3;5I(k}s&vuqeMQYnPg=Hvh1ySJX;K zM-$a|uS+hvW|@`DuwMW1r!sN8|Jmry@f{89zRh*3{lp7-sls8|`)l+G7QJvG>XuHF zvWdUsb!+jFKknzfzj1_Q)#w`+2ha5taQG}TR@|4GKpgYmf-gqK+MMWv-`+Tr64O3K zy7)@k!Q!R_3k-iLSf4qAx?p!l{8&{qaaker60qUEcRx7$Ruw*nWArsM)63O4C0z{z zx|P8hyc)EK#;T}@}5rnET&3PkgogF}s{wQX6=)5xzUT+_tbpq+SFaigX5!j8~ zZ0XDdyO#n`>QnBP6%;5h$ObzMA##_Fp~`f1fil6v&aXfm<>asDu7@_jAjyyqMLV#k ztD*d6jbcQF_;g99$%GzyqwuW@DsIu?%o-aOEd)$m&NIS?6gO20XW;cBc=_TZOsVD= z?)PYbzh4&CrGAk1C*Q1C=>V<=xfopNxQ_BTN>r@xPp>zpz3w_z{nRsU&jdG@Hy(8c zN+}jj$d1d^`QfV_dR`w7fHLneA!1^myB3*io8(C|ET*d&sUe%<0;=`W!ZBuS=-y1^ zE_n;=?Frp$;Tg0v)(qWh`<}^@m4%Q-A&eUE;_wK(PIk_X$k5G4uvhXfnjFj!Dr=u| zeEP2v@Zj91@Kb)9MS(>?^cKPpI1SeD)|hF^@m2KA2U(XX-Mmk8KQA^dY9sLik$iMz^Q^c zkLh4jdAafx`;W-3)%``V2HaR?InWjcrO3#;S(q2M=TVp++Ca0Jh zs$=dyox#5;|7c`4bH(YMhlBmjR3)q)4ttR6NywGwV;MwJElVe1jR%phi$^KOGDiTpmx!D zbF0GV!g!qb{jV*rnk0&Bc-oiK-rAQ788EY@lX;*&r}ufo*4I)nrC+8zfK!Lx@GH6B zJZ+D~RgY$kRPIo`B+=>=b&?U>5`?_LbuFR-@SlSi93bO#tzXl!nPaqdDKhH!qZ^H% z6^F)ip zYw3IOljjBw*&g|+mDMHNZI0hGX&YZ>+t^`c7w4tIPIIM&B3&u8`rY#$y8676*@Uwh zs)?j&e}gdOm{)PStXiUpZ>4t)69c2WU-8q;KmR4w-WO_Hp%mrE zc*n1C&b7u^m!fxzUQbOerl%8QYbQfUv>mb;*3r=!>*KV`L?mxoVwSsAlMQ9mIujW< zAFTkt@8IN5FqZB}qPST7bU1@a>P&SMV|vD6a~hp4{8|iO2?Yv>DE%8`jUzEqVz+n8 zI1)w3Z;wjyA(*GD6aYvJ-xQM=ZXinj*1p>Cl-&69734ow9~ zWn@XAGoBSY;i$lULeqo{#1Qb3yH%12`gEMH4Cc&E`Gv}z?i&liaokx9nA-utWZ*Uc zkqB2E? zZWIdAel_w29p4Kn83S?XF@soWe1;o9v;-o8Ijub^2%P?RXiWswW1--hhdrt=4Xi~O#{V;rPU)aDN0yDj@j2jPdj)I<3isx`&;lg*ZVpL(e}Yoq#`3}S6Q z+9;nGm!!RiK@?%2<9r&TT`&`f>SmWS!u+~HLVfSPx;6&s%>^_>*yl`+w*)E_@@*K%H-x_tm|9O6NWfcJ8`yjDN#{ z(DEps)TWO%+1F^&e%+4K{PbbV*e4*ougQO2Z#UhGxMoeHICjh61wLqCg%(({PKP_2 zAuTu7SaS|7i*;J1u(g3kTkiX~JH^sErPuv5SdH{{!HkW@t7iBS>#-4AFJcqsxf0=# z%CUY?sZMCN z+0`oI+PGeB)xq-9b!V1@*1vXixwJe;>ayina>F~%mKD2XsmxXxQ`>QBgqwx0Iu_`+ z9*>vV(^|rtQ14hh%j4J+fjwDO#tnU>)6Y&fiHKxa-5K12j?3sU+&28A8NZ3LRy#bI6db zlxA$v$%B-GlF3pOnj(qZ-uFD8p6kQ^_q#s+zg$kvj#MjO0(b&=0|4+T0Dkire%l4J zDe$*BxHfipco=Rmj#2ym{rkrp|A~orU%q^S>A>{#6ny76JpJm| zuV0&sQ}8)S_{)2E?Falc8}2BC2Y7u$Bk<}sINJpd#K38^_p@Kd8w%iX*`>A3>7^Ao zL=0YEg98w-uzz!7Z<~B;YO~u@TM2L@?)kd01u^ZTSO@(rwXoUH5vi8(G*6;;VJ z%k+%f-227nZe{x$YRUqZHTU96c=Zj=WtsjOV%mi@cW>4`{uIs} z3(6>{yM~p;#9Q#Vf-v0n2Pfys?{`MaSyY#d_7vM+RkgRC?(OlnQuMYn-tmG=j?2|vY< zk-AgOL4CHX+XR_6dvBoszPKpY6rku{IoPq#eOmp5yLUbE!O3R3Eu|xE{`-?Kcx@X9 zWGAsbE^UVh0)(R^PL4>a~&ZM{oo(74j z_?SDoFXPN6ud4@Pw=jnT!Y-YFekc8$jMBaxd@{TZC#!|RRQ7P$Ms6G$sF_ISXOLVN zfEcP*Phls4P8W#hD^$&7A38xMiEF$vSCvl0dv>Rp;q&~i^(LT%wM zl!+6QCv&j?qvugC4ynNHNV0m=2_Q)*6+QyO7|wB#@em(lNK18>#uqBt`%z-bAt^0R zZm0N7mI^nuFQMvew8XAKFSJ~Ufj`@munc0O96230J&1dwP*Q;o-ZQ*u1{O-9`UEvm z7^4L#?RX9()+&93iMGz?u_R6MIc&5Ek2a)?yR$xj9zz|ObfDW1q~`200jeF!WzHN zxVWjp+(S!OccoEA`t7&L}#}MBxGsmSV;iAay?i7CJAYQ zE!D_c5!aupOALR_L5lzgRR6ZaA>{Bsz5hU_s)&p%0g3jG{lMa}tE4(d5fEqW&p!f% zYSK#qX$IhwZVO-uP{&aK!A_tf<2JOxT_PshM~JG^5&(E!drd77Z1mAok>(x>Sj|4H z${&LzFYW8zf7Ct*6bQ*yDSw1JGmW^Z$iDnO^+JR2keQQwEe7+dRd2X9WJdtWOfx(H z>ft>Lwa8G>5E;yp*DrL1phM;)j#NXQGoYmAX>I*N+Lw}^?hl2b#*IWxXW9|Z&7 zIK#&{2=!c|%BX5&AVEu5!%*e-8#f{)pYSU^Tt;Y!pDY!TtUS0z(<49b_{+}?sfJzX z|Cqz{Z%w=T{ev#<4{Gr61{(A)9x)#m0~e1o@n#{zrn z4WZhZ-|vTpA*K+$>|Y3ohy{_M`XHpnziV9zAuG*q`}02=j&~il3)e?pL>e3?Rh1)+ zS*TU_tq$dPzv>m{ZyQGLVflWvn_M6N*}mo>BX@?EBGE|kppL%z5FV&>%1$n1iZ=%s zI$;i)aBn7c3BdH*&_edK$M3|WNHZb5Fm>0BnX*m{ecI>X%Kj#h#avYn=*(+;ZA#9l zPj3^NIDEA?sJNy*eR}iBN~QVMZ8xMwryC^E}|wb$9Sd@-l>u$zsVU0HF845~%gKOw@c7XvsDpg_~?G%+;h?=V@2 zEPvGBRyAj!!T&m6hQ8tP`r`efq?eI(eWyIWeJ!V`xhCi=Azq7H%sC3bGQBWZ&w=%k zwtGK#EbXc14@3>aq>*H`M>8z}(2I&IbEi{Le}J5k(rNl9p3AOIRT$)wl=!#PmL7AY7`*p24O3rKOV+6N?bAq5_TtLna zFdDp;`aEG`B$hlKO7J-QW;xI#$Mv@I!8K3G%semT+QG-s4lhrS5EpkYp9BH6h$-bsH`+S$WSL(m2RWbY#S+?4BADCW0U^$mJ`mJX!F2$ zlp}$I_4V*$Gy*Zrz_1R6$_1-u{9howE1Y((=L4Fzg2&dbX%ir7%Qd(W>;Cyyjc&4C{jK}|ex;}r?T1z##s4fA$(dAQYd064PKg%w1#0{gf;P$NfG6J@D1 zfTzU*m6}t=si(zgfJ?Eq1rZqRI<+JRUYm9Nf=;HJCqJtU7hol$E&^bgxB<^a4G$oM z6m>4>SS9{@$~3M8k@?f4#~itZ;(#bJQVp_Aws+jXddku*ENG;21ptYBT$_GK%qs1s zcN)4A&?e_6TIDMf0~qE;5^mY6LMF8N<5chD#Brjn6 z_1j_t_}cF(wgvpqO^7Ns#xq56Y{T*?7ICzprCE^LrS(DBc5 zmU0UpMS_71*Kaw;3~)#)LU3o#fTCzh>5EPd`2`xW zl7FX|Hr^@w{8y1H%EE5k!FAn1tyq2s%6ff%zeIi6)8(=sOf)*6oGxc5>0ocA8n4w= z9yXqDMJai!r8}Q{G&HVa^-{r^4_04uX_;E~77CSHtcuGA(LeDg_vsP+q%i@ddOT6NC-4Uqde>OZqpk%C9F4(4;Dq8MjXvh!|pu7sI?8;?UI*}7rU0qSJ za32`6^D1nRNmnd>$_6hto@@*ChHT$jR@k6lB zqgHrOVeP)=Oou@=fWQL2!p9HXLG^cFfzFgBy$^iQ`d~p-iGQD9{IE7t*0Wfpe05wY zl&jSK*!-Vp^=N}yQ0+n3;4UeSrVK;1=?JoxX<6FBTQwx$DXQ0*gw!nHS4utjSN&(S PKVZ}VWg%?=VE6w4dr96O literal 0 HcmV?d00001 diff --git a/images/openid.gif b/images/openid.gif new file mode 100644 index 0000000000000000000000000000000000000000..cde836c893f64bcfec04b9c817e3371ff122fe19 GIT binary patch literal 237 zcmVb{bmUKcqz}))c5uC(7v?)v4a2P)ZNa- z@$&T2)z|&~{r~^}A^8LV00000EC2ui01yBW000GQ;3tk`X`bk)Wk@<6#nZYULKH{p zEx|?+kif!I0vIL|#ZMubBmjWH2OtmxIFVa~6JQ7!1CK!f5W#StOTv&C3=E8h2vI1s n+#cd5;2fT3B_0kF0v!+!GARoV78n&7dMN`JIW(4+BOw4gP{MS* literal 0 HcmV?d00001 diff --git a/images/openid.ico b/images/openid.ico new file mode 100644 index 0000000000000000000000000000000000000000..7df593adb45fc01726c5b9fc858ff7777ce74b55 GIT binary patch literal 1406 zcmeH@%SyvQ6o&uFD0XOL3(HBJmO$Tt%q3a1)J}DuJZq zl(tf+3qf$zGn`@mZ_dm)`~Z>5bs={XyBg2}uqjg_Y{(q(xgcYbrYY|GeFQ;(qA1|( z4dLz!L{Wt7L*Qv35XUi2&Tf!p8Nx6`p65uC1ay0dAiBfjbB^%Bhwu9^cS>v>6zD}) zcpH4;`1BfwM*_VEf%D4*@2@YojU$w02_eMXSPQUlVBx_3IlzAtv~A3k+11v{a)(U_ zt9r`1TdSE9O+3qHhNew4)tx2I&5@>*G0t=aoXdNIw=LbG9Ew7^bycf(iK94_bLd{9 r(o`!A&jO_8%qDA1)g|_{rTkBo?`Oi$zWa~qYG*pnOw~`vzdHQ|w_;{m literal 0 HcmV?d00001 diff --git a/images/openid.png b/images/openid.png new file mode 100644 index 0000000000000000000000000000000000000000..08ea181aa5eac1d4dc488e0674653e809767cd1a GIT binary patch literal 797 zcmV+&1LFLNP)K@>gn-pszYyKnRMi@OQfhzbEA;wKPT45DD6*jflS7Ah86DQPWiv`M2jTH07> zX^1HVZG<2a+{R!eF-taX_M5l+{@!_;HJ{Fb3p30;=bn4+46`f?tyT*m0M3AdaA-%T zkV5Fdk56Z@Ub%_$ckjS+JTOFe9^+_Is)9XBk_15jcsAW|VDare%-;Bn%7=4c%tPQJ z^j!1<1y;Z#B0dC5VxJglj=+f9LBe$;!Bup&Rm2jOOzg09s+zEa(KZE(zM zTI`F6$G<6xLLG#H55d0gPc#~hN4;L}a!5Y#k$gP_<3nw9U*2rgugP4)Fy@oVua%C zobh}gdS_kwMk%{fW}y$)v42w4TSDx5uXMZJaY?4@`gg%;Jk*lv+vGFbGalLpKGGVC z>XLc?LdoTFb8xFRW0!47&LCm@o97Lj0wgsZB$naO0XbUK|^^d~NosTDJwe%?!E<_b(rsZL}f z2R$$-15ZqY2O>_%)r1c1j3A$?$-SQ3QCl?MwA5SrR|DNig)WpGT%PfAsA%PdMQ z&o9a@_TMb;2UH^lQsZ2dnpl#VpQjL#nVZUBV5)Cmsc&FmZ^mB_RKerv;uyj)GdV$l Z7sxYUV050cJsrqm@O1TaS?83{1OR3}F6saP literal 0 HcmV?d00001 diff --git a/images/seatbelt/icon-logo.png b/images/seatbelt/icon-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..db73c5c495cad2febecb54d71773885427efa393 GIT binary patch literal 1517 zcmVVK?$D`X1b@p?^~XFT`h0k^vt{=v67qD?^WG<&%Jf- zt!gHvr0}?U^(uLuM~>r=VHo85l2U?bMV4sBQr{!0O%jO&(eGm$iLq6*<%Ms^ZqBgB zSo?WxxFcPLAY-xdRoNeUW0_!=GBe}ZmY+({y-*R_RlPLddvTVCLt zchH1}h~wKs7GRVcl7!iu36_zCX#c)dqe{~bQqI`?wnc0T00fslRg508c#>A|sj3JC z2ItLk!=lNlI=P-tLKv~^ig^5Efdn`_=bnl$Hd;>418aOg%UL^*la>08D--1_rt4nh zoc)INQ!ElbyAu*BCD(P=+qV6pWm%7-Lgo9|MFFWa>MzgiG(P(V7?HX~;uL~hg zrqk(>R4Vly-W}ifP3Z5y>hEI#P>#_fiuHPZxYcUy#qXTz88FsJB+Q?3nbbJeJcp$z zMgDX;HI_=J|BznuA%I*DXL|s84?vv;h{Le)EcCOn04V2ZL<3MhjOaJ1Z)i%FVdx_` zKWYeZ>29~#!wt3@m{Q~u^%k-}cuPy~PIEU2ZIVGGrmF$}&`E2LPw=ftb4F+z?^T_1+ZdiwlV2 zFEBXh>jl4xUf_0`ZI@m_luwEO0L2-z07Os6+o+j#J9leSy6J>LeWiTxTiyu(F!h8| z*7d&*+tH_vWz`95q7ZBao5!@$?JZ@o1Ab9Ol&EShK^FWNAhNV_X(2c; zVxNYJ@!4$lXMlJDO#V}YcoiT{p+bh3@7+~SE+gB>R9VNY!s5AR`?7NTLek!j96%K3 zq>h-2gy6#QVzKxzoW6z17(zGO08V@iQ12O5X3TbSW>6V3;A;&^|IIyT| zc^AiOoY9U7p}x}@`!zAc92nI)xy+IxAf`3d#y3~J%aNXuc^xS4$TH{^jNoV9tC@onGcu$|I>Q~hyVZBi2vZ9|J#oVj{pDF zfq;O30D%9%z`y_i0RR90A^8LW000jFEC2ui01yBW000D0@X1N5eJ*Qv@!r<=RibkU z4M&NMu&ArHoDGwb+9@E$9cPPO5B78L6a?Z#gJ?K?p@l*5VsroiC!w(zBrJe{cra)b S01Rt|oG291>5g0y0RTH+Fh?N( literal 0 HcmV?d00001 diff --git a/index.php b/index.php new file mode 100644 index 0000000..f687e03 --- /dev/null +++ b/index.php @@ -0,0 +1,84 @@ + aka Dako. + * Copyright 1996-2011 by 5Group & Co. http://www.5group.com/ + * See LICENSE file for more details. + */ + +// Get username from request URL. Remove any leading / +$userid = ltrim($_REQUEST['user'],'/'); + +/** + * User profile + * @name $profile + * @global array $GLOBALS['profile'] + */ +$GLOBALS['profile'] = array( + # Basic Config - Required + 'auth_username' => $userid, // user logon name + 'auth_cn' => $userid, // user CN value, see ldap['lookupcn'] + + # Optional Config - Please see README before setting these +# 'microid' => array('mailto:user@site', 'http://delegator'), +# 'pavatar' => 'http://your.site.com/path/pavatar.img', + + # Advanced Config - Please see README before setting these +# 'allow_gmp' => false, +# 'allow_test' => false, +# 'auth_realm' => 'OpenID', +# 'force_bigmath' => false, +# 'idp_url' => 'http://your.site.com/path', +# 'lifetime' => 1440, +# 'paranoid' => false, # EXPERIMENTAL +# 'force_ssl' => false, # EXPERIMENTAL + + # Logging Config - Please see README before setting these +# 'debug' => false, +# 'authlog' => false, +# 'logfile' => '/var/log/openid' +); + +/** + * Simple Registration Extension + * @name $sreg + * @global array $GLOBALS['sreg'] + * DO NOT ENABLE OR EDIT ARRAY ENTRIES! Call to find_ldap() bellow will populate them! + */ +$GLOBALS['sreg'] = array ( +# 'nickname' => 'Joe', +# 'email' => 'joe@example.com', +# 'fullname' => 'Joe Example', +# 'dob' => '1970-10-31', +# 'gender' => 'M', +# 'postcode' => '22000', +# 'country' => 'US', +# 'language' => 'en', +# 'timezone' => 'America/New_York' +); + +/** + * Some HTML templates + * @name @html + * @global array $GLOBALS['html'] + */ +$GLOBALS['html'] = array ( + 'page_head' => '', + 'page_title' => 'OpenID Provider', + 'page_header' => '
This is our OpenID Provider endpoint.', + 'seatbelt_text' => 'Our OpenID Provider endpoint.', + 'welcome_text' => 'Dear, employee, as a valued member of our company you have an unique identifier (%s, for example). You can then use this identifier to log on to all Web sites that are OpenID-enabled (over 450 and counting, see the Directory for a partial listing of the sites). Please enter your company username bellow to find out what is your identifier. In order to login you have to provide your company password. If you do not have one yet, contact the Systems administartors.
Username:
', // note "%s" which represents example OpenID + 'welcome_help' => 'You can find more information at http://openid.net/.', + 'user_not_found' => 'User "%s" is not found in our database, sorry.' // note "%s" is username here +); + + +require('ldap.php'); +find_ldap($userid); // lookup user and populate sreg info + +require('engine.php'); +?> diff --git a/ldap.php b/ldap.php new file mode 100644 index 0000000..514ccfc --- /dev/null +++ b/ldap.php @@ -0,0 +1,172 @@ + aka Dako. + * Copyright 1996-2011 by 5Group & Co. http://www.5group.com/ + * See LICENSE file for more details. + */ + +/** + * LDAP connection settings + * @name $ldap + * @global array $GLOBALS['ldap'] + */ +$GLOBALS['ldap'] = array ( + # Connection settings + 'primary' => '10.0.0.111', + 'fallback' => '10.0.0.222', + 'protocol' => 3, + # AD specific + 'isad' => true, // are we connecting to Active Directory? + 'lookupcn' => true, // should we extract CN after the search? + # Binding account + 'binddn' => 'cn=,cn=users,dc=domain,dc=local', + 'password' => '', + # User account + 'autodn' => false, // extract DN from search result, ignore 'testdn' + 'testdn' => 'cn=%s,cn=users,dc=domain,dc=local', + # Searching data + 'searchdn' => 'cn=users,dc=domain,dc=local', + 'filter' => '(&(objectCategory=user)(mail=*)(sAMAccountName=%s))', + + # SREG names matching to LDAP attribute names + 'nickname' => 'uid', + 'email' => 'mail', + 'fullname' => array('givenName', 'sn'), +# 'dob' => '', +# 'postcode' => '', +# 'language' => '', +# 'timezone' => '', +# 'gender' => '', + 'country' => 'c', + + # Default SREG values (default server settings) + 'def_language' => 'en', + 'def_postcode' => '1000', + 'def_timezone' => 'Europe/Sofia' +); + + +/** + * Search for LDAP account by username. Populate $sreg if found + * string $username + */ +function find_ldap ($username) { + global $sreg, $ldap, $profile; + + $no = "no"; + $profile['user_found'] = false; + + if ($username != "") { + $ds = ldap_connect($ldap['primary']) or $ds = ldap_connect($ldap['fallback']); + if ($ds) { + ldap_set_option($ds,LDAP_OPT_PROTOCOL_VERSION,$ldap['protocol']); + if ($ldap['isad'] == true) ldap_set_option($ds,LDAP_OPT_REFERRALS,0); + + $r = ldap_bind($ds,$ldap['binddn'],$ldap['password']); + $sr = ldap_search($ds,$ldap['searchdn'],sprintf($ldap['filter'],$username)); + $info = ldap_get_entries($ds, $sr); + + if ($info["count"] == 1) { + $no = "ok"; + $profile['user_found'] = true; + if ($ldap['lookupcn'] == true) $profile['auth_cn'] = $info[0]['cn'][0]; + if ($ldap['autodn'] == true) $ldap['testdn'] = $info['0']['dn']; + + # Populate user information from LDAP - if (array_key_exists('keyname', $ldap))... + $sreg['nickname'] = $info[0][$ldap['nickname']][0]; + $sreg['email'] = $info[0][$ldap['email']][0]; + + $values = is_array($ldap['fullname']) ? $ldap['fullname'] : array($ldap['fullname']); + $fullname = ''; + foreach ($values as $vname) { + $aname = $info[0][$vname][0]; + if ($aname != '') $fullname = ($fullname == '' ? $aname : $fullname . ' ' . $aname); + } + $sreg['fullname'] = $fullname; + + $sreg['country'] = $info[0][$ldap['country']][0]; + + # Values not obtained from LDAP + $sreg['language'] = $ldap['def_language']; + $sreg['postcode'] = $ldap['def_postcode']; + $sreg['timezone'] = $ldap['def_timezone']; + } + ldap_close($ds); + } + } + return $no; +} + + +/** + * Perform LDAP bind test with provided username and password + * string $username, string $password + */ +function test_ldap ($username, $password) { + global $ldap; + $no = "no"; + # Ignore empty password as well + if (($username != "") && ($password != "")) { + $ds = ldap_connect($ldap['primary']) or $ds = ldap_connect($ldap['fallback']); + if ($ds) { + ldap_set_option($ds,LDAP_OPT_PROTOCOL_VERSION,$ldap['protocol']); + if ($ldap['isad'] == true) ldap_set_option($ds,LDAP_OPT_REFERRALS,0); + + if ($ldap['autodn'] == true) { + if (ldap_bind($ds,$ldap['testdn'],$password)) $no = "ok"; + } else { + if (ldap_bind($ds,sprintf($ldap['testdn'],$username),$password)) $no = "ok"; + } + ldap_close($ds); + } + } + return $no; +} + +/* notepad here: + ... This was acheived with this stanza in slapd.conf + + access to attr=userPassword + by self write + by anonymous auth + by * none + + print "

Change password "; + if (ldap_mod_replace ($ldapconn, "uid=".$username.",dc=example,dc=com", + array('userpassword' => "{MD5}".base64_encode(pack("H*",md5($newpass))) { + print "succeded"; } else { print "failed"; } + print ".

\n"; + + + ++ private String detectActiveDirectory( IRootDSE rootDSE ) ++ { ++ ++ String result = null; ++ ++ // check active directory ++ IAttribute rdncAttribute = rootDSE.getAttribute( "rootDomainNamingContext" ); ++ if ( rdncAttribute != null ) ++ { ++ IAttribute ffAttribute = rootDSE.getAttribute( "forestFunctionality" ); ++ if ( ffAttribute != null ) ++ { ++ result = "Microsoft Active Directory 2003"; ++ } ++ else ++ { ++ result = "Microsoft Active Directory 2000"; ++ } ++ } ++ ++ return result; ++ } + +*/ + +?> diff --git a/showme.php b/showme.php new file mode 100644 index 0000000..442eb69 --- /dev/null +++ b/showme.php @@ -0,0 +1,40 @@ + aka Dako. + * Copyright 1996-2011 by 5Group & Co. http://www.5group.com/ + * See LICENSE file for more details. + */ + +// This code runs if the form has been submitted +if (isset($_POST['submit'])) { + + // Get username from POST request + $userid = trim($_POST['login']); + if ($userid == "") { + $userid = 'index.php'; + } + + // Redirect to nwe URL + header('Location: ' . $userid); + + echo ' + + ... + + + +

Redirecting to ' . $userid . '

+ +'; + +} else { + + // Show error message + echo 'You can not use this page directly'; +} +?> diff --git a/style.css b/style.css new file mode 100644 index 0000000..3ff0271 --- /dev/null +++ b/style.css @@ -0,0 +1,27 @@ +/** + * OpenID-LDAP-PHP + * An open source PHP-based OpenID IdP package using LDAP as backend. + * + * By Zdravko Stoychev aka Dako. + * Copyright 1996-2011 by 5Group & Co. http://www.5group.com/ + * See LICENSE file for more details. + */ + +.loginurl { + background: #FFFFFF url('images/user.gif') no-repeat scroll 0pt 50%; + padding-left: 18px; +} + +#login { + background-color:#FFFFFF; + border:1px solid #CCCCCC; + color:#000044; + padding:1px; + text-decoration:none; + background: #FFFFFF url('images/user.gif') no-repeat scroll 0pt 50%; + padding-left: 18px; +} + +#login:hover { + background-color:#FFF0D0; +}