1<?php
2/***********************************************************
3 Copyright (C) 2011-2015 Hewlett-Packard Development Company, L.P.
4 Copyright (C) 2015 Siemens AG
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License version 2.1 as published by the Free Software Foundation.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this library; if not, write to the Free Software Foundation, Inc.0
17 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 ***********************************************************/
19
20/**
21 * \file
22 * \brief This file contains common authentication function
23 */
24
25
26/**
27 * \brief Check if SiteMinder is enabled.
28 *
29 * \note This can be used for other authentication agents by changing
30 *  $IDEnvVar
31 * \return -1 if not enabled, or the users SEA if enabled
32 */
33function siteminder_check()
34{
35  // $IDEnvVar = 'HPPF_AUTH_UID';  // for example for PingIdentity
36  $IDEnvVar = 'HTTP_SMUNIVERSALID';
37  if (isset($_SERVER[$IDEnvVar])) {
38    $SEA = $_SERVER[$IDEnvVar];
39    return $SEA;
40  }
41  return(-1);
42} // siteminder_check()
43
44/**
45 * \brief Check if the external HTTP authentication is enabled.
46 *  The mapping variables should be configured in fossology.conf
47 *  Usernames are forced lowercase.
48 * \return false if not enabled
49 */
50function auth_external_check()
51{
52  $EXT_AUTH_ENABLE = false;
53  if (array_key_exists('EXT_AUTH', $GLOBALS['SysConf'])) {
54    if (array_key_exists('CONF_EXT_AUTH_ENABLE', $GLOBALS['SysConf']['EXT_AUTH'])) {
55        $EXT_AUTH_ENABLE = $GLOBALS['SysConf']['EXT_AUTH']['CONF_EXT_AUTH_ENABLE'];
56    }
57  }
58  if ($EXT_AUTH_ENABLE) {
59    $EXT_AUTH_USER_KW = $GLOBALS['SysConf']['EXT_AUTH']['CONF_EXT_AUTH_ENV_USER'];
60    $EXT_AUTH_USER = $GLOBALS['_SERVER']["{$EXT_AUTH_USER_KW}"];
61    if (isset($EXT_AUTH_USER) && !empty($EXT_AUTH_USER)) {
62      if ($GLOBALS['SysConf']['EXT_AUTH']['CONF_EXT_AUTH_LOWERCASE_USER']) {
63          $EXT_AUTH_USER = strtolower($EXT_AUTH_USER);
64      }
65      $out['useAuthExternal']         = true;
66      $out['loginAuthExternal']       = $EXT_AUTH_USER;
67      $out['passwordAuthExternal']    = sha1($EXT_AUTH_USER);
68      $EXT_AUTH_MAIL_KW = $GLOBALS['SysConf']['EXT_AUTH']['CONF_EXT_AUTH_ENV_MAIL'];
69      $out['emailAuthExternal']       = $GLOBALS['_SERVER']["{$EXT_AUTH_MAIL_KW}"];
70      $EXT_AUTH_DESC_KW = $GLOBALS['SysConf']['EXT_AUTH']['CONF_EXT_AUTH_ENV_DESC'];
71      $out['descriptionAuthExternal'] = $GLOBALS['_SERVER']["{$EXT_AUTH_DESC_KW}"];
72      return $out;
73    }
74  }
75  return $out['useAuthExternal'] = false;
76}
77
78/**
79 * \brief check if this account is correct
80 *
81 * \param string &$user   User name, reference variable
82 * \param string &$passwd Password, reference variable
83 * \param string &$group  Group, reference variable (optional)
84 *
85 * \return User id on success, exit(1) on failure.
86 */
87function account_check(&$user, &$passwd, &$group = "")
88{
89  global $SysConf;
90  $dbManager = $GLOBALS['container']->get('db.manager');
91  /* get username/passwd from ~/.fossology.rc */
92  $user_passwd_file = getenv("HOME") . "/.fossology.rc";
93  if (empty($user) && empty($passwd) && file_exists($user_passwd_file)) {
94    $user_passwd_array = parse_ini_file($user_passwd_file, true, INI_SCANNER_RAW);
95
96    /* get username and password from conf file */
97    if (! empty($user_passwd_array) && ! empty($user_passwd_array['user'])) {
98      $user = $user_passwd_array['user'];
99    }
100    if (! empty($user_passwd_array) && ! empty($user_passwd_array['username'])) {
101      $user = $user_passwd_array['username'];
102    }
103    if (! empty($user_passwd_array) && ! empty($user_passwd_array['groupname'])) {
104      $group = $user_passwd_array['groupname'];
105    }
106    if (! empty($user_passwd_array) && ! empty($user_passwd_array['password'])) {
107      $passwd = $user_passwd_array['password'];
108    }
109  }
110  /* check if the user name/passwd is valid */
111  if (empty($user)) {
112    /*
113     * $uid_arr = posix_getpwuid(posix_getuid());
114     * $user = $uid_arr['name'];
115     */
116    echo "FATAL: You should add '--username USERNAME' when running OR add " .
117      "'username=USERNAME' in ~/.fossology.rc before running.\n";
118    exit(1);
119  }
120  if (empty($passwd)) {
121    echo "The user is: $user, please enter the password:\n";
122    system('stty -echo');
123    $passwd = trim(fgets(STDIN));
124    system('stty echo');
125    if (empty($passwd)) {
126      echo "You entered an empty password.\n";
127    }
128  }
129
130  if (! empty($user)) {
131    $userDao = $GLOBALS['container']->get('dao.user');
132    try {
133      $row = $userDao->getUserAndDefaultGroupByUserName($user);
134    } catch (Exception $e) {
135      echo $e->getMessage(), "\n";
136      exit(1);
137    }
138    $userId = $row['user_pk'];
139    $SysConf['auth']['UserId'] = $userId;
140
141    if (empty($group)) {
142      $group = $row['group_name'];
143      $groupId = $row['group_fk'];
144    } else {
145      $rowGroup = $dbManager->getSingleRow(
146        "SELECT group_pk
147        FROM group_user_member INNER JOIN groups ON groups.group_pk = group_user_member.group_fk
148        WHERE user_fk = $1 AND group_name = $2", array($userId, $group),
149        __METHOD__ . ".lookUpGroup");
150      if (false === $rowGroup) {
151        echo "User is not in group.\n";
152        exit(1);
153      }
154      $groupId = $rowGroup['group_pk'];
155    }
156    $SysConf['auth']['GroupId'] = $groupId;
157    if (empty($groupId)) {
158      echo "Group '$group' not found.\n";
159      exit(1);
160    }
161
162    if (! empty($row['user_pass'])) {
163      $options = array('cost' => 10);
164      if (password_verify($passwd, $row['user_pass'])) {
165        if (password_needs_rehash($row['user_pass'], PASSWORD_DEFAULT, $options)) {
166          $newHash = password_hash($passwd, PASSWORD_DEFAULT, $options);
167          /* Update old hash with new hash  */
168          update_password_hash($user, $newHash);
169        }
170        return true;
171      } else if (! empty($row['user_seed'])) {
172        $passwd_hash = sha1($row['user_seed'] . $passwd);
173        /* If verify with new hash fails check with the old hash */
174        if (strcmp($passwd_hash, $row['user_pass']) == 0) {
175          $newHash = password_hash($passwd, PASSWORD_DEFAULT, $options);
176          /* Update old hash with new hash */
177          update_password_hash($user, $newHash);
178          return true;
179        } else {
180          echo "User name or password is invalid.\n";
181          exit(1);
182        }
183      }
184    }
185  }
186  return $userId;
187}
188
189/**
190 * \brief Check if the user has the permission to read the
191 * copyright/license/etc information of this upload
192 *
193 * \param int    $upload Upload id
194 * \param string $user   User name
195 *
196 * \return 1: has the permission; 0: no permission
197 */
198function read_permission($upload, $user)
199{
200  $ADMIN_PERMISSION = 10;
201  $dbManager = $GLOBALS['container']->get('db.manager');
202
203  /* check if the user if the owner of this upload */
204  $row = $dbManager->getSingleRow(
205    "SELECT 1
206    FROM upload INNER JOIN users ON users.user_pk = upload.user_fk
207    WHERE users.user_name = $1 AND upload.upload_pk = $2",
208    array($user, $upload),
209    __METHOD__.".checkUpload"
210  );
211
212  if (! empty($row)) {
213    /* user has permission */
214    return 1;
215  }
216
217  /* check if the user is administrator */
218  $row = $dbManager->getSingleRow(
219    "SELECT 1
220    FROM users
221    WHERE user_name = $1 AND user_perm = $2",
222    array($user, $ADMIN_PERMISSION),
223    __METHOD__.".checkPerm"
224  );
225
226  if (! empty($row)) {
227    /* user has permission */
228    return 1;
229  }
230
231  /* user does not have permission */
232  return 0;
233}
234
235/**
236 * Check if the password policy has been enabled
237 * @return boolean
238 */
239function passwordPolicyEnabled()
240{
241  $sysconfig = $GLOBALS['SysConf']['SYSCONFIG'];
242  if (! array_key_exists('PasswdPolicy', $sysconfig) ||
243    $sysconfig['PasswdPolicy'] == 'false') {
244    return false;
245  }
246  return true;
247}
248
249/**
250 * Generate the password policy regex from sysconfig
251 * @return string Regex based on policy selected
252 */
253function generate_password_policy()
254{
255  $sysconfig = $GLOBALS['SysConf']['SYSCONFIG'];
256  if (! passwordPolicyEnabled()) {
257    return ".*";
258  }
259  $limit = "*";
260  $min = trim($sysconfig['PasswdPolicyMinChar']);
261  $max = trim($sysconfig['PasswdPolicyMaxChar']);
262  if (!empty($min) || !empty($max)) {
263    if (empty($min)) {
264      $min = 0;
265    }
266    $min = intval($min) < 0 ? 0 : $min;
267    $max = intval($max) < 0 ? 0 : $max;
268    $limit = '{' . $min . ",$max}";
269  }
270  $lookAhead = "";
271  $charset = "a-zA-Z\\d";
272  if ($sysconfig['PasswdPolicyLower'] == 'true') {
273    $lookAhead .= '(?=.*[a-z])';
274  }
275  if ($sysconfig['PasswdPolicyUpper'] == 'true') {
276    $lookAhead .= '(?=.*[A-Z])';
277  }
278  if ($sysconfig['PasswdPolicyDigit'] == 'true') {
279    $lookAhead .= '(?=.*\\d)';
280  }
281  $special = trim($sysconfig['PasswdPolicySpecial']);
282  if (!empty($special)) {
283    $lookAhead .= "(?=.*[$special])";
284    $charset .= $special;
285    $charset = '[' . $charset . ']';
286  } else {
287    $charset = '.';  // Allow any special character
288  }
289  return $lookAhead . $charset . $limit;
290}
291
292/**
293 * Translate selected password policy into user understandable string
294 * @return string
295 */
296function generate_password_policy_string()
297{
298  $sysconfig = $GLOBALS['SysConf']['SYSCONFIG'];
299  if (! passwordPolicyEnabled()) {
300    return "No policy defined.";
301  }
302  $limit = "Any length.";
303  $min = trim($sysconfig['PasswdPolicyMinChar']);
304  $max = trim($sysconfig['PasswdPolicyMaxChar']);
305  if (!empty($min) || !empty($max)) {
306    if (empty($min)) {
307      $min = 0;
308    }
309    $limit = "Minimum $min";
310    if (!empty($max)) {
311      $limit .= ", maximum $max";
312    }
313    $limit .= " characters.";
314  }
315  $others = [];
316  if ($sysconfig['PasswdPolicyLower'] == 'true') {
317    $others[] = "lower case";
318  }
319  if ($sysconfig['PasswdPolicyUpper'] == 'true') {
320    $others[] = "upper case";
321  }
322  if ($sysconfig['PasswdPolicyDigit'] == 'true') {
323    $others[] = "digit";
324  }
325  if (!empty($others)) {
326    $others = "At least one " . join(", ", $others);
327  } else {
328    $others = "";
329  }
330  $special = trim($sysconfig['PasswdPolicySpecial']);
331  if (!empty($special)) {
332    if (!empty($others)) {
333      $others .= " and";
334    }
335    $others .= " one of <em>$special</em>";
336  }
337  return "$limit $others.";
338}
339