OpenLDAP_Driver.php 79.5 KB
Newer Older
1 2 3 4 5
<?php

/**
 * Samba OpenLDAP driver class.
 *
6 7 8
 * @category   apps
 * @package    samba
 * @subpackage libraries
9
 * @author     ClearFoundation <[email protected]>
10
 * @copyright  2003-2014 ClearFoundation
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
 * @license    http://www.gnu.org/copyleft/lgpl.html GNU Lesser General Public License version 3 or later
 * @link       http://www.clearfoundation.com/docs/developer/apps/samba/
 */

///////////////////////////////////////////////////////////////////////////////
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// N A M E S P A C E
///////////////////////////////////////////////////////////////////////////////

namespace clearos\apps\samba;

///////////////////////////////////////////////////////////////////////////////
// B O O T S T R A P
///////////////////////////////////////////////////////////////////////////////

$bootstrap = getenv('CLEAROS_BOOTSTRAP') ? getenv('CLEAROS_BOOTSTRAP') : '/usr/clearos/framework/shared';
require_once $bootstrap . '/bootstrap.php';

///////////////////////////////////////////////////////////////////////////////
// T R A N S L A T I O N S
///////////////////////////////////////////////////////////////////////////////

clearos_load_language('samba');
50
clearos_load_language('samba_common');
51 52 53 54 55

///////////////////////////////////////////////////////////////////////////////
// D E P E N D E N C I E S
///////////////////////////////////////////////////////////////////////////////

56 57 58
// Factories
//----------

59 60
use \clearos\apps\groups\Group_Factory as Group;
use \clearos\apps\groups\Group_Manager_Factory as Group_Manager;
61 62 63
use \clearos\apps\users\User_Factory as User;
use \clearos\apps\users\User_Manager_Factory as User_Manager;

64 65
clearos_load_library('groups/Group_Factory');
clearos_load_library('groups/Group_Manager_Factory');
66 67 68
clearos_load_library('users/User_Factory');
clearos_load_library('users/User_Manager_Factory');

69 70 71
// Classes
//--------

72
use \clearos\apps\accounts\Accounts_Configuration as Accounts_Configuration;
73 74 75 76
use \clearos\apps\base\Engine as Engine;
use \clearos\apps\base\File as File;
use \clearos\apps\base\Folder as Folder;
use \clearos\apps\base\Shell as Shell;
77
use \clearos\apps\groups\Group_Engine as Group_Engine;
78
use \clearos\apps\ldap\LDAP_Utilities as LDAP_Utilities;
79 80 81
use \clearos\apps\ldap\Nslcd as Nslcd;
use \clearos\apps\mode\Mode_Engine as Mode_Engine;
use \clearos\apps\mode\Mode_Factory as Mode_Factory;
82
use \clearos\apps\openldap\LDAP_Driver as LDAP_Driver;
83
use \clearos\apps\openldap_directory\Accounts_Driver as Accounts_Driver;
84 85 86 87
use \clearos\apps\openldap_directory\Group_Driver as Group_Driver;
use \clearos\apps\openldap_directory\Group_Manager_Driver as Group_Manager_Driver;
use \clearos\apps\openldap_directory\OpenLDAP as OpenLDAP;
use \clearos\apps\openldap_directory\User_Manager_Driver as User_Manager_Driver;
88
use \clearos\apps\samba\OpenLDAP_Driver_Not_Initialized as OpenLDAP_Driver_Not_Initialized;
89
use \clearos\apps\samba_common\Nmbd as Nmbd;
90
use \clearos\apps\samba_common\Samba as Samba;
91 92
use \clearos\apps\samba_common\Smbd as Smbd;
use \clearos\apps\samba_common\Winbind as Winbind;
93
use \clearos\apps\users\User_Engine as User_Engine;
94
use \clearos\apps\users\User_Factory as User_Factory;
95

96
clearos_load_library('accounts/Accounts_Configuration');
97 98 99 100
clearos_load_library('base/Engine');
clearos_load_library('base/File');
clearos_load_library('base/Folder');
clearos_load_library('base/Shell');
101
clearos_load_library('groups/Group_Engine');
102
clearos_load_library('ldap/LDAP_Utilities');
103 104 105
clearos_load_library('ldap/Nslcd');
clearos_load_library('mode/Mode_Engine');
clearos_load_library('mode/Mode_Factory');
106
clearos_load_library('openldap/LDAP_Driver');
107
clearos_load_library('openldap_directory/Accounts_Driver');
108 109 110 111
clearos_load_library('openldap_directory/Group_Driver');
clearos_load_library('openldap_directory/Group_Manager_Driver');
clearos_load_library('openldap_directory/OpenLDAP');
clearos_load_library('openldap_directory/User_Manager_Driver');
112
clearos_load_library('samba/OpenLDAP_Driver_Not_Initialized');
113
clearos_load_library('samba_common/Nmbd');
114
clearos_load_library('samba_common/Samba');
115 116
clearos_load_library('samba_common/Smbd');
clearos_load_library('samba_common/Winbind');
117
clearos_load_library('users/User_Engine');
118
clearos_load_library('users/User_Factory');
119 120 121 122

