xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
Commit b4e220bf8823cad220183930949e80a6ec7f2fb3

Mostly totp fixes; all tests pass; css fixes
Author: Catalin(ux) M. BOIE
Author date (UTC): 2015-10-17 12:02
Committer name: Catalin(ux) M. BOIE
Committer date (UTC): 2015-10-17 12:02
Parent(s): 9078444271ce3e4fa25cff60afdb4efa8741fa01
Signing key:
Tree: 94048627d9fcf6e05db1979aba8d29f29963106b
File Lines added Lines deleted
Compare.txt 4 0
TODO 29 6
inc/admin.inc.php 1 1
inc/cache.inc.php 6 4
inc/events.inc.php 1 3
inc/git.inc.php 3 3
inc/ssh.inc.php 63 41
inc/struct.inc.php 21 8
inc/totp.inc.php 700 107
inc/user.inc.php 10 5
inc/user/home-page.php 13 1
inc/user/settings.php 1 1
inc/util.inc.php 6 8
root/index.php 2 4
root/themes/default/main.css 19 4
root/themes/default/repo/list/line.html 1 1
root/themes/default/repo/list/nodata.html 1 1
root/themes/default/user/hints/totp.html 6 0
root/themes/default/user/login.html 1 1
root/themes/default/user/settings/menu.html 1 1
root/themes/default/user/settings/totp/enroll.html 5 6
root/themes/default/user/settings/totp/hints.html 15 6
root/themes/default/user/settings/totp/hints_enroll.html 0 0
root/themes/default/user/settings/totp/list/header.html 1 1
root/themes/default/user/settings/totp/menu.html 8 0
root/themes/default/user/settings/totp/sc/delete_ok.html 3 0
root/themes/default/user/settings/totp/sc/gen.html 14 0
root/themes/default/user/settings/totp/sc/gen_ok.html 8 0
root/themes/default/user/settings/totp/sc/hints.html 2 0
root/themes/default/user/settings/totp/sc/list/footer.html 0 0
root/themes/default/user/settings/totp/sc/list/header.html 3 6
root/themes/default/user/settings/totp/sc/list/line.html 6 0
root/themes/default/user/settings/totp/sc/list/nodata.html 1 1
root/themes/default/user/settings/totp/sc/list_err.html 3 0
root/themes/default/user/settings/totp/ver_error.html 1 1
root/themes/extract_texts.sh 1 1
scripts/cache.php 51 28
scripts/cachec.php 2 0
scripts/remote.php 4 3
tests/.gitignore 1 0
tests/Makefile 8 2
tests/cache.php 1 1
tests/git2.php 7 44
tests/git2.sh 1 0
tests/helpers.inc.php 97 0
tests/http.inc.php 7 0
tests/http_settings.php 2 0
tests/http_totp.php 198 4
tests/ssh.php 238 0
tests/totp.php 1 1
File Compare.txt changed (mode: 100644) (index 4080772..f73470f)
... ... Languages 1 ? ? ? ? ?
14 14 IPv6 Yes ? ? ? ? ? IPv6 Yes ? ? ? ? ?
15 15 Submodules ? ? ? ? ? ? Submodules ? ? ? ? ? ?
16 16 Usable with lynx ? ? ? ? ? ? Usable with lynx ? ? ? ? ? ?
17 2fa Yes ? yes 1) ? ? ?
17 18
18 19
19 20 [Rights] [Rights]
 
... ... IP control Yes ? ? ? ? ?
25 26 [Details] [Details]
26 27 Language PHP Ruby+Perl Ruby Ruby ? Perl Language PHP Ruby+Perl Ruby Ruby ? Perl
27 28 Cache custom Redis ? ? ? No Cache custom Redis ? ? ? No
29
30
31 1) Seems is not really secure.
File TODO changed (mode: 100644) (index 0af8ec3..279f1f2)
2 2 [ ] [ ]
3 3
4 4 == BEFORE NEXT RELEASE == == BEFORE NEXT RELEASE ==
5 [ ] When I am in "My repositories" and I am doing a search, other users'
6 repositories are shown.
7 [ ] In user/home-page.php, in hints section, add a message when the user is
8 low on scratch codes. Not hint.
9 [ ] If 'rg_debug'? is defined, do not send mails.
10 [ ] test with "short" (0 prepended) codes in unit testing.
11 For scs, done, test for devices? This is a little bit harder.
12 [ ] totp:ssh: do we need a command to remove a set of scratch codes?
13 Something like 'remove-sc [<itime>]'. If <itime> is missing, list the
14 sets. The IP must be authorized?
15 [ ] http hook: use curl's CURLOPT_SSLCERT to authenticate to the client server.
16 [ ] In report, just show the newly added repos, not the totals. Totals in body.
17 [ ] Apply to become a member of Software Freedom Conservancy?
18 [ ] Why do we index 'users' by 'username'?! Seems wrong!
19 [ ] totp: add sc for ssh!
20 Should I validate one after asking the user to store them safe?
21 Think about power down before scratch codes hit the printer.
22 [ ] Get rid of {{}} stuff.
23 [ ] Some other menus were added, we must load all this pages.
24 At least totp/{list,enroll,sc}.
25 [ ] I inconsistently use /op/repo/create and /user/catab/settings!
26 Why not /user/catab/repo/create?
27 [ ] Git stats are done only on master branch. We must done them per branch.
5 28 [ ] Test with an empty commit what happens in rg_git_log with patches == TRUE. [ ] Test with an empty commit what happens in rg_git_log with patches == TRUE.
6 29 Can happen? Maybe for a rename? Can happen? Maybe for a rename?
7 30 [ ] Add repo stats to ssh repo command. [ ] Add repo stats to ssh repo command.
 
94 117 Check http://www.postgresql.org/docs/9.4/interactive/sql-grant.html Check http://www.postgresql.org/docs/9.4/interactive/sql-grant.html
95 118 web user must not be able to create roles/tables/databases/etc. web user must not be able to create roles/tables/databases/etc.
96 119 Hm. What about the settings?! I must be able to do a select... Hm. What about the settings?! I must be able to do a select...
97 [ ] totp: warn user if s/he is low on scratch login codes.
98 [ ] totp:scratch: delete them after use?
99 120 [ ] totp: Build an Android application which will be able to authenticate also [ ] totp: Build an Android application which will be able to authenticate also
100 121 the server to the user. the server to the user.
101 122 [ ] totp: switch to 'password' type for login_token (login page)? [ ] totp: switch to 'password' type for login_token (login page)?
 
173 194 http://www.valdyas.org/fading/index.cgi/2015/05/29#no-github http://www.valdyas.org/fading/index.cgi/2015/05/29#no-github
174 195 http://www.adamhyde.net/why-github-is-bad-for-open-source/ http://www.adamhyde.net/why-github-is-bad-for-open-source/
175 196 [ ] Add sha1sum of the VM images [ ] Add sha1sum of the VM images
176 [ ] Show user the entry that must be added for known_hosts
197 [ ] ssh: Show user the entry that must be added for known_hosts
177 198 [ ] LDAP: http://mageconfig.blogspot.ro/2014/06/configure-gitgerrit-with-open-ldap-for.html [ ] LDAP: http://mageconfig.blogspot.ro/2014/06/configure-gitgerrit-with-open-ldap-for.html
178 199 [ ] Leave alone the ssh key comment! More exactly, do not convert unk chars. [ ] Leave alone the ssh key comment! More exactly, do not convert unk chars.
179 200 [ ] Pass only uid to events, we already have it in cache! [ ] Pass only uid to events, we already have it in cache!
 
182 203 [ ] When we push by ssh, we have the user, so we can give more info about [ ] When we push by ssh, we have the user, so we can give more info about
183 204 why the push failed. Carefull, not too much info. For example: why the push failed. Carefull, not too much info. For example:
184 205 "You have no key uploaded, go to ..." "You have no key uploaded, go to ..."
206 No key uploaded is not working. ssh server will ask for pass...
207 Should we set a special shell and use an empty pass for rocketgit account?
208 [ ] Should we just set no password somehow for ssh access to be able to signal
209 the user that has no key uploaded?
185 210 [ ] For 'log' and 'tree' we have decorations for links! [ ] For 'log' and 'tree' we have decorations for links!
186 211 [ ] Sign vm images. [ ] Sign vm images.
187 212 [ ] In "Tree" section, seems the path is doubled. [ ] In "Tree" section, seems the path is doubled.
 
191 216 Anyway, we already do double replace for hints) Anyway, we already do double replace for hints)
192 217 [ ] Saving fields in forms when session exired to be reused next time. [ ] Saving fields in forms when session exired to be reused next time.
193 218 [ ] Compression off for ssh because objects are already compressed? [ ] Compression off for ssh because objects are already compressed?
194 [ ] Add a random token in header to prevent watermarking (this is the name?).
219 [ ] Add a random token in HTTP header to prevent watermarking (this is the name?).
195 220 [ ] Add "Spread the word!" on website. [ ] Add "Spread the word!" on website.
196 221 [ ] https://www.kernel.org/pub/software/scm/git/docs/gitworkflows.html [ ] https://www.kernel.org/pub/software/scm/git/docs/gitworkflows.html
197 222 [ ] git-name-rev is nice. [ ] git-name-rev is nice.
 
205 230 oriunde - still needed?! oriunde - still needed?!
206 231 [ ] Backup for rg2! [ ] Backup for rg2!
207 232 [ ] Add uid to events so we can delete old events for tests or abusing users? [ ] Add uid to events so we can delete old events for tests or abusing users?
208 [ ] Should we just set no password somehow for ssh access to be able to signal
209 the user that has no key uploaded?
210 233 [ ] rocketgit.com: When getting another IP, allow ssh on port 443(https)? [ ] rocketgit.com: When getting another IP, allow ssh on port 443(https)?
211 234 [ ] Investigate --decorate/--word-diff for git log. [ ] Investigate --decorate/--word-diff for git log.
212 235 [ ] client_win.html hint is not used. [ ] client_win.html hint is not used.
File inc/admin.inc.php changed (mode: 100644) (index 2353a2e..2c98283)
... ... function rg_admin_invite_one($db, $event)
52 52 $rg = array(); $rg = array();
53 53 $subject = str_replace('{NAME}', $event['name'], $event['subject']); $subject = str_replace('{NAME}', $event['name'], $event['subject']);
54 54 $subject = "=?UTF-8?B?" . base64_encode(trim($subject)) . "?="; $subject = "=?UTF-8?B?" . base64_encode(trim($subject)) . "?=";
55 $header = rg_template("mail/common.head.txt", $rg, FALSE /* xss */);
55 $header = rg_template("mail/common.head.txt", $rg, FALSE /*xss*/);
56 56 $header = trim($header); $header = trim($header);
57 57 $header .= "\nFrom: $admin_name <" . $rg_admin_email . ">"; $header .= "\nFrom: $admin_name <" . $rg_admin_email . ">";
58 58 rg_log_ml("DEBUG: header=$header"); rg_log_ml("DEBUG: header=$header");
File inc/cache.inc.php changed (mode: 100644) (index f54ff89..bab3b1c)
... ... if (!isset($rg_cache_enable))
11 11 $rg_cache_enable = TRUE; $rg_cache_enable = TRUE;
12 12
13 13 // timeout in miliseconds // timeout in miliseconds
14 $rg_cache_timeout = 100;
14 $rg_cache_timeout = 500;
15 15
16 16 $rg_cache_count = 0; $rg_cache_count = 0;
17 17 $rg_cache_tries = 3; $rg_cache_tries = 3;
 
... ... function rg_cache_send($cmd, $para, $flags)
337 337
338 338 $id = intval($t[1]); $id = intval($t[1]);
339 339 if ($id < $rg_cache_count) { if ($id < $rg_cache_count) {
340 rg_log('DEBUG: id: ' . $id . ' < ' . $rg_cache_count);
340 if ($rg_cache_debug)
341 rg_log('DEBUG: id: ' . $id . ' < ' . $rg_cache_count);
341 342 continue; continue;
342 343 } }
343 rg_log('DEBUG: id: ' . $id . ' == ' . $rg_cache_count);
344 if ($rg_cache_debug)
345 rg_log('DEBUG: id: ' . $id . ' == ' . $rg_cache_count);
344 346
345 347 if (strcmp($t[0], 'OK') != 0) { if (strcmp($t[0], 'OK') != 0) {
346 rg_log('DEBUG: not an OK answer: ' . $t[0]);
348 //rg_log('DEBUG: not an OK answer: ' . $t[0]);
347 349 return FALSE; return FALSE;
348 350 } }
349 351
File inc/events.inc.php changed (mode: 100644) (index 70682b0..9f67caa)
... ... function rg_event_add($db, $event)
89 89 rg_prof_start("event_add"); rg_prof_start("event_add");
90 90 rg_log_enter("event_add: event=" . rg_array2string($event)); rg_log_enter("event_add: event=" . rg_array2string($event));
91 91
92 if (!isset($event['IP'])) {
92 if (!isset($event['IP']))
93 93 $event['IP'] = rg_var_str('REMOTE_ADDR'); $event['IP'] = rg_var_str('REMOTE_ADDR');
94 rg_log("DEBUG: Switch IP to " . $event['IP']);
95 }
96 94
97 95 $ret = FALSE; $ret = FALSE;
98 96 while (1) { while (1) {
File inc/git.inc.php changed (mode: 100644) (index 8aa9f87..1708276)
... ... function rg_git_diff($a, $template_file)
1053 1053 ); );
1054 1054
1055 1055 $ret .= rg_template_string($template, 0 /*off*/, $ret .= rg_template_string($template, 0 /*off*/,
1056 $a, FALSE /*xss_protection*/);
1056 $a, FALSE /*xss*/);
1057 1057 } }
1058 1058 } }
1059 1059 $ret .= "</table>\n"; $ret .= "</table>\n";
 
... ... function rg_git_update_branch($db, $a)
1310 1310 $x['needed_rights'] = 'H'; $x['needed_rights'] = 'H';
1311 1311 if (rg_rights_allow($db, $x) !== TRUE) { if (rg_rights_allow($db, $x) !== TRUE) {
1312 1312 $_z = array(); $_z = array();
1313 $msg = rg_template("msg/push_not_allowed.txt", $_z, FALSE /* xss */);
1313 $msg = rg_template("msg/push_not_allowed.txt", $_z, FALSE /*xss*/);
1314 1314 rg_git_fatal($a['refname']. "\n" . $msg); rg_git_fatal($a['refname']. "\n" . $msg);
1315 1315 } }
1316 1316
 
... ... function rg_git_update_branch($db, $a)
1334 1334 if ($r !== TRUE) if ($r !== TRUE)
1335 1335 rg_git_fatal($a['refname'] . ": " . rg_mr_error()); rg_git_fatal($a['refname'] . ": " . rg_mr_error());
1336 1336 $_x = array(); $_x = array();
1337 $msg = rg_template("msg/push_merge_request.txt", $_x, FALSE /* xss */);
1337 $msg = rg_template("msg/push_merge_request.txt", $_x, FALSE /*xss*/);
1338 1338 rg_git_info($a['refname'] . "\n" . $msg); rg_git_info($a['refname'] . "\n" . $msg);
1339 1339
1340 1340 $history['history_category'] = REPO_CAT_GIT_BRANCH_ANON_PUSH; $history['history_category'] = REPO_CAT_GIT_BRANCH_ANON_PUSH;
File inc/ssh.inc.php changed (mode: 100644) (index 59f81dd..25057e5)
... ... function rg_ssh_repo($db, $uid, $paras)
88 88 } }
89 89 } }
90 90
91 /*
92 * Helper for totp_verify_ip - mostly to not duplicate error messages
93 */
94 function rg_ssh_totp_verify_ip($db, $uid, $ip)
95 {
96 $ret = FALSE;
97 while (1) {
98 $r = rg_totp_verify_ip($db, $uid, $ip);
99 if (($r['ok'] == 0) || (empty($r['ip_list']))) {
100 echo 'Error: ' . rg_totp_error() . ".\n";
101 break;
102 }
103 if ($r['enrolled'] == 0) {
104 echo 'Info: You are not enrolled.' . "\n";
105 break;
106 }
107
108 $ret = $r['ip_list'];
109 break;
110 }
111
112 return $ret;
113 }
114
91 115 /* /*
92 116 * Deal with TOTP stuff * Deal with TOTP stuff
93 117 */ */
 
