-
Notifications
You must be signed in to change notification settings - Fork 1
/
ZimbraAuth.php
157 lines (130 loc) · 5.29 KB
/
ZimbraAuth.php
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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<?php
//declare(strict_types=1);
namespace SimpleSAML\Module\zimbraauth\Auth\Source;
use Exception;
use SimpleSAML\Assert\Assert;
use SimpleSAML\Error;
use SimpleSAML\Module\core\Auth\UserPassBase;
use SimpleSAML\Utils;
/*
LDAP authentication source - username & password for Zimbra.
Set-up in config/authsources.php as follows:
'zimbra' => array (
'zimbraauth:ZimbraAuth',
'zimbraServer' => 'zimbraserver.example.com',
'zimbraPort' => 389
)
Enable the module in config/config.php, add zimbraauth to module.enable as following example:
'module.enable' => [
'admin' => true,
'zimbraauth' => true,
],
This file should be in : modules/zimbraauth/src/Auth/Source/ZimbraAuth.php don't forget:
chown www-data:www-data modules/ -R
Assuming you installed SimpleSAMLPhp in simplesaml Apache location, you can test the authentication source via:
https://your-saml-server.example.com/simplesaml/module.php/admin/test/zimbra
First login using your SimpleSAMLPhp admin credentials and then use a Zimbra account for testing.
1. The Zimbra authentication module requires users to use their full email address to log-in.
2. Login by the use of an alias email address is not and will not be supported.
3. This module requires TLS for the connection between this module and the Zimbra LDAP.
4. This module was designed for SimpleSAMLPhp version 2.0.4.
Upon successful auth the following attributes will be available: "ou", "sn", "givenname", "mail", "uid".
If this works you can set-up SimpleSAMLPhp as an IDP and use Zimbra as the authentication source see:
https://simplesamlphp.org/docs/stable/simplesamlphp-idp.html
Bare minimum example of metadata/saml20-idp-hosted.php, notice 'auth'=>'zimbra' is what tells SimpleSAMLPhp
to use the Zimbra authentication module:
```
<?php
$metadata['https://your-saml-server.example.com/simplesaml/saml2/idp/metadata.php'] = [
'host' => '__DEFAULT__',
'privatekey' => 'server.pem',
'certificate' => 'server.crt',
'auth' => 'zimbra',
];
```
*/
class ZimbraAuth extends UserPassBase
{
/**
* Constructor for this authentication source.
*
* @param array $info Information about this authentication source.
* @param array $config Configuration.
*/
public function __construct(array $info, array $config)
{
// Call the parent constructor first, as required by the interface
parent::__construct($info, $config);
$this->config = $config;
}
protected function login(string $username, string $password): array
{
$username = strtolower($username);
$authresult= $this->verifyLdapUser($username, $password);
if($authresult)
{
return $authresult;
}
else
{
throw new \SimpleSAML\Error\Error('WRONGUSERPASS');
}
}
public function logout(array &$state): void
{
}
public function verifyLdapUser($username,$password)
{
$ds=@ldap_connect($this->config['zimbraServer'], $this->config['zimbraPort']);
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
ldap_set_option($ds, LDAP_OPT_NETWORK_TIMEOUT, 10);
$uid = ldap_escape(explode("@",$username)[0], "", LDAP_ESCAPE_FILTER);
$domain = @ldap_escape(explode("@",$username)[1], "", LDAP_ESCAPE_FILTER);
if(empty($domain)) {
return false;
}
$ou = "ou=people,dc=".str_replace(".",",dc=",$domain);
$dn = "uid=".$uid.",".$ou;
if(ldap_start_tls($ds))
{
$bind=@ldap_bind($ds, $dn, $password);
if($bind)
{
$attributes = array("ou", "sn", "givenname", "mail", "uid");
$sr=@ldap_search($ds, $ou, "uid=".$uid, $attributes); //this search should always give one single result.
$info = ldap_get_entries($ds, $sr);
//Technically the mail attribute is multi-valued, meaning if a user has alias email addresses these also are in the mail attribute.
//since the first mail attribute == the zimbra account name, this is what we want to use for authentication if we want to use if for authentication.
//alias email addresses should never be a part of any login flow, as this is a security risk.
//When doing integrations, it is almost always better and more secure to work with single-value attributes, which is why we [0] everything below.
if($info["count"] == 1)
{
return [
'ou' => [$ou],
'dn' => [$dn],
'sn' => @[$info[0]['sn'][0]],
'givenname' => @[$info[0]['givenname'][0]],
'mail' => [$info[0]['mail'][0]],
'uid' => [$info[0]['uid'][0]]
];
}
else
{
echo "Fatal: LDAP search returned multiple entries, something is not right here...";
//An LDAP search with something like dn: uid=admin,ou=people,dc=barrydegraaff,dc=nl in an OU like ou=people,dc=barrydegraaff,dc=nl should give exactly one entry... if not I dunno what happened but it can't be good.
return false;
}
}
else
{
return false;
}
}
else
{
echo "Fatal: LDAP TLS failed";
return false;
}
}
}