/* * Copyright (C) 2008 Martin Willi * Copyright (C) 2008 Philip Boetschi, Adrian Doerig * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #define _GNU_SOURCE #include #include "user_controller.h" #include typedef struct private_user_controller_t private_user_controller_t; /** * private data of the user_controller */ struct private_user_controller_t { /** * public functions */ user_controller_t public; /** * database connection */ database_t *db; /** * user session */ user_t *user; /** * minimum required password length */ u_int password_length; }; /** * hash the password for database storage */ static chunk_t hash_password(char *login, char *password) { hasher_t *hasher; chunk_t hash, data; hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); if (!hasher) { return chunk_empty; } data = chunk_cata("cc", chunk_create(login, strlen(login)), chunk_create(password, strlen(password))); if (!hasher->allocate_hash(hasher, data, &hash)) { hasher->destroy(hasher); return chunk_empty; } hasher->destroy(hasher); return hash; } /** * Login a user. */ static void login(private_user_controller_t *this, request_t *request) { if (request->get_query_data(request, "submit")) { char *login, *password; login = request->get_query_data(request, "login"); password = request->get_query_data(request, "password"); if (login && password) { enumerator_t *query; u_int id = 0; chunk_t hash; hash = hash_password(login, password); query = this->db->query(this->db, "SELECT id FROM user WHERE login = ? AND password = ?", DB_TEXT, login, DB_BLOB, hash, DB_UINT); if (query) { query->enumerate(query, &id); query->destroy(query); } free(hash.ptr); if (id) { this->user->set_user(this->user, id); return request->redirect(request, "peer/list"); } } request->setf(request, "error=Invalid username or password."); } request->render(request, "templates/user/login.cs"); } /** * Logout a user. */ static void logout(private_user_controller_t *this, request_t *request) { request->redirect(request, "user/login"); request->close_session(request); } /** * verify a user entered username for validity */ static bool verify_login(private_user_controller_t *this, request_t *request, char *login) { if (!login || *login == '\0') { request->setf(request, "error=Username is missing."); return FALSE; } while (*login != '\0') { switch (*login) { case 'a' ... 'z': case 'A' ... 'Z': case '0' ... '9': case '-': case '_': case '@': case '.': login++; continue; default: request->setf(request, "error=Username invalid, " "valid characters: A-Z a-z 0-9 - _ @ ."); } } return TRUE; } /** * verify a user entered password for validity */ static bool verify_password(private_user_controller_t *this, request_t *request, char *password, char *confirm) { if (!password || *password == '\0') { request->setf(request, "error=Password is missing."); return FALSE; } if (strlen(password) < this->password_length) { request->setf(request, "error=Password requires at least %d characters.", this->password_length); return FALSE; } if (!confirm || !streq(password, confirm)) { request->setf(request, "error=Password not confirmed."); return FALSE; } return TRUE; } /** * Register a user. */ static void add(private_user_controller_t *this, request_t *request) { char *login = ""; while (request->get_query_data(request, "register")) { char *password, *confirm; chunk_t hash; u_int id; login = request->get_query_data(request, "new_login"); password = request->get_query_data(request, "new_password"); confirm = request->get_query_data(request, "confirm_password"); if (!verify_login(this, request, login) || !verify_password(this, request, password, confirm)) { break; } hash = hash_password(login, password); if (!hash.ptr || this->db->execute(this->db, &id, "INSERT INTO user (login, password) VALUES (?, ?)", DB_TEXT, login, DB_BLOB, hash) < 0) { request->setf(request, "error=Username already exists."); free(hash.ptr); break; } free(hash.ptr); this->user->set_user(this->user, id); return request->redirect(request, "peer/list"); } request->set(request, "new_login", login); request->setf(request, "password_length=%d", this->password_length); request->render(request, "templates/user/add.cs"); } /** * Edit the logged in user */ static void edit(private_user_controller_t *this, request_t *request) { enumerator_t *query; char *old_login; /* lookup old login */ query = this->db->query(this->db, "SELECT login FROM user WHERE id = ?", DB_INT, this->user->get_user(this->user), DB_TEXT); if (!query || !query->enumerate(query, &old_login)) { DESTROY_IF(query); request->close_session(request); return request->redirect(request, "user/login"); } old_login = strdupa(old_login); query->destroy(query); /* back pressed */ if (request->get_query_data(request, "back")) { return request->redirect(request, "peer/list"); } /* delete pressed */ if (request->get_query_data(request, "delete")) { this->db->execute(this->db, NULL, "DELETE FROM user WHERE id = ?", DB_UINT, this->user->get_user(this->user)); this->db->execute(this->db, NULL, "DELETE FROM peer WHERE user = ?", DB_UINT, this->user->get_user(this->user)); return logout(this, request); } /* save pressed */ while (request->get_query_data(request, "save")) { char *new_login, *old_pass, *new_pass, *confirm; chunk_t old_hash, new_hash; new_login = request->get_query_data(request, "old_login"); old_pass = request->get_query_data(request, "old_password"); new_pass = request->get_query_data(request, "new_password"); confirm = request->get_query_data(request, "confirm_password"); if (!verify_login(this, request, new_login) || !verify_password(this, request, new_pass, confirm)) { old_login = new_login; break; } old_hash = hash_password(old_login, old_pass); new_hash = hash_password(new_login, new_pass); if (this->db->execute(this->db, NULL, "UPDATE user SET login = ?, password = ? " "WHERE id = ? AND password = ?", DB_TEXT, new_login, DB_BLOB, new_hash, DB_UINT, this->user->get_user(this->user), DB_BLOB, old_hash) <= 0) { free(new_hash.ptr); free(old_hash.ptr); old_login = new_login; request->setf(request, "error=Password verification failed."); break; } free(new_hash.ptr); free(old_hash.ptr); return request->redirect(request, "peer/list"); } /* on error/template rendering */ request->set(request, "old_login", old_login); request->setf(request, "password_length=%d", this->password_length); request->render(request, "templates/user/edit.cs"); } METHOD(controller_t, get_name, char*, private_user_controller_t *this) { return "user"; } METHOD(controller_t, handle, void, private_user_controller_t *this, request_t *request, char *action, char *p2, char *p3, char *p4, char *p5) { if (action) { if (streq(action, "add")) { return add(this, request); } if (streq(action, "login")) { return login(this, request); } else if (streq(action, "logout")) { return logout(this, request); } else if (streq(action, "edit")) { return edit(this, request); } else if (streq(action, "help")) { return request->render(request, "templates/user/help.cs"); } } request->redirect(request, "user/login"); } METHOD(controller_t, destroy, void, private_user_controller_t *this) { free(this); } /* * see header file */ controller_t *user_controller_create(user_t *user, database_t *db) { private_user_controller_t *this; INIT(this, .public = { .controller = { .get_name = _get_name, .handle = _handle, .destroy = _destroy, }, }, .user = user, .db = db, .password_length = lib->settings->get_int(lib->settings, "medsrv.password_length", 6), ); return &this->public.controller; }