├── .gitreview ├── AUTHORS ├── COPYING ├── ChangeLog ├── INSTALL ├── LICENSE ├── Makefile.am ├── NEWS ├── README ├── authn_linotp.load ├── configure.ac └── mod_authn_linotp.c /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=harrison 3 | project=mod_authn_linotp 4 | defaultbranch=master 5 | defaultremote=origin 6 | defaultrebase=0 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinOTP/mod_authn_linotp/5879a2242d8b8f0ff456368ebdb3dcc27f3b1d5f/AUTHORS -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | mod_authn_linotp - Apache module to talk to one time password 2 | solution LinOTP (http://linotp.org) 3 | 4 | Copyright 2012 LSE Leading Security Experts GmbH 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | Version 0.0.1 released 05 May 2012 2 | 3 | - initial release 4 | 5 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Compiling 2 | --------- 3 | 4 | libtoolize 5 | aclocal 6 | automake --add-missing 7 | autoconf 8 | 9 | ./configure 10 | make 11 | 12 | Install 13 | ------- 14 | 15 | make install 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | all-local: module 2 | 3 | module: mod_authn_linotp.c 4 | if test "$(srcdir)" != "."; then $(CP) $(srcdir)/mod_authn_linotp.c .; fi 5 | $(APXS) -c -D_REENTRANT `echo $(GCC_WARN_FLAGS) | sed -r 's/ -/ -Wc,-/g'` -l crypto -l curl mod_authn_linotp.c 6 | 7 | install-exec-local: module 8 | $(APXS) -S LIBEXECDIR="$(DESTDIR)`$(APXS) -q LIBEXECDIR`" -i mod_authn_linotp.la 9 | 10 | install-data-local: 11 | install authn_linotp.load $(DESTDIR)`$(APXS) -q SYSCONFDIR`/mods-available/ 12 | 13 | install-exec-hook: 14 | test -z "$(STRIP)" || $(STRIP) $(DESTDIR)`$(APXS) -q LIBEXECDIR`/mod_authn_linotp.so 15 | 16 | CLEANFILES= *.la *.lo *.o *.so *.slo .libs/* 17 | 18 | EXTRA_DIST= ChangeLog LICENSE mod_authn_linotp.c authn_linotp.load 19 | 20 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinOTP/mod_authn_linotp/5879a2242d8b8f0ff456368ebdb3dcc27f3b1d5f/NEWS -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | mod_authn_linotp - Apache module for authenticating with LinOTP 2 | =============================================================== 3 | 4 | This apache module can be used to authenticate users against 5 | LinOTP (http://linotp.org) 6 | 7 | This auth module only works with basic authentication. 8 | It uses a cookie to cache the authenticated user. 9 | The authentication of the first request is done against the LinOTP server. 10 | The consecutive requests are handled by the cookie. 11 | 12 | Configuration parameters: 13 | ------------------------- 14 | 15 | The module can only be configured in a directory context. 16 | 17 | 18 | 19 | LinOTPValidateURL 20 | mandatory 21 | Context: Dir 22 | Format: https://server/validate/simplecheck 23 | Default: - 24 | 25 | LinOTPRealm 26 | optional 27 | Context: Dir 28 | The realm name, the user will be authenticated in. 29 | Format: 30 | Default: - 31 | 32 | LinOTPResolver 33 | optional 34 | Context: Dir 35 | The useridresolver, the user will be authenticated in. 36 | Format: 37 | Default: - 38 | 39 | LinOTPCookieSecret 40 | mandatory 41 | Context: Dir 42 | A secret that is used to encrypt the cookie. 43 | Format: 44 | Default: - 45 | 46 | LinOTPCookieName 47 | optional 48 | Context: Dir 49 | The name of the cookie 50 | Format: 51 | Default: linotp_auth_module 52 | 53 | LinOTPSSLCertVerify 54 | optional 55 | Context: Dir 56 | Whether the certificate of the LinOTP server should be verified 57 | Format: On or Off 58 | Default: Off 59 | 60 | LinOTPSSLHostVerify 61 | optional 62 | Context: Dir 63 | Whether the hostname in the certificate of the LinOTP server should be verified 64 | Format: On or Off 65 | Default: Off 66 | 67 | LinOTPLogUser 68 | optional 69 | Context: Dir 70 | Whether the username should appear in the log file 71 | Format: On or Off 72 | Default: On 73 | 74 | LinOTPLogPassword 75 | optional 76 | Context: Dir 77 | Whether the password should appear in the log file 78 | Format: On or Off 79 | Default: Off 80 | 81 | LinOTPTimeout 82 | optional 83 | Context: Dir 84 | maximum time (in seconds) for which a one-time password is valid 85 | Format: a number 86 | Default: 600 87 | 88 | Example: 89 | ======== 90 | 91 | 92 | Options Indexes FollowSymLinks MultiViews 93 | AllowOverride None 94 | Order allow,deny 95 | allow from all 96 | 97 | AuthType basic 98 | AuthName "LinOTP protected" 99 | AuthBasicProvider LinOTP 100 | Require valid-user 101 | LinOTPSSLCertVerify Off 102 | LinOTPSSLHostVerify Off 103 | LinOTPLogUser On 104 | LinOTPLogPassword On 105 | LinOTPCookieName myCookieName 106 | LinOTPValidateUrl https://localhost/validate/simplecheck 107 | LinOTPCookieSecret 098as0d9uasldj0a7sda 108 | LinOTPRealm Realm1 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /authn_linotp.load: -------------------------------------------------------------------------------- 1 | LoadModule authn_linotp_module /usr/lib/apache2/modules/mod_authn_linotp.so 2 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # mod_authn_linotp - Apache module to talk to one time password 2 | # solution LinOTP (http://linotp.org) 3 | # 4 | # Copyright 2012 LSE Leading Security Experts GmbH 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | AC_INIT([mod_authn_linotp Apache module to talk to one time password solution LinOTP (http://linotp.org)], [0.0.1], [http://github.com/lsexperts/], [mod_authn_linotp]) 20 | AC_CONFIG_AUX_DIR(scripts) 21 | AM_INIT_AUTOMAKE 22 | dnl AM_MAINTAINER_MODE 23 | AC_PREREQ(2.59) 24 | AC_REVISION($Id$) 25 | AC_PREFIX_DEFAULT(/usr) 26 | AC_PROG_MAKE_SET 27 | 28 | # Check for required programs 29 | AC_PROG_CC 30 | AC_PROG_INSTALL 31 | AC_PATH_PROG(STRIP, strip) 32 | AC_PATH_PROG(CP, cp) 33 | AC_PATH_PROG(APXS, apxs, "no", [/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /usr/local/apache/bin]) 34 | AC_PATH_PROG(APXS2, apxs2, "no", [/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /usr/local/apache/bin]) 35 | [test x"$APXS2" = "xno" -a x"$APXS" = "xno" && ] AC_MSG_ERROR([cannot find apxs or apxs2]) 36 | [test x"$APXS2" != "xno" && APXS="$APXS2"] 37 | 38 | # Add GCC flags 39 | [GCC_WARN_FLAGS="" 40 | CFLAGS="" 41 | if test x"$GCC" = "xyes"; then 42 | GCC_WARN_FLAGS="-Wall -Waggregate-return -Wcast-align -Wchar-subscripts -Wcomment -Wformat \ 43 | -Wimplicit -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wno-long-long \ 44 | -Wparentheses -Wpointer-arith -Wredundant-decls -Wreturn-type -Wswitch -Wtrigraphs \ 45 | -Wuninitialized -Wunused -Wwrite-strings -Wshadow -Wstrict-prototypes -Wcast-qual" 46 | CFLAGS="-O3 $GCC_WARN_FLAGS" 47 | fi] 48 | AC_SUBST(GCC_WARN_FLAGS) 49 | AC_SUBST(CFLAGS) 50 | 51 | # Check for required libraries 52 | AC_CHECK_LIB(crypto, EVP_sha1,, 53 | [AC_MSG_ERROR([required library libcrypto missing])]) 54 | 55 | # Check for required header files 56 | AC_HEADER_STDC 57 | AC_CHECK_HEADERS(ctype.h errno.h openssl/evp.h openssl/hmac.h openssl/md5.h stdio.h string.h time.h unistd.h, [], 58 | [AC_MSG_ERROR([required header file '$ac_header' not found])]) 59 | AC_CHECK_HEADERS(err.h, [], []) 60 | 61 | # Generated files 62 | AC_CONFIG_FILES(Makefile) 63 | 64 | # Go 65 | AC_OUTPUT 66 | -------------------------------------------------------------------------------- /mod_authn_linotp.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | mod_authn_linotp - Apache module to talk to one time password 4 | solution LinOTP (http://linotp.org) 5 | 6 | Copyright 2012 LSE Leading Security Experts GmbH 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | 20 | * This is module is based on 21 | * mod_authn_otp (http://code.google.com/p/mod-authn-otp/) and 22 | * mod_auth_radius (http://freeradius.org/mod_auth_radius/) 23 | * 24 | * This module only supports basic authentication 25 | * It stores the authentication state in a configurable cookie. 26 | * 27 | * TODO: 28 | * - use per-server config 29 | * 30 | */ 31 | 32 | #include "apr_lib.h" 33 | #include "ap_config.h" 34 | #include "ap_provider.h" 35 | #include "mod_auth.h" 36 | #include "ap_release.h" 37 | 38 | #define APR_WANT_STRFUNC 39 | #include "apr_want.h" 40 | #include "apr_strings.h" 41 | #include "apr_file_io.h" 42 | #include "apr_time.h" 43 | 44 | #include "httpd.h" 45 | #include "http_config.h" 46 | #include "http_core.h" 47 | #include "http_log.h" 48 | #include "http_protocol.h" 49 | #include "http_request.h" 50 | #include "util_md5.h" 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | #include 59 | //#include # was removed from libcurl 7.21.7 60 | #include 61 | 62 | /* Apache backward-compat */ 63 | #ifndef AUTHN_PROVIDER_VERSION 64 | #define AUTHN_PROVIDER_VERSION "0" 65 | #endif 66 | 67 | /* Module definition */ 68 | module AP_MODULE_DECLARE_DATA authn_linotp_module; 69 | 70 | /* Our unique authentication provider name */ 71 | #define LINOTP_AUTHN_PROVIDER_NAME "LinOTP" 72 | 73 | 74 | /* Default configuration settings */ 75 | #define DEFAULT_TIMEOUT (10 * 60) /* 10 minutes */ 76 | #define DEFAULT_LOG_USER 1 77 | #define DEFAULT_LOG_PASSWORD 0 78 | #define DEFAULT_SSL_CERT_VERIFY 0 79 | #define DEFAULT_SSL_HOSTNAME_VERIFY 0 80 | #define DEFAULT_COOKIE_NAME "linotp_auth_module" 81 | 82 | 83 | /* Buffer size for OTPs */ 84 | #define OTP_BUF_SIZE 16 85 | 86 | /* Per-directory configuration */ 87 | struct linotp_config { 88 | char *validateurl; /* The validation URL like https://localhost/validate/simplecheck */ 89 | int sslcertverify; /* wether the certficate should be checked */ 90 | int sslhostnameverify; /* wether the hostname in the cert should be checked */ 91 | char *realm; /* the optional realm name */ 92 | char *resConf; /* the optional resolver name */ 93 | int loguser; /* wether the username should be logged */ 94 | int logpassword; /* wether the password should be logged */ 95 | int timeout; /* Maximum time for which the OTP is valid */ 96 | char *secret; /* secret to encrypt the cookie */ 97 | const char *cookie_name; /* name of the cookie */ 98 | }; 99 | 100 | 101 | /* Internal functions */ 102 | static authn_status authn_linotp_check_password(request_rec *r, const char *username, const char *password); 103 | static struct linotp_config *get_config_dir(request_rec *r); 104 | static void register_hooks(apr_pool_t *p); 105 | static void *create_authn_linotp_dir_config(apr_pool_t *p, char *d); 106 | static void *merge_authn_linotp_dir_config(apr_pool_t *p, void *base_conf, void *new_conf); 107 | 108 | /********************************************************************************** 109 | * 110 | * 111 | * cookie stuff 112 | * 113 | */ 114 | 115 | #define COOKIE_SIZE 1024 116 | /* make a cookie based on secret + public information */ 117 | static char * 118 | make_cookie(request_rec *r, time_t expires, const char *passwd, const char *string) 119 | { 120 | char one[COOKIE_SIZE], two[COOKIE_SIZE]; 121 | char *cookie = apr_pcalloc(r->pool, COOKIE_SIZE); 122 | conn_rec *c = r->connection; 123 | 124 | //server_rec *s = r->server; 125 | #if AP_SERVER_MINORVERSION_NUMBER < 4 126 | char * remote_ip = c->remote_ip; 127 | #else 128 | char * remote_ip = c->client_ip; 129 | #endif 130 | 131 | struct linotp_config *const scr = get_config_dir(r); 132 | 133 | // TODO: This can be used to get the server or directory config 134 | //ap_get_module_config(s->module_config, &authn_linotp_module); 135 | const char *hostname; 136 | 137 | if ((hostname = ap_get_remote_host(c, r->per_dir_config, REMOTE_NAME, NULL)) == NULL) 138 | hostname = "no.one@example.com"; 139 | 140 | /* 141 | * Arg! We can't use 'ntohs(c->remote_addr.sin_port)', because I.E. 142 | * ignores keepalives, and opens a new connection on EVERY request! 143 | * This is a BAD security problem! It allows multiple users on the 144 | * same machine to access the data. 145 | * 146 | * A derivative security problem is users authenticating from 147 | * behind a firewall. 148 | * All users appear to be coming from the firewall. A malicious 149 | * agent working in the same company as the authorized user can sniff 150 | * the cookie, and and use it themselves. Since they appear to be 151 | * coming from the same IP address (firewall), they're let in. 152 | * Oh well, at least the connection is traceable to a particular machine. 153 | */ 154 | 155 | /* 156 | * Piotr Klaban says: 157 | * 158 | * > The "squid" proxy set HTTP_X_FORWARDED_FOR variable - the 159 | * > original IP of the client. We can use HTTP_X_FORWARDED_FOR 160 | * > variable besides REMOTE_ADDR. 161 | * 162 | * > If cookie is stolen, then atacker could use the same proxy as 163 | * > the client, to validate the cookie. If we would use 164 | * > HTTP_X_FORWARDED_FOR, then useing the proxy would not be 165 | * > sufficient. 166 | * 167 | * We don't do this, mainly because I haven't gotten around to 168 | * writing the code... 169 | */ 170 | 171 | /* 172 | * Make a cookie based on secret + public information. 173 | * 174 | * cookie = MAC(M) = apr_md5(secret, MD5(secret, M)) 175 | * 176 | * See Scheier, B, "Applied Cryptography" 2nd Ed., p.458 177 | * Also, RFC 2104. I don't know if the HMAC gives any additional 178 | * benefit here. 179 | */ 180 | 181 | apr_snprintf(one, COOKIE_SIZE, "%s%s%s%s%s%08x", scr->secret, 182 | r->user, passwd, remote_ip, hostname, (int)expires); 183 | 184 | /* MD5 the cookie to make it secure, and add more secret information */ 185 | apr_snprintf(two, COOKIE_SIZE, "%s%s", scr->secret, ap_md5(r->pool, one)); 186 | if (string == NULL) { 187 | apr_snprintf(cookie, COOKIE_SIZE, "%s%08x", 188 | ap_md5(r->pool, two), (int)expires); 189 | } else { 190 | apr_snprintf(cookie, COOKIE_SIZE, "%s%08x%s", 191 | ap_md5(r->pool, two), (int)expires, string); 192 | } 193 | return cookie; 194 | } 195 | static int 196 | valid_cookie(request_rec *r, const char *cookie, const char *passwd) 197 | { 198 | time_t expires, now; 199 | 200 | if (strlen(cookie) < (16 + 4)*2) { /* MD5 is 16 bytes, and expiry date is 4*/ 201 | return FALSE; /* invalid */ 202 | } 203 | 204 | sscanf(&cookie[32], "%8lx", &expires); 205 | 206 | now = time(NULL); 207 | if (expires < now) { /* valid only for a short window of time */ 208 | return FALSE; /* invalid: expired */ 209 | } 210 | 211 | /* Is the returned cookie identical to one made from our secret? */ 212 | if (strcmp(cookie, make_cookie(r, expires, passwd, NULL)) == 0) 213 | return TRUE; 214 | 215 | return FALSE; /* cookie doesn't match: re-validate */ 216 | } 217 | 218 | static void 219 | add_cookie(request_rec *r, apr_table_t *header, char *cookie, time_t expires) 220 | { 221 | struct linotp_config *const conf = get_config_dir(r); 222 | 223 | char *new_cookie = apr_pcalloc(r->pool, COOKIE_SIZE); /* so it'll stick around */ 224 | 225 | if (expires != 0) { 226 | char buffer[1024]; 227 | 228 | strftime(buffer, sizeof(buffer), "%a %d-%b-%Y %H:%M:%S %Z", 229 | gmtime(&expires)); 230 | apr_snprintf(new_cookie, 1024, "%s=%s; path=/; expires=%s;", 231 | conf->cookie_name, cookie, buffer); 232 | } else { 233 | apr_snprintf(new_cookie, 1024, 234 | "%s=%s; path=/; expires=Wed, 01-Oct-97 01:01:01 GMT;", 235 | conf->cookie_name, cookie); 236 | } 237 | 238 | apr_table_set(header,"Set-Cookie", new_cookie); 239 | } 240 | /* Spot a cookie in an incoming request */ 241 | static char * 242 | spot_cookie(request_rec *r) 243 | { 244 | const char *cookie; 245 | char *value; 246 | 247 | struct linotp_config *const conf = get_config_dir(r); 248 | 249 | if ((cookie = apr_table_get(r->headers_in, "Cookie"))) { 250 | if ((value=strstr(cookie, conf->cookie_name))) { 251 | char *cookiebuf, *cookieend; 252 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,r->server,"Found LinOTP auth Cookie, now check if it's valid..."); 253 | value += strlen(conf->cookie_name); /* skip the name */ 254 | 255 | /* 256 | * Ensure there's an '=' after the name. 257 | */ 258 | if (*value != '=') { 259 | return NULL; 260 | } else { 261 | value++; 262 | } 263 | 264 | cookiebuf = apr_pstrdup( r->pool, value ); 265 | cookieend = strchr(cookiebuf,';'); 266 | if (cookieend) *cookieend = '\0'; /* Ignore anything after a ; */ 267 | 268 | /* Set the cookie in a note, for logging */ 269 | return cookiebuf; /* Theres already a cookie, no new one */ 270 | } 271 | } 272 | return NULL; /* no cookie was found */ 273 | } 274 | 275 | 276 | /********************************************************************************** 277 | * Utils 278 | * 279 | */ 280 | 281 | 282 | static int inline valid_char(unsigned char c) 283 | { 284 | int i; 285 | 286 | /* We disallow anything except known good */ 287 | const char allowed_nonalpha[] = "-_+.@"; 288 | 289 | /* a-z */ 290 | if (c >= 'a' && c <= 'z') 291 | return 1; 292 | 293 | /* A-Z */ 294 | if (c >= 'A' && c <= 'Z') 295 | return 1; 296 | 297 | /* 0-9 */ 298 | if (c >= '0' && c <= '9') 299 | return 1; 300 | 301 | /* Non-alphanumeric */ 302 | for (i=0; i < sizeof(allowed_nonalpha)-1; i++) 303 | if (c == allowed_nonalpha[i]) 304 | return 1; 305 | 306 | /* Invalid */ 307 | return 0; 308 | } 309 | 310 | static int inline valid_realm_char(unsigned char c) 311 | { 312 | int i; 313 | 314 | /* We disallow anything except known good */ 315 | const char allowed_nonalpha[] = "-_"; 316 | 317 | /* a-z */ 318 | if (c >= 'a' && c <= 'z') 319 | return 1; 320 | 321 | /* A-Z */ 322 | if (c >= 'A' && c <= 'Z') 323 | return 1; 324 | 325 | /* 0-9 */ 326 | if (c >= '0' && c <= '9') 327 | return 1; 328 | 329 | /* Non-alphanumeric */ 330 | for (i=0; i < sizeof(allowed_nonalpha)-1; i++) 331 | if (c == allowed_nonalpha[i]) 332 | return 1; 333 | 334 | /* Invalid */ 335 | return 0; 336 | } 337 | 338 | 339 | #ifndef LINOTP_MAX_USERNAME_LEN 340 | #define LINOTP_MAX_USERNAME_LEN 256 341 | #endif 342 | 343 | #ifndef LINOTP_MAX_REALMNAME_LEN 344 | #define LINOTP_MAX_REALMNAME_LEN 256 345 | #endif 346 | 347 | // username and password was correct 348 | #define LINOTPD_OK ":-)" 349 | #define LINOTPD_REJECT ":-(" 350 | #define LINOTPD_FAIL ":-/" 351 | 352 | 353 | static int inline valid_username(const char *s) 354 | { 355 | size_t len = 0; 356 | 357 | while (*s) 358 | { 359 | if (!valid_char(*s)) 360 | return 0; 361 | 362 | if (++len >= LINOTP_MAX_USERNAME_LEN) 363 | return 0; 364 | 365 | ++s; 366 | } 367 | 368 | return 1; 369 | } 370 | 371 | static int inline valid_realm(const char *s ) 372 | { 373 | size_t len = 0; 374 | if (s == NULL) 375 | { 376 | return 1; 377 | } 378 | while (*s) 379 | { 380 | if (!valid_realm_char(*s)) 381 | return 0; 382 | if (++len >= LINOTP_MAX_REALMNAME_LEN) 383 | return 0; 384 | ++s; 385 | } 386 | return 1; 387 | } 388 | 389 | /*********************************************** 390 | Curl stuff 391 | ***********************************************/ 392 | struct MemoryStruct { 393 | char *memory; 394 | size_t size; 395 | }; 396 | 397 | static void *myrealloc(void *ptr, size_t size) 398 | { 399 | void * ret = NULL; 400 | 401 | if (size > 1024 * 1024) 402 | { 403 | ret = NULL; 404 | } 405 | 406 | /* There might be a realloc() out there that doesn't like reallocing 407 | NULL pointers, so we take care of it here */ 408 | if(ptr) 409 | ret = realloc(ptr, size); 410 | else 411 | ret = malloc(size); 412 | 413 | return ret; 414 | } 415 | 416 | static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) 417 | { 418 | size_t realsize = size * nmemb; 419 | struct MemoryStruct *mem = (struct MemoryStruct *)data; 420 | 421 | /* failsafe */ 422 | if (realsize > 1024*1024) 423 | { 424 | //error("The linotpd responded to our authentication request with more than 1MB of data! Something is really wrong here!"); 425 | return mem->size; 426 | } 427 | 428 | mem->memory = myrealloc(mem->memory, mem->size + realsize + 1); 429 | if (mem->memory) 430 | { 431 | memcpy(&(mem->memory[mem->size]), ptr, realsize); 432 | mem->size += realsize; 433 | mem->memory[mem->size] = 0; 434 | } 435 | return realsize; 436 | } 437 | 438 | 439 | 440 | 441 | static char * createUrl(request_rec *r, CURL *curl_handle, char * validateurl, char * realm, char * resConf, const char * user, const char * password) 442 | { 443 | char * url = NULL; 444 | char * url2 = NULL; 445 | int size = 300; 446 | int nchars = 0; 447 | 448 | // escape user and password 449 | char *escPassword = curl_easy_escape(curl_handle, password, 0); 450 | char *escUser = curl_easy_escape(curl_handle, user, 0); 451 | char *escRealm; 452 | char *escResConf; 453 | 454 | if ( realm == NULL ) 455 | { 456 | escRealm = NULL; 457 | } else { 458 | escRealm = curl_easy_escape(curl_handle, realm, 0); 459 | } 460 | if ( resConf == NULL ) 461 | { 462 | escResConf = NULL; 463 | } else { 464 | escResConf = curl_easy_escape(curl_handle, realm, 0); 465 | } 466 | 467 | if (escPassword == NULL || escUser == NULL) 468 | { 469 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"faild to escape user or password"); 470 | goto cleanup; 471 | } 472 | 473 | url = (char*) malloc (size); 474 | if (url == NULL) 475 | { 476 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"could not allocate size for url"); 477 | goto cleanup; 478 | } 479 | 480 | // allocate the memory for url string 481 | memset(url,'\0',size); 482 | if (escRealm == NULL && escResConf == NULL ) 483 | { 484 | nchars = snprintf( url, size-1, "%s?user=%s&pass=%s", validateurl, 485 | escUser, escPassword); 486 | } 487 | if (escRealm == NULL && escResConf != NULL ) 488 | { 489 | nchars = snprintf( url, size-1, "%s?user=%s&pass=%s&resConf=%s", validateurl, 490 | escUser, escPassword, escResConf); 491 | } 492 | if (escRealm != NULL && escResConf != NULL ) 493 | { 494 | nchars = snprintf( url, size-1, "%s?user=%s&pass=%s&resConf=%s&realm=%s", validateurl, 495 | escUser, escPassword, escResConf, escRealm); 496 | } 497 | if (escRealm != NULL && escResConf == NULL ) 498 | { 499 | nchars = snprintf( url, size-1, "%s?user=%s&pass=%s&realm=%s", validateurl, 500 | escUser, escPassword, escRealm); 501 | } 502 | 503 | 504 | 505 | 506 | if (nchars >= size-1) 507 | { 508 | // reallocate 509 | size = nchars +1; 510 | url2 = (char*) myrealloc(url, size); 511 | if (url2 == NULL) 512 | { 513 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"failed to alloc space for url + user and password"); 514 | free(url); 515 | url = NULL; // we need to return NULL 516 | goto cleanup; 517 | } 518 | 519 | memset(url,'\0',size); 520 | if (escRealm == NULL && escResConf == NULL ) 521 | { 522 | snprintf( url, size-1, "%s?user=%s&pass=%s", validateurl, 523 | escUser, escPassword); 524 | } 525 | if (escRealm == NULL && escResConf != NULL ) 526 | { 527 | snprintf( url, size-1, "%s?user=%s&pass=%s&resConf=%s", validateurl, 528 | escUser, escPassword, escResConf); 529 | } 530 | if (escRealm != NULL && escResConf != NULL ) 531 | { 532 | snprintf( url, size-1, "%s?user=%s&pass=%s&resConf=%s&realm=%s", validateurl, 533 | escUser, escPassword, escResConf, escRealm); 534 | } 535 | if (escRealm != NULL && escResConf == NULL ) 536 | { 537 | snprintf( url, size-1, "%s?user=%s&pass=%s&realm=%s", validateurl, 538 | escUser, escPassword, escRealm); 539 | } 540 | } 541 | 542 | cleanup: 543 | return url; 544 | } 545 | static int sendRequest(request_rec *r, CURL *curl_handle, char * url, 546 | struct MemoryStruct * chunk) 547 | { 548 | /* 549 | * url has the complete URL with username and password 550 | */ 551 | 552 | 553 | struct linotp_config *const conf = get_config_dir(r); 554 | int nosslhostnameverify = (!conf->sslhostnameverify); 555 | int nosslcertverify = (!conf->sslcertverify); 556 | 557 | int all_status = 0; 558 | int status = 0; 559 | 560 | all_status = 0; 561 | status = curl_easy_setopt(curl_handle, CURLOPT_URL, url); 562 | all_status += status; 563 | if (status) { 564 | if ( conf->logpassword && conf->loguser ) 565 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"Error setting option CURLOPT_URL %s: %i", url, status); 566 | else 567 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"Error setting option CURLOPT_URL %s: %i", conf->validateurl, status); 568 | } 569 | 570 | status = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); 571 | all_status += status; 572 | if (status) 573 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"Error setting option CURLOPT_WRITEFUNCTION: %i", status); 574 | 575 | status = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, chunk); 576 | all_status += status; 577 | if (status) 578 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"Error setting option CURLOPT_WRITEDATA: %i", status); 579 | 580 | status = curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); 581 | all_status += status; 582 | if (status) 583 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"Error setting option CURLOPT_USERAGENT: %i", status); 584 | 585 | if ( nosslhostnameverify ) { 586 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server,"NO SSL hostname verify"); 587 | status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); 588 | } else { 589 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server,"SSL hostname verify"); 590 | status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L); 591 | } 592 | all_status += status; 593 | if (status) 594 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"Error setting option CURLOPT_SSL_VERIFYHOST: %i", status); 595 | 596 | 597 | if ( nosslcertverify ) { 598 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server,"NO SSL cert verify"); 599 | status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); 600 | } else { 601 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server,"SSL cert verify"); 602 | status = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L); 603 | } 604 | all_status += status; 605 | if (status) 606 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"Error setting option CURLOPT_SSL_VERIFYPEER: %i", status); 607 | 608 | status = curl_easy_perform(curl_handle); 609 | all_status += status; 610 | if (status) { 611 | if ( conf->logpassword && conf->loguser ) 612 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"Error in curl_easy_perform: %i, url: %s", status, url); 613 | else 614 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,"Error in curl_easy_perform: %i, url: %s", status, conf->validateurl); 615 | } 616 | 617 | curl_easy_cleanup(curl_handle); 618 | 619 | return all_status; 620 | 621 | } 622 | 623 | /* 624 | * HTTP basic authentication 625 | */ 626 | static authn_status 627 | authn_linotp_check_password(request_rec *r, const char *username, const char *otp_given) 628 | { 629 | struct linotp_config *const conf = get_config_dir(r); 630 | 631 | int returnValue = AUTH_GENERAL_ERROR; 632 | char errorBuffer[CURL_ERROR_SIZE]; 633 | CURL * curl_handle = NULL; 634 | CURLcode all_status = 0; 635 | char *cookie; 636 | time_t expires; 637 | char *url = NULL; 638 | struct MemoryStruct chunk; 639 | chunk.memory = NULL; /* we expect realloc(NULL, size) to work */ 640 | chunk.size = 0; /* no data at this point */ 641 | 642 | curl_global_init(CURL_GLOBAL_ALL); 643 | 644 | /* check for the existence of a cookie: do weak authentication if so */ 645 | if ((cookie = spot_cookie(r)) != NULL) { 646 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "Found cookie=%s for user=%s : ", cookie, r->user); 647 | /* valid username, passwd, and expiry date: don't do LinOTP auth */ 648 | if (valid_cookie(r, cookie, otp_given)) { 649 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server,"cookie still valid. Serving page."); 650 | returnValue=AUTH_GRANTED; 651 | goto cleanup; 652 | } else { /* the cookie has probably expired */ 653 | /* don't bother logging the fact: we probably don't care */ 654 | add_cookie(r, r->err_headers_out, cookie, 0); 655 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," invalid or expired. telling browser to delete cookie"); 656 | returnValue = HTTP_UNAUTHORIZED; 657 | goto cleanup; 658 | } 659 | } else { 660 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," No cookie found. Trying LinOTP authentication."); 661 | } 662 | 663 | /* check if validate url is defined */ 664 | if (conf->validateurl == NULL) { 665 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "No LinOTPValidateURL defined!"); 666 | returnValue = AUTH_GENERAL_ERROR; 667 | goto cleanup; 668 | } 669 | 670 | if (!username || !otp_given) 671 | { 672 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Username or password not given!"); 673 | returnValue = AUTH_GENERAL_ERROR; 674 | goto cleanup; 675 | } 676 | 677 | if (!valid_username(username)) 678 | { 679 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Username or password not given!"); 680 | returnValue = AUTH_GENERAL_ERROR; 681 | goto cleanup; 682 | } 683 | 684 | if ( conf->realm != NULL ) 685 | if (!valid_realm(conf->realm)) 686 | { 687 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Realm includes invalid characters."); 688 | returnValue = AUTH_GENERAL_ERROR; 689 | goto cleanup; 690 | } 691 | 692 | if ( conf->resConf != NULL ) 693 | if (!valid_realm(conf->resConf)) 694 | { 695 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Realm includes invalid characters!"); 696 | returnValue = AUTH_GENERAL_ERROR; 697 | goto cleanup; 698 | } 699 | 700 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server, "Doing curl_easy_init!"); 701 | curl_handle = curl_easy_init(); 702 | if (curl_handle == NULL) 703 | { 704 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Could not get curl handle!"); 705 | returnValue = AUTH_GENERAL_ERROR; 706 | goto cleanup; 707 | } 708 | curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer); 709 | 710 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server, "creating the URL."); 711 | 712 | url = createUrl(r, curl_handle, conf->validateurl, conf->realm, conf->resConf, 713 | username, otp_given); 714 | 715 | if ( conf->logpassword && conf->loguser ) 716 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server, "url created: '%s' \n", url); 717 | 718 | 719 | if (url == NULL) 720 | { 721 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "could not allocate size for url!"); 722 | goto cleanup; 723 | } 724 | 725 | all_status = sendRequest(r, curl_handle, url, (void *)&chunk); 726 | 727 | if (all_status != 0) 728 | { 729 | if ( conf->logpassword && conf->loguser ) 730 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Error talking to linotpd server %s: %s. See CURLcode in curl.h for detailes (%i)", url, errorBuffer, all_status); 731 | else 732 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Error talking to linotpd server %s: %s. See CURLcode in curl.h for detailes (%i)", conf->validateurl, errorBuffer, all_status); 733 | // Error communicating to linotp server 734 | returnValue = AUTH_GENERAL_ERROR; 735 | goto cleanup; 736 | } 737 | 738 | /* 739 | * Now, our chunk.memory points to a memory block that is chunk.size 740 | * bytes big and contains the remote file. 741 | * You should be aware of the fact that at this point we might have an 742 | * allocated data block, and nothing has yet deallocated that data. So when 743 | * you're done with it, you should free() it as a nice application. 744 | */ 745 | if(chunk.memory == NULL) 746 | { 747 | if ( conf->logpassword && conf->loguser ) 748 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "No response returned for %s: %s", url, errorBuffer); 749 | else 750 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "No response returned: %s", errorBuffer); 751 | goto cleanup; 752 | } 753 | 754 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server, "LinOTPd on %s returned '%s'", conf->validateurl, chunk.memory); 755 | 756 | if (strcmp(chunk.memory, LINOTPD_REJECT) == 0) 757 | { 758 | if ( conf->loguser ) 759 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Rejecting authentication for user '%s'", username); 760 | else 761 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Rejecting authentication"); 762 | returnValue = AUTH_DENIED; 763 | goto cleanup; 764 | } 765 | 766 | if (strcmp(chunk.memory, LINOTPD_FAIL) == 0) 767 | { 768 | if ( conf->loguser ) 769 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "authentication for '%s' failed", username); 770 | else 771 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "authentication failed"); 772 | returnValue = AUTH_GENERAL_ERROR; 773 | goto cleanup; 774 | } 775 | if (strcmp(chunk.memory, LINOTPD_OK) == 0) 776 | { 777 | if ( conf->loguser ) 778 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server, "user '%s' authenticated successfully", username); 779 | else 780 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0, r->server, "user authenticated successfully"); 781 | /* 782 | * we also set a cookie now 783 | */ 784 | expires = time(NULL) + conf->timeout; 785 | cookie = make_cookie(r, expires, otp_given, NULL); 786 | 787 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server,"Cookie expiry in %d seconds.", (int) expires); 788 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server,"Adding cookie %s\n", cookie); 789 | add_cookie(r, r->headers_out, cookie, expires); 790 | 791 | returnValue = AUTH_GRANTED; 792 | goto cleanup; 793 | } 794 | {//default 795 | if ( conf->loguser ) 796 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Rejecting fall-through '%s'", username); 797 | else 798 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Rejecting fall-through" ); 799 | returnValue = AUTH_GENERAL_ERROR; 800 | goto cleanup; 801 | } 802 | 803 | cleanup: 804 | 805 | /* we're done with libcurl, so clean it up */ 806 | curl_global_cleanup(); 807 | 808 | if (url != NULL) 809 | { 810 | free(url); 811 | } 812 | if (chunk.memory != NULL) 813 | { 814 | free(chunk.memory); 815 | } 816 | return returnValue; 817 | 818 | } 819 | 820 | /* 821 | * Get configuration in directory context 822 | */ 823 | static struct linotp_config * 824 | get_config_dir(request_rec *r) 825 | { 826 | struct linotp_config *dir_conf; 827 | struct linotp_config *conf; 828 | 829 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server,"get linotp config"); 830 | 831 | /* I don't understand this bug: sometimes r->per_dir_config == NULL. Some weird linking problem. */ 832 | if (r->per_dir_config == NULL) { 833 | ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server, "Oops, bug detected in mod_authn_linotp: r->per_dir_config == NULL?"); 834 | dir_conf = create_authn_linotp_dir_config(r->pool, NULL); 835 | } else 836 | dir_conf = ap_get_module_config(r->per_dir_config, &authn_linotp_module); 837 | 838 | /* Make a copy of the current per-directory config */ 839 | conf = apr_pcalloc(r->pool, sizeof(*conf)); 840 | if (dir_conf->validateurl != NULL) 841 | conf->validateurl = apr_pstrdup(r->pool, dir_conf->validateurl); 842 | if (dir_conf->realm != NULL) 843 | conf->realm = apr_pstrdup(r->pool, dir_conf->realm); 844 | if (dir_conf->validateurl != NULL) 845 | conf->resConf = apr_pstrdup(r->pool, dir_conf->resConf); 846 | if (dir_conf->secret != NULL) 847 | conf->secret = apr_pstrdup(r->pool, dir_conf->secret); 848 | if (dir_conf->cookie_name != NULL) 849 | conf->cookie_name = apr_pstrdup(r->pool, dir_conf->cookie_name); 850 | 851 | conf->timeout = dir_conf->timeout; 852 | conf->loguser = dir_conf->loguser; 853 | conf->logpassword = dir_conf->logpassword; 854 | conf->sslcertverify = dir_conf->sslcertverify; 855 | conf->sslhostnameverify = dir_conf->sslhostnameverify; 856 | 857 | /* Apply defaults for any unset values */ 858 | if (conf->timeout == -1) 859 | conf->timeout = DEFAULT_TIMEOUT; 860 | if (conf->loguser == -1) 861 | conf->loguser = DEFAULT_LOG_USER; 862 | if (conf->logpassword == -1) 863 | conf->logpassword = DEFAULT_LOG_PASSWORD; 864 | if (conf->sslcertverify == -1) 865 | conf->sslcertverify = DEFAULT_SSL_CERT_VERIFY; 866 | if (conf->sslhostnameverify == -1) 867 | conf->sslhostnameverify = DEFAULT_SSL_HOSTNAME_VERIFY; 868 | if (conf->cookie_name == NULL) 869 | conf->cookie_name = DEFAULT_COOKIE_NAME; 870 | 871 | /* Done */ 872 | return conf; 873 | } 874 | 875 | /* 876 | * Constructor for per-directory configuration 877 | */ 878 | static void * 879 | create_authn_linotp_dir_config(apr_pool_t *p, char *d) 880 | { 881 | struct linotp_config *conf = apr_pcalloc(p, sizeof(struct linotp_config)); 882 | 883 | conf->timeout = -1; 884 | conf->logpassword = 0; 885 | conf->loguser = 0; 886 | conf->realm = NULL; 887 | conf->resConf = NULL; 888 | conf->validateurl = NULL; 889 | conf->sslcertverify = 0; 890 | conf->sslhostnameverify = 0; 891 | conf->secret = NULL; 892 | conf->cookie_name = NULL; 893 | 894 | return conf; 895 | } 896 | 897 | static void * 898 | merge_authn_linotp_dir_config(apr_pool_t *p, void *base_conf, void *new_conf) 899 | { 900 | struct linotp_config *const conf1 = base_conf; 901 | struct linotp_config *const conf2 = new_conf; 902 | struct linotp_config *conf = apr_pcalloc(p, sizeof(struct linotp_config)); 903 | 904 | if (conf2->cookie_name != NULL) 905 | conf->cookie_name = apr_pstrdup(p, conf2->cookie_name); 906 | else if (conf1->cookie_name != NULL) 907 | conf->cookie_name = apr_pstrdup(p, conf1->cookie_name); 908 | 909 | if (conf2->secret != NULL) 910 | conf->secret = apr_pstrdup(p, conf2->secret); 911 | else if (conf1->cookie_name != NULL) 912 | conf->secret = apr_pstrdup(p, conf1->secret); 913 | 914 | 915 | if (conf2->validateurl != NULL) 916 | conf->validateurl = apr_pstrdup(p, conf2->validateurl); 917 | else if (conf1->validateurl != NULL) 918 | conf->validateurl = apr_pstrdup(p, conf1->validateurl); 919 | 920 | if (conf2->realm != NULL) 921 | conf->realm = apr_pstrdup(p, conf2->realm); 922 | else if (conf1->realm != NULL) 923 | conf->realm = apr_pstrdup(p, conf1->realm); 924 | 925 | if (conf2->resConf != NULL) 926 | conf->resConf = apr_pstrdup(p, conf2->resConf); 927 | else if (conf1->resConf != NULL) 928 | conf->resConf = apr_pstrdup(p, conf1->resConf); 929 | 930 | conf->timeout = conf2->timeout != -1 ? conf2->timeout : conf1->timeout; 931 | conf->logpassword = conf2->logpassword != -1 ? conf2->logpassword : conf1->logpassword; 932 | conf->loguser = conf2->loguser != -1 ? conf2->loguser : conf1->loguser; 933 | conf->sslcertverify = conf2->sslcertverify != -1 ? conf2->sslcertverify : conf1->sslcertverify; 934 | conf->sslhostnameverify = conf2->sslhostnameverify != -1 ? conf2->sslhostnameverify : conf1->sslhostnameverify; 935 | 936 | return conf; 937 | } 938 | 939 | 940 | /* Authorization provider information */ 941 | static const authn_provider authn_linotp_provider = 942 | { 943 | &authn_linotp_check_password, 944 | NULL // No digest Auth 945 | }; 946 | 947 | static void 948 | register_hooks(apr_pool_t *p) 949 | { 950 | ap_register_provider(p, AUTHN_PROVIDER_GROUP, LINOTP_AUTHN_PROVIDER_NAME, AUTHN_PROVIDER_VERSION, &authn_linotp_provider); 951 | } 952 | 953 | /* Configuration directives */ 954 | static const command_rec authn_linotp_cmds[] = 955 | { 956 | AP_INIT_TAKE1("LinOTPValidateURL", 957 | ap_set_string_slot, 958 | (void *)APR_OFFSETOF(struct linotp_config, validateurl), 959 | OR_AUTHCFG, 960 | "URL of the LinOTP validation server"), 961 | AP_INIT_TAKE1("LinOTPRealm", 962 | ap_set_string_slot, 963 | (void *)APR_OFFSETOF(struct linotp_config, realm), 964 | OR_AUTHCFG, 965 | "The realm name, the user will be authenticated in."), 966 | AP_INIT_TAKE1("LinOTPResolver", 967 | ap_set_string_slot, 968 | (void *)APR_OFFSETOF(struct linotp_config, resConf), 969 | OR_AUTHCFG, 970 | "The useridresolver, the user will be authenticated in."), 971 | AP_INIT_TAKE1("LinOTPCookieSecret", 972 | ap_set_string_slot, 973 | (void *)APR_OFFSETOF(struct linotp_config, secret), 974 | OR_AUTHCFG, 975 | "A secret that is used to encrypt the cookie."), 976 | AP_INIT_TAKE1("LinOTPCookieName", 977 | ap_set_string_slot, 978 | (void *)APR_OFFSETOF(struct linotp_config, cookie_name), 979 | OR_AUTHCFG, 980 | "The name of the cookie"), 981 | AP_INIT_FLAG("LinOTPSSLCertVerify", 982 | ap_set_flag_slot, 983 | (void *)APR_OFFSETOF(struct linotp_config, sslcertverify), 984 | OR_AUTHCFG, 985 | "Wether the certificate of the LinOTP server should be verified (0|1)"), 986 | AP_INIT_FLAG("LinOTPSSLHostVerify", 987 | ap_set_flag_slot, 988 | (void *)APR_OFFSETOF(struct linotp_config, sslhostnameverify), 989 | OR_AUTHCFG, 990 | "Wether the hostname in the certificate of the LinOTP server should be verified (0|1)"), 991 | AP_INIT_FLAG("LinOTPLogUser", 992 | ap_set_flag_slot, 993 | (void *)APR_OFFSETOF(struct linotp_config, loguser), 994 | OR_AUTHCFG, 995 | "Wether the certificate of the LinOTP server should be verified (0|1)"), 996 | AP_INIT_FLAG("LinOTPLogPassword", 997 | ap_set_flag_slot, 998 | (void *)APR_OFFSETOF(struct linotp_config, logpassword), 999 | OR_AUTHCFG, 1000 | "Wether the certificate of the LinOTP server should be verified (0|1)"), 1001 | AP_INIT_TAKE1("LinOTPTimeout", 1002 | ap_set_int_slot, 1003 | (void *)APR_OFFSETOF(struct linotp_config, timeout), 1004 | OR_AUTHCFG, 1005 | "maximum time (in seconds) for which a one-time password is valid"), 1006 | 1007 | 1008 | { NULL } 1009 | }; 1010 | 1011 | /* Module declaration */ 1012 | module AP_MODULE_DECLARE_DATA authn_linotp_module = { 1013 | STANDARD20_MODULE_STUFF, 1014 | create_authn_linotp_dir_config, /* create per-dir config */ 1015 | merge_authn_linotp_dir_config, /* merge per-dir config */ 1016 | NULL, /* create per-server config */ 1017 | NULL, /* merge per-server config */ 1018 | authn_linotp_cmds, /* command apr_table_t */ 1019 | register_hooks /* register hooks */ 1020 | }; 1021 | 1022 | --------------------------------------------------------------------------------