PageRenderTime 149ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/frontend/php/include/account.php

#
PHP | 388 lines | 275 code | 47 blank | 66 comment | 49 complexity | 3841e8c088b234e9dc8b29d1cc5a6f8c MD5 | raw file
Possible License(s): AGPL-3.0
  1. <?php
  2. # All the forms and functions to manage unix users
  3. #
  4. # Copyright 1999-2000 (c) The SourceForge Crew
  5. # Copyright 2003-2006 (c) Mathieu Roy <yeupou--gnu.org>
  6. # Copyright (C) 2007 Sylvain Beucler
  7. #
  8. # This file is part of Savane.
  9. #
  10. # Savane is free software: you can redistribute it and/or modify
  11. # it under the terms of the GNU Affero General Public License as
  12. # published by the Free Software Foundation, either version 3 of the
  13. # License, or (at your option) any later version.
  14. #
  15. # Savane is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU Affero General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU Affero General Public License
  21. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. require_once(dirname(__FILE__).'/pwqcheck.php');
  23. function account_password_help() {
  24. global $use_pwqcheck;
  25. $help = _("(not too short, must contain multiple character classes: symbols, digits (0-9), upper and lower case letters)");
  26. if ($use_pwqcheck) {
  27. $pwqgen = exec("pwqgen");
  28. $help .= " ".sprintf(_("(for instance: %s)"), htmlspecialchars($pwqgen));
  29. }
  30. return $help;
  31. }
  32. // Modified from http://www.openwall.com/articles/PHP-Users-Passwords#enforcing-password-policy
  33. function account_pwvalid ($newpass, $oldpass = '', $user = '')
  34. {
  35. global $use_pwqcheck, $pwqcheck_args;
  36. if ($use_pwqcheck) {
  37. $check = pwqcheck($newpass, $oldpass, $user, '', $pwqcheck_args);
  38. } else {
  39. /* Some really trivial and obviously-insufficient password strength checks -
  40. * we ought to use the pwqcheck(1) program instead. */
  41. $check = '';
  42. if (strlen($newpass) < 7)
  43. $check = 'way too short';
  44. else if (stristr($oldpass, $newpass) ||
  45. (strlen($oldpass) >= 4 && stristr($newpass, $oldpass)))
  46. $check = 'is based on the old one';
  47. else if (stristr($user, $newpass) ||
  48. (strlen($user) >= 4 && stristr($newpass, $user)))
  49. $check = 'is based on the username';
  50. else
  51. $check = 'OK';
  52. }
  53. if ($check != 'OK') {
  54. $GLOBALS['register_error'] = "Bad password ($check)";
  55. fb($check, 1);
  56. return 0;
  57. }
  58. return 1;
  59. }
  60. function account_namevalid ($name, $allow_dashes=0, $allow_underscores=1, $allow_dots=0, $nameof=0, $MAX_ACCNAME_LENGTH=16, $MIN_ACCNAME_LENGTH=3)
  61. {
  62. $underscore = '';
  63. $dashe = '';
  64. $dot = '';
  65. # By default, we are supposed to check for an account name. But it may
  66. # be a list name or whatever
  67. if (!$nameof) {
  68. $nameof = _("account name");
  69. }
  70. # By default, underscore are allowed, creating no specific issue for an
  71. # account name. It may creates trouble if the account is use to handle DNS...
  72. if ($allow_underscores) {
  73. $underscore = "_";
  74. }
  75. # By default, dashes are not allowed, creating issue with mailing list name
  76. # and many other potential conflicts. However, it is usually convenient for
  77. # groups name.
  78. $dash = $allow_dashes ? '-' : '';
  79. # By default, dots are not allowed. Unix systems may allow it but it
  80. # is a source of confusion (for instance, a problem if you have the habit
  81. # to things like `chown user.group`)
  82. # However, it is sometimes wise to allow it, for instance if we check for
  83. # a mailing-list name, which is almost like an account name + dots
  84. $dot = $allow_dots ? '.' : '';
  85. # no spaces
  86. if (strrpos($name,' ') > 0)
  87. {
  88. fb(sprintf(_("There cannot be any spaces in the %s"), $nameof),1);
  89. return 0;
  90. }
  91. # min and max length
  92. if (strlen($name) < $MIN_ACCNAME_LENGTH)
  93. {
  94. fb(sprintf(_("The %s is too short"), $nameof), 1);
  95. fb(sprintf(ngettext("It must be at least %s character.", "It must be at least %s characters.", $MIN_ACCNAME_LENGTH), $MIN_ACCNAME_LENGTH),1);
  96. return 0;
  97. }
  98. if (strlen($name) > $MAX_ACCNAME_LENGTH)
  99. {
  100. fb(sprintf(_("The %s is too long"), $nameof), 1);
  101. fb(sprintf(ngettext("It must be at most %s character.", "It must be at most %s characters.", $MAX_ACCNAME_LENGTH), $MAX_ACCNAME_LENGTH),1);
  102. return 0;
  103. }
  104. # must start with an alphanumeric non numeric
  105. if (strspn($name,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0)
  106. {
  107. fb(sprintf(_("The %s must begin with an alphabetical character."), $nameof),1);
  108. return 0;
  109. }
  110. # must contain only legal characters and underscores, and maybe dashes and
  111. # underscore, depending on the arguments
  112. if (strspn($name,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$underscore$dash$dot")
  113. != strlen($name))
  114. {
  115. $tolerated = '';
  116. if ($allow_underscores)
  117. { $tolerated .= _("underscores").', '; }
  118. if ($allow_dashes)
  119. { $tolerated .= _("dashes").', '; }
  120. if ($allow_dots)
  121. { $tolerated .= _("dots").', '; }
  122. if ($tolerated)
  123. { $tolerated = ' ('._("tolerated:").' '.rtrim($tolerated, ', ').')'; }
  124. fb(sprintf(_("The %s must only contain alphanumerics%s."), $nameof, $tolerated),1);
  125. return 0;
  126. }
  127. # illegal names
  128. if (eregi("^((root)|(savane-keyrings)|(bin)|(daemon)|(adm)|(lp)|(sync)|(shutdown)|(halt)|(mail)|(news)"
  129. . "|(uucp)|(apache)|(operator)|(invalid)|(games)|(mysql)|(httpd)|(nobody)|(dummy)|(opensource)"
  130. . "|(web)|(www)|(cvs)|(anoncvs)|(anonymous)|(shell)|(ftp)|(irc)|(debian)|(ns)|(download))$",$name))
  131. {
  132. fb(sprintf(_("That %s is reserved."), $nameof),1);
  133. return 0;
  134. }
  135. return 1;
  136. }
  137. # Just check if the email address domain is not from a forbidden domain
  138. # or if it is not already associated to an email account
  139. function account_emailvalid ($email)
  140. {
  141. if (db_numrows(db_execute("SELECT user_id FROM user WHERE "
  142. . "email LIKE ?", array($email))) > 0)
  143. {
  144. fb(_("An account associated with that email address has already been created."),1);
  145. return 0;
  146. }
  147. utils_get_content("forbidden_mail_domains");
  148. if (!empty($GLOBALS['forbid_mail_domains_regexp']))
  149. {
  150. if (preg_match($GLOBALS['forbid_mail_domains_regexp'], $email))
  151. {
  152. fb(_("It is not allowed to associate an account with this email address."),1);
  153. return 0;
  154. }
  155. }
  156. return 1;
  157. }
  158. function account_groupnamevalid ($name)
  159. {
  160. # Test with the usual namevalid function, allowing dashes
  161. if (!account_namevalid($name, 1, 0))
  162. { return 0; }
  163. utils_get_content("forbidden_group_names");
  164. # All these groups are invalid by default. There can be used for system
  165. # services and already be existing on the system.
  166. # Please, keep that list in alphabetic order.
  167. $forbid_group_regexp = "/^(".
  168. "(adm)|".
  169. "(admin)|".
  170. "(apache)|".
  171. "(bin)|".
  172. "(compile)|".
  173. "(cvs[0-9]?)|".
  174. "(daemon)|".
  175. "(disk)|".
  176. "(download[0-9]?)|".
  177. "(exim)|".
  178. "(fencepost)|".
  179. "(ftp)|".
  180. "(ftp[0-9]?)|".
  181. "(gnudist)|".
  182. "(ident)|".
  183. "(irc[0-9]?)|".
  184. "(lists)|".
  185. "(lp)|".
  186. "(mail[0-9]?)|".
  187. "(man)|".
  188. "(monitor)|".
  189. "(mirrors?)|".
  190. "(nogroup)|".
  191. "(ns[0-9]?)|".
  192. "(news[0-9]?)|".
  193. "(ntp)|".
  194. "(postfix)|".
  195. "(projects)|".
  196. "(pub)|".
  197. "(root)|".
  198. "(rpc)|".
  199. "(rpcuser)|".
  200. "(shadow)|".
  201. "(shell[0-9]?)|".
  202. "(slayer)|".
  203. "(sshd)|".
  204. "(staff)|".
  205. "(sudo)|".
  206. "(savane-keyrings)|". # reserved for keyrings
  207. "(svusers)|". # users group for savane users
  208. "(sys)|".
  209. "(tty)|".
  210. "(uucp)|".
  211. "(users)|".
  212. "(utmp)|".
  213. "(web.*)|".
  214. "(wheel)|".
  215. "(www[0-9]?)|".
  216. "(www-data)|".
  217. "(xfs)".
  218. ")$/";
  219. # Illegal names: check the hardcoded list unless the variable
  220. # $only_specific_forbid_group_regexp is true
  221. if (!$GLOBALS['only_specific_forbid_group_regexp'])
  222. {
  223. dbg("apply standard regexp");
  224. if (preg_match($forbid_group_regexp,$name))
  225. {
  226. fb(_("This group name is not allowed."),1);
  227. return 0;
  228. }
  229. }
  230. # Illegal names: check the site specific list if a list is given
  231. # (by consequence, the variable return true)
  232. if ($GLOBALS['specific_forbid_group_regexp'])
  233. {
  234. dbg("apply specific regexp");
  235. if (preg_match($GLOBALS['specific_forbid_group_regexp'],$name))
  236. {
  237. fb(_("This group name is not allowed."),1);
  238. return 0;
  239. }
  240. }
  241. if (eregi("_",$name))
  242. {
  243. fb(_("Group name cannot contain underscore for DNS reasons."),1);
  244. return 0;
  245. }
  246. return 1;
  247. }
  248. // <phpass>
  249. // From http://www.openwall.com/phpass/
  250. // Version 0.3 / genuine
  251. // Public domain
  252. // Author: Solar Designer
  253. function account_encode64($input, $count)
  254. {
  255. $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  256. $output = '';
  257. $i = 0;
  258. do {
  259. $value = ord($input[$i++]);
  260. $output .= $itoa64[$value & 0x3f];
  261. if ($i < $count)
  262. $value |= ord($input[$i]) << 8;
  263. $output .= $itoa64[($value >> 6) & 0x3f];
  264. if ($i++ >= $count)
  265. break;
  266. if ($i < $count)
  267. $value |= ord($input[$i]) << 16;
  268. $output .= $itoa64[($value >> 12) & 0x3f];
  269. if ($i++ >= $count)
  270. break;
  271. $output .= $itoa64[($value >> 18) & 0x3f];
  272. } while ($i < $count);
  273. return $output;
  274. }
  275. function account_get_random_bytes($count)
  276. {
  277. $random_state = microtime();
  278. $output = '';
  279. if (is_readable('/dev/urandom') &&
  280. ($fh = @fopen('/dev/urandom', 'rb'))) {
  281. $output = fread($fh, $count);
  282. fclose($fh);
  283. }
  284. if (strlen($output) < $count) {
  285. $output = '';
  286. for ($i = 0; $i < $count; $i += 16) {
  287. $random_state =
  288. md5(microtime() . $random_state);
  289. $output .=
  290. pack('H*', md5($random_state));
  291. }
  292. $output = substr($output, 0, $count);
  293. }
  294. return $output;
  295. }
  296. // </phpass>
  297. function account_gensalt($salt_base64_length=16)
  298. {
  299. // Note: $salt_base64_length=16 for SHA-512, cf. crypt(3)
  300. $salt_byte_length = $salt_base64_length * 6 / 8;
  301. return account_encode64(account_get_random_bytes($salt_byte_length), $salt_byte_length);
  302. }
  303. # generate unix pw
  304. function account_genunixpw($plainpw)
  305. {
  306. return account_encryptpw($plainpw);
  307. }
  308. function account_encryptpw($plainpw)
  309. {
  310. // rounds=5000 is the 2010 glibc default, possibly we'll upgrade in
  311. // the future, better have this explicit
  312. // Cf. http://www.akkadia.org/drepper/sha-crypt.html
  313. if (version_compare(PHP_VERSION, '5.3.2', '>=')) {
  314. return crypt($plainpw, '$6$rounds=5000$' . account_gensalt(16));
  315. } else {
  316. // The PHP version in Lenny 5.2.6 has troubles with the above
  317. // (truncated hash at 9 chars)
  318. return crypt($plainpw, '$6$' . account_gensalt(16));
  319. }
  320. }
  321. # returns next userid
  322. function account_nextuid()
  323. {
  324. db_query("SELECT max(unix_uid) AS maxid FROM user");
  325. $row = db_fetch_array();
  326. return ($row[maxid] + 1);
  327. }
  328. # print out shell selects
  329. function account_shellselects($current)
  330. {
  331. $shells = file("/etc/shells");
  332. for ($i = 0; $i < count($shells); $i++)
  333. {
  334. $this_shell = chop($shells[$i]);
  335. echo "<option ".(($current == $this_shell)?"selected ":"")."value=$this_shell>$this_shell</option>\n";
  336. }
  337. }
  338. function account_validpw($stored_pw, $plain_pw)
  339. {
  340. return (crypt($plain_pw,$stored_pw) == $stored_pw);
  341. }