gPass

gPass Git Source Tree

Root/server/functions.php

1<?php
2/*
3 Copyright (C) 2013-2017 Grégory Soutadé
4
5 This file is part of gPass.
6
7 gPass is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 gPass is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with gPass. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21/*
22 login is stored as :
23 url;login + 16 bytes padding * \0 + sha256(url;login + padding)[8:24]
24
25 Password is salted (3 random characters) and encrypted
26
27 All is encrypted with AES256-CBC and key PBKDF2(hmac_sha256, master key, server url, 1000)
28 level is server configurable
29 iv is PBKDF2(hmac_sha256, server url, master key, 1000)[0:16]
30 */
31$MAX_ENTRY_LEN = 512;
32$USERS_PATH = "./users/";
33$TARGET_DB_VERSION = 2;
34
35function sanitize($val)
36{
37 return (isset($_POST[$val])) ? addslashes($_POST[$val]) : "";
38}
39
40// From http://php.net/manual/en/function.copy.php
41function recurse_copy($src,$dst) {
42 $dir = opendir($src);
43 if ($dir == FALSE) return FALSE;
44 if (!@mkdir($dst)) return FALSE;
45 while(false !== ( $file = readdir($dir)) ) {
46 if (( $file != '.' ) && ( $file != '..' )) {
47 if ( is_dir($src . '/' . $file) ) {
48 return recurse_copy($src . '/' . $file,$dst . '/' . $file);
49 }
50 else {
51 copy($src . '/' . $file,$dst . '/' . $file);
52 }
53 }
54 }
55 closedir($dir);
56 return TRUE;
57}
58
59function create_user($user)
60{
61 global $USERS_PATH;
62
63 if (strpos($user, "..") || strpos($user, "/") || $user[0] == "." || $user[0] == "_")
64 {
65 echo "<div class=\"error\">Invalid user</div>";
66 }
67 else
68 {
69 $user = $USERS_PATH . $user;
70
71 if (file_exists($user))
72 {
73 echo "<div class=\"error\">User already exists</div>";
74 }
75 else
76 {
77 if (!recurse_copy("./ref", $user))
78 {
79 echo "<div class=\"error\">Cannot create user $user</div>";
80 }
81 else
82 {
83 return true;
84 }
85 }
86 }
87
88 return false;
89}
90
91function _migrate_0($user, $db)
92{
93 try {
94 $db->query("ALTER TABLE gpass ADD access_token VARCHAR(32)");
95 $db->query("ALTER TABLE gpass ADD shadow_login VARCHAR(32)");
96 $db->query("ALTER TABLE gpass ADD salt VARCHAR(32)");
97
98 $db->query("CREATE TABLE db_version(version INTEGER)");
99 $db->query("INSERT INTO db_version (version) VALUES (1)");
100 }
101 catch(Exception $e)
102 {
103 $db->close();
104 echo "<div class=\"error\">Unable to load database for user $user ! : $e</div>";
105 return -1;
106 }
107
108 return 0;
109}
110
111function _migrate_1($user, $db)
112{
113 try {
114 $db->query("CREATE TABLE conf(db_version INTEGER, last_access_time INTEGER)");
115 $db->query("INSERT INTO conf VALUES(2, 0)");
116 }
117 catch(Exception $e)
118 {
119 $db->close();
120 echo "<div class=\"error\">Unable to load database for user $user ! : $e</div>";
121 return -1;
122 }
123
124 return 0;
125}
126
127function migrate_database($user, $db)
128{
129 global $TARGET_DB_VERSION;
130
131 $migration_functions = ['_migrate_0', '_migrate_1'];
132
133 $version = $db->querySingle("SELECT db_version FROM conf");
134 if ($version == NULL || $version == -1)
135 {
136 $version = $db->querySingle("SELECT version FROM db_version");
137 if ($version == NULL || $version == -1)
138 $version = 0;
139 }
140
141 for($i=$version; $i<$TARGET_DB_VERSION; $i++)
142 {
143 if ($migration_functions[$i]($user, $db))
144 return -1;
145 }
146
147 return 0;
148}
149
150function load_database($user)
151{
152 global $USERS_PATH;
153
154 try {
155 $db = new SQLite3($USERS_PATH . "$user/gpass.bdd", SQLITE3_OPEN_READWRITE);
156 }
157 catch(Exception $e)
158 {
159 echo "<div class=\"error\">Unable to load database for user $user !</div>";
160 return null;
161 }
162
163 if (migrate_database($user, $db))
164 return null;
165
166 // New access need to reset crypto
167 unset($_SESSION['td']);
168
169 return $db;
170}
171
172function add_entry($user, $login, $password,
173 $shadow_login, $salt, $access_token)
174{
175 global $USE_SHADOW_LOGINS;
176
177 $db = load_database($user);
178
179 if ($db == null)
180 {
181 echo "Unknown user";
182 return false;
183 }
184
185 if ($USE_SHADOW_LOGINS && (strlen($shadow_login) != 32 ||
186 strlen($salt) != 32 || strlen($access_token) != 32))
187 {
188 $db->close();
189 echo "Shadow login not configured";
190 return false;
191 }
192
193 $count = $db->querySingle("SELECT COUNT(*) FROM gpass WHERE login='" . $login . "'");
194
195 if ($count != NULL && $count != 0)
196 {
197 echo "Entry already exists";
198 return false;
199 }
200
201 $result = $db->exec("INSERT INTO gpass ('login', 'password', 'shadow_login', 'salt', 'access_token') VALUES
202 ('" . $login . "', '" . $password . "', '" . $shadow_login . "', '" . $salt . "', '" . $access_token . "')");
203
204 /* error_log("INSERT INTO gpass ('login', 'password', 'shadow_login', 'salt', 'access_token') VALUES */
205 /* ('" . $login . "', '" . $password . "', '" . $shadow_login . "', '" . $salt . "', '" . $access_token . "')"); */
206 $db->close();
207
208 if (!$result)
209 {
210 echo "Error " . $db->lastErrorMsg();
211 return false;
212 }
213 else
214 {
215 echo "OK";
216 return true;
217 }
218}
219
220function delete_entry($user, $login, $access_token)
221{
222 global $USE_SHADOW_LOGINS;
223
224 $db = load_database($user);
225
226 if ($db == null)
227 {
228 echo "Unknown user";
229 return false;
230 }
231
232 if ($USE_SHADOW_LOGINS)
233 {
234 $db_ac = $db->querySingle("SELECT access_token FROM gpass WHERE login='" . $login . "'");
235 if ($db_ac != NULL && strcmp($db_ac, $access_token))
236 {
237 $db->close();
238 echo "Bad access token";
239 return false;
240 }
241 }
242
243 $result = $db->exec("DELETE FROM gpass WHERE login='" . $login . "'");
244 $db->close();
245
246 if (!$result)
247 {
248 echo "Error " . $db->lastErrorMsg();
249 return false;
250 }
251 else
252 {
253 echo "OK";
254 return true;
255 }
256}
257
258function update_entry($user, $mkey, $old_login, $url, $login, $password, $shadow_login, $salt, $old_access_token, $new_access_token)
259{
260 if (delete_entry($user, $old_login, $old_access_token))
261 return add_entry($user, $mkey, $url, $login, $password, $shadow_login, $salt, $new_access_token);
262
263 return false;
264}
265
266function list_entries($user)
267{
268 global $USE_SHADOW_LOGINS;
269
270 $db = load_database($user);
271
272 if ($db == null) return;
273
274 $result = $db->query("SELECT * FROM gpass");
275
276 $first = false;
277 header('Content-Type: application/json');
278 echo "{ \"entries\" : [\n";
279
280 while (($row = $result->fetchArray()))
281 {
282 if ($first) echo ",";
283 else $first = true;
284 if (!strlen($row['shadow_login']) || !$USE_SHADOW_LOGINS)
285 echo "{\"login\" : \"" . $row['login'] . "\", \"password\" : \"" . $row['password'] . "\" }\n";
286 else
287 echo "{\"shadow_login\" : \"" . $row['shadow_login'] . "\", \"salt\" : \"" . $row['salt'] . "\" }\n";
288 }
289
290 echo "]}";
291
292 $db->close();
293}
294
295function get_secure_entries($user, $access_tokens)
296{
297 $db = load_database($user);
298
299 if ($db == null) return;
300
301 $query = "SELECT access_token, login, password FROM gpass WHERE access_token IN (";
302 $first = false;
303
304 foreach (preg_split("/,/", $access_tokens) as $ac)
305 {
306 /* error_log($ac); */
307 if ($first) $query .= ", ";
308 else $first = true;
309 $query .= "'$ac'";
310 }
311 $query .= ")";
312
313 //error_log($query);
314 $result = $db->query($query);
315
316 header('Content-Type: application/json');
317 $first = false;
318 echo "{ \"entries\" : [\n";
319
320 while (($row = $result->fetchArray()))
321 {
322 if ($first) echo ",";
323 else $first = true;
324 echo "{\"access_token\" : \"" . $row['access_token'] . "\", \"login\" : \"" . $row['login'] . "\", \"password\" : \"" . $row['password'] . "\" }\n";
325 }
326
327 echo "]}";
328
329 $db->close();
330}
331
332?>

Archive Download this file