// Exceptions
//-----------

123
use \Exception as Exception;
124
use \clearos\apps\accounts\Accounts_Driver_Not_Set_Exception as Accounts_Driver_Not_Set_Exception;
125 126
use \clearos\apps\base\Engine_Exception as Engine_Exception;
use \clearos\apps\base\Validation_Exception as Validation_Exception;
127
use \clearos\apps\samba_common\Samba_Not_Initialized_Exception as Samba_Not_Initialized_Exception;
128

129
clearos_load_library('accounts/Accounts_Driver_Not_Set_Exception');
130 131
clearos_load_library('base/Engine_Exception');
clearos_load_library('base/Validation_Exception');
132
clearos_load_library('samba_common/Samba_Not_Initialized_Exception');
133 134 135 136 137 138 139 140

///////////////////////////////////////////////////////////////////////////////
// C L A S S
///////////////////////////////////////////////////////////////////////////////

/**
 * Samba OpenLDAP driver class.
 *
141 142 143
 * @category   apps
 * @package    samba
 * @subpackage libraries
144
 * @author     ClearFoundation <[email protected]>
145
 * @copyright  2003-2014 ClearFoundation
146 147 148 149 150 151
 * @license    http://www.gnu.org/copyleft/lgpl.html GNU Lesser General Public License version 3 or later
 * @link       http://www.clearfoundation.com/docs/developer/apps/samba/
 */

class OpenLDAP_Driver extends Engine
{
152 153 154 155
    ///////////////////////////////////////////////////////////////////////////////
    // C O N S T A N T S
    ///////////////////////////////////////////////////////////////////////////////

156
    const FILE_INITIALIZED = '/var/clearos/samba/initialized_openldap';
157 158 159 160
    const FILE_INITIALIZING = '/var/clearos/samba/lock/initializing';

    const COMMAND_NET = '/usr/bin/net';
    const COMMAND_SMBPASSWD = '/usr/bin/smbpasswd';
161 162
    const COMMAND_SAMBA_OPENLDAP_INITIALIZE = '/usr/sbin/app-samba-openldap-initialize';
    const COMMAND_SAMBA_INITIALIZE = '/usr/sbin/app-samba-initialize';
163

164 165
    const CACHE_FLAG_TIME = 60; // in seconds

166 167 168 169 170 171 172
    const STATUS_SAMBA_INITIALIZED = 'samba_initialized';
    const STATUS_SAMBA_INITIALIZING = 'samba_initializing';
    const STATUS_OPENLDAP_UNINITIALIZED = 'uninitialized';
    const STATUS_OPENLDAP_INITIALIZED = 'initialized';
    const STATUS_OPENLDAP_INITIALIZING = 'initializing';
    const STATUS_OPENLDAP_BLOCKED_SLAVE = 'blocked_slave';

173 174 175 176 177 178 179 180
    const CONSTANT_WINADMIN_USERNAME = 'winadmin';
    const CONSTANT_GUEST_CN = 'Guest Account';
    const CONSTANT_GUEST_USERNAME = 'guest';
    const CONSTANT_WINADMIN_CN = 'Windows Administrator';
    const CONSTANT_GID_DOMAIN_COMPUTERS = '1000515';

    const DEFAULT_ADMIN_PRIVS = 'SeMachineAccountPrivilege SePrintOperatorPrivilege SeAddUsersPrivilege SeDiskOperatorPrivilege SeMachineAccountPrivilege SeTakeOwnershipPrivilege';

181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
    ///////////////////////////////////////////////////////////////////////////////
    // V A R I A B L E S
    ///////////////////////////////////////////////////////////////////////////////

    protected $ldaph = NULL;

    ///////////////////////////////////////////////////////////////////////////////
    // M E T H O D S
    ///////////////////////////////////////////////////////////////////////////////

    /**
     * OpenLDAP accounts constructor.
     *
     * @return void
     */

    public function __construct()
    {
        clearos_profile(__METHOD__, __LINE__);
    }

    /**
     * Adds a computer.
     *
Peter Baldwin's avatar
Peter Baldwin committed
205 206 207 208 209
     * The required $ sign will be appended if not present.
     *
     * Note: this method does not add the Samba attributes since
     * this is done by Samba when adding a machine.
     *
210 211 212 213 214 215 216 217 218 219
     * @param string $name computer name
     *
     * @return void
     * @throws Validation_Exception, Engine_Exception
     */

