OSDN Git Service

BugTrack/2411 SAML plugin: PukiWiki as a SAML service provider
[pukiwiki/pukiwiki.git] / lib / auth.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone
3 // auth.php
4 // Copyright 2003-2017 PukiWiki Development Team
5 // License: GPL v2 or (at your option) any later version
6 //
7 // Authentication related functions
8
9 define('PKWK_PASSPHRASE_LIMIT_LENGTH', 512);
10
11 /////////////////////////////////////////////////
12 // Auth type
13
14 define('AUTH_TYPE_NONE', 0);
15 define('AUTH_TYPE_BASIC', 1);
16 define('AUTH_TYPE_EXTERNAL', 2);
17 define('AUTH_TYPE_FORM', 3);
18
19 define('AUTH_TYPE_EXTERNAL_REMOTE_USER', 4);
20 define('AUTH_TYPE_EXTERNAL_X_FORWARDED_USER', 5);
21 define('AUTH_TYPE_SAML', 6);
22
23
24 // Passwd-auth related ----
25
26 function pkwk_login($pass = '')
27 {
28         global $adminpass;
29
30         if (! PKWK_READONLY && isset($adminpass) &&
31                 pkwk_hash_compute($pass, $adminpass) === $adminpass) {
32                 return TRUE;
33         } else {
34                 sleep(2);       // Blocking brute force attack
35                 return FALSE;
36         }
37 }
38
39 // Compute RFC2307 'userPassword' value, like slappasswd (OpenLDAP)
40 // $phrase : Pass-phrase
41 // $scheme : Specify '{scheme}' or '{scheme}salt'
42 // $prefix : Output with a scheme-prefix or not
43 // $canonical : Correct or Preserve $scheme prefix
44 function pkwk_hash_compute($phrase = '', $scheme = '{x-php-md5}', $prefix = TRUE, $canonical = FALSE)
45 {
46         if (! is_string($phrase) || ! is_string($scheme)) return FALSE;
47
48         if (strlen($phrase) > PKWK_PASSPHRASE_LIMIT_LENGTH)
49                 die('pkwk_hash_compute(): malicious message length');
50
51         // With a {scheme}salt or not
52         $matches = array();
53         if (preg_match('/^(\{.+\})(.*)$/', $scheme, $matches)) {
54                 $scheme = & $matches[1];
55                 $salt   = & $matches[2];
56         } else if ($scheme != '') {
57                 $scheme  = ''; // Cleartext
58                 $salt    = '';
59         }
60
61         // Compute and add a scheme-prefix
62         switch (strtolower($scheme)) {
63
64         // PHP crypt()
65         case '{x-php-crypt}' :
66                 $hash = ($prefix ? ($canonical ? '{x-php-crypt}' : $scheme) : '') .
67                         ($salt != '' ? crypt($phrase, $salt) : crypt($phrase));
68                 break;
69
70         // PHP md5()
71         case '{x-php-md5}'   :
72                 $hash = ($prefix ? ($canonical ? '{x-php-md5}' : $scheme) : '') .
73                         md5($phrase);
74                 break;
75
76         // PHP sha1()
77         case '{x-php-sha1}'  :
78                 $hash = ($prefix ? ($canonical ? '{x-php-sha1}' : $scheme) : '') .
79                         sha1($phrase);
80                 break;
81
82         // LDAP CRYPT
83         case '{crypt}'       :
84                 $hash = ($prefix ? ($canonical ? '{CRYPT}' : $scheme) : '') .
85                         ($salt != '' ? crypt($phrase, $salt) : crypt($phrase));
86                 break;
87
88         // LDAP MD5
89         case '{md5}'         :
90                 $hash = ($prefix ? ($canonical ? '{MD5}' : $scheme) : '') .
91                         base64_encode(pkwk_hex2bin(md5($phrase)));
92                 break;
93
94         // LDAP SMD5
95         case '{smd5}'        :
96                 // MD5 Key length = 128bits = 16bytes
97                 $salt = ($salt != '' ? substr(base64_decode($salt), 16) : substr(crypt(''), -8));
98                 $hash = ($prefix ? ($canonical ? '{SMD5}' : $scheme) : '') .
99                         base64_encode(pkwk_hex2bin(md5($phrase . $salt)) . $salt);
100                 break;
101
102         // LDAP SHA
103         case '{sha}'         :
104                 $hash = ($prefix ? ($canonical ? '{SHA}' : $scheme) : '') .
105                         base64_encode(pkwk_hex2bin(sha1($phrase)));
106                 break;
107
108         // LDAP SSHA
109         case '{ssha}'        :
110                 // SHA-1 Key length = 160bits = 20bytes
111                 $salt = ($salt != '' ? substr(base64_decode($salt), 20) : substr(crypt(''), -8));
112                 $hash = ($prefix ? ($canonical ? '{SSHA}' : $scheme) : '') .
113                         base64_encode(pkwk_hex2bin(sha1($phrase . $salt)) . $salt);
114                 break;
115
116         // LDAP CLEARTEXT and just cleartext
117         case '{cleartext}'   : /* FALLTHROUGH */
118         case ''              :
119                 $hash = ($prefix ? ($canonical ? '{CLEARTEXT}' : $scheme) : '') .
120                         $phrase;
121                 break;
122
123         // Invalid scheme
124         default:
125                 $hash = FALSE;
126                 break;
127         }
128
129         return $hash;
130 }
131
132 // LDAP related functions
133
134 function _pkwk_ldap_escape_callback($matches) {
135         return sprintf('\\%02x', ord($matches[0]));
136 }
137
138 function pkwk_ldap_escape_filter($value) {
139         if (function_exists('ldap_escape')) {
140                 return ldap_escape($value, false, LDAP_ESCAPE_FILTER);
141         }
142         return preg_replace_callback('/[\\\\*()\0]/',
143                 '_pkwk_ldap_escape_callback', $value);
144 }
145
146 function pkwk_ldap_escape_dn($value) {
147         if (function_exists('ldap_escape')) {
148                 return ldap_escape($value, false, LDAP_ESCAPE_DN);
149         }
150         return preg_replace_callback('/[\\\\,=+<>;"#]/',
151                 '_pkwk_ldap_escape_callback', $value);
152 }
153
154
155 // Basic-auth related ----
156
157 // Check edit-permission
158 function check_editable($page, $auth_enabled = TRUE, $exit_on_fail = TRUE)
159 {
160         global $script, $_title_cannotedit, $_msg_unfreeze;
161
162         if (edit_auth($page, $auth_enabled, $exit_on_fail) && is_editable($page)) {
163                 // Editable
164                 return TRUE;
165         } else {
166                 // Not editable
167                 if ($exit_on_fail === FALSE) {
168                         return FALSE; // Without exit
169                 } else {
170                         // With exit
171                         $body = $title = str_replace('$1',
172                                 htmlsc(strip_bracket($page)), $_title_cannotedit);
173                         if (is_freeze($page))
174                                 $body .= '(<a href="' . $script . '?cmd=unfreeze&amp;page=' .
175                                         rawurlencode($page) . '">' . $_msg_unfreeze . '</a>)';
176                         $page = str_replace('$1', make_search($page), $_title_cannotedit);
177                         catbody($title, $page, $body);
178                         exit;
179                 }
180         }
181 }
182
183 /**
184  * Whether the page is readable from current user or not.
185  */
186 function is_page_readable($page) {
187         global $read_auth_pages;
188         return _is_page_accessible($page, $read_auth_pages);
189 }
190
191 /**
192  * Whether the page is writable from current user or not.
193  */
194 function is_page_writable($page) {
195         global $edit_auth_pages;
196         return _is_page_accessible($page, $edit_auth_pages);
197 }
198
199 /**
200  * Get whether a current auth user can access the page
201  *
202  * @param $page page name
203  * @param $auth_pages pagepattern -> groups map
204  * @return true if a current user can access the page
205  */
206 function _is_page_accessible($page, $auth_pages) {
207         global $auth_method_type, $auth_user_groups, $auth_user;
208
209         // Checked by:
210         $target_str = '';
211         if ($auth_method_type == 'pagename') {
212                 $target_str = $page; // Page name
213         } else if ($auth_method_type == 'contents') {
214                 $target_str = join('', get_source($page)); // Its contents
215         }
216         $user_list = array();
217         foreach($auth_pages as $key=>$val) {
218                 if (preg_match($key, $target_str)) {
219                         $user_list = array_merge($user_list, explode(',', $val));
220                 }
221         }
222         if (empty($user_list)) return TRUE; // No limit
223         if (!$auth_user) {
224                 // Current user doesen't yet log in.
225                 return FALSE;
226         }
227         if (count(array_intersect($auth_user_groups, $user_list)) === 0) {
228                 return FALSE;
229         }
230         return TRUE;
231 }
232
233 /**
234  * Ensure the page is readable, or show Login UI.
235  * @param $page page
236  */
237 function ensure_page_readable($page) {
238         global $read_auth, $read_auth_pages, $_title_cannotread;
239         if (!$read_auth) {
240                 return true;
241         }
242         return basic_auth($page, true, true,
243                 $read_auth_pages, $_title_cannotread);
244 }
245
246 /**
247  * Ensure the page is writable, or show Login UI.
248  * @param $page page
249  */
250 function ensure_page_writable($page) {
251         global $edit_auth, $edit_auth_pages, $_title_cannotedit;
252         if (!$edit_auth) {
253                 return true;
254         }
255         return basic_auth($page, true, true,
256                 $edit_auth_pages, $_title_cannotedit);
257 }
258
259 /**
260  * Check a page is readable or not, show Auth UI in some cases.
261  *
262  * @param $page page name
263  * @param $auth_enabled true if auth is available (Normally true)
264  * @param $exit_on_fail  (Normally true)
265  * @return true if the page is readable
266  */
267 function check_readable($page, $auth_enabled = TRUE, $exit_on_fail = TRUE)
268 {
269         return read_auth($page, $auth_enabled, $exit_on_fail);
270 }
271
272 function edit_auth($page, $auth_enabled = TRUE, $exit_on_fail = TRUE)
273 {
274         global $edit_auth, $edit_auth_pages, $_title_cannotedit;
275         return $edit_auth ?  basic_auth($page, $auth_enabled, $exit_on_fail,
276                 $edit_auth_pages, $_title_cannotedit) : TRUE;
277 }
278
279 function read_auth($page, $auth_enabled = TRUE, $exit_on_fail = TRUE)
280 {
281         global $read_auth, $read_auth_pages, $_title_cannotread;
282         return $read_auth ?  basic_auth($page, $auth_enabled, $exit_on_fail,
283                 $read_auth_pages, $_title_cannotread) : TRUE;
284 }
285
286 /**
287  * Authentication
288  *
289  * @param $page page name
290  * @param $auth_enabled true if auth is available
291  * @param $exit_on_fail Show forbidden message and stop all following processes
292  * @param $auth_pages accessible users -> pages pattern map
293  * @param $title_cannot forbidden message
294  */
295 function basic_auth($page, $auth_enabled, $exit_on_fail, $auth_pages, $title_cannot)
296 {
297         global $auth_users, $_msg_auth, $auth_user;
298         global $auth_type, $g_query_string;
299         $is_accessible = _is_page_accessible($page, $auth_pages);
300         if ($is_accessible) {
301                 return TRUE;
302         } else {
303                 // Auth failed
304                 pkwk_common_headers();
305                 if ($auth_enabled && !$auth_user) {
306                         if (AUTH_TYPE_BASIC === $auth_type) {
307                                 header('WWW-Authenticate: Basic realm="' . $_msg_auth . '"');
308                                 header('HTTP/1.0 401 Unauthorized');
309                         } elseif (AUTH_TYPE_FORM === $auth_type) {
310                                 $url_after_login = get_script_uri() . '?' . $g_query_string;
311                                 $loginurl = get_script_uri() . '?plugin=loginform'
312                                         . '&page=' . rawurlencode($page)
313                                         . '&url_after_login=' . rawurlencode($url_after_login);
314                                 header('HTTP/1.0 302 Found');
315                                 header('Location: ' . $loginurl);
316                         } elseif (AUTH_TYPE_EXTERNAL === $auth_type ||
317                                 AUTH_TYPE_SAML === $auth_type) {
318                                 $url_after_login = get_script_uri() . '?' . $g_query_string;
319                                 $loginurl = get_auth_external_login_url($page, $url_after_login);
320                                 header('HTTP/1.0 302 Found');
321                                 header('Location: ' . $loginurl);
322                         }
323                 }
324                 if ($exit_on_fail) {
325                         $body = $title = str_replace('$1',
326                                 htmlsc(strip_bracket($page)), $title_cannot);
327                         $page = str_replace('$1', make_search($page), $title_cannot);
328                         catbody($title, $page, $body);
329                         exit;
330                 }
331                 return FALSE;
332         }
333 }
334
335 /**
336  * Send 401 if client send a invalid credentials
337  *
338  * @return true if valid, false if invalid credentials
339  */
340 function ensure_valid_auth_user()
341 {
342         global $auth_type, $auth_users, $_msg_auth, $auth_user, $auth_groups;
343         global $auth_user_groups, $auth_user_fullname;
344         global $ldap_user_account;
345         global $read_auth, $edit_auth;
346         if ($read_auth || $edit_auth) {
347                 switch ($auth_type) {
348                         case AUTH_TYPE_BASIC:
349                         case AUTH_TYPE_FORM:
350                         case AUTH_TYPE_EXTERNAL:
351                         case AUTH_TYPE_EXTERNAL_REMOTE_USER:
352                         case AUTH_TYPE_EXTERNAL_X_FORWARDED_USER:
353                         case AUTH_TYPE_SAML:
354                                 break;
355                         default:
356                                 // $auth_type is not valid, Set form auth as default
357                                 $auth_type = AUTH_TYPE_FORM;
358                 }
359         }
360         $auth_dynamic_groups = null;
361         switch ($auth_type) {
362                 case AUTH_TYPE_BASIC:
363                 {
364                         if (isset($_SERVER['PHP_AUTH_USER'])) {
365                                 $user = $_SERVER['PHP_AUTH_USER'];
366                                 if (in_array($user, array_keys($auth_users))) {
367                                         if (pkwk_hash_compute(
368                                                 $_SERVER['PHP_AUTH_PW'],
369                                                 $auth_users[$user]) === $auth_users[$user]) {
370                                                 $auth_user = $user;
371                                                 $auth_user_fullname = $auth_user;
372                                                 $auth_user_groups = get_groups_from_username($user);
373                                                 return true;
374                                         }
375                                 }
376                                 header('WWW-Authenticate: Basic realm="' . $_msg_auth . '"');
377                                 header('HTTP/1.0 401 Unauthorized');
378                         }
379                         $auth_user = '';
380                         $auth_user_groups = array();
381                         return true; // no auth input
382                 }
383                 case AUTH_TYPE_FORM:
384                 case AUTH_TYPE_EXTERNAL:
385                 case AUTH_TYPE_SAML:
386                 {
387                         session_start();
388                         $user = '';
389                         $fullname = '';
390                         $dynamic_groups = array();
391                         if (isset($_SESSION['authenticated_user'])) {
392                                 $user = $_SESSION['authenticated_user'];
393                                 if (isset($_SESSION['authenticated_user_fullname'])) {
394                                         $fullname = $_SESSION['authenticated_user_fullname'];
395                                         $dynamic_groups = $_SESSION['dynamic_member_groups'];
396                                 } else {
397                                         $fullname = $user;
398                                         if (($auth_type === AUTH_TYPE_EXTERNAL || $auth_type === AUTH_TYPE_SAML) &&
399                                                 $ldap_user_account) {
400                                                 $ldap_user_info = ldap_get_simple_user_info($user);
401                                                 if ($ldap_user_info) {
402                                                         $fullname = $ldap_user_info['fullname'];
403                                                         $dynamic_groups = $ldap_user_info['dynamic_member_groups'];
404                                                 }
405                                         }
406                                         $_SESSION['authenticated_user_fullname'] = $fullname;
407                                         $_SESSION['dynamic_member_groups'] = $dynamic_groups;
408                                 }
409                         }
410                         $auth_user = $user;
411                         $auth_user_fullname = $fullname;
412                         $auth_dynamic_groups = $dynamic_groups;
413                         break;
414                 }
415                 case AUTH_TYPE_EXTERNAL_REMOTE_USER:
416                         $auth_user = $_SERVER['REMOTE_USER'];
417                         $auth_user_fullname = $auth_user;
418                         break;
419                 case AUTH_TYPE_EXTERNAL_X_FORWARDED_USER:
420                         $auth_user =  $_SERVER['HTTP_X_FORWARDED_USER'];
421                         $auth_user_fullname = $auth_user;
422                         break;
423                 default: // AUTH_TYPE_NONE
424                         $auth_user = '';
425                         $auth_user_fullname = '';
426                         break;
427         }
428         $auth_user_groups = get_groups_from_username($auth_user);
429         if ($auth_dynamic_groups && is_array($auth_dynamic_groups)) {
430                 $auth_user_groups = array_values(array_merge($auth_user_groups, $auth_dynamic_groups));
431         }
432         return true; // is not basic auth
433 }
434
435 /**
436  * Return group name array whose group contains the user
437  *
438  * Result array contains reserved 'valid-user' group for all authenticated user
439  * @global array $auth_groups
440  * @param string $user
441  * @return array
442  */
443 function get_groups_from_username($user)
444 {
445         global $auth_groups;
446         if ($user !== '') {
447                 $groups = array();
448                 foreach ($auth_groups as $group=>$users) {
449                         $sp = explode(',', $users);
450                         if (in_array($user, $sp)) {
451                                 $groups[] = $group;
452                         }
453                 }
454                 // Implicit group that has same name as user itself
455                 $groups[] = $user;
456                 // 'valid-user' group for
457                 $valid_user = 'valid-user';
458                 if (!in_array($valid_user, $groups)) {
459                         $groups[] = $valid_user;
460                 }
461                 return $groups;
462         }
463         return array();
464 }
465
466 /**
467  * Get authenticated user name.
468  *
469  * @global type $auth_user
470  * @return type
471  */
472 function get_auth_user()
473 {
474         global $auth_user;
475         return $auth_user;
476 }
477
478 /**
479  * Sign in with username and password
480  *
481  * @param String username
482  * @param String password
483  * @return true is sign in is OK
484  */
485 function form_auth($username, $password)
486 {
487         global $ldap_user_account, $auth_users;
488         $user = $username;
489         if ($ldap_user_account) {
490                 // LDAP account
491                 return ldap_auth($username, $password);
492         } else {
493                 // Defined users in pukiwiki.ini.php
494                 if (in_array($user, array_keys($auth_users))) {
495                         if (pkwk_hash_compute(
496                                 $password,
497                                 $auth_users[$user]) === $auth_users[$user]) {
498                                 session_start();
499                                 session_regenerate_id(true); // require: PHP5.1+
500                                 $_SESSION['authenticated_user'] = $user;
501                                 $_SESSION['authenticated_user_fullname'] = $user;
502                                 return true;
503                         }
504                 }
505         }
506         return false;
507 }
508
509 function ldap_auth($username, $password)
510 {
511         global $ldap_server, $ldap_base_dn, $ldap_bind_dn, $ldap_bind_password;
512         $ldapconn = ldap_connect($ldap_server);
513         if ($ldapconn) {
514                 ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
515                 ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
516                 if (preg_match('#\$login\b#', $ldap_bind_dn)) {
517                         // Bind by user credential
518                         $username_esc = pkwk_ldap_escape_dn($username);
519                         $bind_dn_user = preg_replace('#\$login\b#', $username_esc, $ldap_bind_dn);
520                         $ldap_bind_user = ldap_bind($ldapconn, $bind_dn_user, $password);
521                         if ($ldap_bind_user) {
522                                 $user_info = get_ldap_user_info($ldapconn, $username, $ldap_base_dn);
523                                 if ($user_info) {
524                                         $ldap_groups = get_ldap_groups_with_user($ldapconn, $username, $user_info['is_ad']);
525                                         session_regenerate_id(true); // require: PHP5.1+
526                                         $_SESSION['authenticated_user'] = $user_info['uid'];
527                                         $_SESSION['authenticated_user_fullname'] = $user_info['fullname'];
528                                         $_SESSION['dynamic_member_groups'] = $ldap_groups;
529                                         return true;
530                                 }
531                         }
532                 } else {
533                         // Bind by bind dn
534                         $ldap_bind = ldap_bind($ldapconn, $ldap_bind_dn, $ldap_bind_password);
535                         if ($ldap_bind) {
536                                 $user_info = get_ldap_user_info($ldapconn, $username, $ldap_base_dn);
537                                 if ($user_info) {
538                                         $ldap_bind_user2 = ldap_bind($ldapconn, $user_info['dn'], $password);
539                                         if ($ldap_bind_user2) {
540                                                 $ldap_groups = get_ldap_groups_with_user($ldapconn, $username, $user_info['is_ad']);
541                                                 session_regenerate_id(true); // require: PHP5.1+
542                                                 $_SESSION['authenticated_user'] = $user_info['uid'];
543                                                 $_SESSION['authenticated_user_fullname'] = $user_info['fullname'];
544                                                 $_SESSION['dynamic_member_groups'] = $ldap_groups;
545                                                 return true;
546                                         }
547                                 }
548                         }
549                 }
550         }
551         return false;
552 }
553
554 // Get LDAP user info via bind DN
555 function ldap_get_simple_user_info($username)
556 {
557         global $ldap_server, $ldap_base_dn, $ldap_bind_dn, $ldap_bind_password;
558         $ldapconn = ldap_connect($ldap_server);
559         if ($ldapconn) {
560                 ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
561                 ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
562                 // Bind by bind dn
563                 $ldap_bind = ldap_bind($ldapconn, $ldap_bind_dn, $ldap_bind_password);
564                 if ($ldap_bind) {
565                         $user_info = get_ldap_user_info($ldapconn, $username, $ldap_base_dn);
566                         if ($user_info) {
567                                 $ldap_groups = get_ldap_groups_with_user($ldapconn,
568                                         $username, $user_info['is_ad']);
569                                 $user_info['dynamic_member_groups'] = $ldap_groups;
570                                 return $user_info;
571                         }
572                 }
573         }
574         return false;
575 }
576
577 /**
578  * Search user and get 'dn', 'uid', 'fullname' and 'mail'
579  * @param type $ldapconn
580  * @param type $username
581  * @param type $base_dn
582  * @return boolean
583  */
584 function get_ldap_user_info($ldapconn, $username, $base_dn) {
585         $username_esc = pkwk_ldap_escape_filter($username);
586         $filter = "(|(uid=$username_esc)(sAMAccountName=$username_esc))";
587         $result1 = ldap_search($ldapconn, $base_dn, $filter, array('dn', 'uid', 'cn', 'samaccountname', 'displayname', 'mail'));
588         $entries = ldap_get_entries($ldapconn, $result1);
589         if (!isset($entries[0])) {
590                 return false;
591         }
592         $info = $entries[0];
593         if (isset($info['dn'])) {
594                 $user_dn = $info['dn'];
595                 $cano_username = $username;
596                 $is_active_directory = false;
597                 if (isset($info['uid'][0])) {
598                         $cano_username = $info['uid'][0];
599                 } elseif (isset($info['samaccountname'][0])) {
600                         $cano_username = $info['samaccountname'][0];
601                         $is_active_directory = true;
602                 }
603                 $cano_fullname = $username;
604                 if (isset($info['displayname'][0])) {
605                         $cano_fullname = $info['displayname'][0];
606                 } elseif (isset($info['cn'][0])) {
607                         $cano_fullname = $info['cn'][0];
608                 }
609                 return array(
610                         'dn' => $user_dn,
611                         'uid' => $cano_username,
612                         'fullname' => $cano_fullname,
613                         'mail' => $info['mail'][0],
614                         'is_ad' => $is_active_directory,
615                 );
616         }
617         return false;
618 }
619
620 /**
621  * Redirect after login. Need to assing location or page
622  *
623  * @param type $location
624  * @param type $page
625  */
626 function form_auth_redirect($location, $page)
627 {
628         header('HTTP/1.0 302 Found');
629         if ($location) {
630                 header('Location: ' . $location);
631         } else {
632                 $url = get_script_uri() . '?' . $page;
633                 header('Location: ' . $url);
634         }
635 }
636
637 /**
638  * Get External Auth log-in URL
639  */
640 function get_auth_external_login_url($page, $url_after_login) {
641         global $auth_external_login_url_base;
642         $sep = '&';
643         if (strpos($auth_external_login_url_base, '?') === FALSE) {
644                 $sep = '?';
645         }
646         $url = $auth_external_login_url_base . $sep
647                 . 'page=' . rawurlencode($page)
648                 . '&url_after_login=' . rawurlencode($url_after_login);
649         return $url;
650 }
651
652 function get_auth_user_prefix() {
653         global $ldap_user_account, $auth_type;
654         global $auth_provider_user_prefix_default;
655         global $auth_provider_user_prefix_ldap;
656         global $auth_provider_user_prefix_external;
657         global $auth_provider_user_prefix_saml;
658         $user_prefix = '';
659         switch ($auth_type) {
660                 case AUTH_TYPE_BASIC:
661                         $user_prefix = $auth_provider_user_prefix_default;
662                         break;
663                 case AUTH_TYPE_EXTERNAL:
664                 case AUTH_TYPE_EXTERNAL_REMOTE_USER:
665                 case AUTH_TYPE_EXTERNAL_X_FORWARDED_USER:
666                         $user_prefix = $auth_provider_user_prefix_external;
667                         break;
668                 case AUTH_TYPE_SAML:
669                         $user_prefix = $auth_provider_user_prefix_saml;
670                         break;
671                 case AUTH_TYPE_FORM:
672                         if ($ldap_user_account) {
673                                 $user_prefix = $auth_provider_user_prefix_ldap;
674                         } else {
675                                 $user_prefix = $auth_provider_user_prefix_default;
676                         }
677                         break;
678         }
679         return $user_prefix;
680 }
681
682 function get_ldap_related_groups() {
683         global $read_auth_pages, $edit_auth_pages;
684         global $auth_provider_user_prefix_ldap;
685         $ldap_groups = array();
686         foreach ($read_auth_pages as $pattern=>$groups) {
687                 $sp_groups = explode(',', $groups);
688                 foreach ($sp_groups as $group) {
689                         if (strpos($group, $auth_provider_user_prefix_ldap) === 0) {
690                                 $ldap_groups[] = $group;
691                         }
692                 }
693         }
694         foreach ($edit_auth_pages as $pattern=>$groups) {
695                 $sp_groups = explode(',', $groups);
696                 foreach ($sp_groups as $group) {
697                         if (strpos($group, $auth_provider_user_prefix_ldap) === 0) {
698                                 $ldap_groups[] = $group;
699                         }
700                 }
701         }
702         $ldap_groups_unique = array_values(array_unique($ldap_groups));
703         return $ldap_groups_unique;
704 }
705
706 /**
707  * Get LDAP groups user belongs to
708  *
709  * @param Resource $ldapconn
710  * @param String $user
711  * @param bool $is_ad
712  * @return Array
713  */
714 function get_ldap_groups_with_user($ldapconn, $user, $is_ad) {
715         global $auth_provider_user_prefix_ldap;
716         global $ldap_base_dn;
717         $related_groups = get_ldap_related_groups();
718         if (count($related_groups) == 0) {
719                 return array();
720         }
721         $gfilter = '(|';
722         foreach ($related_groups as $group_full) {
723                 $g = substr($group_full, strlen($auth_provider_user_prefix_ldap));
724                 $gfilter .= sprintf('(cn=%s)', pkwk_ldap_escape_filter($g));
725                 if ($is_ad) {
726                         $gfilter .= sprintf('(sAMAccountName=%s)', pkwk_ldap_escape_filter($g));
727                 }
728         }
729         $gfilter .= ')';
730         $result_g = ldap_search($ldapconn, $ldap_base_dn, $gfilter,
731                 array('dn', 'uid', 'cn', 'samaccountname'));
732         $entries = ldap_get_entries($ldapconn, $result_g);
733         if (!isset($entries[0])) {
734                 return false;
735         }
736         if (!$entries) {
737                 return array();
738         }
739         $entry_count = $entries['count'];
740         $group_list = array();
741         for ($i = 0; $i < $entry_count; $i++) {
742                 $group_name = $entries[$i]['cn'][0];
743                 if ($is_ad) {
744                         $group_name = $entries[$i]['samaccountname'][0];
745                 }
746                 $group_list[] = array(
747                         'name' => $group_name,
748                         'dn' => $entries[$i]['dn']
749                 );
750         }
751         $groups_member = array();
752         $groups_nonmember = array();
753         foreach ($group_list as $gp) {
754                 $fmt = '(&(uid=%s)(memberOf=%s))';
755                 if ($is_ad) {
756                         // LDAP_MATCHING_RULE_IN_CHAIN: Active Directory specific rule
757                         $fmt = '(&(sAMAccountName=%s)(memberOf:1.2.840.113556.1.4.1941:=%s))';
758                 }
759                 $user_gfilter = sprintf($fmt,
760                         pkwk_ldap_escape_filter($user),
761                         pkwk_ldap_escape_filter($gp['dn']));
762                 $result_e = ldap_search($ldapconn, $ldap_base_dn, $user_gfilter,
763                         array('dn'), 0, 1);
764                 $user_e = ldap_get_entries($ldapconn, $result_e);
765                 if (isset($user_e['count']) && $user_e['count'] > 0) {
766                         $groups_member[] = $gp['name'];
767                 } else {
768                         $groups_nonmember[] = $gp['name'];
769                 }
770         }
771         $groups_full = array();
772         foreach ($groups_member as $g) {
773                 $groups_full[] = $auth_provider_user_prefix_ldap . $g;
774         }
775         return $groups_full;
776 }