... ... function rg_ssh_totp($db, $ip, $uid, $paras)
99 123
100 124 $cmd = array_shift($paras); $cmd = array_shift($paras);
101 125 switch ($cmd) { switch ($cmd) {
102 case 'enroll':
126 case 'enroll': // this has nothing to do with scratch codes
103 127 if (empty($paras)) { if (empty($paras)) {
104 128 $secret = rg_totp_base32_generate(16); $secret = rg_totp_base32_generate(16);
105 129
 
... ... function rg_ssh_totp($db, $ip, $uid, $paras)
120 144
121 145 $token = array_shift($paras); $token = array_shift($paras);
122 146
123 $v = rg_totp_verify_all($db, $uid, $token);
124 if ($v['ok'] != 1) {
125 echo 'Error: ' . rg_totp_error() . ".\n";
126 break;
127 }
128 if ($v['id'] == 0) {
147 $v = rg_totp_device_verify($db, $uid, $token);
148 if ($v['token_valid'] != 1) {
129 149 echo 'Error: ' . rg_totp_error() . ".\n"; echo 'Error: ' . rg_totp_error() . ".\n";
130 150 break; break;
131 151 } }
 
... ... function rg_ssh_totp($db, $ip, $uid, $paras)
140 160 $hours = 0; $hours = 0;
141 161 $minutes = 0; $minutes = 0;
142 162 if (empty($paras)) { if (empty($paras)) {
143 $hours = 24;
163 $hours = 1;
144 164 } else { } else {
145 165 $s_expire = array_shift($paras); $s_expire = array_shift($paras);
146 166 $val = intval($s_expire); $val = intval($s_expire);
 
... ... function rg_ssh_totp($db, $ip, $uid, $paras)
155 175 } }
156 176 //rg_log("token=$token days=$days hours=$hours minutes=$minutes"); //rg_log("token=$token days=$days hours=$hours minutes=$minutes");
157 177
158 $v = rg_totp_verify_all($db, $uid, $token);
159 if ($v['ok'] != 1) {
160 echo 'Error: ' . rg_totp_error() . ".\n";
161 break;
162 }
163 if ($v['id'] == 0) {
178 $v = rg_totp_verify_any($db, $uid, $token);
179 if ($v['token_valid'] != 1) {
164 180 echo 'Error: ' . rg_totp_error() . ".\n"; echo 'Error: ' . rg_totp_error() . ".\n";
165 181 break; break;
166 182 } }
 
... ... function rg_ssh_totp($db, $ip, $uid, $paras)
175 191 } }
176 192
177 193 echo 'Success! IP ' . $ip . ' added and is valid till ' echo 'Success! IP ' . $ip . ' added and is valid till '
178 . gmdate('Y-m-d H:i:s', $expire_ts) . ' UTC.' . "\n";
194 . gmdate('Y-m-d H:i:s', $expire_ts) . ' (UTC).' . "\n";
179 195 break; break;
180 196
181 197 case 'list-val': case 'list-val':
182 $r = rg_totp_verify_ip($db, $uid, $ip);
183 if ($r['ok'] != 1) {
184 echo 'Error: ' . rg_totp_error() . ".\n";
185 break;
186 }
187 if ($r['enrolled'] == 0) {
188 echo 'Info: You are not enrolled.' . ".\n";
198 $r = rg_ssh_totp_verify_ip($db, $uid, $ip);
199 if ($r === FALSE)
189 200 break; break;
190 }
191 201
192 202 echo 'Insert date (UTC) Expire date (UTC) IP' . "\n"; echo 'Insert date (UTC) Expire date (UTC) IP' . "\n";
193 foreach ($r['list'] as $t) {
203 foreach ($r as $t) {
194 204 echo gmdate('Y-m-d H:i:s', $t['itime']) echo gmdate('Y-m-d H:i:s', $t['itime'])
195 205 . ' ' . gmdate('Y-m-d H:i:s', $t['expire']) . ' ' . gmdate('Y-m-d H:i:s', $t['expire'])
196 206 . ' ' . $t['ip'] . ' ' . $t['ip']
 
... ... function rg_ssh_totp($db, $ip, $uid, $paras)
202 212 case 'unenroll': case 'unenroll':
203 213 $token = array_shift($paras); $token = array_shift($paras);
204 214
205 $v = rg_totp_verify_all($db, $uid, $token);
206 if ($v['ok'] != 1) {
215 $v = rg_totp_verify_any($db, $uid, $token);
216 if ($v['token_valid'] != 1) {
207 217 echo 'Error: ' . rg_totp_error() . ".\n"; echo 'Error: ' . rg_totp_error() . ".\n";
208 218 break; break;
209 219 } }
210 if ($v['id'] == 0) {
220
221 $r = rg_totp_unenroll($db, $uid);
222 if ($r !== TRUE) {
223 echo'Error: ' . rg_totp_error() . ".\n";
224 break;
225 }
226
227 echo 'You are now unenrolled.' . "\n";
228 break;
229
230 case 'remove-device':
231 $token = array_shift($paras);
232
233 $v = rg_totp_device_verify($db, $uid, $token);
234 if ($v['token_valid'] != 1) {
211 235 echo 'Error: ' . rg_totp_error() . ".\n"; echo 'Error: ' . rg_totp_error() . ".\n";
212 236 break; break;
213 237 } }
 
... ... function rg_ssh_totp($db, $ip, $uid, $paras)
230 254
231 255 $del_ip = array_shift($paras); $del_ip = array_shift($paras);
232 256
233 $r = rg_totp_verify_ip($db, $uid, $ip);
234 if ($r['ok'] != 1) {
235 echo 'Error: ' . rg_totp_error() . ".\n";
257 if (rg_ssh_totp_verify_ip($db, $uid, $ip) === FALSE)
236 258 break; break;
237 }
238 if ($r['enrolled'] == 0) {
239 echo 'Info: You are not enrolled.' . ".\n";
240 break;
241 }
242 259
243 260 $r = rg_totp_del_ip($db, $uid, $del_ip); $r = rg_totp_del_ip($db, $uid, $del_ip);
244 if ($r !== TRUE) {
261 if ($r['found'] != 1) {
245 262 echo 'Error: ' . rg_totp_error() . ".\n"; echo 'Error: ' . rg_totp_error() . ".\n";
246 263 break; break;
247 264 } }
 
... ... function rg_ssh_totp($db, $ip, $uid, $paras)
252 269 default: default:
253 270 echo 'Posible TOTP commands:' . "\n"; echo 'Posible TOTP commands:' . "\n";
254 271 echo ' enroll <token> - adds a new device in the system' . "\n"; echo ' enroll <token> - adds a new device in the system' . "\n";
255 echo ' val [X(w|d|h|m|s)] - adds your IP to the allow list for X time' . "\n";
256 echo ' the default is 24 hours;' . "\n";
257 echo ' X: w for weeks, d for days, h for hours, m for minutes, and s for seconds' . "\n";
272 echo ' val [X(w|d|h|m|s)] <token> - adds your IP to the allow list for X time' . "\n";
273 echo ' the default is 1 hour; X is a number; defauls is \'minutes\';' . "\n";
274 echo ' w=weeks, d=days, h=hours, m=minutes, and s for seconds' . "\n";
258 275 echo ' list-val - lists the already validated IPs' . "\n"; echo ' list-val - lists the already validated IPs' . "\n";
259 276 echo ' inval ip|all - invalidates IP address(es)' . "\n"; echo ' inval ip|all - invalidates IP address(es)' . "\n";
260 echo ' unenroll <token> - removes a device from TOTP system' . "\n";
277 echo ' remove-device <token> - removes a device from TOTP system' . "\n";
278 echo ' unenroll <token> - removes all devices and scratch codes from TOTP system' . "\n";
279 echo "\n";
280 echo 'Notes:' . "\n";
281 echo ' - <token> means a code generated by mobile device or a scratch code' . "\n";
261 282 break; break;
262 283 } }
263 284
 
... ... function rg_ssh_dispatch($db, $ip, $uid, $orig_cmd)
293 314 case 'totp': break; // totp will verify the ip only for some commands case 'totp': break; // totp will verify the ip only for some commands
294 315 default: default:
295 316 $r = rg_totp_verify_ip($db, $uid, $ip); $r = rg_totp_verify_ip($db, $uid, $ip);
296 if ($r['ok'] != 1) {
317 if (($r['ok'] == 0)
318 || (($r['enrolled'] == 1) && (empty($r['ip_list'])))) {
297 319 echo 'Error: ' . rg_totp_error() . ".\n"; echo 'Error: ' . rg_totp_error() . ".\n";
298 320 exit(0); exit(0);
299 321 } }
File inc/struct.inc.php changed (mode: 100644) (index 24b34ec..b39dbd8)
... ... $rg_sql_struct[32]['other'] = array(
415 415 ); );
416 416
417 417 $rg_sql_struct[33] = array(); $rg_sql_struct[33] = array();
418 $rg_sql_struct[33]['tables'] = array();
419 $rg_sql_struct[33]['other'] = array(
420 "suggestion_itime" => "ALTER TABLE suggestions ADD itime INTEGER NOT NULL DEFAULT 0",
421 "suggestion_email" => "ALTER TABLE suggestions DROP email",
418 $rg_sql_struct[33]['tables'] = array(
422 419 "login_tokens" => "CREATE TABLE login_tokens (" "login_tokens" => "CREATE TABLE login_tokens ("
423 420 . "id SERIAL" . "id SERIAL"
424 421 . ", uid INT NOT NULL DEFAULT 0" . ", uid INT NOT NULL DEFAULT 0"
 
... ... $rg_sql_struct[33]['other'] = array(
429 426 . ", ip TEXT NOT NULL DEFAULT ''" . ", ip TEXT NOT NULL DEFAULT ''"
430 427 . ", conf BOOLEAN NOT NULL DEFAULT 't'" . ", conf BOOLEAN NOT NULL DEFAULT 't'"
431 428 . ", last_used_tc INT NOT NULL DEFAULT 0)", . ", last_used_tc INT NOT NULL DEFAULT 0)",
432 "login_tokens_i_uid" =>
433 "CREATE INDEX login_tokens_i_uid ON login_tokens(uid)",
434 'login_tokens_ip' => "CREATE TABLE login_tokens_ip ("
429 "login_tokens_ip" => "CREATE TABLE login_tokens_ip ("
435 430 . "uid INT NOT NULL DEFAULT 0" . "uid INT NOT NULL DEFAULT 0"
436 431 . ", ip TEXT NOT NULL DEFAULT ''" . ", ip TEXT NOT NULL DEFAULT ''"
437 432 . ", itime INT NOT NULL DEFAULT 0" . ", itime INT NOT NULL DEFAULT 0"
438 433 . ", expire INT NOT NULL DEFAULT 0" . ", expire INT NOT NULL DEFAULT 0"
439 . ", token_id INT NOT NULL DEFAULT 0)",
434 . ", token_id INT NOT NULL DEFAULT 0)"
435 );
436 $rg_sql_struct[33]['other'] = array(
437 "suggestion_itime" => "ALTER TABLE suggestions ADD itime INTEGER NOT NULL DEFAULT 0",
438 "suggestion_email" => "ALTER TABLE suggestions DROP email",
439 "login_tokens_i_uid" =>
440 "CREATE INDEX login_tokens_i_uid ON login_tokens(uid)",
440 441 "login_tokens_ip_i_uid" => "login_tokens_ip_i_uid" =>
441 442 "CREATE INDEX login_tokens_ip_i_uid ON login_tokens_ip(uid)" "CREATE INDEX login_tokens_ip_i_uid ON login_tokens_ip(uid)"
442 443 ); );
443 444
445 $rg_sql_struct[34] = array();
446 $rg_sql_struct[34]['tables'] = array(
447 "scratch_codes" =>
448 "CREATE TABLE scratch_codes (uid INT NOT NULL DEFAULT 0"
449 . ", itime INT NOT NULL DEFAULT 0"
450 . ", sc TEXT NOT NULL DEFAULT '')"
451 );
452 $rg_sql_struct[34]['other'] = array(
453 "scratch_codes_i_uid" =>
454 "CREATE INDEX scratch_codes_i_uid ON scratch_codes(uid)",
455 );
456
444 457 // This must be the last line // This must be the last line
445 458 $rg_sql_schema_ver = count($rg_sql_struct); $rg_sql_schema_ver = count($rg_sql_struct);
446 459
File inc/totp.inc.php changed (mode: 100644) (index 6b55fda..96502f1)
... ... function rg_totp_verify($key, $ts, $token)
122 122 rg_prof_start('totp_verify'); rg_prof_start('totp_verify');
123 123 rg_log_enter('totp_verify ts=' . $ts . ', token=' . $token); rg_log_enter('totp_verify ts=' . $ts . ', token=' . $token);
124 124
125 $tc = intval($ts / 30);
126 $list = array($tc, $tc - 1, $tc - 2, $tc + 1, $tc + 2);
127 foreach ($list as $tc) {
128 $ret = rg_totp_verify_tc($key, $tc, $token);
129 //rg_log('DEBUG: using tc ' . $tc . ', ret=' . $ret);
130 if ($ret !== FALSE)
125 $ret = FALSE;
126 while (1) {
127 if (empty($token))
131 128 break; break;
129
130 $token = sprintf("%06u", $token);
131 $tc = intval($ts / 30);
132 $list = array($tc, $tc - 1, $tc - 2, $tc + 1, $tc + 2);
133 foreach ($list as $tc) {
134 $ret = rg_totp_verify_tc($key, $tc, $token);
135 //rg_log('DEBUG: using tc ' . $tc . ', ret=' . $ret);
136 if ($ret !== FALSE)
137 break;
138 }
139
140 break;
132 141 } }
133 142
134 143 rg_log_exit(); rg_log_exit();
 
... ... function rg_totp_cosmetic(&$row)
194 203 } }
195 204 } }
196 205
206 /*
207 * Cosmetic fixes for a scratch token row
208 */
209 function rg_totp_sc_cosmetic(&$row)
210 {
211 if (isset($row['itime']))
212 $row['itime_nice'] = gmdate('Y-m-d H:i', $row['itime']);
213 }
214
215 /*
216 * Returns if the user is enrolled or not
217 */
218 function rg_totp_enrolled($db, $uid)
219 {
220 $ret = array();
221 $ret['ok'] = 0;
222 $ret['enrolled'] = 1;
223 while (1) {
224 $lt = rg_totp_device_list($db, $uid);
225 if ($lt['ok'] != 1)
226 break;
227
228 // We will not consider unconfirmed entries as enrollment
229 foreach ($lt['list'] as $t) {
230 if (strcmp($t['conf'], 't') == 0) {
231 $ret['ok'] = 1;
232 return $ret;
233 }
234 }
235
236 $sc = rg_totp_sc_list($db, $uid);
237 if ($sc['ok'] != 1)
238 break;
239 if (!empty($sc['list'])) {
240 $ret['ok'] = 1;
241 return $ret;
242 }
243
244 $ret['enrolled'] = 0;
245 $ret['ok'] = 1;
246 break;
247 }
248 return $ret;
249 }
250
197 251 /* /*
198 252 * Sets when a login token was last used * Sets when a login token was last used
199 253 */ */
 
... ... function rg_totp_set_last_use($db, $uid, $id, $tc, $ts)
217 271 . ' AND id = @@id@@'; . ' AND id = @@id@@';
218 272 $res = rg_sql_query_params($db, $sql, $params); $res = rg_sql_query_params($db, $sql, $params);
219 273 if ($res === FALSE) { if ($res === FALSE) {
220 rg_user_set_error('cannot update last used (' . rg_sql_error());
274 rg_totp_set_error('cannot update last used (' . rg_sql_error());
221 275 break; break;
222 276 } }
223 277 rg_sql_free_result($res); rg_sql_free_result($res);
224 278
225 279 $key = 'user' . '::' . $uid . '::' . 'login_tokens' $key = 'user' . '::' . $uid . '::' . 'login_tokens'
226 . '::' . 'list' . '::' . $id;
280 . '::' . 'device' . '::' . $id;
227 281 $a = array('used' => $ts, 'last_used_tc' => $tc, 'conf' => 't'); $a = array('used' => $ts, 'last_used_tc' => $tc, 'conf' => 't');
228 282 rg_totp_cosmetic($a); rg_totp_cosmetic($a);
229 283 rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT); rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT);
 