    public function add_computer($name)
    {
        clearos_profile(__METHOD__, __LINE__);

220 221 222 223
        // Append dollar sign and uppercase
        if (!preg_match('/\$$/', $name))
            $name = $name . '$';

224 225 226 227 228 229 230 231 232 233 234
        // $name = strtolower($name);
        $name = strtoupper($name);

        Validation_Exception::is_valid($this->validate_computer($name));

        // If computer exists, delete it
        try {
            $this->delete_computer($name);
        } catch (Engine_Exception $e) {
            // Not fatal
        }
235 236 237 238

        if ($this->ldaph == NULL)
            $this->_get_ldap_handle();

239 240 241
        $samba = new Samba();
        $domain_sid = $samba->get_domain_sid();

242 243 244 245 246
        $accounts = new Accounts_Driver();

        $ldap_object['objectClass'] = array(
            'top',
            'account',
Peter Baldwin's avatar
Peter Baldwin committed
247
            'posixAccount',
248
            'sambaSamAccount'
249 250 251 252
        );

        $ldap_object['cn'] = $name;
        $ldap_object['uid'] = $name;
253
        $ldap_object['description'] = lang('samba_common_computer') . ' ' . preg_replace('/\$$/', '', $name);
254
        $ldap_object['uidNumber'] = $accounts->get_next_uid_number();
255
        $ldap_object['gidNumber'] = self::CONSTANT_GID_DOMAIN_COMPUTERS;
Peter Baldwin's avatar
Peter Baldwin committed
256
        $ldap_object['homeDirectory'] = '/dev/null';
257
        $ldap_object['loginShell'] = '/sbin/nologin';
258
        $ldap_object['sambaSID'] = $domain_sid . '-' . $ldap_object['uidNumber'] ;
259
        $ldap_object['sambaAcctFlags'] = '[W          ]';
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279

        $dn = 'cn=' . $this->ldaph->dn_escape($name) . ',' . OpenLDAP::get_computers_container();

        if (! $this->ldaph->exists($dn))
            $this->ldaph->add($dn, $ldap_object);
    }

    /**
     * Deletes a computer from the domain.
     *
     * @param string $name computer name
     *
     * @return void
     * @throws Validation_Exception, Engine_Exception
     */

    public function delete_computer($name)
    {
        clearos_profile(__METHOD__, __LINE__);

280 281 282 283 284 285
        // Append dollar sign and uppercase
        if (!preg_match('/\$$/', $name))
            $name = $name . '$';

        $name = strtolower($name);

286 287 288
        if ($this->ldaph === NULL)
            $this->_get_ldap_handle();

289
        if (! $this->is_initialized())
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
            throw new Samba_Not_Initialized_Exception();

        $dn = 'cn=' . $this->ldaph->dn_escape($name) . ',' . OpenLDAP::get_computers_container();

        if ($this->ldaph->exists($dn))
            $this->ldaph->delete($dn);
    }

    /**
     * Returns bind password. 
     *
     * @return string bind password
     * @throws Engine_Exception, Samba_Not_Initialized_Exception
     */

    public function get_bind_password()
    {
        clearos_profile(__METHOD__, __LINE__);

309
        $ldap = new LDAP_Driver();
310

311
        return $ldap->get_bind_password();
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
    }

    /**
     * Gets a detailed list of computers for the domain.
     *
     * @return  array  detailed list of computers
     * @throws Engine_Exception
     */

    public function get_computers()
    {
        clearos_profile(__METHOD__, __LINE__);

        if ($this->ldaph === NULL)
            $this->_get_ldap_handle();

328
        if (! $this->is_initialized())
329 330 331 332 333 334 335 336 337 338 339 340
            throw new Samba_Not_Initialized_Exception();

        $computers = array();

        // TODO: the "AddMachine" method does not add the Samba attributes since
        // this is done automagically by Samba.  If this automagic is missed for
        // some reason, then a Computer object may not have the sambaSamAccount object.
        // To be safe, use the posixAccount object so that we can cleanup.

        $result = $this->ldaph->search(
            '(objectclass=posixAccount)',
            OpenLDAP::get_computers_container(),
341
            array('uid', 'sambaSID', 'uidNumber')
342 343 344 345 346 347 348
        );

        $entry = $this->ldaph->get_first_entry($result);

        while ($entry) {
            $attributes = $this->ldaph->get_attributes($entry);

349
            $computer = $attributes['uid']['0'];
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
            $computers[$computer]['SID'] = isset($attributes['sambaSID'][0]) ? $attributes['sambaSID'][0] : "";
            $computers[$computer]['uidNumber'] = $attributes['uidNumber'][0];

            $entry = $this->ldaph->next_entry($entry);
        }
        
        return $computers;
    }   

    /**
     * Returns domain SID.
     *
     * @return string domain SID
     * @throws Engine_Exception, Samba_Not_Initialized_Exception
     */

