diff --git a/Dockerfile b/Dockerfile index ba390fd..fb015cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,9 +4,11 @@ COPY frontend/ /var/www/html/ COPY docker/start.sh . RUN apt-get update && \ - apt-get install -y memcached libmemcached-dev zlib1g-dev && \ + apt-get install -y memcached libmemcached-dev zlib1g-dev libldap2-dev && \ pecl install memcached && \ - docker-php-ext-enable memcached + docker-php-ext-enable memcached && \ + docker-php-ext-configure ldap --with-libdir=lib/*-linux-gnu*/ && \ + docker-php-ext-install ldap EXPOSE 80/tcp VOLUME /etc/hauk diff --git a/backend-php/include/config-sample.php b/backend-php/include/config-sample.php index e58b56c..9b9d07d 100644 --- a/backend-php/include/config-sample.php +++ b/backend-php/include/config-sample.php @@ -63,6 +63,7 @@ // // - PASSWORD: Use a static, shared server password for everyone // - HTPASSWD: Require a username and separate password for each user +// - LDAP: Authenticate users against an LDAP server "auth_method" => PASSWORD, /*----------------------------------------------------------------------------*\ @@ -95,6 +96,37 @@ // - htpasswd -BC 10 /etc/hauk/users.htpasswd "htpasswd_path" => '/etc/hauk/users.htpasswd', +/*----------------------------------------------------------------------------*\ +| LDAP AUTHENTICATION | +\*----------------------------------------------------------------------------*/ + +// URI that points to the LDAP server. Use "ldap://" for unencrypted LDAP as +// well as when using StartTLS, use "ldaps://" for regular LDAP over TLS. Port +// number is typically 389 (ldap) or 636 (ldaps). +"ldap_uri" => 'ldaps://ldap.example.com:636', + +// Whether or not you wish to use StartTLS. StartTLS cannot be used in +// combination with `ldaps`. +"ldap_start_tls" => false, + +// Base DN to search for users. +"ldap_base_dn" => 'ou=People,dc=example,dc=com', + +// DN to bind to to perform user search. This should ideally be a read-only +// account as the password is stored in plain-text in this config file. +"ldap_bind_dn" => 'cn=admin,dc=example,dc=com', +"ldap_bind_pass" => 'Adm1nP4ssw0rd', + +// A filter that finds the user trying to authenticate. %s is substituted with +// the username provided by the user in the app. +// +// You can also use this to restrict access to Hauk to only authorized users if +// you do not wish to grant all LDAP users permission to use your Hauk instance. +// For example, (&(uid=%s)(memberOf=cn=HaukUsers,ou=Groups,dc=example,dc=com)) +// will only let the user connect if they are part of the "HaukUsers" group in +// the "Groups" OU. +"ldap_user_filter" => '(uid=%s)', + /*----------------------------------------------------------------------------*\ | GENERAL SETTINGS | \*----------------------------------------------------------------------------*/ diff --git a/backend-php/include/inc.php b/backend-php/include/inc.php index 077199d..39eb92b 100644 --- a/backend-php/include/inc.php +++ b/backend-php/include/inc.php @@ -28,6 +28,7 @@ const REDIS = 1; // Authentication methods. const PASSWORD = 0; const HTPASSWD = 1; +const LDAP = 2; // Share link types. const LINK_4_PLUS_4_UPPER_CASE = 0; @@ -125,6 +126,12 @@ const DEFAULTS = array( "auth_method" => PASSWORD, "password_hash" => '$2y$10$4ZP1iY8A3dZygXoPgsXYV.S3gHzBbiT9nSfONjhWrvMxVPkcFq1Ka', "htpasswd_path" => '/etc/hauk/users.htpasswd', + "ldap_uri" => 'ldaps://ldap.example.com:636', + "ldap_start_tls" => false, + "ldap_base_dn" => 'ou=People,dc=example,dc=com', + "ldap_bind_dn" => 'cn=admin,dc=example,dc=com', + "ldap_bind_pass" => 'Adm1nP4ssw0rd', + "ldap_user_filter" => '(uid=%s)', "allow_link_req" => true, "reserved_links" => [], "reserve_whitelist" => false, @@ -776,12 +783,12 @@ function requirePOST(...$args) { function authenticated() { switch (getConfig("auth_method")) { case PASSWORD: - // Static shared password authentication + // Static shared password authentication. requirePOST("pwd"); return password_verify($_POST["pwd"], getConfig("password_hash")); case HTPASSWD: - // .htpasswd file based authentication + // .htpasswd file based authentication. global $LANG; if (!isset($_POST["usr"])) die($LANG["username_required"]); requirePOST("pwd", "usr"); @@ -798,6 +805,55 @@ function authenticated() { return $authed; } + case LDAP: + // LDAP-based authentication. + global $LANG; + if (!extension_loaded("ldap")) die($LANG["ldap_extension_missing"]); + if (!isset($_POST["usr"])) die($LANG["username_required"]); + requirePOST("pwd", "usr"); + if (strlen($_POST["pwd"]) == 0) die($LANG["incorrect_password"]); + + // Connect to the LDAP server. + $ldc = @ldap_connect(getConfig("ldap_uri")); + if ($ldc === false) die($LANG["ldap_config_error"]); + ldap_set_option($ldc, LDAP_OPT_PROTOCOL_VERSION, 3); + ldap_set_option($ldc, LDAP_OPT_REFERRALS, 0); + if (getConfig("ldap_start_tls")) ldap_start_tls($ldc); + + // Bind the admin user. + $ldbind = @ldap_bind($ldc, getConfig("ldap_bind_dn"), getConfig("ldap_bind_pass")); + if ($ldbind === false) die($LANG["ldap_connection_failed"]); + + // Search for the user. + $ldsearch = @ldap_search($ldc, getConfig("ldap_base_dn"), str_replace("%s", $_POST["usr"], getConfig("ldap_user_filter")), array("dn")); + if ($ldsearch === false) { + ldap_unbind($ldc); + die($LANG["ldap_search_failed"]); + }; + $ldentries = @ldap_get_entries($ldc, $ldsearch); + if ($ldentries["count"] == 0) { + // No users matched; throw a generic "incorrect password" + // message anyway to prevent user enumeration issues. + ldap_unbind($ldc); + die($LANG["ldap_user_unauthorized"]); + } elseif ($ldentries["count"] > 1) { + // Filter matched multiple users; most likely a misconfiguration + // of the filter. + ldap_unbind($ldc); + die($LANG["ldap_search_ambiguous"]); + } + + // Bind as the user. + $ldbind = @ldap_bind($ldc, $ldentries[0]["dn"], $_POST["pwd"]); + if ($ldbind === false) { + ldap_unbind($ldc); + die($LANG["ldap_user_unauthorized"]); + } + + // Successful authentication and authorization. + ldap_unbind($ldc); + return true; + default: return false; } diff --git a/backend-php/include/lang/en/texts.php b/backend-php/include/lang/en/texts.php index 476927f..817116a 100644 --- a/backend-php/include/lang/en/texts.php +++ b/backend-php/include/lang/en/texts.php @@ -18,3 +18,9 @@ $LANG['session_invalid'] = 'Invalid session!'; $LANG['location_invalid'] = 'Invalid location!'; $LANG['group_e2e_unsupported'] = 'Group shares cannot be password protected!'; $LANG['e2e_adoption_not_allowed'] = 'This share is password protected and cannot be adopted!'; +$LANG['ldap_extension_missing'] = 'The ldap extension is not enabled in your PHP config!'; +$LANG['ldap_config_error'] = 'Failed to set LDAP connection parameters!'; +$LANG['ldap_connection_failed'] = 'Failed to connect to the LDAP server!'; +$LANG['ldap_search_failed'] = 'Failed to look up user on the LDAP server!'; +$LANG['ldap_user_unauthorized'] = 'User not found, not authorized, or incorrect password!'; +$LANG['ldap_search_ambiguous'] = 'Matched multiple users - the LDAP filter is too broad!'; diff --git a/docker/Dockerfile.amd64 b/docker/Dockerfile.amd64 index c5d7a48..1ddab09 100644 --- a/docker/Dockerfile.amd64 +++ b/docker/Dockerfile.amd64 @@ -21,9 +21,11 @@ COPY frontend/ /var/www/html/ COPY docker/start.sh . RUN apt-get update && \ - apt-get install -y memcached libmemcached-dev zlib1g-dev && \ + apt-get install -y memcached libmemcached-dev zlib1g-dev libldap2-dev && \ pecl install memcached && \ - docker-php-ext-enable memcached + docker-php-ext-enable memcached && \ + docker-php-ext-configure ldap --with-libdir=lib/*-linux-gnu*/ && \ + docker-php-ext-install ldap EXPOSE 80/tcp VOLUME /etc/hauk diff --git a/docker/Dockerfile.arm32v7 b/docker/Dockerfile.arm32v7 index 1267628..863efa8 100644 --- a/docker/Dockerfile.arm32v7 +++ b/docker/Dockerfile.arm32v7 @@ -22,9 +22,11 @@ COPY frontend/ /var/www/html/ COPY docker/start.sh . RUN apt-get update && \ - apt-get install -y memcached libmemcached-dev zlib1g-dev && \ + apt-get install -y memcached libmemcached-dev zlib1g-dev libldap2-dev && \ pecl install memcached && \ - docker-php-ext-enable memcached + docker-php-ext-enable memcached && \ + docker-php-ext-configure ldap --with-libdir=lib/*-linux-gnu*/ && \ + docker-php-ext-install ldap EXPOSE 80/tcp VOLUME /etc/hauk diff --git a/docker/Dockerfile.arm64v8 b/docker/Dockerfile.arm64v8 index aa41577..68d1d09 100644 --- a/docker/Dockerfile.arm64v8 +++ b/docker/Dockerfile.arm64v8 @@ -22,9 +22,11 @@ COPY frontend/ /var/www/html/ COPY docker/start.sh . RUN apt-get update && \ - apt-get install -y memcached libmemcached-dev zlib1g-dev && \ + apt-get install -y memcached libmemcached-dev zlib1g-dev libldap2-dev && \ pecl install memcached && \ - docker-php-ext-enable memcached + docker-php-ext-enable memcached && \ + docker-php-ext-configure ldap --with-libdir=lib/*-linux-gnu*/ && \ + docker-php-ext-install ldap EXPOSE 80/tcp VOLUME /etc/hauk