ldapauth.php 7.84 KB
Newer Older
friendica's avatar
friendica committed
1
2
3
<?php
/**
 * Name: LDAP Authenticate
4
 * Description: Authenticate an account against an LDAP directory
friendica's avatar
friendica committed
5
 * Version: 1.0
Zot's avatar
Zot committed
6
7
 * Author: Mike Macgirvin
 * Maintainer: Mike Macgirvin
friendica's avatar
friendica committed
8
 */
9

friendica's avatar
friendica committed
10
/**
11
 *
friendica's avatar
friendica committed
12
13
14
15
16
17
18
19
20
 * Module: LDAP Authenticate
 *
 * Authenticate a user against an LDAP directory
 * Useful for Windows Active Directory and other LDAP-based organisations
 * to maintain a single password across the organisation.
 *
 * Optionally authenticates only if a member of a given group in the directory.
 *
 * Note when using with Windows Active Directory: you may need to set TLS_CACERT in your site
21
22
 * ldap.conf file to the signing cert for your LDAP server.
 *
friendica's avatar
friendica committed
23
24
25
 * The required configuration options for this module may be set in the .htconfig.php file
 * e.g.:
 *
redmatrix's avatar
redmatrix committed
26
 * App::$config['ldapauth']['ldap_server'] = 'host.example.com';
friendica's avatar
friendica committed
27
28
29
30
31
32
33
 *
 * Parameters:
 * ldap_server = DNS hostname of LDAP service, or URL e.g. ldaps://example.com
 * ldap_binddn = LDAP DN of an account to bind with to search LDAP
 * ldap_bindpw = password for LDAP bind DN
 * ldap_searchdn = base DN for the search root of the LDAP directory
 * ldap_userattr = the name of the attribute containing the username, e.g. SAMAccountName
Zot's avatar
Zot committed
34
 * ldap_nameattr = the name of the attribute containing the displayname, e.g. displayName - only required if creating a channel
friendica's avatar
friendica committed
35
 * ldap_group = (optional) DN of group to check membership
redmatrix's avatar
changes    
redmatrix committed
36
 * create_account = (optional) 1 or 0 (default), automatically create Hubzilla accounts based on the LDAP 'mail' attribute
Zot's avatar
Zot committed
37
38
 * create_channel = (optional) 1 or 0 (default), automatically create channel if create_account succeeds using nameattr and userattr
 *    and the system default_permissions_role or 'social'
friendica's avatar
friendica committed
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 *
 */


function ldapauth_load() {
	register_hook('authenticate', 'addon/ldapauth/ldapauth.php', 'ldapauth_hook_authenticate');
}


function ldapauth_unload() {
	unregister_hook('authenticate', 'addon/ldapauth/ldapauth.php', 'ldapauth_hook_authenticate');
}