... ... function rg_totp_set_last_use($db, $uid, $id, $tc, $ts)
240 294 /* /*
241 295 * Returns a list of login tokens from database * Returns a list of login tokens from database
242 296 */ */
243 function rg_totp_list($db, $uid)
297 function rg_totp_device_list($db, $uid)
244 298 { {
245 rg_prof_start('totp_list');
246 rg_log_enter('totp_list');
299 rg_prof_start('totp_device_list');
300 rg_log_enter('totp_device_list');
247 301
302 $ret = array();
303 $ret['ok'] = 0;
248 304 while (1) { while (1) {
249 $key = 'user' . '::' . $uid . '::' . 'login_tokens';
250 $ret = rg_cache_get($key);
251 if ($ret !== FALSE) {
305 $key = 'user' . '::' . $uid . '::' . 'login_tokens'
306 . '::' . 'device';
307 $r = rg_cache_get($key);
308 if ($r !== FALSE) {
309 $ret['list'] = $r;
252 310 $ret['ok'] = 1; $ret['ok'] = 1;
253 311 break; break;
254 312 } }
 
... ... function rg_totp_list($db, $uid)
276 334 } }
277 335 rg_sql_free_result($res); rg_sql_free_result($res);
278 336
279 unset($ret['ok']); // we do not need it in cache
280 rg_cache_set($key, $ret, RG_SOCKET_NO_WAIT);
337 rg_cache_set($key, $ret['list'], RG_SOCKET_NO_WAIT);
281 338 $ret['ok'] = 1; $ret['ok'] = 1;
282 339 break; break;
283 340 } }
284 341
285 342 rg_log_exit(); rg_log_exit();
286 rg_prof_end('totp_list');
343 rg_prof_end('totp_device_list');
287 344 return $ret; return $ret;
288 345 } }
289 346
290 347 /* /*
291 * Validates a token
348 * Validates a device token (not a scratch code)
292 349 * Also, it marks the tokens as 'confirmed' if needed * Also, it marks the tokens as 'confirmed' if needed
293 350 */ */
294 function rg_totp_verify_all($db, $uid, $token)
351 function rg_totp_device_verify($db, $uid, $token)
295 352 { {
296 rg_prof_start('totp_verify_all');
297 rg_log_enter('totp_verify_all token=' . $token);
353 rg_prof_start('totp_device_verify');
354 rg_log_enter('totp_device_verify token=' . $token);
298 355
299 356 $now = time(); $now = time();
300 357
301 358 $ret = array(); $ret = array();
302 359 $ret['ok'] = 0; $ret['ok'] = 0;
360 $ret['enrolled'] = 0;
361 $ret['token_valid'] = 0;
362 $ret['id'] = 0;
363 $ret['reuse'] = 0;
303 364 while (1) { while (1) {
304 $lt = rg_totp_list($db, $uid);
365 $lt = rg_totp_device_list($db, $uid);
305 366 if ($lt['ok'] != 1) if ($lt['ok'] != 1)
306 367 break; break;
307 368
308 369 $ret['ok'] = 1; $ret['ok'] = 1;
309 $ret['enrolled'] = 0;
310 $ret['id'] = 0;
311 $ret['conf_error'] = 0;
312 $ret['tc'] = 0;
313
314 if (strlen($token) == 0) {
315 rg_totp_set_error('token is empty');
316 break;
317 }
318 $token = sprintf("%06u", $token);
319 370
320 rg_totp_set_error('wrong token; sync the time');
371 $err_set = FALSE;
321 372 foreach ($lt['list'] as $t) { foreach ($lt['list'] as $t) {
322 373 if (strcmp($t['conf'], 't') == 0) if (strcmp($t['conf'], 't') == 0)
323 374 if ($ret['enrolled'] == 0) if ($ret['enrolled'] == 0)
 
... ... function rg_totp_verify_all($db, $uid, $token)
328 379 continue; continue;
329 380
330 381 if ($tc <= $t['last_used_tc']) { if ($tc <= $t['last_used_tc']) {
331 rg_totp_set_error('cannot reuse the login token');
382 $ret['reuse'] = 1;
332 383 break; break;
333 384 } }
334 385
386 $ret['token_valid'] = 1;
335 387 $ret['id'] = $t['id']; $ret['id'] = $t['id'];
336 $ret['tc'] = $tc;
337 388
338 389 // Mark it as used and update 'conf' status // Mark it as used and update 'conf' status
339 390 $r = rg_totp_set_last_use($db, $uid, $t['id'], $tc, $now); $r = rg_totp_set_last_use($db, $uid, $t['id'], $tc, $now);
340 391 if ($r !== TRUE) { if ($r !== TRUE) {
392 $err_set = TRUE;
341 393 $ret['ok'] = 0; $ret['ok'] = 0;
342 394 break; break;
343 395 } }
344 396
345 // we just confirmed an unconf entry, so we are enrolled
397 // We just confirmed an unconf entry, so we are enrolled
346 398 if ($ret['enrolled'] == 0) if ($ret['enrolled'] == 0)
347 399 $ret['enrolled'] = 1; $ret['enrolled'] = 1;
348 400 break; break;
349 401 } }
350 402
403 if ($err_set) {
404 // Do nothing
405 } else if ($ret['reuse'] == 1) {
406 rg_totp_set_error('cannot reuse the login token');
407 } else if ($ret['enrolled'] == 0) {
408 rg_totp_set_error('you are not enrolled');
409 } else if ($ret['token_valid'] != 1) {
410 rg_totp_set_error('invalid token; sync the time');
411 }
412
351 413 break; break;
352 414 } }
353 415
354 416 rg_log_exit(); rg_log_exit();
355 rg_prof_end('totp_verify_all');
417 rg_prof_end('totp_device_verify');
356 418 return $ret; return $ret;
357 419 } }
358 420
 
... ... function rg_totp_enroll($db, $uid, $name, $secret, $ip, $conf)
386 448 $params['id'] = $row['id']; $params['id'] = $row['id'];
387 449 rg_totp_cosmetic($params); rg_totp_cosmetic($params);
388 450 $key = 'user' . '::' . $uid . '::' . 'login_tokens' $key = 'user' . '::' . $uid . '::' . 'login_tokens'
389 . '::' . 'list' . '::' . $params['id'];
451 . '::' . 'device' . '::' . $params['id'];
390 452 rg_cache_set($key, $params, RG_SOCKET_NO_WAIT); rg_cache_set($key, $params, RG_SOCKET_NO_WAIT);
391 453
392 454 $ret = TRUE; $ret = TRUE;
 
... ... function rg_totp_add_ip($db, $uid, $token_id, $ip, $expire_ts)
424 486 } }
425 487 rg_sql_free_result($res); rg_sql_free_result($res);
426 488
427 $key = 'user' . '::' . $uid . '::' . 'login_tokens_ip';
428 rg_cache_apush($key, $params, RG_SOCKET_NO_WAIT);
489 unset($params['uid']);
490 $eip = str_replace(':', '_', $ip);
491 $key = 'user' . '::' . $uid . '::' . 'login_tokens'
492 . '::' . 'ip' . '::' . $eip;
493 rg_cache_set($key, $params, RG_SOCKET_NO_WAIT);
429 494
430 495 $ret = TRUE; $ret = TRUE;
431 496 break; break;
 
... ... function rg_totp_add_ip($db, $uid, $token_id, $ip, $expire_ts)
437 502 } }
438 503
439 504 /* /*
440 * Del an ip from login_tokens_ip table
505 * Deletes an ip from login_tokens_ip table
441 506 */ */
442 507 function rg_totp_del_ip($db, $uid, $ip) function rg_totp_del_ip($db, $uid, $ip)
443 508 { {
444 509 rg_prof_start('totp_del_ip'); rg_prof_start('totp_del_ip');
445 510 rg_log_enter('totp_del_ip ip=' . $ip); rg_log_enter('totp_del_ip ip=' . $ip);
446 511
447 $ret = FALSE;
512 $ret = array();
513 $ret['ok'] = 0;
514 $ret['found'] = 0;
448 515 while (1) { while (1) {
449 516 $params = array('uid' => $uid, 'ip' => $ip); $params = array('uid' => $uid, 'ip' => $ip);
450 517 if (strcasecmp($ip, 'all') == 0) { if (strcasecmp($ip, 'all') == 0) {
 
... ... function rg_totp_del_ip($db, $uid, $ip)
463 530 $aff = rg_sql_affected_rows($res); $aff = rg_sql_affected_rows($res);
464 531 rg_sql_free_result($res); rg_sql_free_result($res);
465 532
533 $ret['ok'] = 1;
534
466 535 if ($aff == 0) { if ($aff == 0) {
467 536 rg_totp_set_error('ip not found'); rg_totp_set_error('ip not found');
468 537 break; break;
469 538 } }
470 539
471 $key = 'user' . '::' . $uid . '::' . 'login_tokens_ip';
540 $key = 'user' . '::' . $uid . '::' . 'login_tokens'
541 . '::' . 'ip';
542 if (strcasecmp($ip, 'all') != 0) {
543 $eip = str_replace(':', '_', $ip);
544 $key .= '::' . $eip;
545 }
472 546 rg_cache_unset($key, RG_SOCKET_NO_WAIT); rg_cache_unset($key, RG_SOCKET_NO_WAIT);
473 547
474 $ret = TRUE;
548 $ret['found'] = 1;
475 549 break; break;
476 550 } }
477 551
 
... ... function rg_totp_del_ip($db, $uid, $ip)
480 554 return $ret; return $ret;
481 555 } }
482 556
557 /*
558 * Returns a list of login tokens IPs from database
559 */
560 function rg_totp_list_ip($db, $uid)
561 {
562 rg_prof_start('totp_list_ip');
563 rg_log_enter('totp_list_ip');
564
565 while (1) {
566 $key = 'user' . '::' . $uid . '::' . 'login_tokens'
567 . '::' . 'ip';
568 $list = rg_cache_get($key);
569 if ($list !== FALSE) {
570 $ret['list'] = $list;
571 $ret['ok'] = 1;
572 break;
573 }
574
575 $ret = array();
576 $ret['ok'] = 0;
577
578 $params = array('uid' => $uid, 'now' => time());
579 $sql = 'SELECT * FROM login_tokens_ip'
580 . ' WHERE uid = @@uid@@'
581 . ' AND expire >= @@now@@'
582 . ' ORDER BY itime';
583 $res = rg_sql_query_params($db, $sql, $params);
584 if ($res === FALSE) {
585 rg_totp_set_error('cannot load login tokens ip');
586 break;
587 }
588
589 $ret['list'] = array();
590 while (($row = rg_sql_fetch_array($res))) {
591 unset($row['uid']);
592 $eip = str_replace(':', '_', $row['ip']);
593 $ret['list'][$eip] = $row;
594 }
595 rg_sql_free_result($res);
596
597 rg_cache_set($key, $ret['list'], RG_SOCKET_NO_WAIT);
598 $ret['ok'] = 1;
599 break;
600 }
601
602 rg_log_exit();
603 rg_prof_end('totp_list_ip');
604 return $ret;
605 }
606
607 /*
608 * Verifies that an IP is in the 'validated' list
609 * Returns ip_list to be used for an optional dump of the list.
610 */
611 function rg_totp_verify_ip($db, $uid, $ip)
612 {
613 rg_prof_start('totp_verify_ip');
614 rg_log_enter('totp_verify_ip ip=' . $ip);
615
616 $ret = array();
617 $ret['ok'] = 0;
618 $ret['enrolled'] = 1;
619 $ret['ip_list'] = array();
620 while (1) {
621 $r = rg_totp_enrolled($db, $uid);
622 if ($r['ok'] != 1)
623 break;
624 if ($r['enrolled'] == 0) {
625 $ret['enrolled'] = 0;
626 $ret['ok'] = 1;
627 break;
628 }
629
630 $r = rg_totp_list_ip($db, $uid);
631 if ($r['ok'] != 1)
632 break;
633
634 foreach ($r['list'] as $eip => $t) {
635 if (strcasecmp($t['ip'], $ip) == 0) {
636 $ret['ip_list'] = $r['list'];
637 $ret['ok'] = 1;
638 break;
639 }
640 }
641
642 if (empty($ret['ip_list']))
643 rg_totp_set_error('you have no IP validated;'
644 . ' run \'ssh ... totp\' for help');
645
646 break;
647 }
648
649 rg_log_exit();
650 rg_prof_end('totp_verify_ip');
651 return $ret;
652 }
653
483 654 /* /*
484 655 * Remove a list of login tokens * Remove a list of login tokens
485 656 */ */
 
... ... function rg_totp_remove($db, $uid, $list)
525 696
526 697 foreach ($my_list as $junk => $_id) { foreach ($my_list as $junk => $_id) {
527 698 $key = 'user' . '::' . $uid . '::' . 'login_tokens' $key = 'user' . '::' . $uid . '::' . 'login_tokens'
528 . '::' . 'list' . $_id;
699 . '::' . 'device' . '::' . $_id;
529 700 rg_cache_unset($key, RG_SOCKET_NO_WAIT); rg_cache_unset($key, RG_SOCKET_NO_WAIT);
530 701 } }
531 702
 
... ... function rg_totp_remove($db, $uid, $list)
539 710 } }
540 711
541 712 /* /*
542 * Returns a list of login tokens IPs from database
713 * Validates a scratch code
714 * TODO: it deletes the used code.
543 715 */ */
544 function rg_totp_list_ip($db, $uid)
716 function rg_totp_sc_verify($db, $uid, $token)
545 717 { {
546 rg_prof_start('totp_list_ip');
547 rg_log_enter('totp_list_ip');
718 rg_prof_start('totp_sc_verify');
719 rg_log_enter('totp_sc_verify token=' . $token);
720
721 $now = time();
722 $token = sprintf("%08u", $token);
548 723
724 $ret = array();
725 $ret['ok'] = 0;
726 $ret['enrolled'] = 0;
727 $ret['token_valid'] = 0;
728 $ret['id'] = 0; // we do not have an id for scratch codes; but, we may
729 // be forced to use one to delete the IPs associated.
549 730 while (1) { while (1) {
550 $key = 'user' . '::' . $uid . '::' . 'login_tokens_ip';
551 $list = rg_cache_get($key);
552 if ($list !== FALSE) {
553 $ret['list'] = $list;
554 $ret['ok'] = 1;
731 $sc = rg_totp_sc_list($db, $uid);
732 if ($sc['ok'] != 1)
555 733 break; break;
734
735 $ret['ok'] = 1;
736
737 $done = FALSE;
738 foreach ($sc['list'] as $itime => $per_itime) {
739 if ($ret['enrolled'] == 0)
740 $ret['enrolled'] = 1;
741
742 foreach ($per_itime as $sc => $junk) {
743 rg_log('DEBUG: comparing with ' . $sc);
744 if (strcmp($sc, $token) == 0) {
745 $r = rg_totp_sc_remove($db, $uid,
746 $itime, $token);
747 if ($r !== TRUE)
748 $ret['ok'] = 0;
749 else
750 $ret['token_valid'] = 1;
751 $done = TRUE;
752 break;
753 }
754 }
755 if ($done)
756 break;
556 757 } }
557 758
558 $ret = array();
559 $ret['ok'] = 0;
759 if ($ret['enrolled'] == 0)
760 rg_totp_set_error('you are not enrolled');
761 else if ($ret['token_valid'] != 1)
762 rg_totp_set_error('invalid token');
763 break;
764 }
560 765
561 $params = array('uid' => $uid, 'now' => time());
562 $sql = 'SELECT * FROM login_tokens_ip'
766 rg_log_ml('DEBUG: sc_verify returns: ' . print_r($ret, TRUE));
767
768 rg_log_exit();
769 rg_prof_end('totp_sc_verify');
770 return $ret;
771 }
772
773 /*
774 * List the scratch codes
775 */
776 function rg_totp_sc_list($db, $uid)
777 {
778 rg_prof_start('totp_sc_list');
779 rg_log_enter('totp_sc_list');
780
781 $ret = array();
782 $ret['ok'] = 0;
783 while (1) {
784 $key = 'user' . '::' . $uid . '::' . 'login_tokens'
785 . '::' . 'sc';
786 $r = rg_cache_get($key);
787 if ($r !== FALSE) {
788 $ret['list'] = $r;
789 $ret['ok'] = 1;
790 break;
791 }
792
793 $params = array('uid' => $uid);
794 $sql = 'SELECT * FROM scratch_codes'
563 795 . ' WHERE uid = @@uid@@' . ' WHERE uid = @@uid@@'
564 . ' AND expire >= @@now@@'
565 . ' ORDER BY itime';
796 . ' ORDER BY itime DESC';
566 797 $res = rg_sql_query_params($db, $sql, $params); $res = rg_sql_query_params($db, $sql, $params);
567 798 if ($res === FALSE) { if ($res === FALSE) {
568 rg_totp_set_error('cannot load login tokens ip');
799 rg_totp_set_error('cannot load scratch codes');
569 800 break; break;
570 801 } }
571 802
572 803 $ret['list'] = array(); $ret['list'] = array();
573 804 while (($row = rg_sql_fetch_array($res))) { while (($row = rg_sql_fetch_array($res))) {
574 unset($row['uid']);
575 $ret['list'][] = $row;
805 $itime = $row['itime'];
806 $sc = $row['sc'];
807
808 if (!isset($ret['list'][$itime]))
809 $ret['list'][$itime] = array();
810
811 $ret['list'][$itime][$sc] = 1;
576 812 } }
577 813 rg_sql_free_result($res); rg_sql_free_result($res);
578 814
 
... ... function rg_totp_list_ip($db, $uid)
581 817 break; break;
582 818 } }
583 819
820 //rg_log_ml('DEBUG: sc_list ret[list]: ' . print_r($ret['list'], TRUE));
821
584 822 rg_log_exit(); rg_log_exit();
585 rg_prof_end('totp_list_ip');
823 rg_prof_end('totp_sc_list');
586 824 return $ret; return $ret;
587 825 } }
588 826
589 827 /* /*
590 * Verifies that an IP is in the 'validated' list
591 * Returns -1 on error, 0 if no IP were present and 1 if all is OK
828 * Generates a list of scratch codes for a user
592 829 */ */
593 function rg_totp_verify_ip($db, $uid, $ip)
830 function rg_totp_sc_generate($db, $uid, $count)
594 831 { {
595 rg_prof_start('totp_verify_ip');
596 rg_log_enter('totp_verify_ip ip=' . $ip);
832 rg_prof_start('totp_sc_generate');
833 rg_log_enter('totp_sc_generate count=' . $count);
597 834
598 835 $ret = array(); $ret = array();
599 $ret['ok'] = -1;
600 $ret['enrolled'] = 0;
836 $ret['ok'] = 0;
837 $ret['list'] = array();
601 838 while (1) { while (1) {
602 // we will return OK if user is not enrolled
603 $r = rg_totp_list($db, $uid);
604 if ($r['ok'] != 1)
839 $bin = rg_random_bytes($count * 4);
840 if ($bin === FALSE)
605 841 break; break;
606 if (empty($r['list'])) {
607 $ret['ok'] = 1;
842
843 for ($i = 0; $i < $count; $i++) {
844 $t = substr($bin, $i * 4, 4);
845 $t2 = unpack('L', $t);
846 $t2 = $t2[1] % 100000000;
847 $sc = sprintf('%08u', $t2);
848 $ret['list'][$sc] = 1;
849 }
850
851 $count -= count($ret['list']);
852 if ($count != 0)
853 continue;
854
855 $now = time();
856
857 $params = array('uid' => $uid, 'itime' => $now);
858 $sql_add = '';
859 $add = '';
860 $i = 0;
861 foreach ($ret['list'] as $sc => $junk) {
862 $params['token_' . $i] = $sc;
863 $sql_add .= $add . '(@@uid@@, @@itime@@, @@token_' . $i . '@@)';
864 $add = ',';
865 $i++;
866 }
867
868 $sql = 'INSERT INTO scratch_codes (uid, itime, sc)'
869 . ' VALUES ' . $sql_add;
870 $res = rg_sql_query_params($db, $sql, $params);
871 if ($res === FALSE) {
872 rg_totp_set_error('cannot insert scratch codes; try again later');
608 873 break; break;
609 874 } }
875 rg_sql_free_result($res);
610 876
611 // let's see if we enrolled
612 foreach ($r['list'] as $t) {
613 if (strcmp($t['conf'], 't') == 0) {
614 $ret['enrolled'] = 1;
615 break;
616 }
877 rg_totp_cosmetic($params);
878 $key = 'user' . '::' . $uid . '::' . 'login_tokens'
879 . '::' . 'sc' . '::' . $now;
880 rg_cache_set($key, $ret['list'], RG_SOCKET_NO_WAIT);
881
882 $ret['ok'] = 1;
883 break;
884 }
885
886 rg_log_exit();
887 rg_prof_end('totp_sc_generate');
888 return $ret;
889 }
890
891 /*
892 * Removes one scratch code
893 */
894 function rg_totp_sc_remove($db, $uid, $itime, $token)
895 {
896 rg_prof_start('totp_sc_remove');
897 rg_log_enter('totp_sc_remove uid=' . $uid
898 . ' token=' . $token);
899
900 $ret = FALSE;
901 while (1) {
902 $params = array('uid' => $uid ,
903 'itime' => $itime,
904 'token' => $token);
905 $sql = 'DELETE FROM scratch_codes'
906 . ' WHERE uid = @@uid@@'
907 . ' AND itime = @@itime@@'
908 . ' AND sc = @@token@@';
909 $res = rg_sql_query_params($db, $sql, $params);
910 if ($res === FALSE) {
911 rg_totp_set_error('cannot remove scratch code');
912 break;
617 913 } }
914 rg_sql_free_result($res);
618 915
619 $r = rg_totp_list_ip($db, $uid);
916 $key = 'user' . '::' . $uid . '::' . 'login_tokens'
917 . '::' . 'sc' . '::' . $itime . '::' . $token;
918 rg_cache_unset($key, RG_SOCKET_NO_WAIT);
919
920 $ret = TRUE;
921 break;
922 }
923
924 rg_log_exit();
925 rg_prof_end('totp_sc_remove');
926 return $ret;
927 }
928
929 /*
930 * Remove a list of scratch codes
931 */
932 function rg_totp_sc_remove_list($db, $uid, $list)
933 {
934 rg_prof_start('totp_sc_remove_list');
935 rg_log_enter('totp_sc_remove_list uid=' . $uid
936 . ' list=' . rg_array2string($list));
937
938 $ret = FALSE;
939 while (1) {
940 if (empty($list)) {
941 rg_totp_set_error('you did not select anything');
942 break;
943 }
944
945 $my_list = array();
946 foreach ($list as $id => $junk)
947 $my_list[] = sprintf("%u", $id);
948
949 $params = array('uid' => $uid);
950 $sql_list = implode(', ', $my_list);
951
952 $sql = 'DELETE FROM scratch_codes'
953 . ' WHERE uid = @@uid@@'
954 . ' AND itime IN (' . $sql_list . ')';
955 $res = rg_sql_query_params($db, $sql, $params);
956 if ($res === FALSE) {
957 rg_totp_set_error('cannot remove scratch codes');
958 break;
959 }
960 rg_sql_free_result($res);
961
962 foreach ($my_list as $junk => $_itime) {
963 $key = 'user' . '::' . $uid . '::' . 'login_tokens'
964 . '::' . 'sc' . '::' . $_itime;
965 rg_cache_unset($key, RG_SOCKET_NO_WAIT);
966 }
967
968 $ret = TRUE;
969 break;
970 }
971
972 rg_log_exit();
973 rg_prof_end('totp_sc_remove_list');
974 return $ret;
975 }
976
977 /*
978 * Unenroll function - clean everything
979 */
980 function rg_totp_unenroll($db, $uid)
981 {
982 rg_prof_start('totp_unenroll');
983 rg_log_enter('totp_unenroll');
984
985 $ret = FALSE;
986
987 $rollback = FALSE;
988 while (1) {
989 $key = 'user' . '::' . $uid . '::' . 'login_tokens';
990 $r = rg_cache_unset($key, 0);
991 if ($r === FALSE) {
992 rg_totp_set_error('cannot clear cache');
993 break;
994 }
995
996 $r = rg_sql_begin($db);
997 if ($r === FALSE) {
998 rg_totp_set_error('cannot start transaction');
999 break;
1000 }
1001 $rollback = TRUE;
1002
1003 $r = rg_totp_del_ip($db, $uid, "all");
620 1004 if ($r['ok'] != 1) if ($r['ok'] != 1)
621 1005 break; break;
622 1006
623 $ret['ok'] = 0;
624 foreach ($r['list'] as $t) {
625 if (strcasecmp($t['ip'], $ip) == 0) {
626 $ret['list'] = $r['list'];
627 $ret['ok'] = 1;
1007 $ok = TRUE;
1008 $params = array('uid' => $uid);
1009 $list = array('login_tokens', 'login_tokens_ip', 'scratch_codes');
1010 foreach ($list as $t) {
1011 $sql = 'DELETE FROM ' . $t
1012 . ' WHERE uid = @@uid@@';
1013 $res = rg_sql_query_params($db, $sql, $params);
1014 if (!$res) {
1015 rg_totp_set_error('cannot delete login tokens information');
1016 $ok = FALSE;
628 1017 break; break;
629 1018 } }
630 1019 } }
1020 if (!$ok)
1021 break;
631 1022
632 if ($ret['ok'] == 0)
633 rg_totp_set_error('you have no IP validated;'
634 . ' run \'ssh ... totp\' for help');
1023 rg_sql_commit($db);
1024 $ret = TRUE;
1025 $rollback = FALSE;
1026 break;
1027 }
1028
1029 if ($rollback)
1030 rg_sql_rollback($db);
1031
1032 rg_log_exit();
1033 rg_prof_end('totp_unenroll');
1034 return $ret;
1035 }
1036
1037 /*
1038 * Verifies either a login token generated by a device or scratch codes
1039 */
1040 function rg_totp_verify_any($db, $uid, $token)
1041 {
1042 rg_prof_start('totp_verify_any');
1043 rg_log_enter('totp_verify_any token=' . $token);
1044
1045 $ret = array();
1046 $ret['ok'] = 1;
1047 $ret['id'] = 0;
1048 $ret['token_valid'] = 0;
1049 $ret['enrolled'] = 0;
1050 while (1) {
1051 $r = rg_totp_device_verify($db, $uid, $token);
1052 if ($r['ok'] != 1) {
1053 $ret['ok'] = 0;
1054 break;
1055 }
1056
1057 if ($r['enrolled'] == 1)
1058 $ret['enrolled'] = 1;
1059
1060 if ($r['reuse'] == 1)
1061 break;
1062
1063 if ($r['token_valid'] == 1) {
1064 $ret['token_valid'] = 1;
1065 $ret['id'] = $r['id'];
1066 break;
1067 }
1068
1069 $r = rg_totp_sc_verify($db, $uid, $token);
1070 if ($r['ok'] != 1) {
1071 $ret['ok'] = 0;
1072 break;
1073 }
1074
1075 if ($r['enrolled'] == 1)
1076 $ret['enrolled'] = 1;
1077
1078 if ($r['token_valid'] == 1) {
1079 $ret['token_valid'] = 1;
1080 break;
1081 }
635 1082
636 1083 break; break;
637 1084 } }
638 1085
1086 rg_log_ml("DEBUG: verify_any returns: " . print_r($ret, TRUE));
1087
639 1088 rg_log_exit(); rg_log_exit();
640 rg_prof_end('totp_verify_ip');
1089 rg_prof_end('totp_verify_any');
1090 return $ret;
1091 }
1092
1093 /*
1094 * High-level function for scratch codes
1095 */
1096 function rg_totp_sc_high_level($db, $rg, $paras)
1097 {
1098 rg_prof_start('totp_sc_high_level');
1099 rg_log_enter('totp_sc_high_level');
1100
1101 $ret = '';
1102
1103 $rg['HTML:gen_errmsg'] = '';
1104 $gen_errmsg = array();
1105 $generate = rg_var_uint('generate');
1106 while ($generate == 1) {
1107 if (!rg_valid_referer()) {
1108 $gen_errmsg[] = 'invalid referer; try again';
1109 break;
1110 }
1111
1112 if (!rg_token_valid($db, $rg, 'sc', FALSE)) {
1113 $gen_errmsg[] = 'invalid token; try again.';
1114 break;
1115 }
1116
1117 $r = rg_totp_sc_generate($db, $rg['login_ui']['uid'], 20);
1118 if ($r['ok'] != 1) {
1119 $gen_errmsg[] = rg_totp_error();
1120 break;
1121 }
1122
1123 $rg['scratch_codes'] = implode(' ', array_keys($r['list']));
1124 $ret .= rg_template('user/settings/totp/sc/gen_ok.html', $rg, TRUE /*xss*/);
1125 break;
1126 }
1127 $rg['HTML:gen_errmsg'] = rg_template_errmsg($gen_errmsg);
1128
1129 $rg['HTML:del_errmsg'] = '';
1130 $rg['HTML:del_status'] = '';
1131 $del_errmsg = array();
1132 $delete = rg_var_uint('delete');
1133 while ($delete == 1) {
1134 if (!rg_valid_referer()) {
1135 $del_errmsg[] = 'invalid referer; try again';
1136 break;
1137 }
1138
1139 if (!rg_token_valid($db, $rg, 'sc', FALSE)) {
1140 $del_errmsg[] = 'invalid token; try again.';
1141 break;
1142 }
1143
1144 $list = rg_var_str("delete_list");
1145 $r = rg_totp_sc_remove_list($db, $rg['login_ui']['uid'], $list);
1146 if ($r !== TRUE) {
1147 $del_errmsg[] = 'cannot delete: ' . rg_totp_error();
1148 break;
1149 }
1150
1151 $rg['HTML:del_status'] = rg_template('user/settings/totp/sc/delete_ok.html',
1152 $rg, TRUE /*xss*/);
1153 break;
1154 }
1155 $rg['HTML:del_errmsg'] = rg_template_errmsg($del_errmsg);
1156
1157 $rg['rg_form_token'] = rg_token_get($db, $rg, 'sc');
1158 $ret .= rg_template('user/settings/totp/sc/gen.html', $rg, TRUE /*xss*/);
1159
1160 $r = rg_totp_sc_list($db, $rg['login_ui']['uid']);
1161 if ($r['ok'] !== 1) {
1162 $rg['totp_errmsg'] = rg_totp_error();
1163 $ret .= rg_template('user/settings/totp/sc/list_err.html',
1164 $rg, TRUE /*xss*/);
1165 } else {
1166 // prepare list
1167 $_list = array();
1168 //rg_log_ml('DEBUG: prepare sc list: ' . print_r($r['list'], TRUE));
1169 foreach ($r['list'] as $itime => $per_itime) {
1170 foreach ($per_itime as $junk => $sc) {
1171 if (!isset($_list[$itime])) {
1172 $_list[$itime] = array(
1173 'itime' => $itime,
1174 'sc_count' => 1);
1175 rg_totp_sc_cosmetic($_list[$itime]);
1176 } else {
1177 $_list[$itime]['sc_count']++;
1178 }
1179 }
1180 }
1181
1182 $ret .= rg_template_table('user/settings/totp/sc/list', $_list, $rg);
1183 }
1184
1185 // hints
1186 $hints = array();
1187 $hints[]['HTML:hint'] = rg_template("user/settings/totp/sc/hints.html", $rg, TRUE /*xss*/);
1188 $ret .= rg_template_table("hints/list", $hints, $rg);
1189
1190 rg_log_exit();
1191 rg_prof_end('totp_sc_high_level');
641 1192 return $ret; return $ret;
642 1193 } }
643 1194
644 1195 /* /*
645 1196 * High-level function for listing tokens * High-level function for listing tokens
646 1197 */ */
647 function rg_totp_list_high_level($db, $rg)
1198 function rg_totp_list_high_level($db, $rg, $paras)
648 1199 { {
1200 rg_prof_start('totp_list_high_level');
1201 rg_log_enter('totp_list_high_level');
1202
649 1203 $ret = ''; $ret = '';
650 1204
651 1205 $del_errmsg = array(); $del_errmsg = array();
 
... ... function rg_totp_list_high_level($db, $rg)
676 1230 break; break;
677 1231 } }
678 1232
679 $r = rg_totp_list($db, $rg['login_ui']['uid']);
1233 $r = rg_totp_device_list($db, $rg['login_ui']['uid']);
680 1234 if ($r['ok'] !== 1) { if ($r['ok'] !== 1) {
681 1235 $rg['totp_errmsg'] = rg_totp_error(); $rg['totp_errmsg'] = rg_totp_error();
682 1236 $ret .= rg_template('user/settings/totp/list_err.html', $ret .= rg_template('user/settings/totp/list_err.html',
 
... ... function rg_totp_list_high_level($db, $rg)
687 1241 $ret .= rg_template_table('user/settings/totp/list', $r['list'], $rg); $ret .= rg_template_table('user/settings/totp/list', $r['list'], $rg);
688 1242 } }
689 1243
1244 rg_log_exit();
1245 rg_prof_end('totp_list_high_level');
690 1246 return $ret; return $ret;
691 1247 } }
692 1248
693 1249 /* /*
694 * Main function for TOTP login token
1250 * Enroll function for TOTP login token
695 1251 */ */
696 function rg_totp_high_level($db, $rg)
1252 function rg_totp_enroll_high_level($db, $rg, $paras)
697 1253 { {
698 rg_prof_start('totp_high_level');
699 rg_log_enter('totp_high_level');
1254 rg_prof_start('totp_enroll_high_level');
1255 rg_log_enter('totp_enroll_high_level');
700 1256
701 1257 $now = time(); $now = time();
702 1258
 
... ... function rg_totp_high_level($db, $rg)
728 1284 break; break;
729 1285 } }
730 1286
731 if (!rg_token_valid($db, $rg, 'user_totp', FALSE)) {
1287 if (!rg_token_valid($db, $rg, 'user_totp_enroll', FALSE)) {
732 1288 $errmsg[] = "invalid token; try again"; $errmsg[] = "invalid token; try again";
733 1289 break; break;
734 1290 } }
 
... ... function rg_totp_high_level($db, $rg)
754 1310 break; break;
755 1311 } }
756 1312
757 $ret .= rg_totp_list_high_level($db, $rg);
758
759 1313 // defaults // defaults
760 1314 if ($enroll == 0) { if ($enroll == 0) {
761 1315 $name = ''; $name = '';
 
... ... function rg_totp_high_level($db, $rg)
777 1331
778 1332 $rg['HTML:errmsg'] = rg_template_errmsg($errmsg); $rg['HTML:errmsg'] = rg_template_errmsg($errmsg);
779 1333
780 $rg['rg_form_token'] = rg_token_get($db, $rg, 'user_totp');
781 $ret .= rg_template('user/settings/totp/main.html', $rg, TRUE /*xss*/);
1334 $rg['rg_form_token'] = rg_token_get($db, $rg, 'user_totp_enroll');
1335 rg_log_ml('DEBUG rg: ' . print_r($rg, TRUE));
1336 $ret .= rg_template('user/settings/totp/enroll.html', $rg, TRUE /*xss*/);
782 1337
783 // hints
784 $hints = array();
785 $hints[]['HTML:hint'] = rg_template("user/settings/totp/hints.html", $rg, FALSE /*xss*/);
786 $ret .= rg_template_table("hints/list", $hints, $rg);
1338 rg_log_exit();
1339 rg_prof_end('totp_enroll_high_level');
1340 return $ret;
1341 }
1342
1343 /*
1344 * Main HL function for TOTP login token
1345 */
1346 function rg_totp_high_level($db, $rg, $paras)
1347 {
1348 rg_prof_start('totp_high_level');
1349 rg_log_enter('totp_high_level');
1350
1351 $now = time();
1352
1353 $ret = '';
1354
1355 $op = empty($paras) ? 'info' : array_shift($paras);
1356 $rg['menu']['totp'][$op] = 1;
1357
1358 $ret .= rg_template('user/settings/totp/menu.html', $rg, TRUE /*xss*/);
1359
1360 switch ($op) {
1361 case 'info': // we show only the hints
1362 // hints
1363 $hints = array();
1364 $hints[]['HTML:hint'] = rg_template("user/settings/totp/hints.html", $rg, TRUE /*xss*/);
1365 $ret .= rg_template_table("hints/list", $hints, $rg);
1366 break;
1367
1368 case 'enroll':
1369 $ret .= rg_totp_enroll_high_level($db, $rg, $paras);
1370 break;
1371
1372 case 'sc':
1373 $ret .= rg_totp_sc_high_level($db, $rg, $paras);
1374 break;
1375
1376 default:
1377 $ret .= rg_totp_list_high_level($db, $rg, $paras);
1378 break;
1379 }
787 1380
788 1381 rg_log_exit(); rg_log_exit();
789 1382 rg_prof_end('totp_high_level'); rg_prof_end('totp_high_level');
File inc/user.inc.php changed (mode: 100644) (index 6ca3783..1c4bf0d)
... ... function rg_user_info($db, $uid, $user, $email)
605 605 $ret['exists'] = 0; $ret['exists'] = 0;
606 606 $ret['uid'] = 0; $ret['uid'] = 0;
607 607 $ret['is_admin'] = 0; $ret['is_admin'] = 0;
608 $ret['rights'] = "";
608 $ret['rights'] = '';
609 $ret['homepage'] = '';
609 610 while (1) { while (1) {
610 611 $params = array("uid" => $uid, $params = array("uid" => $uid,
611 612 "user" => $user, "user" => $user,
 
... ... function rg_user_info($db, $uid, $user, $email)
618 619 if ($c !== FALSE) { if ($c !== FALSE) {
619 620 if (isset($c['info'])) { if (isset($c['info'])) {
620 621 $ret = $c['info']; $ret = $c['info'];
622 $ret['exists'] = 1;
621 623 break; break;
622 624 } }
623 625 } }
 
... ... function rg_user_info($db, $uid, $user, $email)
665 667 } }
666 668
667 669 $ret = array_merge($ret, $row); $ret = array_merge($ret, $row);
668 $ret['exists'] = 1;
670
671 $ret['homepage'] = rg_re_userpage($ret);
669 672
670 673 rg_cache_set('user' . '::' . $ret['uid'] . '::' . 'info', rg_cache_set('user' . '::' . $ret['uid'] . '::' . 'info',
671 674 $ret, RG_SOCKET_NO_WAIT); $ret, RG_SOCKET_NO_WAIT);
 
... ... function rg_user_info($db, $uid, $user, $email)
673 676 $ret['uid'], RG_SOCKET_NO_WAIT); $ret['uid'], RG_SOCKET_NO_WAIT);
674 677 rg_cache_set('email_to_uid::' . $ret['email'], $ret['uid'], rg_cache_set('email_to_uid::' . $ret['email'], $ret['uid'],
675 678 RG_SOCKET_NO_WAIT); RG_SOCKET_NO_WAIT);
679
680 $ret['exists'] = 1;
676 681 break; break;
677 682 } }
678 683
 