    public function get_domain_sid()
    {
        clearos_profile(__METHOD__, __LINE__);

        if ($this->ldaph === NULL)
            $this->_get_ldap_handle();

373
        if (! $this->is_initialized())
374 375 376 377 378 379 380 381 382 383 384 385 386 387
            throw new Samba_Not_Initialized_Exception();

        $result = $this->ldaph->search(
            "(objectclass=sambaDomain)",
            OpenLDAP::get_base_dn(),
            array("sambaDomainName", "sambaSID")
        );

        $entry = $this->ldaph->get_first_entry($result);

        if ($entry) {
            $attributes = $this->ldaph->get_attributes($entry);
            $sid = $attributes['sambaSID'][0];
        } else {
388
            throw new Engine_Exception(lang('samba_common_domain_name_missing_in_ldap'));
389 390 391 392 393
        }

        return $sid;
    }

394 395 396 397 398 399 400 401 402 403 404 405 406 407
    /**
     * Returns domain.
     *
     * @return string domain
     * @throws Engine_Exception, Samba_Not_Initialized_Exception
     */

    public function get_domain()
    {
        clearos_profile(__METHOD__, __LINE__);

        if ($this->ldaph === NULL)
            $this->_get_ldap_handle();

408 409 410 411
        $sysmode = Mode_Factory::create();
        $mode = $sysmode->get_mode();

        if (($mode !== Mode_Engine::MODE_SLAVE) && !$this->is_initialized())
412 413 414 415 416 417 418 419 420 421 422 423 424 425
            throw new Samba_Not_Initialized_Exception();

        $result = $this->ldaph->search(
            "(objectclass=sambaDomain)",
            OpenLDAP::get_base_dn(),
            array("sambaDomainName", "sambaSID")
        );

        $entry = $this->ldaph->get_first_entry($result);

        if ($entry) {
            $attributes = $this->ldaph->get_attributes($entry);
            $domain = $attributes['sambaDomainName'][0];
        } else {
426
            throw new Engine_Exception(lang('samba_common_domain_name_missing_in_ldap'));
427 428 429 430 431
        }

        return $domain;
    }

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
    /**
     * Returns status of Samba initialization.
     *
     * @return string status
     * @throws Engine_Exception
     */

