<?php require_once($INC . "/sql.inc.php"); require_once($INC . "/state.inc.php"); require_once($INC . "/prof.inc.php"); require_once($INC . "/ldap_core.inc.php"); /* * Get data from cache */ function rg_ldap_sync_get_cache($db, $v) { rg_log_enter('ldap_sync_get_cache'); $ret = array('ok' => 0); while (1) { $params = array('v' => $v); $sql = 'SELECT a.* FROM ldap_cache AS a, ldap_servers AS b' . ' WHERE a.server_id = b.id' . ' AND (mail = @@v@@' . ' OR ldap_uid = @@v@@' . ' OR cn = @@v@@)' . ' ORDER BY b.prio'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { $ret['errmsg'] = 'cannot select from cache'; break; } $ret['list'] = array(); while (($row = rg_sql_fetch_array($res))) $ret['list'][] = $row; rg_sql_free_result($res); $ret['ok'] = 1; break; } rg_log_exit(); return $ret; } /* * Updates ldap_cache table * TODO: should be moved to ldap.inc.php because is called from there? * And get rid of "_sync_" in name. */ function rg_ldap_sync_update_cache($db, $l) { rg_log_enter('ldap_sync_update_cache'); rg_log_ml('DEBUG: l: ' . print_r($l, TRUE)); $ret = array('ok' => 0); while (1) { // We update uid only if != 0 $add = ''; if ($l['uid'] > 0) $add .= ', uid = @@uid@@'; $sql = 'UPDATE ldap_cache SET' . ' ldap_uid = @@ldap_uid@@' . $add . ', password = @@password@@' . ', sn = @@sn@@' . ', gn = @@gn@@' . ', gid = @@gid@@' . ', mail = @@mail@@' . ', cn = @@cn@@' . ', shadow_expire = @@shadow_expire@@' . ' WHERE server_id = @@server_id@@' . ' AND uuid = @@uuid@@'; $res = rg_sql_query_params($db, $sql, $l); if ($res === FALSE) { $ret['errmsg'] = 'error in update'; break; } $arows = rg_sql_affected_rows($res); rg_sql_free_result($res); if ($arows > 0) { $ret['ok'] = 1; break; } $sql = 'INSERT INTO ldap_cache (uid, ldap_uid, password' . ', sn, gn, gid, mail, cn, shadow_expire' . ', server_id, uuid)' . ' VALUES (@@uid@@, @@ldap_uid@@, @@password@@' . ', @@sn@@, @@gn@@, @@gid@@, @@mail@@' . ', @@cn@@, @@shadow_expire@@, @@server_id@@' . ', @@uuid@@)'; $ignore = array(RG_SQL_UNIQUE_VIOLATION); $res = rg_sql_query_params_ignore($db, $sql, $l, $ignore, $ignore_kicked); if ($res === FALSE) { if (!$ignore_kicked) { $ret['errmsg'] = 'error in update'; break; } rg_log('Entry already in cache.'); } else { rg_sql_free_result($res); } $ret['ok'] = 1; break; } rg_log_exit(); return $ret; } /* * Updates ldap_servers table - for now, CSNs * TODO: It should be used to set also the last error message? * Maybe we should have a different log for this kind of problems, * and send it to the admin. */ function rg_ldap_sync_update_server($db, $server_id, $csn) { rg_log_enter('ldap_sync_update_server'); $ret = array('ok' => 0); while (1) { if (empty($csn)) { $ret['ok'] = 1; break; } $params = array( 'server_id' => $server_id, 'csn' => $csn ); $sql = 'UPDATE ldap_servers SET' . ' csn = @@csn@@' . ' WHERE id = @@server_id@@'; $res = rg_sql_query_params($db, $sql, $params); if ($res === FALSE) { $ret['errmsg'] = 'error in update'; break; } rg_sql_free_result($res); $ret['ok'] = 1; break; } rg_log_exit(); return $ret; } /* * It applies the updates to the database. * Input is a ldif parsed as an array (from rg_ldap_core_ldif2array) */ function rg_ldap_sync_apply($db, $server_id, $data) { rg_log_enter('ldap_sync_apply'); $ret = array(); $ret['ok'] = 0; while (1) { rg_log_ml('data: ' . print_r($data, TRUE)); if (rg_sql_begin($db) !== TRUE) { $ret['errmsg'] = 'cannot start transaction'; break; } $rollback = TRUE; $csn = array(); foreach ($data as $block_id => $block) { if (isset($block['entryCSN'][0])) { rg_log('DEBUG: entryCSN is present!'); $c = $block['entryCSN'][0]; // Example: 20171001075924.155248Z#000000#001#000000 $_t = explode('#', $c); $_s = isset($_t[2]) ? $_t[2] : ''; if (!isset($csn[$_s]) || ($c > $csn[$_s])) $csn[$_s] = $c; } else { rg_log('DEBUG: entryCSN is NOT present!'); } if (!isset($block['objectClass'])) { $ret['errmsg'] = 'missing objectClass'; break; } // Try to detect if is a user entry $is_user = FALSE; foreach ($block['objectClass'] as $oc) { if (strcasecmp($oc, 'inetOrgPerson') == 0) { $is_user = TRUE; break; } } if (!$is_user) continue; $l = array(); $l['uid'] = 0; $l['ldap_uid'] = isset($block['uid'][0]) ? $block['uid'][0] : ''; $l['password'] = isset($block['userPassword'][0]) ? $block['userPassword'][0] : ''; $l['sn'] = isset($block['sn'][0]) ? $block['sn'][0] : ''; $l['gn'] = isset($block['givenName'][0]) ? $block['givenName'][0] : ''; $l['gid'] = isset($block['gidNumber'][0]) ? $block['gidNumber'][0] : 0; $l['mail'] = isset($block['mail'][0]) ? $block['mail'][0] : ''; $l['cn'] = isset($block['cn'][0]) ? $block['cn'][0] : ''; $l['shadow_expire'] = isset($block['shadowExpire'][0]) ? $block['shadowExpire'][0] : '99999'; $l['server_id'] = $server_id; $l['uuid'] = isset($block['entryUUID'][0]) ? $block['entryUUID'][0] : ''; // Verifications if (empty($l['ldap_uid'])) { rg_log('DEBUG: ldap_uid is not present!'); continue; } if (empty($l['uuid'])) { rg_log('DEBUG: entryUUID is not present!'); continue; } rg_log_ml('DEBUG: l:' . print_r($l, TRUE)); $r = rg_ldap_sync_update_cache($db, $l); if ($r['ok'] !== 1) { $r['errmsg'] = $r['errmsg']; break; } /* TODO foreach ($block['memberOf'] as $m) $pairs_groups[] = array($uuid, $m); */ } if (isset($ret['errmsg'])) break; rg_log_ml('DEBUG: csn: ' . print_r($csn, TRUE)); $r = rg_ldap_sync_update_server($db, $server_id, implode(';', $csn)); if ($r['ok'] !== 1) { $ret['errmsg'] = $r['errmsg']; break; } if (rg_sql_commit($db) !== TRUE) { $ret['errmsg'] = 'cannot commit'; break; } $ret['ok'] = 1; $rollback = FALSE; break; } if ($rollback) rg_sql_rollback($db); rg_log_exit(); return $ret; } /* * Function called to process raw ldif blocks -> database */ function rg_ldap_sync_data($db, $server_id, $s) { rg_log_enter('ldap_sync_data'); rg_log('DEBUG: s=' . $s); $ret = array(); $ret['ok'] = 0; while (1) { $a = rg_ldap_core_ldif2array($s); if ($a['ok'] != 1) { $ret['errmsg'] = $a['errmsg']; break; } // Update database if (!empty($a['data'])) { $r = rg_ldap_sync_apply($db, $server_id, $a['data']); if ($r['ok'] != 1) { $ret['errmsg'] = $r['errmsg']; break; } } $ret['ok'] = 1; $ret['used'] = $a['used']; break; } rg_log_exit(); return $ret; } /* * 'input' callback for rg_ldap_sync_* functions */ function rg_ldap_sync_cb_input($index, &$info, $stream) { rg_log_enter('ldap_sync_cb_input: index=' . $index . ' stream=' . $stream); while (1) { //rg_log_ml('info: ' . print_r($info, TRUE)); $c = &$info['custom']; switch ($stream) { case 1: //rg_log('Got data: ' . $info['in_buf']); $r = rg_ldap_sync_data($c['db'], $c['server_id'], $info['in_buf']); if ($r['ok'] != 1) { rg_log('Error: ' . $r['errmsg']); $info['done'] = TRUE; break; } $info['in_buf'] = substr($info['in_buf'], $r['used']); break; case 2: // TODO: should we do something with this? // Maybe store it in the synchronization log? // Send it do admin? rg_log_ml('error received: ' . $info['err_buf']); $info['err_buf'] = ''; break; } break; } rg_log_exit(); } /* * 'error' calback for rg_ldap_sync_* functions */ function rg_ldap_sync_cb_error($index, &$info, $msg) { rg_log('ldap_sync_cb_error: ' . $msg); $info['done'] = TRUE; } /* * Sync function using SyncRepl protocol (ro way) */ function rg_ldap_sync_ro($db, $data) { // TODO: move this in the higher level part // Load the list of servers in decreasing priority order (increasing numeric) // because we want the best one to overwrite the lower priority ones. // Hm! Have a problem: sync_rp with multiple servers! $ret = array(); $ret['ok'] = 0; while (1) { // Store password $r = rg_id(16); // TODO: choose a better name; choose a better dir $pass_file = '/tmp/' . $r; $f = @fopen($pass_file, 'w'); if ($f === FALSE) { $ret['errmsg'] = 'cannot create pass file'; break; } if (@chmod($pass_file, 0600) !== TRUE) { fclose($f); $ret['errmsg'] = 'cannot chmod pass'; break; } $r = @fwrite($f, $data['bind_pass']); if ($r === FALSE) { fclose($f); $ret['errmsg'] = 'cannot write pass'; break; } fclose($f); $cmd = 'ldapsearch -d-1 -LLL -v -b ' . escapeshellarg($data['base']) . ' -x -H ldap://' . escapeshellarg($data['addr']) . ':' . escapeshellarg($data['port']) . ' -D ' . escapeshellarg($data['bind_user']) . ' -y ' . escapeshellarg($pass_file) . ' -s sub -P 3'; if (isset($data['rid']) && isset($data['csn'])) { $cmd .= ' -E ' . escapeshellarg('sync=ro/' . 'rid=' . $data['rid'] . ',sid=001' . ',csn=' . $data['csn']); } else { $cmd .= ' -E sync=ro/rid=001,sid=001,csn=aaa'; } if (isset($data['fields'])) { $fields = ''; $add = ''; foreach($data['fields'] as $f) { $fields .= $add . $f; $add = ','; } } else { $fields = '*'; } $cmd .= ' "(|' . '(objectClass=groupOfNames)' . '(objectClass=groupOfUniqueNames)' . '(structuralObjectClass=inetOrgPerson)' . ')"'; $cmd .= ' ' . escapeshellarg($fields) . ' +'; $a = array( 'cmds' => array( 'cmd1' => array( 'cmd' => $cmd, 'cb_input' => 'rg_ldap_sync_cb_input', 'cb_error' => 'rg_ldap_sync_cb_error', 'custom' => array( 'db' => $db, 'server_id' => $data['server_id'] ) ) ) ); $r = rg_exec2($a); @unlink($pass_file); if ($r['ok'] != 1) { $ret['errmsg'] = $r['errmsg']; break; } $ret['ok'] = 1; break; } return $ret; } ?>