... ... function rg_user_login_by_user_pass($db, $user, $pass, $login_token, $lock_ip,
902 907 break; break;
903 908 } }
904 909
905 $vi = rg_totp_verify_all($db, $ui0['uid'], $login_token);
910 $vi = rg_totp_verify_any($db, $ui0['uid'], $login_token);
906 911 if ($vi['ok'] != 1) { if ($vi['ok'] != 1) {
907 912 rg_user_set_error('login token error: ' . rg_totp_error()); rg_user_set_error('login token error: ' . rg_totp_error());
908 913 break; break;
909 914 } }
910 915 //rg_log_ml('DEBUG: vi: ' . print_r($vi, TRUE)); //rg_log_ml('DEBUG: vi: ' . print_r($vi, TRUE));
911 if (($vi['enrolled'] == 1) && ($vi['id'] == 0)) {
916 if (($vi['enrolled'] == 1) && ($vi['token_valid'] != 1)) {
912 917 rg_user_set_error('invalid user, pass or login token'); rg_user_set_error('invalid user, pass or login token');
913 918 rg_log('invalid token'); rg_log('invalid token');
914 919 break; break;
 
... ... function rg_user_edit_high_level($db, &$rg)
1543 1548 $ui['tos'] = rg_var_uint('tos'); $ui['tos'] = rg_var_uint('tos');
1544 1549
1545 1550 if ($ui['tos'] != 1) { if ($ui['tos'] != 1) {
1546 $errmsg[] = rg_template('user/tos_deny.html', $rg, FALSE /*xss*/);
1551 $errmsg[] = rg_template('user/tos_deny.html', $rg, TRUE /*xss*/);
1547 1552 break; break;
1548 1553 } }
1549 1554
File inc/user/home-page.php changed (mode: 100644) (index 7540b39..bd05f96)
... ... if ($rg['page_ui']['exists'] == 0) {
11 11
12 12 $_home .= rg_template('user/home.html', $rg, TRUE /*xss*/); $_home .= rg_template('user/home.html', $rg, TRUE /*xss*/);
13 13
14 // list of repositories
14
15 // List of repositories
15 16 $_home .= rg_repo_list($db, $rg, '', $rg['page_ui']['uid']); $_home .= rg_repo_list($db, $rg, '', $rg['page_ui']['uid']);
17
18
19 // hints
20 $hints = array();
21
22 $r = rg_totp_enrolled($db, $rg['login_ui']['uid']);
23 if (($r['ok'] == 1) && ($r['enrolled'] == 0))
24 $hints[]['HTML:hint'] = rg_template('user/hints/totp.html',
25 $rg, TRUE /*xss*/);
26
27 $_home .= rg_template_table('hints/list', $hints, $rg);
16 28 ?> ?>
File inc/user/settings.php changed (mode: 100644) (index 912c88f..cddd83b)
... ... case 'keys':
35 35 break; break;
36 36
37 37 case 'totp': case 'totp':
38 $_settings .= rg_totp_high_level($db, $rg);
38 $_settings .= rg_totp_high_level($db, $rg, $paras);
39 39 break; break;
40 40 } }
41 41
File inc/util.inc.php changed (mode: 100644) (index 94d70e8..d94706d)
... ... function rg_mail_template($template, $more)
1340 1340 $more['HTML:utf8_rg_admin_name'] = "=?UTF-8?B?" $more['HTML:utf8_rg_admin_name'] = "=?UTF-8?B?"
1341 1341 . base64_encode($rg_admin_name) . "?="; . base64_encode($rg_admin_name) . "?=";
1342 1342
1343 $subject = rg_template($template . ".subj.txt", $more, FALSE /* xss */);
1343 $subject = rg_template($template . ".subj.txt", $more, FALSE /*xss*/);
1344 1344 $subject = trim($subject); $subject = trim($subject);
1345 1345 $subject = str_replace("\r", '', $subject); $subject = str_replace("\r", '', $subject);
1346 1346 $subject = str_replace("\n", '', $subject); $subject = str_replace("\n", '', $subject);
1347 1347 // TODO: do not encode it as UTF-8 if not needed // TODO: do not encode it as UTF-8 if not needed
1348 1348 $subject = "=?UTF-8?B?" . base64_encode($subject) . "?="; $subject = "=?UTF-8?B?" . base64_encode($subject) . "?=";
1349 1349
1350 $header = rg_template("mail/common.head.txt", $more, FALSE /* xss */);
1351 $header .= rg_template($template . ".head.txt", $more, FALSE /* xss */);
1350 $header = rg_template("mail/common.head.txt", $more, FALSE /*xss*/);
1351 $header .= rg_template($template . ".head.txt", $more, FALSE /*xss*/);
1352 1352 $header = trim($header); $header = trim($header);
1353 1353
1354 $body = rg_template($template . ".body.txt", $more, FALSE /* xss */);
1354 $body = rg_template($template . ".body.txt", $more, FALSE /*xss*/);
1355 1355
1356 1356 rg_log("CHECK: mail_template(" . $more['ui']['email'] . ", rg_log("CHECK: mail_template(" . $more['ui']['email'] . ",
1357 1357 $subject, $body, $header, -f $rg_admin_email"); $subject, $body, $header, -f $rg_admin_email");
 
... ... function rg_socket($path, $buf, $timeout, $tries, $flags)
1492 1492 if (isset($rg_socket_cache[$path])) { if (isset($rg_socket_cache[$path])) {
1493 1493 $socket = $rg_socket_cache[$path]; $socket = $rg_socket_cache[$path];
1494 1494 } else { } else {
1495 rg_prof_start('sock_create');
1495 rg_prof_start('socket-connect');
1496 1496 $socket = @socket_create(AF_UNIX, SOCK_STREAM, 0); $socket = @socket_create(AF_UNIX, SOCK_STREAM, 0);
1497 1497 if ($socket === FALSE) { if ($socket === FALSE) {
1498 1498 rg_log("Could not create socket (" . socket_strerror(socket_last_error()) . ")!"); rg_log("Could not create socket (" . socket_strerror(socket_last_error()) . ")!");
 
... ... function rg_socket($path, $buf, $timeout, $tries, $flags)
1500 1500 } }
1501 1501
1502 1502 while ($tries > 0) { while ($tries > 0) {
1503 rg_prof_start('sock_conn');
1504 1503 $r = @socket_connect($socket, $path); $r = @socket_connect($socket, $path);
1505 rg_prof_end('sock_conn');
1506 1504 if ($r === FALSE) { if ($r === FALSE) {
1507 1505 $tries--; $tries--;
1508 1506 usleep(50 * 1000); usleep(50 * 1000);
 
... ... function rg_socket($path, $buf, $timeout, $tries, $flags)
1515 1513 rg_log("Could not connect the socket (" . socket_strerror(socket_last_error()) . ")!"); rg_log("Could not connect the socket (" . socket_strerror(socket_last_error()) . ")!");
1516 1514 break; break;
1517 1515 } }
1518 rg_prof_end('sock_create');
1516 rg_prof_end('socket-connect');
1519 1517
1520 1518 $rg_socket_cache[$path] = $socket; $rg_socket_cache[$path] = $socket;
1521 1519 } }
File root/index.php changed (mode: 100644) (index 149704e..87616a5)
... ... if ($r === FALSE) {
148 148 } }
149 149
150 150 if ($rg['login_ui']['uid'] > 0) { if ($rg['login_ui']['uid'] > 0) {
151 $rg['login_ui']['homepage'] = rg_re_userpage($rg['login_ui']);
152 151 $rg['logout_token'] = rg_token_get($db, $rg, 'logout'); $rg['logout_token'] = rg_token_get($db, $rg, 'logout');
153 152 } else { } else {
154 $rg['login_ui']['username'] = "";
155 $rg['login_ui']['homepage'] = "";
156 $rg['logout_token'] = "";
153 $rg['login_ui']['username'] = '';
154 $rg['logout_token'] = '';
157 155 } }
158 156
159 157
File root/themes/default/main.css changed (mode: 100644) (index 6ded205..672c0b9)
... ... form input[type="radio"] {
62 62 form select option { padding: 1px 4px 1px 4px; } form select option { padding: 1px 4px 1px 4px; }
63 63 form input[type="submit"] { form input[type="submit"] {
64 64 color: #F00; color: #F00;
65 display: inline-block;
66 65 font-weight: bold; font-weight: bold;
67 66 font-size: 11pt; font-size: 11pt;
68 67 padding: 2px 4px 2px 4px; padding: 2px 4px 2px 4px;
 
... ... legend { padding: 0px 2pt; }
143 142
144 143
145 144 .main_title { .main_title {
145 text-align: left;
146 146 margin-bottom: 10px; margin-bottom: 10px;
147 147 color: #F00; color: #F00;
148 148 font-size: 20pt; font-size: 20pt;
149 149 font-weight: bold; font-weight: bold;
150 150 padding-bottom: 5px; padding-bottom: 5px;
151 151 border-bottom: 2px solid #F00; border-bottom: 2px solid #F00;
152 xxx-width: 70%;
153 margin-left: auto;
154 margin-right: auto;
152 155 } }
153 156
154 157 .junk {} .junk {}
 
... ... legend { padding: 0px 2pt; }
166 169
167 170
168 171 #main_container { #main_container {
169 width: 100%;
172 width: 80%;
173 margin-left: auto;
174 margin-right: auto;
170 175 display: table-row; display: table-row;
171 176 } }
172 177
173 178 #main { #main {
174 179 margin-top: 10px; margin-top: 10px;
180 width: 80%;
175 181 margin-left: auto; margin-left: auto;
176 182 margin-right: auto; margin-right: auto;
177 183 padding: 10px 15px; padding: 10px 15px;
178 184 line-height: 120%; line-height: 120%;
179 185 display: table; display: table;
186 text-align: center;
180 187 } }
181 188
182 189 #header { #header {
 
... ... legend { padding: 0px 2pt; }
241 248 } }
242 249
243 250 .formarea { .formarea {
251 text-align: left;
244 252 margin-top: 5pt; margin-top: 5pt;
245 253 border: 1px solid #999998; border: 1px solid #999998;
246 254 padding: 5pt; padding: 5pt;
 
... ... legend { padding: 0px 2pt; }
319 327 background-color: #eee; background-color: #eee;
320 328 } }
321 329
322 .repo_container { }
330 .repo_container {
331 text-align: left;
332 }
323 333
324 334 .repo_header { } .repo_header { }
325 335
 