    public function get_status()
    {
        clearos_profile(__METHOD__, __LINE__);

        // Samba initialized
        //------------------

        $file = new File(Samba::FILE_INITIALIZED);

        if ($file->exists())
            return self::STATUS_SAMBA_INITIALIZED;

        // Samba initializing
        //-------------------

        $file = new File(Samba::FILE_INITIALIZING);
        $initializing_lock = fopen(Samba::FILE_INITIALIZING, 'r');

        if ($file->exists() && !flock($initializing_lock, LOCK_SH | LOCK_NB))
            return self::STATUS_SAMBA_INITIALIZING;

        // Samba OpenLDAP initialized
        //---------------------------

        $file = new File(self::FILE_INITIALIZED);

        if ($file->exists())
            return self::STATUS_OPENLDAP_INITIALIZED;

        // Samba OpenLDAP initializing
        //----------------------------

        $file = new File(self::FILE_INITIALIZING);
        $initializing_lock = fopen(self::FILE_INITIALIZING, 'r');

        if ($file->exists() && !flock($initializing_lock, LOCK_SH | LOCK_NB))
            return self::STATUS_OPENLDAP_INITIALIZING;

        // Slave waiting on master initialization
        //---------------------------------------

        $sysmode = Mode_Factory::create();
        $mode = $sysmode->get_mode();

483
        if (($mode === Mode_Engine::MODE_SLAVE) && !$this->is_ready())
484 485 486 487 488 489 490 491
            return self::STATUS_OPENLDAP_BLOCKED_SLAVE;

        // Samba OpenLDAP uninitialized
        //-----------------------------

        return self::STATUS_OPENLDAP_UNINITIALIZED;
    }

492 493 494
    /**
     * Initializes the local Samba system environment.
     *
495 496 497 498
     * @param string  $netbios_  name netbios_name
     * @param string  $workgroup workgroup/Windows domain
     * @param string  $password  password for winadmin
     * @param boolean $force     force initialization
499 500 501 502 503
     *
     * @return void
     * @throws Engine_Exception, Samba_Not_Initialized_Exception
     */

504
    public function initialize_samba_as_master_or_standalone($netbios, $workgroup, $password, $force = FALSE)
505 506 507
    {
        clearos_profile(__METHOD__, __LINE__);

508 509 510
        // Note: an empty password is passed by the ClearOS 5.x to 6 import tool.
        // There's no need to run the actions that require a password on an import.

511 512 513
        // Directory needs to be initialized
        //----------------------------------

514 515
        if (! $this->is_initialized())
            throw new OpenLDAP_Driver_Not_Initialized();
516

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
        // Handle force flag
        //------------------

        $samba = new Samba();

        if ($samba->is_initialized()) {
            if ($force) {
                clearos_log('samba', 'forcing Samba initialization');
                $samba->set_initialized(FALSE);
            } else {
                return;
            }
        }

        clearos_log('samba', 'initializing Samba system');
532 533 534 535

        // Lock state file
        //----------------

536 537
        $lock_file = new File(Samba::FILE_INITIALIZING);
        $initializing_lock = fopen(Samba::FILE_INITIALIZING, 'w');
538 539

        if (!flock($initializing_lock, LOCK_EX | LOCK_NB)) {
540
            clearos_log('samba', 'Samba initialization is already running');
541 542 543 544 545 546
            return;
        }

        // Set the winadmin password
        //--------------------------

547 548
        if (! empty($password)) {
            clearos_log('samba', 'setting winadmin password');
549

550 551 552 553 554 555 556
            try {
                $user = User_Factory::create(self::CONSTANT_WINADMIN_USERNAME);
                $user->reset_password($password, $password, self::CONSTANT_WINADMIN_USERNAME);
            } catch (Exception $e) {
                $this->_delete_lock($initializing_lock, Samba::FILE_INITIALIZING);
                throw new Exception($e);
            }
557
        }
558 559 560 561 562 563

        // Set default mode
        //-----------------

        clearos_log('samba', "setting netbios name and workgroup: $netbios - $workgroup");

564 565 566
        try {
            $sysmode = Mode_Factory::create();
            $mode = $sysmode->get_mode();
567

568 569 570 571 572 573 574 575 576 577 578 579 580 581
            $samba = new Samba();
            $samba->set_netbios_name($netbios);
            $samba->set_workgroup($workgroup);

            if (($mode === Mode_Engine::MODE_MASTER) || ($mode === Mode_Engine::MODE_STANDALONE)) {
                clearos_log('samba', 'setting mode to PDC');
                $samba->set_mode(Samba::MODE_PDC);
            } else if ($mode === Mode_Engine::MODE_SLAVE) {
                clearos_log('samba', 'setting mode to BDC');
                $samba->set_mode(Samba::MODE_BDC);
            }
        } catch (Exception $e) {
            $this->_delete_lock($initializing_lock, Samba::FILE_INITIALIZING);
            throw new Exception($e);
582 583 584 585 586 587 588
        }

        // Save the LDAP an Idmap passwords
        //---------------------------------

        clearos_log('samba', 'storing LDAP credentials');

589 590 591 592 593 594 595
        try {
            $this->_save_bind_password();
            $this->_save_idmap_password();
        } catch (Exception $e) {
            $this->_delete_lock($initializing_lock, Samba::FILE_INITIALIZING);
            throw new Exception($e);
        }
596 597 598 599 600 601 602 603 604 605 606 607

        // Net calls for privs an joining system to domain
        // Note: Samba needs to be running for the next steps
        //---------------------------------------------------

        clearos_log('samba', 'starting Samba services for net calls');

        $nmbd = new Nmbd();
        $smbd = new Smbd();
        $winbind = new Winbind();
        $nslcd = new Nslcd();

608 609 610 611 612 613 614 615
        try {
            // Do a hard reset on Nslcd (brutal)
            if ($nslcd->is_installed()) {
                if ($nslcd->get_running_state())
                    $nslcd->restart();
                else
                    $nslcd->set_running_state(TRUE);
            }
616

617 618 619 620 621
            // Do a hard reset on Winbind
            if ($winbind->get_running_state())
                $winbind->restart();
            else
                $winbind->set_running_state(TRUE);
622

623 624 625 626 627
            $nmbd->set_running_state(TRUE);
        } catch (Exception $e) {
            $this->_delete_lock($initializing_lock, Samba::FILE_INITIALIZING);
            throw new Exception($e);
        }
628 629 630 631 632 633 634 635 636 637 638 639 640 641

        try {
            $smbd->set_running_state(TRUE);
        } catch (Exception $e) {
            // Not the end of the world, can fail if LDAP is not quite ready
        }

        $nmbd->set_boot_state(TRUE);
        $smbd->set_boot_state(TRUE);
        $winbind->set_boot_state(TRUE);

        // Grant default privileges to winadmin et al
        //-------------------------------------------

642 643
        if (! empty($password)) {
            clearos_log('samba', 'granting privileges for domain_users');
644

645 646 647 648 649 650
            try {
                $this->_net_grant_default_privileges($password);
            } catch (Exception $e) {
                $this->_delete_lock($initializing_lock, Samba::FILE_INITIALIZING);
                throw new Exception($e);
            }
651
        }
652 653 654 655

        // Join the local system to itself
        //--------------------------------

656 657
        if (! empty($password)) {
            clearos_log('samba', 'joining system to the domain');
658

659 660 661 662 663 664
            try {
                $this->_net_rpc_join($password);
            } catch (Exception $e) {
                $this->_delete_lock($initializing_lock, Samba::FILE_INITIALIZING);
                throw new Exception($e);
            }
665
        }
666 667 668 669

        // Update local file permissions
        //------------------------------

670 671 672 673 674 675
        try {
            $samba->update_local_file_permissions();
        } catch (Exception $e) {
            $this->_delete_lock($initializing_lock, Samba::FILE_INITIALIZING);
            throw new Exception($e);
        }
676 677 678 679 680 681

        // Set the local system initialized flag
        //--------------------------------------

        clearos_log('samba', 'finished local initialization');

682 683 684 685 686 687
        try {
            $samba->set_initialized(TRUE);
        } catch (Exception $e) {
            $this->_delete_lock($initializing_lock, Samba::FILE_INITIALIZING);
            throw new Exception($e);
        }
688 689 690 691 692

        // Cleanup LDAP
        //-------------

        try {
693 694
            $samba_mode = $samba->get_mode();
            if (($samba_mode === Samba::MODE_PDC) || ($samba_mode === Samba::MODE_SIMPLE_SERVER)) {
695
                $this->cleanup_entries();
696 697
                $this->cleanup_sids();
            }
698 699 700 701
        } catch (Exception $e) {
            // Not fatal
        }

702 703
        // Do a hard reset on Winbind and smb... again
        //---------------------------------------------
704 705 706 707 708 709 710 711 712 713

        try {
            if ($winbind->get_running_state())
                $winbind->restart();
            else
                $winbind->set_running_state(TRUE);
        } catch (Exception $e) {
            // Not fatal
        }

714 715 716 717 718 719 720 721 722
        try {
            if ($smbd->get_running_state())
                $smbd->restart();
            else
                $smbd->set_running_state(TRUE);
        } catch (Exception $e) {
            // Not fatal
        }

723 724 725
        // Cleanup file / file lock
        //-------------------------

726
        $this->_delete_lock($initializing_lock, Samba::FILE_INITIALIZING);
727 728 729 730 731
    }

