diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffce6a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +Makefile +Mk/autoconf.mk +Mk/compile.mk +Mk/subdir.mk +autom4te.cache/ +config.h +config.h.in +config.log +config.status +configure +docs/Makefile +docs/doxyfile +src/Makefile +tests/Makefile +tools/Makefile +AuthLDAP.xcodeproj/project.xcworkspace/xcuserdata/ +AuthLDAP.xcodeproj/xcuserdata/ diff --git a/README.md b/README.md index 9ed7921..4a823a6 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ The OpenVPN Auth-LDAP Plugin implements username/password authentication via LDA * LDAP group-based access restrictions. * Integration with the OpenBSD packet filter, supporting adding and removing VPN clients from PF tables based on group membership. * Tested against OpenLDAP, the plugin will authenticate against any LDAP server that supports LDAP simple binds -- including Active Directory. + * Supports OpenVPN Challenge/Response protocol, enabling it to be used in combination with one time password systems like Google Authenticator ## Building @@ -21,12 +22,38 @@ The OpenVPN Auth-LDAP Plugin implements username/password authentication via LDA To build, you will need to configure the sources appropriately. Example: ``` -./configure --prefix=/usr/local --with-openldap=/usr/local --with-openvpn=/usr/ports/security/openvpn/work/openvpn-2.0.2 +./configure --prefix=/usr/local --with-openldap=/usr/local --with-openvpn=/home/sean/work/openvpn-2.0.2 ``` -The module will be build in src/openvpn-auth-ldap.so and installed as +The module will be built in src/openvpn-auth-ldap.so and installed as `${prefix}/lib/openvpn-auth-ldap.so`. + +#### Building On Ubuntu 16.04 #### + +The following steps were tested on a clean Ubuntu 16.04 LTS Amazon EC2 m5.large instance in January 2018 (source AMI: ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-20180109 - ami-41e0b93b). + +If you wish to repeat this process, follow these steps on your own machine: + +``` +git clone https://github.com/snowrider311/openvpn-auth-ldap +cd openvpn-auth-ldap/ +./ubuntu_16.04_lts_build.sh +``` + +The `ubuntu_16.04_lts_build.sh` script will install all needed build dependencies, perform the build, and install `openvpn-auth-ldap.so` to `/usr/local/lib`. + +If you then wish to create a Debian package, you can then run this script: + +``` +./ubuntu_16.04_lts_package.sh +``` + +That script will install [FPM](https://github.com/jordansissel/fpm) and then use it to build a Debian package. If you then run `sudo dpkg -i openvpn-auth-ldap-snowrider311_2.0.3-1_amd64.deb`, then `openvpn-auth-ldap.so` will be installed to `/usr/lib/openvpn`, the same location as the standard, unforked `openvpn-auth-ldap` Debian package installs to. + +Note: Superuser privileges are required to run these scripts. + + ## Usage Add the following to your OpenVPN configuration file (adjusting the plugin path as required): diff --git a/aclocal.m4 b/aclocal.m4 index e509555..e5b7dbf 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -157,11 +157,20 @@ AC_DEFUN([OD_OBJC_RUNTIME],[ AC_LINK_IFELSE([ AC_LANG_PROGRAM([ #include + #ifdef __GNU_LIBOBJC__ + #include + #else #include + #endif ], [ + #ifdef __GNU_LIBOBJC_ + Class class = objc_lookUpClass("Object"); + puts(class_getName(class));_ + #else id class = objc_lookup_class("Object"); id obj = @<:@class alloc@:>@; puts(@<:@obj name@:>@); + #endif ]) ], [ od_cv_objc_runtime_gnu="yes" diff --git a/auth-ldap.conf b/auth-ldap.conf index 6be38a4..300f9c4 100644 --- a/auth-ldap.conf +++ b/auth-ldap.conf @@ -46,9 +46,16 @@ # Add non-group members to a PF table (disabled) #PFTable ips_vpn_users + # Uncomment and set to true to support OpenVPN Challenge/Response + #PasswordIsCR false - # Match full user DN if true, uid only if false - RFC2307bis true + # Default is true. Match full user DN if true, uid only if false. + # RFC2307bis true + + # Default is true. Uncomment and set to false if you want to use a Search operation to determine group + # membership instead of Compare. Lower performance, so Compare should generally be used, but Search is + # required in certain LDAP environments. + # UseCompareOperation true BaseDN "ou=Groups,dc=example,dc=com" SearchFilter "(|(cn=developers)(cn=artists))" diff --git a/src/Makefile.in b/src/Makefile.in index b0eac7b..a568b9f 100755 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -34,7 +34,9 @@ AUTH_OBJS= TRArray.o \ TRVPNSession.o \ hash.o \ strlcpy.o \ - xmalloc.o + xmalloc.o \ + base64.o \ + openvpn-cr.o GEN_SRCS= TRConfigParser.m \ TRConfigParser.h \ diff --git a/src/TRAuthLDAPConfig.h b/src/TRAuthLDAPConfig.h index 9089816..4aa2c88 100644 --- a/src/TRAuthLDAPConfig.h +++ b/src/TRAuthLDAPConfig.h @@ -60,6 +60,7 @@ TRString *_pfTable; TRArray *_ldapGroups; BOOL _pfEnabled; + BOOL _passwordISCR; /* Parser State */ TRString *_configFileName; @@ -126,4 +127,7 @@ - (TRArray *) ldapGroups; +- (BOOL) passWordIsCR; +- (void) setPassWordIsCR: (BOOL)newCRSetting; + @end diff --git a/src/TRAuthLDAPConfig.m b/src/TRAuthLDAPConfig.m index d06ebe8..ed1a461 100644 --- a/src/TRAuthLDAPConfig.m +++ b/src/TRAuthLDAPConfig.m @@ -77,6 +77,10 @@ /* Group Section Variables */ LF_GROUP_MEMBER_ATTRIBUTE, /* Group Membership Attribute */ LF_GROUP_MEMBER_RFC2307BIS, /* Look for full DN for user in attribute */ + LF_GROUP_MEMBER_USECOMPAREOPERATION, /* Use LDAP Compare operation instead of Search (Search is faster but doesn't work in all LDAP environments) */ + + /* OpenVPN Challenge/Response */ + LF_AUTH_PASSWORD_CR, /* Password is in challenge/repsonse format */ /* Misc Shared */ LF_UNKNOWN_OPCODE, /* Unknown Opcode */ @@ -152,7 +156,15 @@ static OpcodeTable GroupSectionVariables[] = { /* name opcode multi required */ { "MemberAttribute", LF_GROUP_MEMBER_ATTRIBUTE, NO, NO }, - { "RFC2307bis", LF_GROUP_MEMBER_RFC2307BIS, NO, NO }, + { "RFC2307bis", LF_GROUP_MEMBER_RFC2307BIS, NO, NO }, + { "UseCompareOperation", LF_GROUP_MEMBER_USECOMPAREOPERATION, NO, NO }, + { NULL, 0 } +}; + +/* OpenVPN Challenge/Response */ +static OpcodeTable OpenVPNCRVariables[] = { + /* name opcode multi required */ + { "PasswordIsCR", LF_AUTH_PASSWORD_CR, NO, NO }, { NULL, 0 } }; @@ -173,7 +185,8 @@ AuthSectionVariables, GenericLDAPVariables, GenericPFVariables, - NULL + OpenVPNCRVariables, + NULL }; /* Group Section Definition */ @@ -181,6 +194,7 @@ GroupSectionVariables, GenericLDAPVariables, GenericPFVariables, + NULL }; @@ -684,6 +698,7 @@ - (void) setKey: (TRConfigToken *) key value: (TRConfigToken *) value { switch(opcodeEntry->opcode) { BOOL requireGroup; + BOOL passWordCR; case LF_AUTH_REQUIRE_GROUP: if (![value boolValue: &requireGroup]) { @@ -706,6 +721,14 @@ - (void) setKey: (TRConfigToken *) key value: (TRConfigToken *) value { [self setPFEnabled: YES]; break; + case LF_AUTH_PASSWORD_CR: + if (![value boolValue: &passWordCR]) { + [self errorBoolValue: value]; + return; + } + [self setPassWordIsCR: passWordCR]; + break; + /* Unknown Setting */ default: [self errorUnknownKey: key]; @@ -722,6 +745,7 @@ - (void) setKey: (TRConfigToken *) key value: (TRConfigToken *) value { switch(opcodeEntry->opcode) { TRLDAPGroupConfig *config; BOOL memberRFC2307BIS; + BOOL useCompareOperation; case LF_GROUP_MEMBER_ATTRIBUTE: config = [self currentSectionContext]; @@ -737,6 +761,15 @@ - (void) setKey: (TRConfigToken *) key value: (TRConfigToken *) value { [config setMemberRFC2307BIS: memberRFC2307BIS]; break; + case LF_GROUP_MEMBER_USECOMPAREOPERATION: + config = [self currentSectionContext]; + if (![value boolValue: &useCompareOperation]) { + [self errorBoolValue: value]; + return; + } + [config setUseCompareOperation: useCompareOperation]; + break; + case LF_LDAP_BASEDN: config = [self currentSectionContext]; [config setBaseDN: [value string]]; @@ -979,4 +1012,11 @@ - (TRArray *) ldapGroups { return _ldapGroups; } +- (BOOL) passWordIsCR { + return (_passwordISCR); +} + +- (void) setPassWordIsCR: (BOOL) newCRSetting { + _passwordISCR = newCRSetting; +} @end diff --git a/src/TRLDAPGroupConfig.h b/src/TRLDAPGroupConfig.h index 1fb41fe..e5927dd 100644 --- a/src/TRLDAPGroupConfig.h +++ b/src/TRLDAPGroupConfig.h @@ -41,6 +41,7 @@ TRString *_searchFilter; TRString *_memberAttribute; BOOL _memberRFC2307BIS; + BOOL _useCompareOperation; TRString *_pfTable; } @@ -56,6 +57,9 @@ - (BOOL) memberRFC2307BIS; - (void) setMemberRFC2307BIS: (BOOL) memberRFC2307BIS; +- (BOOL) useCompareOperation; +- (void) setUseCompareOperation: (BOOL) useCompareOperation; + - (TRString *) pfTable; - (void) setPFTable: (TRString *) tableName; diff --git a/src/TRLDAPGroupConfig.m b/src/TRLDAPGroupConfig.m index c51afc6..7612589 100644 --- a/src/TRLDAPGroupConfig.m +++ b/src/TRLDAPGroupConfig.m @@ -59,6 +59,7 @@ - (id) init { return self; _memberRFC2307BIS = YES; + _useCompareOperation = YES; return self; } @@ -100,6 +101,14 @@ - (void) setMemberRFC2307BIS: (BOOL) memberRFC2307BIS { _memberRFC2307BIS = memberRFC2307BIS; } +- (BOOL) useCompareOperation { + return (_useCompareOperation); +} + +- (void) setUseCompareOperation: (BOOL) useCompareOperation { + _useCompareOperation = useCompareOperation; +} + - (void) setPFTable: (TRString *) tableName { if (_pfTable) [_pfTable release]; diff --git a/src/TRObject.h b/src/TRObject.h index ade292e..34e6efb 100644 --- a/src/TRObject.h +++ b/src/TRObject.h @@ -39,6 +39,7 @@ #import #import +#include #import "PXObjCRuntime.h" diff --git a/src/auth-ldap.m b/src/auth-ldap.m index ec7564a..ca90dee 100644 --- a/src/auth-ldap.m +++ b/src/auth-ldap.m @@ -43,6 +43,8 @@ #import +#include "openvpn-cr.h" + /* Plugin Context */ typedef struct ldap_ctx { TRAuthLDAPConfig *config; @@ -398,23 +400,19 @@ static BOOL auth_ldap_user(TRLDAPConnection *ldap, TRAuthLDAPConfig *config, TRL if (!ldapEntries) break; - if ([groupConfig memberRFC2307BIS]) { - /* Iterate over the returned entries */ - entryIter = [ldapEntries objectEnumerator]; - while ((entry = [entryIter nextObject]) != nil) { - if ([ldap compareDN: [entry dn] withAttribute: [groupConfig memberAttribute] value: [ldapUser dn]]) { - /* Group match! */ - result = groupConfig; - } - } - } else { - /* Iterate over the returned entries */ - entryIter = [ldapEntries objectEnumerator]; - while ((entry = [entryIter nextObject]) != nil) { - if ([ldap compare: [entry dn] withAttribute: [groupConfig memberAttribute] value: [ldapUser rdn]]) { - /* Group match! */ - result = groupConfig; - } + /* If RFC2307BIS flag is true, search for full DN, otherwise just search for uid */ + TRString *searchValue = [groupConfig memberRFC2307BIS] ? [ldapUser dn] : [ldapUser rdn]; + + /* This will be used if we're using the "search" operation instead of the "compare" operation */ + TRString *searchFilter = [TRString stringWithFormat: "(%s=%s)", [[groupConfig memberAttribute] cString], [searchValue cString]]; + + /* Iterate over the returned entries */ + entryIter = [ldapEntries objectEnumerator]; + while ((entry = [entryIter nextObject]) != nil) { + if ((![groupConfig useCompareOperation] && [ldap searchWithFilter: searchFilter scope: LDAP_SCOPE_SUBTREE baseDN: [entry dn] attributes: NULL]) || + ([groupConfig useCompareOperation] && [ldap compareDN: [entry dn] withAttribute: [groupConfig memberAttribute] value: searchValue])) { + /* Group match! */ + result = groupConfig; } } @@ -429,8 +427,19 @@ static BOOL auth_ldap_user(TRLDAPConnection *ldap, TRAuthLDAPConfig *config, TRL static int handle_auth_user_pass_verify(ldap_ctx *ctx, TRLDAPConnection *ldap, TRLDAPEntry *ldapUser, const char *password) { TRLDAPGroupConfig *groupConfig; + const char *auth_password = password; + if ([ctx->config passWordIsCR]) { + openvpn_response resp; + char *parse_error; + if (!extract_openvpn_cr(password, &resp, &parse_error)) { + [TRLog error: "Error extracting challenge/response from password. Parse error = '%s'", parse_error]; + return (OPENVPN_PLUGIN_FUNC_ERROR); + } + auth_password = (const char*)resp.password; + } + /* Authenticate the user */ - if (!auth_ldap_user(ldap, ctx->config, ldapUser, password)) { + if (!auth_ldap_user(ldap, ctx->config, ldapUser, auth_password)) { [TRLog error: "Incorrect password supplied for LDAP DN \"%s\".", [[ldapUser dn] cString]]; return (OPENVPN_PLUGIN_FUNC_ERROR); } @@ -535,10 +544,11 @@ static int handle_client_connect_disconnect(ldap_ctx *ctx, TRLDAPConnection *lda /* Per-request allocation pool. */ pool = [[TRAutoreleasePool alloc] init]; - username = get_env("username", envp); - TRString *userName=[[TRString alloc]initWithCString: username]; - password = get_env("password", envp); - remoteAddress = get_env("ifconfig_pool_remote_ip", envp); + username = get_env("username", envp); + TRString *userName=[[TRString alloc]initWithCString: username]; + password = get_env("password", envp); + remoteAddress = get_env("ifconfig_pool_remote_ip", envp); + /* At the very least, we need a username to work with */ if (!username) { diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..eec7d98 --- /dev/null +++ b/src/base64.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* Base64 encoder/decoder. Originally Apache file ap_base64.c + */ + +#include + +#include "base64.h" + +/* aaaack but it's fast and const should make it shared text page. */ +static const unsigned char pr2six[256] = +{ + /* ASCII table */ + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 +}; + +int Base64decode_len(const char *bufcoded) +{ + int nbytesdecoded; + register const unsigned char *bufin; + register int nprbytes; + + bufin = (const unsigned char *) bufcoded; + while (pr2six[*(bufin++)] <= 63); + + nprbytes = (bufin - (const unsigned char *) bufcoded) - 1; + nbytesdecoded = ((nprbytes + 3) / 4) * 3; + + return nbytesdecoded + 1; +} + +int Base64decode(char *bufplain, const char *bufcoded) +{ + int nbytesdecoded; + register const unsigned char *bufin; + register unsigned char *bufout; + register int nprbytes; + + bufin = (const unsigned char *) bufcoded; + while (pr2six[*(bufin++)] <= 63); + nprbytes = (bufin - (const unsigned char *) bufcoded) - 1; + nbytesdecoded = ((nprbytes + 3) / 4) * 3; + + bufout = (unsigned char *) bufplain; + bufin = (const unsigned char *) bufcoded; + + while (nprbytes > 4) { + *(bufout++) = + (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); + *(bufout++) = + (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); + *(bufout++) = + (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); + bufin += 4; + nprbytes -= 4; + } + + /* Note: (nprbytes == 1) would be an error, so just ingore that case */ + if (nprbytes > 1) { + *(bufout++) = + (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); + } + if (nprbytes > 2) { + *(bufout++) = + (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); + } + if (nprbytes > 3) { + *(bufout++) = + (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); + } + + *(bufout++) = '\0'; + nbytesdecoded -= (4 - nprbytes) & 3; + return nbytesdecoded; +} + +static const char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +int Base64encode_len(int len) +{ + return ((len + 2) / 3 * 4) + 1; +} + +int Base64encode(char *encoded, const char *string, int len) +{ + int i; + char *p; + + p = encoded; + for (i = 0; i < len - 2; i += 3) { + *p++ = basis_64[(string[i] >> 2) & 0x3F]; + *p++ = basis_64[((string[i] & 0x3) << 4) | + ((int) (string[i + 1] & 0xF0) >> 4)]; + *p++ = basis_64[((string[i + 1] & 0xF) << 2) | + ((int) (string[i + 2] & 0xC0) >> 6)]; + *p++ = basis_64[string[i + 2] & 0x3F]; + } + if (i < len) { + *p++ = basis_64[(string[i] >> 2) & 0x3F]; + if (i == (len - 1)) { + *p++ = basis_64[((string[i] & 0x3) << 4)]; + *p++ = '='; + } + else { + *p++ = basis_64[((string[i] & 0x3) << 4) | + ((int) (string[i + 1] & 0xF0) >> 4)]; + *p++ = basis_64[((string[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + + *p++ = '\0'; + return p - encoded; +} diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..2915796 --- /dev/null +++ b/src/base64.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + + +#ifndef _BASE64_H_ +#define _BASE64_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int Base64encode_len(int len); +int Base64encode(char * coded_dst, const char *plain_src,int len_plain_src); + +int Base64decode_len(const char * coded_src); +int Base64decode(char * plain_dst, const char *coded_src); + +#ifdef __cplusplus +} +#endif + +#endif //_BASE64_H_ diff --git a/src/openvpn-cr.c b/src/openvpn-cr.c new file mode 100644 index 0000000..7151e79 --- /dev/null +++ b/src/openvpn-cr.c @@ -0,0 +1,77 @@ +#include "openvpn-cr.h" + +#include "base64.h" +#include + +static const char * static_cr_label = "SCRV1"; +static const char * dynamic_cr_label = "CRV1"; + +int set_token_b64(const char * source, char * destination) +{ + if (Base64decode_len(source) >= MAXTOKENLENGTH) + return 0; + Base64decode(destination, source); + return 1; +} + +int set_token(const char * source, char * destination) +{ + if (strlen(source) >= MAXTOKENLENGTH) + return 0; + strncpy(destination, source, MAXTOKENLENGTH); + return 1; +} + + +int extract_openvpn_cr(const char *response, openvpn_response *result, char **error_message) +{ + const char *tokenIndexes[15]; + tokenIndexes[0] = response; + int tokenCnt = 1; + const char *p; + for (p = response; *p; ++p) { + if (*p == ':') + tokenIndexes[tokenCnt++] = p + 1; + } + + if (tokenCnt == 3 && strstr(response, static_cr_label) == response) + { + if (!set_token(static_cr_label, result->protocol)){ + *error_message = "Unable to set static protocol information."; + return 0; + } + + if (!set_token_b64(tokenIndexes[1], result->password)) { + *error_message = "Unable to extract password from static cr."; + return 0; + } + + if (!set_token_b64(tokenIndexes[2], result->response)) { + *error_message = "Unable to extract response from static cr."; + return 0; + } + } + else if (tokenCnt == 5 && strstr(response, dynamic_cr_label) == response) { + if (!set_token(dynamic_cr_label, result->protocol)) { + *error_message = "Unable to set dynamic protocol information."; + return 0; + } + + if (!set_token_b64(tokenIndexes[2], result->password)) { + *error_message = "Unable to extract password from dynamic cr."; + return 0; + } + + if (!set_token_b64(tokenIndexes[4], result->response)) { + *error_message = "Unable to extract response from dynamic cr."; + return 0; + } + } + else { + *error_message = "Incorrectly formatted cr string."; + return 0; + } + return 1; +} + + diff --git a/src/openvpn-cr.h b/src/openvpn-cr.h new file mode 100644 index 0000000..8ea2997 --- /dev/null +++ b/src/openvpn-cr.h @@ -0,0 +1,19 @@ +#ifndef OPENVPN_CR_H +#define OPENVPN_CR_H + +#define MAXTOKENLENGTH 1024 + +typedef struct +{ + char protocol[6]; + char password[MAXTOKENLENGTH]; + char response[MAXTOKENLENGTH]; +} openvpn_response; + +/* Parse a string containing an openvpn response and store the result + into an openvpn_response struct. + If parsing succeeds result will be in result and 1 is returned. + If parsing fails, 0 is returned, error_message is set */ +int extract_openvpn_cr(const char *response, openvpn_response *result, char **error_message); + +#endif \ No newline at end of file diff --git a/tests/PXTestConsoleResultHandler.h b/tests/PXTestConsoleResultHandler.h index dd7ec91..179d91e 100644 --- a/tests/PXTestConsoleResultHandler.h +++ b/tests/PXTestConsoleResultHandler.h @@ -25,6 +25,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include #import "TRObject.h" #import "PXTestResultHandler.h" diff --git a/ubuntu_16.04_lts_build.sh b/ubuntu_16.04_lts_build.sh new file mode 100755 index 0000000..edcbb9a --- /dev/null +++ b/ubuntu_16.04_lts_build.sh @@ -0,0 +1,13 @@ +#! /bin/bash + +# git clone https://github.com/snowrider311/openvpn-auth-ldap +# cd openvpn-auth-ldap/ +# source ubuntu_16.04_lts_build.sh +# source ubuntu_16.04_lts_package.sh + +sudo apt-get update +sudo apt-get -y install openvpn autoconf re2c libtool libldap2-dev libssl-dev gobjc make +./regen.sh +./configure --with-openvpn=/usr/include/openvpn CFLAGS="-fPIC" OBJCFLAGS="-std=gnu11" +make +sudo make install diff --git a/ubuntu_16.04_lts_package.sh b/ubuntu_16.04_lts_package.sh new file mode 100755 index 0000000..37b7cd1 --- /dev/null +++ b/ubuntu_16.04_lts_package.sh @@ -0,0 +1,13 @@ +#! /bin/bash + +sudo apt-get install -y ruby ruby-dev rubygems build-essential +sudo gem install --no-ri --no-rdoc fpm + +mkdir -p /tmp/openvpn-auth-ldap-build/usr/lib/openvpn +sudo mv /usr/local/lib/openvpn-auth-ldap.so /tmp/openvpn-auth-ldap-build/usr/lib/openvpn +fpm -s dir -C /tmp/openvpn-auth-ldap-build -t deb --name openvpn-auth-ldap-snowrider311 \ + --version 2.0.3 --iteration 1 --depends openvpn --depends gnustep-base-runtime \ + --depends libc6 --depends libgnustep-base1.24 --depends libldap-2.4-2 --depends libobjc4 + +# To install: +# sudo dpkg -i openvpn-auth-ldap-snowrider311_2.0.3-1_amd64.deb