Skip to content

Commit

Permalink
Merge pull request #63 from guywyers/master
Browse files Browse the repository at this point in the history
Support for OpenVPN Challenge/Response protocol
  • Loading branch information
samskivert committed Jul 26, 2018
2 parents 4952ec8 + 007df3c commit dbc1384
Show file tree
Hide file tree
Showing 18 changed files with 592 additions and 29 deletions.
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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/
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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):
Expand Down
9 changes: 9 additions & 0 deletions aclocal.m4
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,20 @@ AC_DEFUN([OD_OBJC_RUNTIME],[
AC_LINK_IFELSE([
AC_LANG_PROGRAM([
#include <objc/objc.h>
#ifdef __GNU_LIBOBJC__
#include <objc/runtime.h>
#else
#include <objc/objc-api.h>
#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"
Expand Down
11 changes: 9 additions & 2 deletions auth-ldap.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
<Group>
# 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))"
Expand Down
4 changes: 3 additions & 1 deletion src/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
4 changes: 4 additions & 0 deletions src/TRAuthLDAPConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
TRString *_pfTable;
TRArray *_ldapGroups;
BOOL _pfEnabled;
BOOL _passwordISCR;

/* Parser State */
TRString *_configFileName;
Expand Down Expand Up @@ -126,4 +127,7 @@

- (TRArray *) ldapGroups;

- (BOOL) passWordIsCR;
- (void) setPassWordIsCR: (BOOL)newCRSetting;

@end
44 changes: 42 additions & 2 deletions src/TRAuthLDAPConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 }
};

Expand All @@ -173,14 +185,16 @@
AuthSectionVariables,
GenericLDAPVariables,
GenericPFVariables,
NULL
OpenVPNCRVariables,
NULL
};

/* Group Section Definition */
static OpcodeTable *GroupSection[] = {
GroupSectionVariables,
GenericLDAPVariables,
GenericPFVariables,

NULL
};

Expand Down Expand Up @@ -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]) {
Expand All @@ -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];
Expand All @@ -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];
Expand All @@ -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]];
Expand Down Expand Up @@ -979,4 +1012,11 @@ - (TRArray *) ldapGroups {
return _ldapGroups;
}

- (BOOL) passWordIsCR {
return (_passwordISCR);
}

- (void) setPassWordIsCR: (BOOL) newCRSetting {
_passwordISCR = newCRSetting;
}
@end
4 changes: 4 additions & 0 deletions src/TRLDAPGroupConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
TRString *_searchFilter;
TRString *_memberAttribute;
BOOL _memberRFC2307BIS;
BOOL _useCompareOperation;
TRString *_pfTable;
}

Expand All @@ -56,6 +57,9 @@
- (BOOL) memberRFC2307BIS;
- (void) setMemberRFC2307BIS: (BOOL) memberRFC2307BIS;

- (BOOL) useCompareOperation;
- (void) setUseCompareOperation: (BOOL) useCompareOperation;

- (TRString *) pfTable;
- (void) setPFTable: (TRString *) tableName;

Expand Down
9 changes: 9 additions & 0 deletions src/TRLDAPGroupConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ - (id) init {
return self;

_memberRFC2307BIS = YES;
_useCompareOperation = YES;
return self;
}

Expand Down Expand Up @@ -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];
Expand Down
1 change: 1 addition & 0 deletions src/TRObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

#import <stdint.h>
#import <stdbool.h>
#include <stdarg.h>

#import "PXObjCRuntime.h"

Expand Down
54 changes: 32 additions & 22 deletions src/auth-ldap.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@

#import <TRVPNPlugin.h>

#include "openvpn-cr.h"

/* Plugin Context */
typedef struct ldap_ctx {
TRAuthLDAPConfig *config;
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit dbc1384

Please sign in to comment.