    /**
     * Initializes the local Samba system environment on a slave.
     *
732 733 734
     * @param string  $netbios_ name netbios_name
     * @param string  $password password for winadmin
     * @param boolean $force    force initialization
735 736 737 738 739
     *
     * @return void
     * @throws Engine_Exception, Samba_Not_Initialized_Exception
     */

740
    public function initialize_samba_as_slave($netbios, $password, $force = FALSE)
741 742 743 744 745 746
    {
        clearos_profile(__METHOD__, __LINE__);

        // Run initialization again, but with Netbios name and password to join domain
        //----------------------------------------------------------------------------

747
        $this->_initialize_slave_system($netbios, $password);
748 749 750 751 752 753 754 755 756 757

        // Update local file permissions
        //------------------------------

        $samba = new Samba();
        $samba->update_local_file_permissions();

        // Set the local system initialized flag
        //--------------------------------------

758
        $samba->set_initialized(TRUE);
759 760 761 762 763 764 765 766 767 768 769
    }

    /**
     * Initializes system using sane defaults.
     *
     * @param boolean $force    force initialization
     *
     * @return void
     * @throws Engine_Exception
     */

770
    public function initialize($force = FALSE, $domain = 'CLEARSYSTEM')
771 772 773 774 775 776
    {
        clearos_profile(__METHOD__, __LINE__);

        // Bail if initialized
        //--------------------

777 778 779 780
        if ($this->is_initialized()) {
            if ($force) {
                clearos_log('samba', 'forcing initialization');
                $this->_set_initialized(FALSE);
781

782 783 784 785 786 787
                $samba = new Samba();
                $samba->set_initialized(FALSE);
            } else {
                return;
            }
        }
788 789 790 791 792 793 794 795 796 797 798

        // Bail if driver not set
        //-----------------------

        try {
            $accounts = new Accounts_Configuration();
            $driver = $accounts->get_driver();
        } catch (Accounts_Driver_Not_Set_Exception $e) {
            return;
        }

799
        // Bail if OpenLDAP is not ready
800 801 802 803 804 805
        // Bail if slave, but master is not Samba-ready
        //---------------------------------------------

        $sysmode = Mode_Factory::create();
        $mode = $sysmode->get_mode();

806 807 808 809 810
        if ($mode === Mode_Engine::MODE_SLAVE) {
            if (!$this->is_ready())
                return;
        } else {
            $accounts = new Accounts_Driver();
811
            $accounts_ready = $accounts->is_ready();
812

813
            if (!$accounts_ready)
814 815
                return;
        }
816 817 818 819

        // Lock state file
        //----------------

820
        $lock_file = new File(self::FILE_INITIALIZING);
821 822 823 824 825 826 827 828 829 830
        $initializing_lock = fopen(self::FILE_INITIALIZING, 'w');

        if (!flock($initializing_lock, LOCK_EX | LOCK_NB)) {
            clearos_log('samba', 'initialization is already running');
            return;
        }

        // Initialize Samba system
        //------------------------

831 832
        try {
            if (($mode === Mode_Engine::MODE_MASTER) || ($mode === Mode_Engine::MODE_STANDALONE))
833
                $this->_initialize_master_system($domain, NULL, $force);
834 835 836 837 838 839 840 841
            else if ($mode === Mode_Engine::MODE_SLAVE)
                $this->_initialize_slave_system('BDC');
            else
                return;
        } catch (Exception $e) {
            $this->_delete_lock($initializing_lock, self::FILE_INITIALIZING);
            throw new Exception($e);
        }
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866

        // Delete SID cache
        //-----------------

        $file = new File(Samba::FILE_DOMAIN_SID_CACHE);

        if ($file->exists())
            $file->delete();

        // Start winbind, man
        //-------------------

        try {
            clearos_log('samba', 'starting winbind');

            $winbind = new Winbind();
            $winbind->set_boot_state(TRUE);
            $winbind->set_running_state(TRUE);
        } catch (Exception $e) {
            // Not fatal
        }

        // And scene
        //----------

867
        clearos_log('samba', 'finished directory initialization... whew');
868

869 870
        $this->_set_initialized(TRUE);
        $this->_delete_lock($initializing_lock, self::FILE_INITIALIZING);
871 872 873
    }

