diff --git a/include/application/application.h b/include/application/application.h index e9113c4..8a89c10 100644 --- a/include/application/application.h +++ b/include/application/application.h @@ -31,6 +31,7 @@ #include "auth/credential.h" #include "storage.h" #include "session.h" +#include "user.h" struct randval { @@ -48,9 +49,11 @@ CLASS(Application) { struct randval * val; Storage users; + Storage passwords; }; int applicationLogin(Application, Credential, Session); +int applicationSignup(Application, Credential, User, Session); Session applicationSessionStart(Application); Session applicationSessionGet(Application, const char *); diff --git a/include/auth.h b/include/auth.h index 00bc28e..a3ec86a 100644 --- a/include/auth.h +++ b/include/auth.h @@ -25,6 +25,7 @@ #include "auth/auth.h" #include "auth/ldap.h" +#include "auth/storage.h" #include "auth/credential.h" #include "auth/interface/auth.h" diff --git a/include/auth/storage.h b/include/auth/storage.h new file mode 100644 index 0000000..5409f36 --- /dev/null +++ b/include/auth/storage.h @@ -0,0 +1,49 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2012 Georg Hopp + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __AUTH_STORAGE_H__ +#define __AUTH_STORAGE_H__ + +#include + +#include + +#include "class.h" + + +#define SALT_SIZE 32 +#define HASH_SIZE SHA512_DIGEST_LENGTH + + +CLASS(AuthStorage) { + Storage store; +}; + +/* + * @TODO In future this should use a more general purpose hash + * function, which then will be in utils/hash.c + */ +int hash_pw(const char *, const size_t, unsigned char *, unsigned char **); + +#endif // __AUTH_STORAGE_H__ + +// vim: set ts=4 sw=4: diff --git a/include/storage.h b/include/storage.h index c2fcb8b..277a9da 100644 --- a/include/storage.h +++ b/include/storage.h @@ -29,12 +29,21 @@ #include "class.h" +typedef enum e_StoragePutResults { + SPR_OK = 0, + SPR_READ_ONLY = 1, + SPR_EXISTS = 2, + SPR_UNKNOWN = -1 +} StoragePutResult; + + CLASS(Storage) { GDBM_FILE gdbm; char * db_name; }; -void storagePut(Storage, char *, size_t, char *, size_t); +StoragePutResult storagePut(Storage, char *, size_t, char *, size_t); +StoragePutResult storageUpdate(Storage, char *, size_t, char *, size_t); void storageGet(Storage, char *, size_t, char **, size_t *); #endif // __STORAGE_H__ diff --git a/src/application/adapter/http/update.c b/src/application/adapter/http/update.c index bca45e8..a78b28a 100644 --- a/src/application/adapter/http/update.c +++ b/src/application/adapter/http/update.c @@ -35,6 +35,7 @@ #include "http/header.h" #include "http/response.h" #include "auth/credential.h" +#include "user.h" #include "utils/memory.h" @@ -121,7 +122,6 @@ getSession(Queue sess_queue, const char * sid) return sess; } - static void loginAdapter(Application application, HttpWorker worker, Session session) @@ -159,6 +159,73 @@ loginAdapter(Application application, HttpWorker worker, Session session) delete(credential); } +static +void +signupAdapter(Application application, HttpWorker worker, Session session) +{ + HashValue email; + HashValue password; + HashValue pwrepeat; + HashValue firstname; + HashValue surname; + + Credential credential; + User user; + + email = hashGet( + worker->current_request->post, + CSTRA("email")); + password = hashGet( + worker->current_request->post, + CSTRA("password")); + pwrepeat = hashGet( + worker->current_request->post, + CSTRA("pwrepeat")); + firstname = hashGet( + worker->current_request->post, + CSTRA("firstname")); + surname = hashGet( + worker->current_request->post, + CSTRA("surname")); + + if ( + NULL == email || + NULL == password || + NULL == pwrepeat || + NULL == firstname || + NULL == surname) { + // maybe this is not a 500...have to check repsonse codes. + worker->current_response = httpResponse500(); + return; + } + + if (password->nvalue != pwrepeat->nvalue || + 0 != memcmp(password->value, pwrepeat->value, password->nvalue)) { + // maybe this is not a 500...have to check repsonse codes. + worker->current_response = httpResponse500(); + return; + } + + credential = new(Credential, + CRED_PASSWORD, + (char *)(email->value), email->nvalue, + (char *)(password->value), password->nvalue); + + user = new(User, + (char *)(email->value), email->nvalue, + (char *)(firstname->value), firstname->nvalue, + (char *)(surname->value), surname->nvalue); + + if (! applicationSignup(application, credential, user, session)) { + worker->current_response = httpResonse500(); + } else { + loginAdapter(application, worker, session); + } + + delete(credential); +} + + void applicationAdapterHttpUpdate(void * _this, void * subject) { @@ -190,6 +257,11 @@ applicationAdapterHttpUpdate(void * _this, void * subject) loginAdapter(this->application, worker, session); return; } + + if (0 == strcmp("/signup/", worker->current_request->path)) { + signupAdapter(this->application, worker, session); + return; + } } if (0 == strcmp("GET", worker->current_request->method)) { diff --git a/src/application/application.c b/src/application/application.c index 31c146f..ffa1d7c 100644 --- a/src/application/application.c +++ b/src/application/application.c @@ -49,7 +49,15 @@ applicationCtor(void * _this, va_list * params) this->active_sessions = new(Queue); - this->users = new(Storage, "./run/users.db"); + /* + * @TODO for both of these...each user should be identified + * by a number...that way I could use that number in the + * passwords db and no direct association between email and + * password could be made when someone get the hands on the + * password database. + */ + this->users = new(Storage, "./run/users.db"); + this->passwords = new(Storage, "./run/passwords.db") return 0; } @@ -61,6 +69,8 @@ applicationDtor(void * _this) Application this = _this; size_t i; + delete(this->passwords); + delete(this->users); delete(this->active_sessions); for (i=0; inauth; i++) { diff --git a/src/application/signup.c b/src/application/signup.c new file mode 100644 index 0000000..acd6b0c --- /dev/null +++ b/src/application/signup.c @@ -0,0 +1,78 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2012 Georg Hopp + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include "class.h" +#include "auth.h" +#include "user.h" +#include "application/application.h" + +#include "utils/memory.h" + +int +applicationSignup( + Application this, + Credential credential, + User user, + Session session) +{ + unsigned char hash[SALT_SIZE+HASH_SIZE]; + + if (NULL != userLoad(user, this->users)) { + /* + * if any user is found with this email return false + * as on signup equal email adresses are not allowed + * at all. + */ + return 0; + } + + userSave(user, this->users); + + if (FALSE == hash_pw( + CRED_PWD(cred).pass, + CRED_PWD(cred).npass, + &hash, + &(hash+SALT_SIZE))) { + /* + * @TODO if we come here we have to delete the previously saved + * user again... + */ + return 0; + } + + storagePut( + this->passwords, + CRED_PWD(cred).user, + CRED_PWD(cred).nuser, + hash, + SALT_SIZE + HASH_SIZE); + + return 0; +} + +// vim: set ts=4 sw=4: diff --git a/src/auth/storage/hash_pw.c b/src/auth/storage/hash_pw.c new file mode 100644 index 0000000..93228f3 --- /dev/null +++ b/src/auth/storage/hash_pw.c @@ -0,0 +1,103 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2012 Georg Hopp + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include + +#include "class.h" +#include "storage.h" +#include "utils/memory.h" + +/* + * I have to hash the passwords, maybe this will move in + * a separate class in future, but now everything is done + * here + */ +#define PBKDF2_ITERATIONS 2048 + +/* + * base64 decode via openssl... + * I do not need this i think, but I keep it...maybe I have + * use for it later. + * +#include +#include + +#define B64_SALT "q36MilkD6Ezlt6+G394aPYWrSwAdEhdnK8k=" + +BIO_METHOD * BIO_f_base64(void); + +void +base64decode(char * data) { + BIO * bio, + * b64; + FILE * b64_salt = fmemopen(B64_SALT, sizeof(B64_SALT)-1, "r"); + + b64 = BIO_new(BIO_f_base64()); + bio = BIO_new_fp(b64_salt, BIO_NOCLOSE); + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); + + if (SALT_SIZE != BIO_read(bio, data, SALT_SIZE)) { + return -1; + } + + BIO_free_all(bio); + fclose(b64_salt); +} +*/ + +int +hash_pw( + const char * password, + const size_t npassword, + unsigned char * hash, + unsigned char ** salt) +{ + if (NULL == *salt) { + *salt = memMalloc(SALT_SIZE * sizeof(unsigned char)); + if (0 > RAND_pseudo_bytes(unsigned char *buf, int num)) { + MEM_FREE(*salt); + return FALSE; + } + } + + if (0 == PKCS5_PBKDF2_HMAC( + password, + npassword, + *salt, + SALT_SIZE, + PBKDF2_ITERATIONS, + EVP_sha512(), + HASH_SIZE, + hash)) { + MEM_FREE(*salt); + return FALSE; + } + + return TRUE; +} + +// vim: set ts=4 sw=4: diff --git a/src/auth/storage/signup.c b/src/auth/storage/signup.c new file mode 100644 index 0000000..2db9efb --- /dev/null +++ b/src/auth/storage/signup.c @@ -0,0 +1,53 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2012 Georg Hopp + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "class.h" +#include "storage.h" +#include "auth/storage.h" +#include "auth/credential.h" + +int +authStorageSignup(AuthStorage this, Credential cred) +{ + unsigned char hash[SALT_SIZE+HASH_SIZE]; + + if (FALSE == hash_pw( + CRED_PWD(cred).pass, + CRED_PWD(cred).npass, + &hash, + &(hash+SALT_SIZE))) { + return 0; + } + + storagePut( + this->store, + CRED_PWD(cred).user, + CRED_PWD(cred).nuser, + hash, + SALT_SIZE + HASH_SIZE); + + return 1; +} + +// vim: set ts=4 sw=4: diff --git a/src/auth/storage/storage.c b/src/auth/storage/storage.c new file mode 100644 index 0000000..5aa8e47 --- /dev/null +++ b/src/auth/storage/storage.c @@ -0,0 +1,95 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2012 Georg Hopp + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "class.h" +#include "storage.h" +#include "auth/storage.h" +#include "auth/credential.h" +#include "commons.h" + +static +int +authStorageCtor(void * _this, va_list * params) +{ + AuthStorage this = _this; + + this->store = va_arg(*params, Storage); + + return 0; +} + +static +void +authStorageDtor(void * _this) +{ +} + +static +int +authStorageAuthenticate(void * _this, Credential cred) +{ + AuthStorage this = _this; + + unsigned char current_hash[HASH_SIZE]; + unsigned char * found_hash = NULL; + size_t nfound_hash = 0; + + if (CRED_PASSWORD != cred->type) { + return FALSE; + } + + storageGet( + this->store, + CRED_PWD(cred).user, + CRED_PWD(cred).nuser, + &found_hash, + &nfound_hash); + + if (NULL == found_hash || (SALT_SIZE + HASH_SIZE) != nfound_hash) { + /* user not found or found hash is invalid */ + return FALSE; + } + + /* found_hash <=> salt+hash */ + if (FALSE == hash_pw( + CRED_PWD(cred).pass, + CRED_PWD(cred).npass, + current_hash, + &found_hash)) { + MEM_FREE(found_hash); + return FALSE; + } + + if (0 != memcmp(current_hash, found_hash+SALT_SIZE, HASH_SIZE)) { + MEM_FREE(found_hash); + return FALSE; + } + + MEM_FREE(found_hash); + return TRUE; +} + +INIT_IFACE(Class, authStorageCtor, authStorageDtor, NULL); +INIT_IFACE(Auth, authStorageAuthenticate); +CREATE_CLASS(AuthLdap, NULL, IFACE(Class), IFACE(Auth)); + +// vim: set ts=4 sw=4: diff --git a/src/storage/put.c b/src/storage/put.c index f6d8079..c992e4f 100644 --- a/src/storage/put.c +++ b/src/storage/put.c @@ -29,13 +29,32 @@ #include "utils/memory.h" -void +typedef enum e_StoragePutResults { + SPR_OK = 0, + SPR_READ_ONLY = 1, + SPR_EXISTS = 2, + SPR_UNKNOWN = -1 +} StoragePutResult; + + +StoragePutResult storagePut(Storage this, char * _key, size_t nkey, char * data, size_t ndata) { datum key = {_key, nkey}; datum value = {data, ndata}; - gdbm_store(this->gdbm, key, value, GDBM_REPLACE); + switch (gdbm_store(this->gdbm, key, value, GDBM_INSERT)) { + case 0: + return SPR_OK; + case 1: + return SPR_EXISTS; + case -1: + return SPR_READ_ONLY; + default: + return SPR_UNKNOWN; + } + + return SPR_UNKNOWN; } // vim: set ts=4 sw=4: diff --git a/src/storage/update.c b/src/storage/update.c new file mode 100644 index 0000000..a48ef96 --- /dev/null +++ b/src/storage/update.c @@ -0,0 +1,50 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2012 Georg Hopp + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "storage.h" +#include "class.h" + +#include "utils/memory.h" + +StoragePutResult +storagePut(Storage this, char * _key, size_t nkey, char * data, size_t ndata) +{ + datum key = {_key, nkey}; + datum value = {data, ndata}; + + switch (gdbm_store(this->gdbm, key, value, GDBM_REPLACE)) { + case 0: + return SPR_OK; + case -1: + return SPR_READ_ONLY; + default: + return SPR_UNKNOWN; + } + + return SPR_UNKNOWN; +} + +// vim: set ts=4 sw=4: diff --git a/src/user/save.c b/src/user/save.c index 17e546f..acb268f 100644 --- a/src/user/save.c +++ b/src/user/save.c @@ -39,7 +39,10 @@ userSave(User this, Storage storage) *this->nsurname + 1 + 3 * sizeof(size_t); - storagePut( + /* + * @TODO user return value for error handling + */ + storageUpdate( storage, this->email, *this->nemail, this->email, storage_size); diff --git a/src/utils/hash.c b/src/utils/hash.c index ea46085..aef07bc 100644 --- a/src/utils/hash.c +++ b/src/utils/hash.c @@ -24,6 +24,8 @@ #include #include +#include + #include "utils/hash.h" /** @@ -50,4 +52,21 @@ sdbm(const unsigned char * str, size_t len) return hash; } + +/* + * this will use openssl to hash a given password with a given salt. + * If salt is NULL a random salt is generated and returned in salt. + * The memory for this is allocated via memMalloc and has to be freed + * by the caller via MEM_FREE. + * The size of the salt is always SALT_SIZE and that of hash is always + * hash size. Both are defined in auth/storage.h + */ +int +hash_pw( + const char * password, + const size_t npassword, + unsigned char * pw_hash, + unsigned char ** salt) +{ +} // vim: set ts=4 sw=4: