1<?php
2/**
3 * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
4 * This file is licensed under the Affero General Public License version 3 or
5 * later.
6 * See the COPYING-README file.
7 */
8
9/**
10 * User authentication via samba (smbclient)
11 *
12 * @category Apps
13 * @package  UserExternal
14 * @author   Robin Appelman <icewind@owncloud.com>
15 * @license  http://www.gnu.org/licenses/agpl AGPL
16 * @link     http://github.com/owncloud/apps
17 */
18class OC_User_SMB extends \OCA\user_external\Base {
19	private $host;
20
21	const SMBCLIENT = 'smbclient -L';
22	const LOGINERROR = 'NT_STATUS_LOGON_FAILURE';
23
24	/**
25	 * Create new samba authentication provider
26	 *
27	 * @param string $host Hostname or IP of windows machine
28	 */
29	public function __construct($host) {
30		parent::__construct($host);
31		$this->host=$host;
32	}
33
34	/**
35	 * @param string $uid
36	 * @param string $password
37	 * @return string|bool
38	 */
39	private function tryAuthentication($uid, $password) {
40		$uidEscaped = \escapeshellarg($uid);
41		$password = \escapeshellarg($password);
42		$command = self::SMBCLIENT.' '.\escapeshellarg('//' . $this->host . '/dummy').' -U'.$uidEscaped.'%'.$password;
43		$lastline = \exec($command, $output, $retval);
44		if ($retval === 127) {
45			OCP\Util::writeLog(
46				'user_external', 'ERROR: smbclient executable missing',
47				OCP\Util::ERROR
48			);
49			return false;
50		} elseif (\strpos($lastline, self::LOGINERROR) !== false) {
51			//normal login error
52			return false;
53		} elseif (\strpos($lastline, 'NT_STATUS_BAD_NETWORK_NAME') !== false) {
54			//login on minor error
55			goto login;
56		} elseif ($retval != 0) {
57			//some other error
58			OCP\Util::writeLog(
59				'user_external', 'ERROR: smbclient error: ' . \trim($lastline),
60				OCP\Util::ERROR
61			);
62			return false;
63		} else {
64			login:
65			return $uid;
66		}
67	}
68
69	/**
70	 * Check if the password is correct without logging in the user
71	 *
72	 * @param string $uid      The username
73	 * @param string $password The password
74	 *
75	 * @return string|bool
76	 */
77	public function checkPassword($uid, $password) {
78		// Check with an invalid password, if the user authenticates then fail
79		$attemptWithInvalidPassword = $this->tryAuthentication($uid, \base64_encode($password));
80		if (\is_string($attemptWithInvalidPassword)) {
81			return false;
82		}
83
84		// Check with valid password
85		$attemptWithValidPassword = $this->tryAuthentication($uid, $password);
86		if (\is_string($attemptWithValidPassword)) {
87			$this->storeUser($uid);
88			return $uid;
89		}
90
91		return false;
92	}
93}
94