    /**
874
     * Checks to see if Samba directory has been initialized.
875
     *
876 877
     * @return boolean TRUE if Samba has been initialized in LDAP
     * @throws Engine_Exception, LDAP_Offline_Exception
878 879
     */

880
    public function is_initialized()
881 882 883
    {
        clearos_profile(__METHOD__, __LINE__);

884
        $file = new File(self::FILE_INITIALIZED);
885

886 887
        // TODO: remove file_exists check on old path in version 7
        if ($file->exists() || file_exists('/var/clearos/samba/initialized_directory'))
888
            return TRUE;
889 890
        else
            return FALSE;
891 892
    }

893
    /**
894
     * Checks to see if directory is Samba-ready.
895
     *
896
     * Slave nodes need to know if Samba components exist in the directory.
897
     *
898 899
     * @return boolean TRUE if directory is Samba-ready
     * @throws Engine_Exception, LDAP_Offline_Exception
900 901
     */

902
    public function is_ready()
903 904 905
    {
        clearos_profile(__METHOD__, __LINE__);

906 907
        if ($this->ldaph === NULL)
            $this->_get_ldap_handle();
908

909 910 911 912 913
        $result = $this->ldaph->search(
            "(objectclass=sambaDomain)",
            OpenLDAP::get_base_dn(),
            array("sambaDomainName", "sambaSID")
        );
914

915
        $entry = $this->ldaph->get_first_entry($result);
916

917 918 919 920
        if ($entry)
            return TRUE;
        else
            return FALSE;
921 922 923
    }

    /**
924
     * Runs directory initialization.
925
     *
926
     * @param boolean $force force initialization
927
     *
928 929 930 931
     * @return void
     * @throws Engine_Exception
     */

932
    public function run_initialize($force = FALSE)
933 934 935
    {
        clearos_profile(__METHOD__, __LINE__);

936
        $options['background'] = TRUE;
937

938
        $force = ($force) ? '-f' : '';
939

940 941
        $shell = new Shell();
        $shell->execute(self::COMMAND_SAMBA_OPENLDAP_INITIALIZE, $force, TRUE, $options);
942 943
    }

944
    /**
945
     * Initializes the Samba system environment.
946
     *
947 948 949 950
     * @param string  $netbios_  name netbios_name
     * @param string  $workgroup workgroup/Windows domain
     * @param string  $password  password for winadmin
     * @param boolean $force     force initialization
951
     *
952 953
     * @return void
     * @throws Engine_Exception, Samba_Not_Initialized_Exception
954 955
     */

956
    public function run_initialize_samba_as_master_or_standalone($netbios, $workgroup, $password, $force)
957 958 959
    {
        clearos_profile(__METHOD__, __LINE__);

960 961 962
        Validation_Exception::is_valid($this->validate_netbios_name($netbios));
        Validation_Exception::is_valid($this->validate_workgroup($workgroup));
        Validation_Exception::is_valid($this->validate_password($password));
963

964
        $options['background'] = TRUE;
965

966
        $force = ($force) ? '-f' : '';
967

968 969
        $shell = new Shell();
        $shell->execute(self::COMMAND_SAMBA_INITIALIZE, "-d '$workgroup' -n '$netbios' -p '$password' $force", TRUE, $options);
970 971
    }

Peter Baldwin's avatar
Peter Baldwin committed
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
    /**
     * Initializes the Samba system environment.
     *
     * @param string  $netbios_  name netbios_name
     * @param string  $password  password for winadmin
     * @param boolean $force     force initialization
     *
     * @return void
     * @throws Engine_Exception, Samba_Not_Initialized_Exception
     */

    public function run_initialize_samba_as_slave($netbios, $password, $force = FALSE)
    {
        clearos_profile(__METHOD__, __LINE__);

        Validation_Exception::is_valid($this->validate_netbios_name($netbios));
        Validation_Exception::is_valid($this->validate_password($password));

        $options['background'] = TRUE;

        $force = ($force) ? '-f' : '';

        $shell = new Shell();
        $shell->execute(self::COMMAND_SAMBA_INITIALIZE, "-n '$netbios' -p '$password' $force", TRUE, $options);
    }

998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
    /**
     * Updates existing groups with Windows Networking group information (mapping).
     *
     * The ClearOS directory is designed to work without the Windows Networking
     * overlay.  When Windows Networking is enabled, we need to go through all the
     * existing groups and add the required Windows fields.
     *
     * @param string $domain_sid domain SID
     *
     * @return void
     * @throws Engine_Exception, Samba_Not_Initialized_Exception
     */