function ldapauth_hook_authenticate($a,&$b) {
Zot's avatar
Zot committed
54

friendica's avatar
friendica committed
55
	$mail = '';
Zot's avatar
Zot committed
56
57
58
59
60
61
	$username = '';
	$displayname = '';

	$perms_role = get_config('system','default_permissions_role','social');

	if(ldapauth_authenticate($b['username'],$b['password'],$mail,$nickname,$displayname)) {
62
63
		$results = q("SELECT * FROM account where account_email = '%s' OR account_email = '%s'  AND account_flags in (0,1) limit 1",
			dbesc($b['username']), dbesc($mail)
friendica's avatar
friendica committed
64
		);
65
		if((! $results) && ($mail) && intval(get_config('ldapauth','create_account')) == 1) {
friendica's avatar
friendica committed
66
			require_once('include/account.php');
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
			$now = datetime_convert();
			$salt = random_string(32);
			$pass = random_string(64);
			$password = $salt . ',' . hash('whirlpool', $salt . $pass);

			q("INSERT INTO register ("
				. "reg_didx, reg_did2, reg_hash, reg_created, reg_startup, reg_expires,"
				. "reg_email, reg_pass, reg_lang, reg_atip, reg_stuff)"
				. " VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ",
				dbesc('e'),
				dbesc($mail),
				dbesc('ldapauth'),
				dbesc($now),
				dbesc($now),
				dbesc(datetime_convert('UTC', 'UTC', $now . ' + 1 hour')),
				dbesc($mail),
				dbesc($password),
Mario's avatar
Mario committed
84
				dbesc(\App::$language),
85
86
87
88
				dbesc($_SERVER['REMOTE_ADDR']),
				dbesc(json_encode([]))
			);

Mario's avatar
Mario committed
89
			$reg = q("SELECT reg_id FROM register WHERE reg_did2 = '%s' AND reg_pass = '%s'",
90
91
92
93
94
				dbesc($mail),
				dbesc($password)
			);

			$acct = create_account_from_register(['reg_id' => $reg[0]['reg_id']]);
friendica's avatar
friendica committed
95
			if($acct['success']) {
96
97
				q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d",
					intval($reg[0]['reg_id'])
Mario's avatar
Mario committed
98
				);
99

friendica's avatar
friendica committed
100
				logger('ldapauth: Created account for ' . $b['username'] . ' using ' . $mail);
101
				info(t('An account has been created for you.'));
friendica's avatar
friendica committed
102
103
104
105
				$b['user_record'] = $acct['account'];
				$b['authenticated'] = 1;
			}

106
107
		} elseif(intval(get_config('ldapauth','create_account')) != 1 && (! $results)) {
                  logger('ldapauth: User '.$b['username'].' authenticated but no db-record and. Rejecting auth.');
108
		  notice( t('Authentication successful but rejected: account creation is disabled.'));
109
		  return;
friendica's avatar
friendica committed
110
		}
Zot's avatar
Zot committed
111
112
		if((! $results) && $b['user_record'] && $nickname && $displayname && intval(get_config('ldapauth','create_channel'))) {
			$c = create_identity( [
113
114
115
				'name' => $displayname,
				'nickname' => $nickname,
				'account_id' => $b['user_record']['account_id'],
Zot's avatar
Zot committed
116
117
118
119
120
121
122
123
				'permissions_role' => $perms_role
			] );

			if(! $c['success']) {
				logger('ldapauth: channel creation failed');
				return;
			}
		}
friendica's avatar
friendica committed
124
125
126
127
128
129
130
131
132
133
		if($results) {
			logger('ldapauth: Login success for ' . $b['username']);
			$b['user_record'] = $results[0];
			$b['authenticated'] = 1;
		}
	}
	return;
}


Zot's avatar
Zot committed
134
function ldapauth_authenticate($username,$password,&$mail,&$nickname,&$displayname) {
135
    logger('ldapauth: Searching user '.$username.'.');
friendica's avatar
friendica committed
136
137
138
139
    $ldap_server   = get_config('ldapauth','ldap_server');
    $ldap_binddn   = get_config('ldapauth','ldap_binddn');
    $ldap_bindpw   = get_config('ldapauth','ldap_bindpw');
    $ldap_searchdn = get_config('ldapauth','ldap_searchdn');
Zot's avatar
Zot committed
140
141
    $ldap_userattr = get_config('ldapauth','ldap_userattr','SAMAccountName');
    $ldap_nameattr = get_config('ldapauth','ldap_nameattr','displayName');
friendica's avatar
friendica committed
142
143
    $ldap_group    = get_config('ldapauth','ldap_group');

144
145
146
147
148
149
150
151
    if(empty($password)) {
        logger('ldapauth: Empty Password not allowed.');
        return false;
    }

    if(!function_exists('ldap_connect')
       || empty($ldap_server)) {
            logger('ldapauth: PHP-LDAP fail or no server set.');
friendica's avatar
friendica committed
152
            return false;
153
    }
friendica's avatar
friendica committed
154
155
156

    $connect = @ldap_connect($ldap_server);

157
158
    if(! $connect) {
	logger('ldapauth: Unable to connect to server');
friendica's avatar
friendica committed
159
        return false;
160
    }
friendica's avatar
friendica committed
161
162
163
164

    @ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION,3);
    @ldap_set_option($connect, LDAP_OPT_REFERRALS, 0);
    if((@ldap_bind($connect,$ldap_binddn,$ldap_bindpw)) === false) {
165
	logger('ldapauth: Unable to bind to server. Check credentials of binddn.');
friendica's avatar
friendica committed
166
167
168
        return false;
    }