... ... legend { padding: 0px 2pt; }
384 394 } }
385 395
386 396 .hints { .hints {
397 text-align: left;
387 398 margin-top: 10pt; margin-top: 10pt;
388 399 background-color: #FFFFFF; background-color: #FFFFFF;
389 400 padding: 4pt; padding: 4pt;
 
... ... legend { padding: 0px 2pt; }
468 479 } }
469 480
470 481 .islands { .islands {
482 text-align: left;
471 483 font-size: 13pt; font-size: 13pt;
472 width: 700px;
484 xxx-width: 70%;
485 margin-left: auto;
486 margin-right: auto;
473 487 } }
474 488
475 489 .island_row { .island_row {
 
... ... legend { padding: 0px 2pt; }
504 518 border: 1px solid #00F; border: 1px solid #00F;
505 519 padding: 5pt; padding: 5pt;
506 520 margin: 5pt 0pt; margin: 5pt 0pt;
521 display: inline-block;
507 522 } }
508 523
509 524 .page_description { .page_description {
File root/themes/default/repo/list/line.html changed (mode: 100644) (index e399320..fcdd4f2)
1 1 <tr> <tr>
2 <td><a href="@@url_user@@">@@owner@@</a>/<a href="@@url_repo@@">@@name@@</a></td>
2 <td><a href="@@url_user@@">@@owner@@</a> / <a href="@@url_repo@@">@@name@@</a> (#@@repo_id@@)</td>
3 3 <td><small>@@description_nlbr@@</small></td> <td><small>@@description_nlbr@@</small></td>
4 4 <td>@@clone_of@@</td> <td>@@clone_of@@</td>
5 5 <td>@@creation@@</td> <td>@@creation@@</td>
File root/themes/default/repo/list/nodata.html changed (mode: 100644) (index fec6095..1b6b4c6)
1 1 <div class="mess ok"> <div class="mess ok">
2 2 No repositories found. No repositories found.
3 Go to <a href="/op/repo">My repositories</a> to create one.
3 Go to <a href="/op/repo/create">My repositories / Create</a> to add one.
4 4 </div> </div>
File root/themes/default/user/hints/totp.html added (mode: 100644) (index 0000000..576e573)
1 You are <b>not</b> enrolled in
2 <a href="https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm" target="_blank">
3 two-factor authentication
4 </a>
5 for extra security.
6 Go to <a href="/op/settings/totp/info">Settings / Login tokens</a> to activate it.
File root/themes/default/user/login.html changed (mode: 100644) (index b4fd388..18b25d6)
19 19 </p> </p>
20 20
21 21 <p> <p>
22 <label for="login_token">Login token (leave empty if you did not enrolled)</label><br />
22 <label for="login_token">Login token / scratch code (leave empty if you did not enrolled)</label><br />
23 23 <input type="text" name="login_token" id="login_token" value="@@login_token@@" autocomplete="off" /> <input type="text" name="login_token" id="login_token" value="@@login_token@@" autocomplete="off" />
24 24 </p> </p>
25 25
File root/themes/default/user/settings/menu.html changed (mode: 100644) (index 3766aea..ca19265)
5 5 <li@@if(@@set_menu::edit_info@@ == 1){{ class="selected"}}{{}}><a href="/op/settings/edit_info">Edit info</a></li> <li@@if(@@set_menu::edit_info@@ == 1){{ class="selected"}}{{}}><a href="/op/settings/edit_info">Edit info</a></li>
6 6 <li@@if(@@set_menu::change_pass@@ == 1){{ class="selected"}}{{}}><a href="/op/settings/change_pass">Change password</a></li> <li@@if(@@set_menu::change_pass@@ == 1){{ class="selected"}}{{}}><a href="/op/settings/change_pass">Change password</a></li>
7 7 <li@@if(@@set_menu::keys@@ == 1){{ class="selected"}}{{}}><a href="/op/settings/keys">SSH keys</a></li> <li@@if(@@set_menu::keys@@ == 1){{ class="selected"}}{{}}><a href="/op/settings/keys">SSH keys</a></li>
8 <li@@if(@@set_menu::totp@@ == 1){{ class="selected"}}{{}}><a href="/op/settings/totp">Login token</a></li>
8 <li@@if(@@set_menu::totp@@ == 1){{ class="selected"}}{{}}><a href="/op/settings/totp">Login tokens</a></li>
9 9 </ul> </ul>
10 10 </div> </div>
File root/themes/default/user/settings/totp/enroll.html renamed from root/themes/default/user/settings/totp/main.html (similarity 80%) (mode: 100644) (index dfd8054..b1a7238)
1 1 <div class="formarea"> <div class="formarea">
2 2
3 <div class="formarea_title">Enroll a new phone</div>
3 <div class="formarea_title">Enroll a new device</div>
4 4
5 5 @@errmsg@@ @@errmsg@@
6 6
7 <form method="post" action="/op/settings/totp">
7 <form method="post" action="/op/settings/totp/enroll">
8 8 <input type="hidden" name="enroll" value="1" /> <input type="hidden" name="enroll" value="1" />
9 9 <input type="hidden" name="token" value="@@rg_form_token@@" /> <input type="hidden" name="token" value="@@rg_form_token@@" />
10 10 <input type="hidden" name="totp::secret" value="@@totp::secret@@" /> <input type="hidden" name="totp::secret" value="@@totp::secret@@" />
 
17 17 <p> <p>
18 18 @@if(@@totp::img@@ == 1){{ @@if(@@totp::img@@ == 1){{
19 19 Scan the following QR Code using the mobile application:<br /> Scan the following QR Code using the mobile application:<br />
20 <img class="secret_token" src="data:image/png;base64,@@totp::png@@" />
20 <img class="secret_token" src="data:image/png;base64,@@totp::png@@" alt="qr code" />
21 21 <br /> <br />
22 22 If you cannot scan the code above, manually enter the following key If you cannot scan the code above, manually enter the following key
23 23 into the mobile application:<br /> into the mobile application:<br />
24 <div class="secret_token">@@totp::secret@@</div>
25 24 }}{{ }}{{
26 25 Manually enter the following key into the mobile application:<br /> Manually enter the following key into the mobile application:<br />
27 <div class="secret_token">@@totp::secret@@</div>
28 26 }} }}
29 27 </p> </p>
28 <div class="secret_token">@@totp::secret@@</div>
30 29
31 30 <p> <p>
32 <label for="ver">Enter the 6 digit number shown on the mobile phone</label><br />
31 <label for="ver">Enter the 6 digit number shown on the mobile device</label><br />
33 32 <input type="text" name="totp::ver" id="ver" value="@@totp::ver@@" /> <input type="text" name="totp::ver" id="ver" value="@@totp::ver@@" />
34 33 </p> </p>
35 34
File root/themes/default/user/settings/totp/hints.html changed (mode: 100644) (index 4cfcc51..7b5182e)
1 1 Login token feature implements two-factor authentication (2FA) using Login token feature implements two-factor authentication (2FA) using
2 2 <a href="https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm" target="_blank"> <a href="https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm" target="_blank">
3 3 TOTP (Time-based One-time Password Algorithm) TOTP (Time-based One-time Password Algorithm)
4 </a> and will add an extra protection to your login process.<br />
4 </a> and will add extra protection to your login process.<br />
5 5 <br /> <br />
6
7 If you do not want to use a device or for cases when the device is not
8 accessible or if you loose your device, you may want to generate and use
9 <a href="/op/settings/totp/sc">scratch codes</a>.<br />
10 <br />
11
6 12 How it works:<br /> How it works:<br />
7 13 <ul> <ul>
8 14 <li> <li>
9 Install a TOTP application on your mobile phone (we recommend
15 Install a TOTP application on your mobile device (we recommend
10 16 <i>FreeOTP</i> or <i>Google Authenticator</i> for <i>FreeOTP</i> or <i>Google Authenticator</i> for
11 17 Android/iPhone/Blackberry and Android/iPhone/Blackberry and
12 18 <i>Authenticator</i> for Windows Phone). <i>Authenticator</i> for Windows Phone).
13 19 </li> </li>
14 20
15 21 <li> <li>
16 Make sure that the device clock is synchronized automatically.
22 Make sure that the device clock is correct (synchronized automatically).
17 23 </li> </li>
18 24
19 25 <li> <li>
20 Using the just installed application, scan the QR Code (if shown)
21 or the secret provided on screen.
26 Using the just installed application,
27 scan the QR Code (if shown) or the secret provided on screen.
22 28 </li> </li>
23 29
24 30 <li> <li>
 
... ... How it works:<br />
34 40 </ul> </ul>
35 41 <br /> <br />
36 42 You may enroll multiple devices if needed. Use the 'name' field to distinguish You may enroll multiple devices if needed. Use the 'name' field to distinguish
37 between them.
43 between them.<br />
44 <br />
45 If you loose your device, login with a scratch code and delete all enrollments
46 done with the lost device.
File root/themes/default/user/settings/totp/hints_enroll.html copied from file root/themes/default/errmsg/nodata.html (similarity 100%)
File root/themes/default/user/settings/totp/list/header.html changed (mode: 100644) (index fde6509..35b0aeb)
3 3 @@del_status@@ @@del_status@@
4 4 @@del_errmsg@@ @@del_errmsg@@
5 5
6 <form method="post" action="/op/settings/totp">
6 <form method="post" action="/op/settings/totp/list">
7 7 <input type="hidden" name="delete" value="1" /> <input type="hidden" name="delete" value="1" />
8 8 <input type="hidden" name="token" value="@@rg_form_token@@" /> <input type="hidden" name="token" value="@@rg_form_token@@" />
9 9
File root/themes/default/user/settings/totp/menu.html added (mode: 100644) (index 0000000..d6af971)
1 <div class="menu menu3">
2 <ul>
3 <li@@if(@@menu::totp::info@@ == 1){{ class="selected"}}><a href="/op/settings/totp/info">Info</a></li>
4 <li@@if(@@menu::totp::list@@ == 1){{ class="selected"}}><a href="/op/settings/totp/list">List devices</a></li>
5 <li@@if(@@menu::totp::enroll@@ == 1){{ class="selected"}}><a href="/op/settings/totp/enroll">Enroll device</a></li>
6 <li@@if(@@menu::totp::sc@@ == 1){{ class="selected"}}><a href="/op/settings/totp/sc">Scratch codes</a></li>
7 </ul>
8 </div>
File root/themes/default/user/settings/totp/sc/delete_ok.html added (mode: 100644) (index 0000000..ed04164)
1 <div class="mess ok">
2 Scratch codes deleted with success.
3 </div>
File root/themes/default/user/settings/totp/sc/gen.html added (mode: 100644) (index 0000000..85d6848)
1 <div class="formarea">
2
3 <div class="formarea_title">Generate a new scratch codes set</div>
4
5 @@gen_errmsg@@
6
7 <form method="post" action="/op/settings/totp/sc">
8 <input type="hidden" name="generate" value="1" />
9 <input type="hidden" name="token" value="@@rg_form_token@@" />
10
11 <input type="submit" name="button" value="Generate" />
12
13 </form>
14 </div>
File root/themes/default/user/settings/totp/sc/gen_ok.html added (mode: 100644) (index 0000000..911499c)
1 <div class="mess ok">
2 Scratch codes generated with success. Please print or save them now
3 - after leaving this page they will not be visible anymore.<br />
4
5 <div class="secret_token">
6 @@scratch_codes@@
7 </div>
8 </div>
File root/themes/default/user/settings/totp/sc/hints.html added (mode: 100644) (index 0000000..7b3b10b)
1 You can print the list of generated scratch codes and keep the paper into your
2 wallet or save them in a password manager on your phone/tablet/PC.
File root/themes/default/user/settings/totp/sc/list/footer.html copied from file root/themes/default/user/settings/totp/list/footer.html (similarity 100%)
File root/themes/default/user/settings/totp/sc/list/header.html copied from file root/themes/default/user/settings/totp/list/header.html (similarity 56%) (mode: 100644) (index fde6509..6b1b78d)
3 3 @@del_status@@ @@del_status@@
4 4 @@del_errmsg@@ @@del_errmsg@@
5 5
6 <form method="post" action="/op/settings/totp">
6 <form method="post" action="/op/settings/totp/sc">
7 7 <input type="hidden" name="delete" value="1" /> <input type="hidden" name="delete" value="1" />
8 8 <input type="hidden" name="token" value="@@rg_form_token@@" /> <input type="hidden" name="token" value="@@rg_form_token@@" />
9 9
10 <table summary="login tokens">
10 <table summary="scratch codes">
11 11 <tr> <tr>
12 12 <th>Select</th> <th>Select</th>
13 <th>Name</th>
14 13 <th>Date (UTC)</th> <th>Date (UTC)</th>
15 <th>Enroll IP</th>
16 <th>Confirmed?</th>
17 <th>Last time used (UTC)</th>
14 <th>Scratch codes left</th>
18 15 </tr> </tr>
File root/themes/default/user/settings/totp/sc/list/line.html added (mode: 100644) (index 0000000..1a67793)
1 <tr>
2 <td><input type="checkbox" name="delete_list[@@itime@@]" /></td>
3 <td>@@itime_nice@@</td>
4 <td>@@sc_count@@</td>
5 </tr>
6
File root/themes/default/user/settings/totp/sc/list/nodata.html copied from file root/themes/default/user/settings/totp/list/nodata.html (similarity 65%) (mode: 100644) (index 5c1aea8..2b6ed5b)
1 1 @@del_status@@ @@del_status@@
2 2
3 3 <div class="mess ok"> <div class="mess ok">
4 No login tokens found.
4 No scratch codes found.
5 5 </div> </div>
File root/themes/default/user/settings/totp/sc/list_err.html added (mode: 100644) (index 0000000..17c26fc)
1 <div class="mess error">
2 Could not load scratch codes (@@totp_errmsg@@). Please try again later.
3 </div>
File root/themes/default/user/settings/totp/ver_error.html changed (mode: 100644) (index eb29e41..28fd8e7)
1 The code could not be validated. Make sure the clock is in sync on your phone.
1 The code could not be validated. Make sure the clock is in sync on your device.
2 2 Also, you may mistyped the code if you entered it manually. Please try again. Also, you may mistyped the code if you entered it manually. Please try again.
File root/themes/extract_texts.sh changed (mode: 100755) (index 8367450..3d6e277)
... ... fi
10 10 cd "${dir}" cd "${dir}"
11 11 find . -type f -name '*.txt' -exec cat {} \; find . -type f -name '*.txt' -exec cat {} \;
12 12 //find . -type f -name '*.html' -exec lynx -dump {} \; //find . -type f -name '*.html' -exec lynx -dump {} \;
13 ) | sort | uniq > extract_texts.out
13 ) > extract_texts.out
File scripts/cache.php changed (mode: 100644) (index 7865345..a07bcab)
... ... require_once($INC . "/ver.php");
31 31
32 32 function rg_destroy($k, &$conn_table) function rg_destroy($k, &$conn_table)
33 33 { {
34 rg_log("Destroying key $k...");
34 35 if (isset($conn_table['r'][$k])) if (isset($conn_table['r'][$k]))
35 36 unset($conn_table['r'][$k]); unset($conn_table['r'][$k]);
36 37 if (isset($conn_table['w'][$k])) if (isset($conn_table['w'][$k]))
37 38 unset($conn_table['w'][$k]); unset($conn_table['w'][$k]);
38 39 // TODO: seems socket is already closed // TODO: seems socket is already closed
39 if (is_resource($conn_table['conns'][$k]['socket']))
40 socket_close($conn_table['conns'][$k]['socket']);
40 if (isset($conn_table['conns'][$k]['socket']))
41 if (is_resource($conn_table['conns'][$k]['socket']))
42 socket_close($conn_table['conns'][$k]['socket']);
41 43 unset($conn_table['conns'][$k]); unset($conn_table['conns'][$k]);
42 44 } }
43 45
44 46 function rg_handle_command($k, &$conn_table, $cmd) function rg_handle_command($k, &$conn_table, $cmd)
45 47 { {
46 rg_log("rg_handle_command: k=$k, cmd=$cmd");
48 //rg_log("rg_handle_command: k=$k, cmd=$cmd");
47 49
48 50 $s = &$conn_table['conns'][$k]; $s = &$conn_table['conns'][$k];
49 51
 
... ... function rg_handle_command($k, &$conn_table, $cmd)
206 208
207 209 function rg_handle_recv($k, &$conn_table) function rg_handle_recv($k, &$conn_table)
208 210 { {
211 //rg_log("handle_recv on key $k...");
209 212 $s = &$conn_table['conns'][$k]; $s = &$conn_table['conns'][$k];
210 213
211 214 $ret = @socket_recv($s['socket'], $buf, 32 * 4096, 0); $ret = @socket_recv($s['socket'], $buf, 32 * 4096, 0);
 
... ... function rg_handle_recv($k, &$conn_table)
215 218 return; return;
216 219 } }
217 220 if ($ret === 0) { if ($ret === 0) {
218 //rg_log("Remote closed the connection.");
221 rg_log("Remote closed the connection (received 0).");
219 222 rg_destroy($k, $conn_table); rg_destroy($k, $conn_table);
220 223 return; return;
221 224 } }
 
... ... function rg_handle_recv($k, &$conn_table)
223 226 rg_log("Received data on key $k [$buf]..."); rg_log("Received data on key $k [$buf]...");
224 227
225 228 $s['recv'] .= $buf; $s['recv'] .= $buf;
226 $s['close_at'] = time() + 300;
229 $s['close_at'] = time() + 30;
227 230
228 231 while (1) { while (1) {
229 232 $pos = strpos($s['recv'], "\n"); $pos = strpos($s['recv'], "\n");
 
... ... function rg_handle_recv($k, &$conn_table)
231 234 return; return;
232 235
233 236 $cmd = substr($s['recv'], 0, $pos); $cmd = substr($s['recv'], 0, $pos);
234 $s['recv'] = substr($s['recv'], $pos + 1);
235
236 237 rg_handle_command($k, $conn_table, $cmd); rg_handle_command($k, $conn_table, $cmd);
237 };
238
239 $s['recv'] = substr($s['recv'], $pos + 1);
240 }
238 241 } }
239 242
240 243 function rg_handle_send($k, &$conn_table) function rg_handle_send($k, &$conn_table)
241 244 { {
245 //rg_log("Sending on key $k...");
242 246 $s = &$conn_table['conns'][$k]; $s = &$conn_table['conns'][$k];
243 247
244 rg_log("Sending on key $k [" . $s['send'] . "]...");
245 248 $ret = @socket_send($s['socket'], $s['send'], strlen($s['send']), 0); $ret = @socket_send($s['socket'], $s['send'], strlen($s['send']), 0);
246 249 if ($ret === FALSE) { if ($ret === FALSE) {
247 250 rg_log("Cannot send (" . socket_strerror(socket_last_error()) . ")"); rg_log("Cannot send (" . socket_strerror(socket_last_error()) . ")");
 
... ... function rg_handle_new($client, &$conn_table)
263 266 "socket" => $client, "socket" => $client,
264 267 "recv" => "", "recv" => "",
265 268 "send" => "", "send" => "",
266 "close_at" => time() + 300
269 "close_at" => time() + 30
267 270 ); );
268 271 $conn_table['r'][$key] = $client; $conn_table['r'][$key] = $client;
269 rg_log("Added client with key $key.");
272 //rg_log("Added client with key $key.");
273
274 /* This way I can enforce the connecting user to be apache/rocketgit
275 but is seems it does not work correctly in PHP...
276 $t = socket_get_option($client, SOL_SOCKET, 17 //SO_PEERCRED);
277 rg_log_ml('PEERCRED: ' . print_r($t, TRUE));
278 */
270 279 } }
271 280
272 281 function rg_handle_idle(&$conn_table) function rg_handle_idle(&$conn_table)
 
... ... function rg_handle_idle(&$conn_table)
274 283 $now = time(); $now = time();
275 284
276 285 foreach ($conn_table['conns'] as $key => $info) { foreach ($conn_table['conns'] as $key => $info) {
277 if (strcmp($key, "master") == 0)
286 if (!isset($info['close_at'])) {
287 rg_internal_error('close_at is not set!');
278 288 continue; continue;
279
289 }
280 290 if ($info['close_at'] < $now) { if ($info['close_at'] < $now) {
281 291 rg_log("Destroy $key because was too much time idle."); rg_log("Destroy $key because was too much time idle.");
282 292 rg_destroy($key, $conn_table); rg_destroy($key, $conn_table);
 
... ... $conn_table['r']['master'] = $master;
329 339 do { do {
330 340 rg_log_buffer_clear(); rg_log_buffer_clear();
331 341
342 rg_log_ml("conn_table: " . print_r($conn_table, TRUE));
343
332 344 $r2 = $conn_table['r']; $w2 = $conn_table['w']; $ex = array(); $r2 = $conn_table['r']; $w2 = $conn_table['w']; $ex = array();
333 $r = socket_select($r2, $w2, $ex, 5);
345 $r = @socket_select($r2, $w2, $ex, 5);
334 346 if ($r === FALSE) if ($r === FALSE)
335 347 rg_fatal("Cannot select (" . socket_strerror(socket_last_error()) . ")!"); rg_fatal("Cannot select (" . socket_strerror(socket_last_error()) . ")!");
336
337 //rg_log_ml("conn_table: " . print_r($conn_table, TRUE));
338
339 foreach ($r2 as $key => $socket) {
340 if (strcmp($key, "master") == 0) {
341 $client = socket_accept($socket);
342 if ($client === FALSE) {
343 rg_log("Connection seems broken!");
348 if ($r > 0) {
349 if (!empty($r2))
350 rg_log_ml('r2: ' . print_r($r2, TRUE));
351 if (!empty($w2))
352 rg_log_ml('w2: ' . print_r($w2, TRUE));
353 if (!empty($ex))
354 rg_log_ml('ex: ' . print_r($ex, TRUE));
355
356 foreach ($r2 as $key => $socket) {
357 if (strcmp($key, "master") == 0) {
358 $client = @socket_accept($socket);
359 if ($client === FALSE) {
360 rg_log("Connection seems broken!");
361 continue;
362 }
363
364 rg_handle_new($client, $conn_table);
344 365 continue; continue;
345 366 } }
346 367
347 rg_handle_new($client, $conn_table);
348 continue;
368 rg_handle_recv($key, $conn_table);
349 369 } }
350 370
351 rg_handle_recv($key, $conn_table);
352 }
371 foreach ($w2 as $key => $sock) {
372 if (isset($conn_table['conns'][$key]))
373 rg_handle_send($key, $conn_table);
374 }
353 375
354 foreach ($w2 as $key => $sock)
355 rg_handle_send($key, $conn_table);
376 foreach ($ex as $key => $sock)
377 rg_destroy($key, $conn_table);
378 }
356 379
357 380 rg_handle_idle($conn_table); rg_handle_idle($conn_table);
358 381 } while (1); } while (1);
File scripts/cachec.php changed (mode: 100644) (index 4a8d8d5..f74357e)
... ... do {
55 55
56 56 $r .= "\n"; $r .= "\n";
57 57 $r = socket_send($master, $r, strlen($r), 0); $r = socket_send($master, $r, strlen($r), 0);
58 if ($r === FALSE)
59 break;
58 60 $r = socket_recv($master, $buf, 4096, 0); $r = socket_recv($master, $buf, 4096, 0);
59 61 echo $buf; echo $buf;
60 62 } while (1); } while (1);
File scripts/remote.php changed (mode: 100644) (index 7861c8c..698b48a)
... ... if (strncasecmp($cmd_repo, "git-upload-pack", 15) == 0) {
137 137 $needed_rights = "P|H"; $needed_rights = "P|H";
138 138 $push = 1; $push = 1;
139 139 } else { } else {
140 fatal("Unknown command [$cmd_repo]!");
140 rg_log("Unknown command [$cmd_repo]");
141 fatal("Unknown command!");
141 142 } }
142 143
143 144 // extract repository name // extract repository name
 
... ... if ($login_uid > 0) {
179 180 info('you are connecting as anonymous.'); info('you are connecting as anonymous.');
180 181 } }
181 182
182 info('you are connecting from IP ' . $ip);
183 info('you are connecting from IP ' . $ip . '.');
183 184
184 185 // Loading info about the repository // Loading info about the repository
185 186 if (rg_repo_ok($repo) !== TRUE) if (rg_repo_ok($repo) !== TRUE)
 
... ... if ($ret !== TRUE)
217 218 if (isset($_SERVER['SSH_CONNECTION'])) { if (isset($_SERVER['SSH_CONNECTION'])) {
218 219 if (($ri['public'] == 0) || ($push == 1)) { if (($ri['public'] == 0) || ($push == 1)) {
219 220 $r = rg_totp_verify_ip($db, $conn_ui['uid'], $ip); $r = rg_totp_verify_ip($db, $conn_ui['uid'], $ip);
220 if ($r['ok'] != 1)
221 if (($r['ok'] == 0) && (empty($r['list'])))
221 222 fatal(rg_totp_error() . '.'); fatal(rg_totp_error() . '.');
222 223 } }
223 224 } }
File tests/.gitignore changed (mode: 100644) (index ad248e1..8151b7c)
... ... git_log1
16 16 q_merge_requests q_merge_requests
17 17 qstats qstats
18 18 git_bin git_bin
19 keys/*
File tests/Makefile changed (mode: 100644) (index 2c556db..0c7b822)
1 tests := totp git_log1.sh \
1 tests := ssh http_totp totp git_log1.sh \
2 2 http_admin http_bug \ http_admin http_bug \
3 3 http_create_account http_login http_settings http_csrf http_top \ http_create_account http_login http_settings http_csrf http_top \
4 4 token util log state cache prof db event rights keys user repo git \ token util log state cache prof db event rights keys user repo git \
 
... ... all: $(tests)
10 10 @-ls -l err-* @-ls -l err-*
11 11 @echo "Do not forget to check for errors in /var/log/rocketgit!" @echo "Do not forget to check for errors in /var/log/rocketgit!"
12 12
13 ssh:
14 php ssh.php
15
16 http_totp:
17 php http_totp.php
18
13 19 totp: totp:
14 20 php totp.php php totp.php
15 21
 
... ... git2:
89 95 clean: clean:
90 96 @rm -rf git_log1 *.log *.strace *.strace.* *.out *.lock err-* *.diff \ @rm -rf git_log1 *.log *.strace *.strace.* *.out *.lock err-* *.diff \
91 97 http.arond *.pub git2key git2 *.in q_merge_requests/mr-* \ http.arond *.pub git2key git2 *.in q_merge_requests/mr-* \
92 qstats/* repos/*
98 qstats/* repos/* helper helper.pub keys/*
93 99
File tests/cache.php changed (mode: 100644) (index bd4cc48..6eea041)
... ... rg_cache_get('test::B');
94 94 $rg_cache_timeout = 3000; $rg_cache_timeout = 3000;
95 95 $r = rg_cache_get('test::A'); $r = rg_cache_get('test::A');
96 96 if ($r !== 'A') { if ($r !== 'A') {
97 rg_log('Small timeout test failed!');
97 rg_log('Small timeout test failed! r=' . print_r($r));
98 98 exit(1); exit(1);
99 99 } }
100 100
File tests/git2.php changed (mode: 100644) (index cb017a7..7b55f2e)
... ... if (php_uname("n") != "r1.embedromix.ro") {
47 47 } }
48 48
49 49
50 rg_log("Generate a SSH key");
51 @unlink("git2key"); @unlink("git2key.pub");
52 system("ssh-keygen -t rsa -N '' -C \"Key for RocketGit\" -f git2key </dev/null &>git2.log");
53 if (!file_exists("git2key.pub")) {
54 rg_log("Could not generate ssh key: " . file_get_contents("git2.log"));
55 exit(1);
56 }
57 chmod("git2key.pub", 0700);
58
59 50 rg_log("Creating a user..."); rg_log("Creating a user...");
60 51 rg_test_create_user($db, $rg_ui); rg_test_create_user($db, $rg_ui);
61 52 rg_test_create_repo($db, $rg_ui, $repo); rg_test_create_repo($db, $rg_ui, $repo);
 
... ... if ($r === FALSE) {
65 56 exit(1); exit(1);
66 57 } }
67 58
68 rg_log("Loading ssh key form...");
69 $data = array();
70 $headers = array("Cookie: sid=" . $good_sid);
71 $r = do_req($test_url . "/op/settings/keys?t=ssh2", $data, $headers);
72 if ($r === FALSE) {
73 rg_log("Cannot load form!");
74 exit(1);
75 }
76 if (empty($r['tokens']['keys'])) {
77 rg_log_ml("token not found! r:" . print_r($r, TRUE));
78 exit(1);
79 }
80
81 rg_log("Uploading the key...");
82 $key = file_get_contents("git2key.pub");
83 $data = array('add' => 1, 'token' => $r['tokens']['keys'], 'key' => $key);
84 $headers = array('Cookie: sid=' . $good_sid);
85 $r = do_req($test_url . '/op/settings/keys?t=git2', $data, $headers);
86 if ($r === FALSE) {
87 rg_log_ml("Cannot upload key: " . print_r($r, TRUE));
88 exit(1);
89 }
90
91 rg_log("Waiting for key to be added to the authorized_keys file");
92 while (1) {
93 $c = file_get_contents("/home/rocketgit/.ssh/authorized_keys");
94 if (strstr($c, $key))
95 break;
96
97 sleep(1);
98 }
59 $key = rg_test_upload_ssh_key($db, $rg_ui, "git2", $good_sid);
99 60
100 61 $remote = 'ssh://rocketgit@rgtest/user/' . escapeshellarg($rg_ui['username']) $remote = 'ssh://rocketgit@rgtest/user/' . escapeshellarg($rg_ui['username'])
101 62 . '/' . escapeshellarg($repo['name']); . '/' . escapeshellarg($repo['name']);
102 63 system("cd git2; git remote add origin $remote"); system("cd git2; git remote add origin $remote");
103 $ll = system("cd git2; git push origin master", $err);
64 exec("cd git2; git push origin master", $out, $err);
104 65 if ($err != 0) { if ($err != 0) {
105 rg_log("Seems I cannot push master! Last line=[$ll] err=$err");
66 rg_log_ml('out: ' . print_r($out, TRUE));
67 rg_log("Seems I cannot push master! err=$err");
106 68 exit(1); exit(1);
107 69 } }
108 $ll = system("cd git2; git push --tags origin", $err);
70 exec("cd git2; git push --tags origin", $out, $err);
109 71 if ($err != 0) { if ($err != 0) {
110 rg_log("Seems I cannot push tags! Last line=[$ll] err=$err");
72 rg_log_ml('out: ' . print_r($out, TRUE));
73 rg_log("Seems I cannot push tags! err=$err");
111 74 exit(1); exit(1);
112 75 } }
113 76
File tests/git2.sh changed (mode: 100755) (index 2ccaa3d..a0c42f1)
1 1 #!/bin/bash #!/bin/bash
2 2
3 rm -rf git2
3 4 mkdir git2 mkdir git2
4 5 cd git2 cd git2
5 6 git init git init
File tests/helpers.inc.php changed (mode: 100644) (index bcbf5b2..e17349f)
... ... function rg_test_create_repo($db, $rg_ui, &$extra)
155 155 return TRUE; return TRUE;
156 156 } }
157 157
158 /*
159 * Helper for creating and uploading a ssh key
160 * Returns the key.
161 */
162 function rg_test_upload_ssh_key($db, $rg_ui, $key_name, $good_sid)
163 {
164 global $test_url;
165
166 rg_log("Generating a SSH key [$key_name]");
167 @unlink("keys/" . $key_name); @unlink("keys/" . $key_name . ".pub");
168 $out = shell_exec("ssh-keygen -t rsa -N '' -C \"Key for RocketGit\""
169 . " -f keys/" . $key_name . " </dev/null");
170 if (!file_exists("keys/" . $key_name . ".pub")) {
171 rg_log("Could not generate ssh key: " . $out);
172 exit(1);
173 }
174
175 $key = file_get_contents("keys/" . $key_name . ".pub");
176
177 rg_log("Loading ssh key form...");
178 $data = array();
179 $headers = array("Cookie: sid=" . $good_sid);
180 $r = do_req($test_url . "/op/settings/keys?t=ssh2", $data, $headers);
181 if ($r === FALSE) {
182 rg_log("Cannot load form!");
183 exit(1);
184 }
185 if (empty($r['tokens']['keys'])) {
186 rg_log_ml("token not found! r:" . print_r($r, TRUE));
187 exit(1);
188 }
189
190 rg_log("Uploading the key...");
191 $data = array('add' => 1, 'token' => $r['tokens']['keys'], 'key' => $key);
192 $headers = array('Cookie: sid=' . $good_sid);
193 $r = do_req($test_url . '/op/settings/keys?t=upload_ssh_key', $data, $headers);
194 if ($r === FALSE) {
195 rg_log_ml("Cannot upload key: " . print_r($r, TRUE));
196 exit(1);
197 }
198
199 rg_log("Waiting for key to be added to the authorized_keys file");
200 while (1) {
201 $c = file_get_contents("/home/rocketgit/.ssh/authorized_keys");
202 if (strstr($c, $key))
203 break;
204
205 sleep(1);
206 }
207
208 rg_log("Uploading done");
209
210 return $key;
211 }
212
213 /*
214 * Helper for generating scratch codes
215 * Returns the scratch codes.
216 */
217 function rg_test_sc_generate($db, $rg_ui, $good_sid)
218 {
219 global $test_url;
220
221 rg_log("Loading generate scratch codes form...");
222 $data = array();
223 $headers = array("Cookie: sid=" . $good_sid);
224 $r = do_req($test_url . "/op/settings/totp/sc", $data, $headers);
225 if ($r === FALSE) {
226 rg_log("Cannot load form!");
227 exit(1);
228 }
229 if (empty($r['tokens']['sc'])) {
230 rg_log_ml("token not found! r:" . print_r($r, TRUE));
231 exit(1);
232 }
233
234 rg_log("Generating scratch codes...");
235 $data = array('generate' => 1, 'token' => $r['tokens']['sc']);
236 $headers = array('Cookie: sid=' . $good_sid);
237 $r = do_req($test_url . '/op/settings/totp/sc', $data, $headers);
238 if ($r === FALSE) {
239 rg_log_ml("Cannot generate scratch codes: " . print_r($r, TRUE));
240 exit(1);
241 }
242
243 rg_log("Generation done");
244
245 $t = explode('<div class="secret_token">', $r['body']);
246 $t = explode('</div>', $t[1]);
247 $t = explode("\n", $t[0]);
248 $sc = explode(' ', trim($t[1]));
249
250 rg_log_ml('DEBUG: sc: ' . print_r($sc, TRUE));
251
252 return $sc;
253 }
254
158 255 ?> ?>
File tests/http.inc.php changed (mode: 100644) (index d87fb47..e6c9857)
... ... function do_req($url, &$data, &$headers)
118 118 $ret['tokens']['logout'] = $t[0]; $ret['tokens']['logout'] = $t[0];
119 119 } }
120 120
121 $ret['totp_secret'] = FALSE;
122 $x = preg_match_all('/ class="secret_token">([A-Z0-9]*)</', $ret['body'], $matches);
123 if (($x !== FALSE) && (isset($matches[1])) && isset($matches[1][0])) {
124 $ret['totp_secret'] = $matches[1][0];
125 rg_log('DEBUG ret[totp_secret]=' . $ret['totp_secret']);
126 }
127
121 128 $x = preg_match('/Location: (.*)\s/', $ret['header'], $matches); $x = preg_match('/Location: (.*)\s/', $ret['header'], $matches);
122 129 if ($x === 1) { if ($x === 1) {
123 130 if (strncmp($url, "http://", 7) == 0) if (strncmp($url, "http://", 7) == 0)
File tests/http_settings.php changed (mode: 100644) (index 93a3b0d..6aad8b0)
... ... $now = time();
24 24
25 25 rg_log("Testing if caching works: cache_enable=" . ($rg_cache_enable ? "true" : "false")); rg_log("Testing if caching works: cache_enable=" . ($rg_cache_enable ? "true" : "false"));
26 26 rg_cache_set("test::a", "1", 0); rg_cache_set("test::a", "1", 0);
27 rg_cache_core_unset("test::a"); // to force "network" access = bypass mem cache
27 28 $r = rg_cache_get("test::a"); $r = rg_cache_get("test::a");
28 29 if (strcmp($r, "1") != 0) { if (strcmp($r, "1") != 0) {
29 30 rg_log("Main cache (set) is not working!"); rg_log("Main cache (set) is not working!");
30 31 exit(1); exit(1);
31 32 } }
32 33 rg_cache_unset("test::a", RG_SOCKET_NO_WAIT); rg_cache_unset("test::a", RG_SOCKET_NO_WAIT);
34 rg_cache_core_unset("test::a"); // to force "network" access = bypass mem cache
33 35 $r = rg_cache_get("test::a"); $r = rg_cache_get("test::a");
34 36 if ($r !== FALSE) { if ($r !== FALSE) {
35 37 rg_log("Main cache (unset) is not working!"); rg_log("Main cache (unset) is not working!");
File tests/http_totp.php changed (mode: 100644) (index 9ca337f..fca104e)
... ... require_once("common.php");
21 21
22 22 $_testns = 'http_totp'; $_testns = 'http_totp';
23 23 $rg_cache_enable = TRUE; $rg_cache_enable = TRUE;
24 $rg_cache_debug = TRUE;
24 25
25 26 $rg_user_max_len = 60; $rg_user_max_len = 60;
26 27
 
... ... rg_test_create_user($db, $rg_ui);
29 30 // Add an totp token to this account // Add an totp token to this account
30 31 $key = 'ACHCBCCVQ7AK4RGM'; $key = 'ACHCBCCVQ7AK4RGM';
31 32
32 $r = rg_totp_enroll($db, $rg_ui['uid'], 'test', $key, '127.0.0.1', TRUE);
33 $r = rg_totp_enroll($db, $rg_ui['uid'], 'test', $key, '127.0.0.1', 't');
33 34 if ($r !== TRUE) { if ($r !== TRUE) {
34 35 rg_log('cannot enroll!'); rg_log('cannot enroll!');
35 36 exit(1); exit(1);
 
... ... $lt = rg_totp_compute($key, time() / 30, 6);
40 41
41 42 // First we need to load the form so we can get the token // First we need to load the form so we can get the token
42 43 // We provide an old cookie to test if we generate a new pre-login one // We provide an old cookie to test if we generate a new pre-login one
44 rg_log('');
45 rg_log_enter('Loading login form...');
43 46 $r = do_req($test_url . "/op/login", $data, $headers); $r = do_req($test_url . "/op/login", $data, $headers);
44 47 if ($r === FALSE) { if ($r === FALSE) {
45 48 rg_log("Cannot load login form."); rg_log("Cannot load login form.");
 
... ... if ($r === FALSE) {
47 50 } }
48 51 $good_sid = $r['sid']; $good_sid = $r['sid'];
49 52 $good_token = $r['tokens']['login']; $good_token = $r['tokens']['login'];
53 rg_log_exit();
50 54
51 55
52 rg_log("Do the login (sid=$good_sid token=$good_token"
56 rg_log('');
57 rg_log_enter("Do the login without login token (sid=$good_sid token=$good_token)...");
58 $data = array(
59 "doit" => 1,
60 "token" => $good_token,
61 "user" => $rg_ui['username'],
62 "pass" => $rg_ui['pass'],
63 "login_token" => '',
64 "lock_ip" => 0);
65 $headers = array("Cookie: sid=" . $good_sid);
66 $r = do_req($test_url . "/op/login", $data, $headers);
67 if ($r === FALSE) {
68 rg_log_ml('r=' . print_r($r, TRUE));
69 rg_log("Cannot login!");
70 exit(1);
71 }
72 if (!strstr($r['body'], "invalid user")) {
73 rg_log_ml('r=' . print_r($r, TRUE));
74 rg_log("Seems we can login without token!");
75 exit(1);
76 }
77 $good_token = $r['tokens']['login'];
78 rg_log_exit();
79
80
81 rg_log('');
82 rg_log_enter("Do the login (sid=$good_sid token=$good_token"
53 83 . " login_token=$lt)..."); . " login_token=$lt)...");
54 84 $data = array( $data = array(
55 85 "doit" => 1, "doit" => 1,
 
... ... $data = array(
61 91 $headers = array("Cookie: sid=" . $good_sid); $headers = array("Cookie: sid=" . $good_sid);
62 92 $r = do_req($test_url . "/op/login", $data, $headers); $r = do_req($test_url . "/op/login", $data, $headers);
63 93 if ($r === FALSE) { if ($r === FALSE) {
64 rg_log_ml("Cannot login: " . print_r($r, TRUE));
94 rg_log_ml('r=' . print_r($r, TRUE));
95 rg_log("Cannot login!");
65 96 exit(1); exit(1);
66 97 } }
67 98 if (strstr($r['body'], "invalid user")) { if (strstr($r['body'], "invalid user")) {
68 rg_log_ml("Login invalid. r=" . print_r($r, TRUE));
99 rg_log_ml('r=' . print_r($r, TRUE));
100 rg_log("Login invalid!");
69 101 exit(1); exit(1);
70 102 } }
103 $good_sid = $r['sid'];
104 rg_log_exit();
105
106
107 rg_log('');
108 rg_log_enter('Testing device enrollment...');
109 rg_log('Loading enroll form...');
110 $data = array();
111 $headers = array("Cookie: sid=" . $good_sid);
112 $r = do_req($test_url . "/op/settings/totp/enroll", $data, $headers);
113 if ($r === FALSE) {
114 rg_log("Cannot load enroll page!");
115 exit(1);
116 }
117 $good_token = $r['tokens']['user_totp_enroll'];
118 $key = $r['totp_secret'];
119 if ($key === FALSE) {
120 rg_log_ml('r: ' . print_r($r, TRUE));
121 rg_log("Cannot find totp::secret!");
122 exit(1);
123 }
124 rg_log('Posting the enroll form...');
125 $data = array(
126 'enroll' => 1,
127 'token' => $good_token,
128 'totp::name' => 'test',
129 'totp::secret' => $key,
130 'totp::ver' => rg_totp_compute($key, time() / 30, 6)
131 );
132 $headers = array("Cookie: sid=" . $good_sid);
133 $r = do_req($test_url . "/op/settings/totp/enroll", $data, $headers);
134 if (!strstr($r['body'], 'You enrolled your new device with success')) {
135 rg_log_ml('r: ' . print_r($r, TRUE));
136 rg_log("Cannot enroll!");
137 exit(1);
138 }
139 $sql = "SELECT 1 FROM login_tokens WHERE secret = '" . $key . "'";
140 $res = rg_sql_query($db, $sql);
141 $rows = rg_sql_num_rows($res);
142 rg_sql_free_result($res);
143 if ($rows != 1) {
144 rg_log("Seems the secret is not in the database!");
145 exit(1);
146 }
147 rg_log_exit();
148
149
150 rg_log('');
151 rg_log_enter('Testing the deletion of scratch codes');
152 $sc1 = rg_test_sc_generate($db, $rg_ui, $good_sid);
153 sleep(1); // to not have the same itime
154 $sc2 = rg_test_sc_generate($db, $rg_ui, $good_sid);
155 $sql = "SELECT DISTINCT itime FROM scratch_codes WHERE uid = " . $rg_ui['uid'];
156 $res = rg_sql_query($db, $sql);
157 $list = array();
158 while (($row = rg_sql_fetch_array($res))) {
159 $list[] = $row['itime'];
160 }
161 rg_sql_free_result($res);
162 rg_log_ml('list=' . print_r($list, TRUE));
163
164 $headers = array("Cookie: sid=" . $good_sid);
165 $r = do_req($test_url . "/op/settings/totp/sc", $data, $headers);
166 if ($r === FALSE) {
167 rg_log("Cannot load sc page!");
168 exit(1);
169 }
170 $good_token = $r['tokens']['sc'];
171
172 $data = array(
173 'delete' => 1,
174 'token' => $good_token,
175 'delete_list[' . $list[0] . ']' => 'on',
176 'delete_list[' . $list[1] . ']' => 'on'
177 );
178 $headers = array("Cookie: sid=" . $good_sid);
179 $r = do_req($test_url . "/op/settings/totp/sc", $data, $headers);
180 if (!strstr($r['body'], 'deleted with success')) {
181 rg_log("Cannot delete scratch codes!");
182 exit(1);
183 }
184 $sql = "SELECT DISTINCT itime FROM scratch_codes WHERE uid = " . $rg_ui['uid'];
185 $res = rg_sql_query($db, $sql);
186 $rows = rg_sql_num_rows($res);
187 rg_sql_free_result($res);
188 if ($rows != 0) {
189 rg_log("Cannot delete scratch codes - sql still returns data!");
190 exit(1);
191 }
192 $key = 'user::' . $rg_ui['uid'] . '::login_tokens::sc';
193 rg_cache_core_unset($key); // else we will get data from local mem!
194 $r = rg_cache_get($key);
195 if (count($r) != 0) {
196 rg_log_ml('cache: ' . print_r($r, TRUE));
197 rg_log('Deleted scratch codes are still in cache!');
198 exit(1);
199 }
200 rg_log_exit();
201
202
203 rg_log('');
204 rg_log_enter('Testing the deletion of a device');
205 $key = 'ACHCBCCVQ7AK4RGM';
206 $r = rg_totp_enroll($db, $rg_ui['uid'], 'test1', $key, 'A', 't');
207 if ($r !== TRUE) {
208 rg_log('cannot enroll!');
209 exit(1);
210 }
211 $key = 'ACHCBCCVQ7AK4RGX';
212 $r = rg_totp_enroll($db, $rg_ui['uid'], 'test2', $key, 'A', 't');
213 if ($r !== TRUE) {
214 rg_log('cannot enroll!');
215 exit(1);
216 }
217 rg_log('Getting ids from database...');
218 $sql = "SELECT id FROM login_tokens WHERE uid = " . $rg_ui['uid'];
219 $res = rg_sql_query($db, $sql);
220 $list = array();
221 while (($row = rg_sql_fetch_array($res))) {
222 $list[] = $row['id'];
223 }
224 rg_sql_free_result($res);
225 if (count($list) < 2) {
226 rg_log_ml('list: ' . print_r($list, TRUE));
227 rg_log('I cannot find the enrollments in database!');
228 exit(1);
229 }
230 rg_log('Loading list devices form...');
231 $data = array();
232 $r = do_req($test_url . "/op/settings/totp/list", $data, $headers);
233 if ($r === FALSE) {
234 rg_log("Cannot load list devices form.");
235 exit(1);
236 }
237 $good_token = $r['tokens']['login_tokens_list'];
238 $data = array( 'delete' => 1, 'token' => $good_token);
239 foreach ($list as $id)
240 $data['delete_list[' . $id . ']'] = 'on';
241 $headers = array("Cookie: sid=" . $good_sid);
242 $r = do_req($test_url . "/op/settings/totp/list", $data, $headers);
243 if (!strstr($r['body'], 'deleted with success')) {
244 rg_log("Cannot delete login tokens!");
245 exit(1);
246 }
247 $sql = "SELECT DISTINCT id FROM login_tokens WHERE uid = " . $rg_ui['uid'];
248 $res = rg_sql_query($db, $sql);
249 $rows = rg_sql_num_rows($res);
250 rg_sql_free_result($res);
251 if ($rows != 0) {
252 rg_log("Cannot delete device codes - sql still returns data!");
253 exit(1);
254 }
255 $key = 'user::' . $rg_ui['uid'] . '::login_tokens::device';
256 rg_cache_core_unset($key); // else we will get data from local mem!
257 $r = rg_cache_get($key);
258 if (count($r) != 0) {
259 rg_log_ml($key . ': ' . print_r($r, TRUE));
260 rg_log('Deleted device codes are still in cache!');
261 exit(1);
262 }
263 rg_log_exit();
264
71 265
72 266 rg_prof_log(); rg_prof_log();
73 267 rg_log("OK!"); rg_log("OK!");
File tests/ssh.php added (mode: 100644) (index 0000000..c3fe87e)
1 <?php
2 error_reporting(E_ALL | E_STRICT);
3 ini_set("track_errors", "On");
4
5 $INC = dirname(__FILE__) . "/../inc";
6 require_once(dirname(__FILE__) . "/config.php");
7 require_once($INC . "/init.inc.php");
8 require_once($INC . "/user.inc.php");
9 require_once("helpers.inc.php");
10 require_once("http.inc.php");
11
12 rg_log_set_file("ssh.log");
13
14 $rg_sql = "host=localhost user=rocketgit dbname=rocketgit connect_timeout=10";
15 $rg_no_db = TRUE;
16 require_once("common.php");
17
18 $_testns = 'ssh';
19 $rg_cache_enable = TRUE;
20 $rg_cache_debug = TRUE;
21
22 // This test makes sense only on my devel machine
23 if (php_uname("n") != "r1.embedromix.ro") {
24 rg_log("OK!");
25 exit(0);
26 }
27
28
29 rg_log("Creating a user...");
30 rg_test_create_user($db, $rg_ui);
31 rg_test_create_repo($db, $rg_ui, $repo);
32 $r = test_login($test_url, $rg_ui, $good_sid);
33 if ($r === FALSE) {
34 rg_log("Cannot login!");
35 exit(1);
36 }
37
38 $cmd = "ssh -i keys/" . $rg_ui['uid'] . " rocketgit@r1i";
39
40 $key = rg_test_upload_ssh_key($db, $rg_ui, $rg_ui['uid'], $good_sid);
41
42 rg_log('');
43 $list = array('', 'status', 'repos', 'repo', 'totp');
44 foreach ($list as $s) {
45 rg_log('Connecting for [' . $s . ']');
46 $r = shell_exec($cmd . ' ' . $s . ' 2>&1');
47 if (!strstr($r, "Welcome to RocketGit")) {
48 rg_log('r=' . $r);
49 rg_log("Trying to get the help detected missing welcome!");
50 exit(1);
51 }
52 if (strstr($r, 'Error: .')) {
53 rg_log('r=' . $r);
54 rg_log('Error is empty for \'' . $s . '\'! Not good!');
55 exit(1);
56 }
57 }
58
59 $list = array('remove-device', 'unenroll');
60 foreach ($list as $s) {
61 rg_log('Connecting for [totp ' . $s . ']');
62 $r = shell_exec($cmd . ' totp ' . $s . ' 2>&1');
63 if (strstr($r, 'Error: .')) {
64 rg_log('r=' . $r);
65 rg_log('Error is empty for \'' . $s . '\'! Not good!');
66 exit(1);
67 }
68 }
69
70
71 rg_log('');
72 rg_log_enter('Testing wrong command');
73 $r = shell_exec($cmd . ' wrongcmd ' . ' 2>&1');
74 if (!strstr($r, "nknown command")) {
75 rg_log('r=' . $r);
76 rg_log("Wrong answer for a wrong command!");
77 exit(1);
78 }
79 rg_log_exit();
80
81
82 rg_log('');
83 rg_log('Testing enroll procedure');
84 $r = shell_exec($cmd . ' totp enroll');
85 $t = explode('enter the following code: ', $r);
86 $t = explode('.', $t[1]);
87 $key = trim($t[0]);
88 rg_log("key=$key");
89
90 $tc = intval(time() / 30) - 1; // we try one in the past
91 $token = rg_totp_compute($key, $tc, 6);
92 $r = shell_exec($cmd . ' totp enroll ' . $token);
93 if (!strstr($r, 'Success!')) {
94 rg_log('r=' . $r);
95 rg_log('Cannot enroll!');
96 exit(1);
97 }
98
99
100 rg_log('');
101 rg_log('Testing \'val\' command');
102 $tc = intval(time() / 30);
103 $token = rg_totp_compute($key, $tc, 6);
104 $r = shell_exec($cmd . ' totp val ' . $token . ' 2m');
105 if (!strstr($r, 'Success!')) {
106 rg_log('r=' . $r);
107 rg_log('Cannot validate ip!');
108 exit(1);
109 }
110 $t = explode('valid till ', $r);
111 $t = explode(' (', $t[1]);
112 $exp = trim($t[0]);
113 rg_log('exp=' . $exp);
114
115 rg_log('');
116 rg_log('Reuse of the token must be forbidden (device)');
117 $r = shell_exec($cmd . ' totp val ' . $token . ' 2m');
118 if (!strstr($r, 'cannot reuse')) {
119 rg_log('r=' . $r);
120 rg_log('we get no error on token reuse!');
121 exit(1);
122 }
123
124
125 rg_log('');
126 rg_log('Testing \'list-val\' command');
127 $r = shell_exec($cmd . ' totp list-val');
128 if (!strstr($r, $exp)) {
129 rg_log('r=' . $r);
130 rg_log('Invalid output for list-val!');
131 exit(1);
132 }
133
134
135 rg_log('');
136 rg_log('Testing \'inval\' command - wrong ip');
137 $tc = intval(time() / 30) + 1; // we try one in the future
138 $token = rg_totp_compute($key, $tc, 6);
139 $r = shell_exec($cmd . ' totp inval 1.1.1.1');
140 if (!strstr($r, 'ip not found')) {
141 rg_log('r=' . $r);
142 rg_log('Cannot invalidate ip!');
143 exit(1);
144 }
145
146
147 rg_log('');
148 rg_log('Testing \'inval\' command - all');
149 $tc = intval(time() / 30) + 1; // we try one in the future
150 $token = rg_totp_compute($key, $tc, 6);
151 $r = shell_exec($cmd . ' totp inval all');
152 if (!strstr($r, 'Success!')) {
153 rg_log('r=' . $r);
154 rg_log('Cannot invalidate all!');
155 exit(1);
156 }
157
158
159 rg_log('');
160 rg_log('Testing \'remove-device\'');
161 $tc = intval(time() / 30) + 2;
162 $token = rg_totp_compute($key, $tc, 6);
163 $_cmd = $cmd . ' totp remove-device ' . $token;
164 rg_log('Sending cmd ' . $_cmd);
165 $r = shell_exec($_cmd);
166 if (!strstr($r, 'Success!')) {
167 rg_log('r=' . $r);
168 rg_log('Cannot remove device!');
169 exit(1);
170 }
171
172
173 $sc = rg_test_sc_generate($db, $rg_ui, $good_sid);
174 $sql = "UPDATE scratch_codes SET sc = '0' || substring(sc from 1 for 7)"
175 . " WHERE uid = " . $rg_ui['uid'];
176 $res = rg_sql_query($db, $sql);
177 rg_sql_free_result($res);
178 // we want to test with a short code, so, insert one and flush cache
179 $key = 'user::' . $rg_ui['uid'] . '::login_tokens::sc';
180 rg_cache_unset($key, 0);
181 foreach ($sc as &$t)
182 $t = substr($t, 0, 7);
183
184
185 rg_log('');
186 rg_log('Testing \'unenroll\'');
187 $token = array_pop($sc);
188 $token = ltrim($token, '0');
189 $_cmd = $cmd . ' totp unenroll ' . $token;
190 rg_log('Sending cmd ' . $_cmd);
191 $r = shell_exec($_cmd);
192 if (!strstr($r, 'You are now unenrolled')) {
193 rg_log('r=' . $r);
194 rg_log('Cannot unenroll!');
195 exit(1);
196 }
197
198 rg_log('');
199 rg_log('After enroll we should not be able to use the scratch codes');
200 $token = array_pop($sc);
201 $r = shell_exec($cmd . ' totp val ' . $token . ' 2m');
202 if (strstr($r, 'Success!')) {
203 rg_log('r=' . $r);
204 rg_log('Seems we are able to use scratch codes after unenroll!');
205 exit(1);
206 }
207
208
209 $sc = rg_test_sc_generate($db, $rg_ui, $good_sid);
210
211
212 rg_log('');
213 rg_log('sc: testing \'val\' cmd...');
214 $token = array_pop($sc);
215 $_cmd = $cmd . ' totp val ' . $token . ' 2m';
216 rg_log('Sending cmd ' . $_cmd);
217 $r = shell_exec($_cmd);
218 if (!strstr($r, 'Success!')) {
219 rg_log('r=' . $r);
220 rg_log('Cannot validate ip!');
221 exit(1);
222 }
223
224
225 rg_log('');
226 rg_log('Reuse of the scratch code must be forbidden (sc)');
227 $_cmd = $cmd . ' totp val ' . $token . ' 2m';
228 rg_log('Sending cmd ' . $_cmd);
229 $r = shell_exec($_cmd);
230 if (!strstr($r, 'invalid token')) {
231 rg_log('r=' . $r);
232 rg_log('we get no error on token reuse!');
233 exit(1);
234 }
235
236
237 rg_log("OK!");
238 ?>
File tests/totp.php changed (mode: 100644) (index b532012..1cdab07)
... ... $rg_no_db = TRUE;
18 18 require_once("common.php"); require_once("common.php");
19 19
20 20 rg_log(''); rg_log('');
21 rg_log('Testing rg_top_verify...');
21 rg_log('Testing rg_totp_verify...');
22 22 $key = 'ACHCBCCVQ7AK4RGM'; $key = 'ACHCBCCVQ7AK4RGM';
23 23 $now = 1441527846; $tc = intval($now / 30); $now = 1441527846; $tc = intval($now / 30);
24 24 $token = 215840; $token = 215840;
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master