Added redis auth module

In order to use hiredis it is needed to move from ANSI C to C99 or
gnu89.

For now redis auth code is untested it's just first compiling prototype
This commit is contained in:
thematdev 2023-07-04 18:39:14 +03:00
parent 10cbea0dac
commit 140738e4e8
Signed by: thematdev
GPG Key ID: D12878639B090D90
6 changed files with 171 additions and 16 deletions

View File

@ -5,7 +5,7 @@ SD ?= src
LIBS_DIR ?= libs
LIBS ?= $(wildcard $(LIBS_DIR)/*)
LDFLAGS ?= -L$(BD)/ -lcgic -lqments
LDFLAGS ?= -L$(BD)/ -lcgic -lqments -lhiredis -ltomcrypt -luuid
SRC_FILES := $(shell find $(SD) -name *.c)
OBJ_FILES := $(patsubst $(SD)/%.c,$(BD)/%.o,$(SRC_FILES))
@ -13,10 +13,11 @@ OBJ_FILES := $(patsubst $(SD)/%.c,$(BD)/%.o,$(SRC_FILES))
INCLUDE_DIRS = -Ilibs/qments/include -Ilibs/cgic
LIB_TARGETS = $(patsubst $(LIBS_DIR)/%,$(BD)/lib%.a,$(LIBS))
CFLAGS ?= -O2 -ansi -pedantic
CFLAGS ?= -O2 -std=c99 -pedantic
$(BD)/libcgic.a:
$(MAKE) -C $(LIBS_DIR)/cgic
mkdir -p $(@D)
cp $(LIBS_DIR)/cgic/libcgic.a $(BD)
$(BD)/libqments.a:

12
src/auth.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef AUTH_H
#define AUTH_H
/* return 0 on success, negative number on error
* if session_id is not associated with any user, empty
* string is put in username */
int user_by_session_id(const char *session_id, char *username);
/* return 0 on failure, 1 on success, negative number on error */
int authenticate(const char *username, const char *password, char *sid);
#endif /* AUTH_H */

18
src/config.h Normal file
View File

@ -0,0 +1,18 @@
#define RA_USER_MAX_LENGTH 64
#define RA_PASSWORD_MAX_LENGTH 256
#define RA_SESSION_EXPIRY 86400
#define RA_CONNECTION_TIMEOUT {1, 500000}
#define RA_HOSTNAME "ra-socket"
#define RA_MAGIC "$z$"
#define COMMENTS_PER_PAGE 20
#define MAX_NAME_SIZE 1024
#define MAX_COMMENT_SIZE 4096
#define QMENTS_PATH "qments-storage"
#define DRIVER_DATA { QMENTS_PATH }
#define DRIVER unix_fs_driver

View File

@ -4,19 +4,12 @@
#include "cgic.h"
#include "driver.h"
#include "drivers/unix_fs/unix_fs_driver.h"
#include "config.h"
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#define COMMENTS_PER_PAGE 20
#define MAX_NAME 1024
#define MAX_COMMENT_SIZE 4096
#define QMENTS_PATH "qments-storage"
#define DRIVER_DATA { QMENTS_PATH }
#define DRIVER unix_fs_driver
int
page_by_id(int id)
{
@ -51,8 +44,8 @@ render_comment(const Comment *comment)
void
allocate_header(CommentHeader *header)
{
header->user_sid = malloc(MAX_NAME);
header->user_displayname = malloc(MAX_NAME);
header->user_sid = malloc(MAX_NAME_SIZE);
header->user_displayname = malloc(MAX_NAME_SIZE);
}
void
@ -163,7 +156,7 @@ print_page()
int
handle_submitted_comment()
{
char displayname[MAX_NAME], text[MAX_COMMENT_SIZE], *sanitized_text;
char displayname[MAX_NAME_SIZE], text[MAX_COMMENT_SIZE], *sanitized_text;
int rid, retval;
cgiFormResultType err;
CommentHeader header;
@ -180,9 +173,9 @@ handle_submitted_comment()
goto defer;
}
err = cgiFormString("displayname", displayname, MAX_NAME);
err = cgiFormString("displayname", displayname, MAX_NAME_SIZE);
if (err == cgiFormTruncated) {
fprintf(cgiOut, "Name too long (max %d bytes)\n", MAX_NAME);
fprintf(cgiOut, "Name too long (max %d bytes)\n", MAX_NAME_SIZE);
goto defer;
}
if (err == cgiFormNotFound) {

131
src/redis_auth.c Normal file
View File

@ -0,0 +1,131 @@
#include "auth.h"
#include "config.h"
#include <hiredis/hiredis.h>
#include <uuid/uuid.h>
#include <string.h>
#include <errno.h>
#include <tomcrypt.h>
#define UUID_SIZE 36
#define SHA512_DIGEST_SIZE 64
#define FAIL_AUTH() do { \
strcpy(session_id, ""); \
retval = 0; \
goto defer; \
} while (0)
int
user_by_session_id(const char *session_id, char *username)
{
redisContext *ctx;
redisReply *reply;
uuid_t uu_tmp;
struct timeval timeout = RA_CONNECTION_TIMEOUT;
int retval = 0;
ctx = NULL;
reply = NULL;
if (uuid_parse(session_id, uu_tmp) < 0) {
retval = -EINVAL;
goto defer;
}
ctx = redisConnectUnixWithTimeout(RA_HOSTNAME, timeout);
if (!ctx || ctx->err) {
retval = -EIO;
goto defer;
}
reply = redisCommand(ctx, "GET session:%s", session_id);
if (reply->type != REDIS_REPLY_STRING) {
strcpy(username, "");
} else {
strcpy(username, reply->str);
}
defer:
redisFree(ctx);
freeReplyObject(reply);
return retval;
}
int
authenticate(const char *username, const char *password, char *session_id)
{
redisContext *ctx;
redisReply *salt_reply, *hash_reply, *sid_reply;
char *sid;
unsigned char db_hash[SHA512_DIGEST_SIZE], got_hash[SHA512_DIGEST_SIZE];
uuid_t uu_tmp;
hash_state md;
struct timeval timeout = RA_CONNECTION_TIMEOUT;
int retval = 0;
ctx = NULL;
salt_reply = hash_reply = sid_reply = NULL;
if (strlen(username) > RA_USER_MAX_LENGTH || strlen(password) > RA_PASSWORD_MAX_LENGTH) {
retval = -EINVAL;
goto defer;
}
ctx = redisConnectUnixWithTimeout(RA_HOSTNAME, timeout);
if (!ctx || ctx->err) {
retval = -EIO;
goto defer;
}
salt_reply = redisCommand(ctx, "GET salt:%s", username);
if (salt_reply->type != REDIS_REPLY_STRING) {
FAIL_AUTH();
}
hash_reply = redisCommand(ctx, "GET hash:%s", username);
if (hash_reply->type != REDIS_REPLY_STRING || hash_reply->len != SHA512_DIGEST_SIZE) {
FAIL_AUTH();
}
memcpy(db_hash, hash_reply->str, SHA512_DIGEST_SIZE);
sha512_init(&md);
sha512_process(&md, RA_MAGIC, strlen(RA_MAGIC));
sha512_process(&md, password, strlen(password));
sha512_process(&md, salt_reply->str, strlen(salt_reply->str));
sha512_done(&md, got_hash);
if (memcmp(db_hash, got_hash, SHA512_DIGEST_SIZE)) {
FAIL_AUTH();
} else {
redisCommand(ctx, "GET session:%s", username);
if (sid_reply->type != REDIS_REPLY_STRING) {
/* assuming there is no session for this user */
freeReplyObject(sid_reply);
uuid_generate(uu_tmp);
uuid_unparse(uu_tmp, session_id);
sid_reply = redisCommand(ctx, "SET session:%s %s", username, session_id);
freeReplyObject(sid_reply);
sid_reply = redisCommand(ctx, "EXPIRY session:%s %d", username, RA_SESSION_EXPIRY);
} else {
strcpy(session_id, sid_reply->str);
}
retval = 1;
}
defer:
redisFree(ctx);
freeReplyObject(salt_reply);
freeReplyObject(hash_reply);
freeReplyObject(sid_reply);
}

View File

@ -50,5 +50,5 @@ sb_init_empty(StringBuffer *buffer)
void
sb_free(StringBuffer *buffer)
{
free(buffer->buf);
if (buffer) free(buffer->buf);
}