    public function update_group_mappings($domain_sid = NULL)
    {
        clearos_profile(__METHOD__, __LINE__);

1015 1016 1017
        if (! $this->is_initialized())
            return;

1018 1019 1020 1021 1022 1023 1024 1025 1026
        if ($this->ldaph === NULL)
            $this->_get_ldap_handle();

        if (empty($domain_sid)) {
            $samba = new Samba();
            $domain_sid = $samba->get_domain_sid();
        }

        $group_manager = new Group_Manager_Driver();
1027
        $group_details = $group_manager->get_details(Group_Driver::FILTER_ALL);
1028 1029

        // Add/Update the groups
1030
        //----------------------
1031

1032 1033 1034 1035 1036
        foreach ($group_details as $group_name => $group_info) {

            // Skip system (non-LDAP) groups
            //------------------------------

1037
            if ($group_info['core']['type'] === Group_Driver::TYPE_SYSTEM)
1038 1039 1040 1041 1042 1043
                continue;

            // Skip groups with existing Samba attributes
            //-------------------------------------------

            if (isset($group_info['extensions']['samba']['sid']))
1044 1045
                continue;

1046 1047 1048
            // Update group
            //-------------

1049
            $group = new Group_Driver($group_name);
1050

1051
            clearos_log('samba', "adding samba mappings to group $group_name");
1052 1053 1054 1055 1056
            $new_group_info['extensions']['samba']['sid'] = $domain_sid . '-' . $group_info['core']['gid_number'];
            $new_group_info['extensions']['samba']['group_type'] = 2;
            $new_group_info['extensions']['samba']['display_name'] = $group_info['core']['group_name'];

            $group->update($new_group_info);
1057 1058 1059
        }
    }

1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
    /**
     * Sets administrator passord.
     *
     * @param string $password
     *
     * @return void
     * @throws Validation_Exception, Engine_Exception
     */

    public function set_administrator_password($password)
    {
        clearos_profile(__METHOD__, __LINE__);

        $user = User_Factory::create(self::CONSTANT_WINADMIN_USERNAME);
        $user->reset_password($password, $password, self::CONSTANT_WINADMIN_USERNAME);

        // Rejoin for good measure (hard time getting net rpc join to run consistently)
        //-----------------------------------------------------------------------------

        $samba = new Samba();

        if ($samba->get_mode() === Samba::MODE_PDC)
            $this->add_computer($samba->get_netbios_name());

1084
        // $this->_net_rpc_join($password);
1085 1086 1087 1088 1089

        // Cleanup LDAP
        //-------------

        try {
1090 1091
            $samba_mode = $samba->get_mode();
            if (($samba_mode === Samba::MODE_PDC) || ($samba_mode === Samba::MODE_SIMPLE_SERVER)) {
1092
                $this->cleanup_entries();
1093
                $this->cleanup_passwords();
1094
            }
1095 1096 1097 1098 1099
        } catch (Exception $e) {
            // Not fatal
        }
    }

1100 1101 1102 1103 1104 1105 1106 1107 1108
    /**
     * Sets workgroup/domain name LDAP objects.
     *
     * @param string $workgroup workgroup name
     *
     * @return void
     * @throws Validation_Exception, Engine_Exception
     */

1109
    public function set_domain($workgroup)
1110 1111 1112
    {
        clearos_profile(__METHOD__, __LINE__);

1113 1114 1115 1116 1117
        Validation_Exception::is_valid($this->validate_workgroup($workgroup));

        // Bail if directory is not initialized
        //-------------------------------------

1118
        if (! $this->is_initialized())
1119 1120 1121 1122 1123
            return;

        if ($this->ldaph == NULL)
            $this->_get_ldap_handle();

1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
        // Bail if not a master/standalone node
        //-------------------------------------

        $sysmode = Mode_Factory::create();
        $mode = $sysmode->get_mode();

        if (! (($mode === Mode_Engine::MODE_MASTER) || ($mode === Mode_Engine::MODE_STANDALONE)))
            return;

        // Expensive call, only do it on a change
        //---------------------------------------

        $workgroup = strtoupper($workgroup);
        $current_workgroup = $this->get_domain();

        if ($workgroup == $current_workgroup)
            return;

1142 1143 1144 1145 1146 1147 1148
        // Update sambaDomainName object
        //------------------------------

        $sid = $this->get_domain_sid();
        $base_dn = OpenLDAP::get_base_dn();

        $result = $this->ldaph->search(
1149
            "(&(objectclass=sambaDomain)(sambaSID=$sid))",