Zot's avatar
Zot committed
169
    $res = @ldap_search($connect,$ldap_searchdn, $ldap_userattr . '=' . $username, [ 'mail', $ldap_nameattr, $ldap_userattr ]);
friendica's avatar
friendica committed
170
171

    if(! $res) {
172
	logger('ldapauth: User '.$username.' not found.');
friendica's avatar
friendica committed
173
174
175
176
177
178
        return false;
    }

    $id = @ldap_first_entry($connect,$res);

    if(! $id) {
179
	logger('ldapauth: User '.$username.' found but unable to load data.');
friendica's avatar
friendica committed
180
181
182
183
184
185
186
187
188
189
190
191
192
193
        return false;
    }

	// get primary email

	$mail = '';

	$attrs = @ldap_get_attributes($connect,$id);
	if($attrs['count'] && $attrs['mail']) {
		if(is_array($attrs['mail']))
			$mail = $attrs['mail'][0];
		else
			$mail = $attrs['mail'];
	}
Zot's avatar
Zot committed
194
195
196
197
198
199
200
201
202
203
204
205
206
	if($attrs['count'] && $attrs[$ldap_nameattr]) {
		if(is_array($attrs[$ldap_nameattr]))
			$displayname = $attrs[$ldap_nameattr][0];
		else
			$displayname = $attrs[$ldap_nameattr];
	}

	if($attrs['count'] && $attrs[$ldap_userattr]) {
		if(is_array($attrs[$ldap_userattr]))
			$nickname = $attrs[$ldap_userattr][0];
		else
			$nickname = $attrs[$ldap_userattr];
	}
friendica's avatar
friendica committed
207
208
209

    $dn = @ldap_get_dn($connect,$id);

210
211
    if(! @ldap_bind($connect,$dn,$password)) {
	logger('ldapauth: User '.$username.' provided wrong credentials.');
friendica's avatar
friendica committed
212
        return false;
213
    }
friendica's avatar
friendica committed
214

215
216
    if(empty($ldap_group)) {
	logger('ldapauth: User '.$username.' authenticated.');
friendica's avatar
friendica committed
217
        return true;
218
    }
friendica's avatar
friendica committed
219
220
221
222
223
224
225
226

    $r = @ldap_compare($connect,$ldap_group,'member',$dn);
    if ($r === -1) {
        $err = @ldap_error($connect);
        $eno = @ldap_errno($connect);
        @ldap_close($connect);

        if ($eno === 32) {
227
            logger('ldapauth: access control group Does Not Exist');
friendica's avatar
friendica committed
228
229
230
231
232
233
234
235
236
237
238
239
            return false;
        }
        elseif ($eno === 16) {
            logger('ldapauth: membership attribute does not exist in access control group');
            return false;
        }
        else {
            logger('ldapauth: error: ' . $err);
            return false;
        }
    }
    elseif ($r === false) {
240
	logger('ldapauth: User '.$username.' is not in the allowed group.');
friendica's avatar
friendica committed
241
242
243
        @ldap_close($connect);
        return false;
    }
244
245
//    logger('ldapauth: User '.$username.' authenticated and in allowed group.');
  return true;
friendica's avatar
friendica committed
246
}