handle zero size Base64 conversions
[strongswan.git] / src / medsrv / controller / user_controller.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Copyright (C) 2008 Philip Boetschi, Adrian Doerig
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include <string.h>
19
20 #include "user_controller.h"
21
22 #include <library.h>
23
24 typedef struct private_user_controller_t private_user_controller_t;
25
26 /**
27 * private data of the user_controller
28 */
29 struct private_user_controller_t {
30
31 /**
32 * public functions
33 */
34 user_controller_t public;
35
36 /**
37 * database connection
38 */
39 database_t *db;
40
41 /**
42 * user session
43 */
44 user_t *user;
45
46 /**
47 * minimum required password lenght
48 */
49 u_int password_length;
50 };
51
52 /**
53 * hash the password for database storage
54 */
55 static chunk_t hash_password(char *login, char *password)
56 {
57 hasher_t *hasher;
58 chunk_t hash, data;
59
60 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
61 if (!hasher)
62 {
63 return chunk_empty;
64 }
65 data = chunk_cata("cc", chunk_create(login, strlen(login)),
66 chunk_create(password, strlen(password)));
67 hasher->allocate_hash(hasher, data, &hash);
68 hasher->destroy(hasher);
69 return hash;
70 }
71
72 /**
73 * Login a user.
74 */
75 static void login(private_user_controller_t *this, request_t *request)
76 {
77 if (request->get_query_data(request, "submit"))
78 {
79 char *login, *password;
80
81 login = request->get_query_data(request, "login");
82 password = request->get_query_data(request, "password");
83
84 if (login && password)
85 {
86 enumerator_t *query;
87 u_int id = 0;
88 chunk_t hash;
89
90 hash = hash_password(login, password);
91 query = this->db->query(this->db,
92 "SELECT id FROM user WHERE login = ? AND password = ?",
93 DB_TEXT, login, DB_BLOB, hash, DB_UINT);
94 if (query)
95 {
96 query->enumerate(query, &id);
97 query->destroy(query);
98 }
99 free(hash.ptr);
100 if (id)
101 {
102 this->user->set_user(this->user, id);
103 return request->redirect(request, "peer/list");
104 }
105 }
106 request->setf(request, "error=Invalid username or password.");
107 }
108 request->render(request, "templates/user/login.cs");
109 }
110
111 /**
112 * Logout a user.
113 */
114 static void logout(private_user_controller_t *this, request_t *request)
115 {
116 request->redirect(request, "user/login");
117 request->close_session(request);
118 }
119
120 /**
121 * verify a user entered username for validity
122 */
123 static bool verify_login(private_user_controller_t *this, request_t *request,
124 char *login)
125 {
126 if (!login || *login == '\0')
127 {
128 request->setf(request, "error=Username is missing.");
129 return FALSE;
130 }
131 while (*login != '\0')
132 {
133 switch (*login)
134 {
135 case 'a' ... 'z':
136 case 'A' ... 'Z':
137 case '0' ... '9':
138 case '-':
139 case '_':
140 case '@':
141 case '.':
142 login++;
143 continue;
144 default:
145 request->setf(request, "error=Username invalid, "
146 "valid characters: A-Z a-z 0-9 - _ @ .");
147 }
148 }
149 return TRUE;
150 }
151
152 /**
153 * verify a user entered password for validity
154 */
155 static bool verify_password(private_user_controller_t *this, request_t *request,
156 char *password, char *confirm)
157 {
158 if (!password || *password == '\0')
159 {
160 request->setf(request, "error=Password is missing.");
161 return FALSE;
162 }
163 if (strlen(password) < this->password_length)
164 {
165 request->setf(request, "error=Password requires at least %d characters.",
166 this->password_length);
167 return FALSE;
168 }
169 if (!confirm || !streq(password, confirm))
170 {
171 request->setf(request, "error=Password not confirmed.");
172 return FALSE;
173 }
174 return TRUE;
175 }
176
177 /**
178 * Register a user.
179 */
180 static void add(private_user_controller_t *this, request_t *request)
181 {
182 char *login = "";
183
184 while (request->get_query_data(request, "register"))
185 {
186 char *password, *confirm;
187 chunk_t hash;
188 u_int id;
189
190 login = request->get_query_data(request, "new_login");
191 password = request->get_query_data(request, "new_password");
192 confirm = request->get_query_data(request, "confirm_password");
193
194 if (!verify_login(this, request, login) ||
195 !verify_password(this, request, password, confirm))
196 {
197 break;
198 }
199
200 hash = hash_password(login, password);
201 if (!hash.ptr || this->db->execute(this->db, &id,
202 "INSERT INTO user (login, password) VALUES (?, ?)",
203 DB_TEXT, login, DB_BLOB, hash) < 0)
204 {
205 request->setf(request, "error=Username already exists.");
206 free(hash.ptr);
207 break;
208 }
209 free(hash.ptr);
210 this->user->set_user(this->user, id);
211 return request->redirect(request, "peer/list");
212 }
213 request->set(request, "new_login", login);
214 request->setf(request, "password_length=%d", this->password_length);
215 request->render(request, "templates/user/add.cs");
216 }
217
218 /**
219 * Edit the logged in user
220 */
221 static void edit(private_user_controller_t *this, request_t *request)
222 {
223 enumerator_t *query;
224 char *old_login;
225
226 /* lookup old login */
227 query = this->db->query(this->db, "SELECT login FROM user WHERE id = ?",
228 DB_INT, this->user->get_user(this->user),
229 DB_TEXT);
230 if (!query || !query->enumerate(query, &old_login))
231 {
232 DESTROY_IF(query);
233 request->close_session(request);
234 return request->redirect(request, "user/login");
235 }
236 old_login = strdupa(old_login);
237 query->destroy(query);
238
239 /* back pressed */
240 if (request->get_query_data(request, "back"))
241 {
242 return request->redirect(request, "peer/list");
243 }
244 /* delete pressed */
245 if (request->get_query_data(request, "delete"))
246 {
247 this->db->execute(this->db, NULL, "DELETE FROM user WHERE id = ?",
248 DB_UINT, this->user->get_user(this->user));
249 this->db->execute(this->db, NULL,
250 "DELETE FROM peer WHERE user = ?",
251 DB_UINT, this->user->get_user(this->user));
252 return logout(this, request);
253 }
254 /* save pressed */
255 while (request->get_query_data(request, "save"))
256 {
257 char *new_login, *old_pass, *new_pass, *confirm;
258 chunk_t old_hash, new_hash;
259
260 new_login = request->get_query_data(request, "old_login");
261 old_pass = request->get_query_data(request, "old_password");
262 new_pass = request->get_query_data(request, "new_password");
263 confirm = request->get_query_data(request, "confirm_password");
264
265 if (!verify_login(this, request, new_login) ||
266 !verify_password(this, request, new_pass, confirm))
267 {
268 old_login = new_login;
269 break;
270 }
271 old_hash = hash_password(old_login, old_pass);
272 new_hash = hash_password(new_login, new_pass);
273
274 if (this->db->execute(this->db, NULL,
275 "UPDATE user SET login = ?, password = ? "
276 "WHERE id = ? AND password = ?",
277 DB_TEXT, new_login, DB_BLOB, new_hash,
278 DB_UINT, this->user->get_user(this->user), DB_BLOB, old_hash) <= 0)
279 {
280 free(new_hash.ptr);
281 free(old_hash.ptr);
282 old_login = new_login;
283 request->setf(request, "error=Password verification failed.");
284 break;
285 }
286 free(new_hash.ptr);
287 free(old_hash.ptr);
288 return request->redirect(request, "peer/list");
289 }
290 /* on error/template rendering */
291 request->set(request, "old_login", old_login);
292 request->setf(request, "password_length=%d", this->password_length);
293 request->render(request, "templates/user/edit.cs");
294 }
295
296 /**
297 * Implementation of controller_t.get_name
298 */
299 static char* get_name(private_user_controller_t *this)
300 {
301 return "user";
302 }
303
304 /**
305 * Implementation of controller_t.handle
306 */
307 static void handle(private_user_controller_t *this, request_t *request, char *action)
308 {
309 if (action)
310 {
311 if (streq(action, "add"))
312 {
313 return add(this, request);
314 }
315 if (streq(action, "login"))
316 {
317 return login(this, request);
318 }
319 else if (streq(action, "logout"))
320 {
321 return logout(this, request);
322 }
323 else if (streq(action, "edit"))
324 {
325 return edit(this, request);
326 }
327 else if (streq(action, "help"))
328 {
329 return request->render(request, "templates/user/help.cs");
330 }
331 }
332 request->redirect(request, "user/login");
333 }
334
335 /**
336 * Implementation of controller_t.destroy
337 */
338 static void destroy(private_user_controller_t *this)
339 {
340 free(this);
341 }
342
343 /*
344 * see header file
345 */
346 controller_t *user_controller_create(user_t *user, database_t *db)
347 {
348 private_user_controller_t *this= malloc_thing(private_user_controller_t);
349
350 this->public.controller.get_name = (char*(*)(controller_t*))get_name;
351 this->public.controller.handle = (void(*)(controller_t*, request_t*, char*, char*, char*, char*, char*))handle;
352 this->public.controller.destroy = (void(*)(controller_t*))destroy;
353
354 this->user = user;
355 this->db = db;
356 this->password_length = lib->settings->get_int(lib->settings,
357 "medsrv.password_length", 6);
358
359 return &this->public.controller;
360 }
361