From ec3afc3a44b0705b248b37c6dc688cc8f342b9bc Mon Sep 17 00:00:00 2001 From: Jan Vidar Krey Date: Tue, 20 Dec 2011 16:20:54 +0100 Subject: [PATCH] Added a simple tool to create and manipulate the uhub sqlite authentication database. --- GNUmakefile | 11 +- src/tools/uhub-passwd.c | 340 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 src/tools/uhub-passwd.c diff --git a/GNUmakefile b/GNUmakefile index 19e4fe6..40dfa67 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -182,6 +182,9 @@ libadc_client_SOURCES := \ uhub_SOURCES := src/core/main.c +uhub-passwd_SOURCES := src/tools/uhub-passwd.c +uhub-passwd_LIBS := -lsqlite3 + adcrush_SOURCES := src/tools/adcrush.c admin_SOURCES := src/tools/admin.c @@ -229,6 +232,7 @@ libadc_client_OBJECTS := $(libadc_client_SOURCES:.c=.o) libadc_common_OBJECTS := $(libadc_common_SOURCES:.c=.o) uhub_OBJECTS := $(uhub_SOURCES:.c=.o) +uhub-passwd_OBJECTS := $(uhub-passwd_SOURCES:.c=.o) adcrush_OBJECTS := $(adcrush_SOURCES:.c=.o) admin_OBJECTS := $(admin_SOURCES:.c=.o) @@ -236,6 +240,7 @@ all_OBJECTS := $(libuhub_OBJECTS) $(uhub_OBJECTS) $(libutils_OBJECTS) $(adcr all_plugins := $(plugin_example_TARGET) $(plugin_logging_TARGET) $(plugin_auth_TARGET) $(plugin_auth_sqlite_TARGET) $(plugin_chat_history_TARGET) uhub_BINARY=uhub$(BIN_EXT) +uhub-passwd_BINARY=uhub-passwd$(BIN_EXT) adcrush_BINARY=adcrush$(BIN_EXT) admin_BINARY=uhub-admin$(BIN_EXT) autotest_BINARY=autotest/test$(BIN_EXT) @@ -250,7 +255,7 @@ endif %.o: %.c version.h revision.h $(MSG_CC) $(CC) -fPIC -c $(CFLAGS) -o $@ $< -all: $(uhub_BINARY) plugins +all: $(uhub_BINARY) $(uhub-passwd_BINARY) plugins plugins: $(uhub_BINARY) $(all_plugins) @@ -278,6 +283,10 @@ $(admin_BINARY): $(admin_OBJECTS) $(libuhub_OBJECTS) $(libutils_OBJECTS) $(libad $(uhub_BINARY): $(uhub_OBJECTS) $(libuhub_OBJECTS) $(libutils_OBJECTS) $(libadc_common_OBJECTS) $(MSG_LD) $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) +$(uhub-passwd_BINARY): $(uhub-passwd_OBJECTS) + $(MSG_LD) $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) $(uhub-passwd_LIBS) + + autotest.c: $(autotest_SOURCES) $(shell exotic --standalone $(autotest_SOURCES) > $@) diff --git a/src/tools/uhub-passwd.c b/src/tools/uhub-passwd.c new file mode 100644 index 0000000..79d3587 --- /dev/null +++ b/src/tools/uhub-passwd.c @@ -0,0 +1,340 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2011, Jan Vidar Krey + * + * 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 "uhub.h" +#include + +// #define DEBUG_SQL + +static sqlite3* db = NULL; +static const char* command = NULL; +static const char* filename = NULL; +static const char* binary = NULL; + +typedef int (*command_func_t)(size_t, const char**); + +static int create(size_t argc, const char** argv); +static int list(size_t argc, const char** argv); +static int pass(size_t argc, const char** argv); +static int add(size_t argc, const char** argv); +static int del(size_t argc, const char** argv); +static int mod(size_t argc, const char** argv); + +static struct commands +{ + command_func_t handle; + const char* command; + const char* usage; +} COMMANDS[6] = { + { &create, "create", "" }, + { &list, "list", "" }, + { &add, "add", "username password [credentials = user]" }, + { &del, "del", "username" }, + { &mod, "mod", "username credentials" }, + { &pass, "pass", "username password" }, +}; + +static void print_usage(const char* str) +{ + fprintf(stderr, "Usage: %s filename %s %s\n", binary, command, str); + exit(1); +} + + +/** + * Escape an SQL statement and return a pointer to the string. + * NOTE: The returned value needs to be free'd. + * + * @return an escaped string. + */ +static char* sql_escape_string(const char* str) +{ + size_t i, n, size; + + for (n = 0, size = strlen(str); n < strlen(str); n++) + if (str[n] == '\'') + size++; + + char* buf = malloc(size+1); + for (n = 0, i = 0; n < strlen(str); n++) + { + if (str[n] == '\'') + buf[i++] = '\''; + buf[i++] = str[n]; + } + buf[i++] = '\0'; + return buf; +} + +/** + * Validate credentials. + */ +static const char* validate_cred(const char* cred_str) +{ + if (!strcmp(cred_str, "admin")) + return "admin"; + + if (!strcmp(cred_str, "super")) + return "super"; + + if (!strcmp(cred_str, "op")) + return "op"; + + if (!strcmp(cred_str, "user")) + return "user"; + + fprintf(stderr, "Invalid user credentials. Must be one of: 'admin', 'super', 'op' or 'user'\n"); + exit(1); +} + + +static void open_database() +{ + int res = sqlite3_open(filename, &db); + + if (res) + { + fprintf(stderr, "Unable to open database: %s (result=%d)\n", filename, res); + exit(1); + } +} + +static int sql_callback(void* ptr, int argc, char **argv, char **colName) { return 0; } + +static int sql_execute(const char* sql, ...) +{ + va_list args; + char query[1024]; + char* errMsg; + int rc; + + va_start(args, sql); + vsnprintf(query, sizeof(query), sql, args); + +#ifdef DEBUG_SQL + printf("SQL: %s\n", query); +#endif + + open_database(); + + rc = sqlite3_exec(db, query, sql_callback, NULL, &errMsg); + if (rc != SQLITE_OK) { + fprintf(stderr, "ERROR: %s\n", errMsg); + sqlite3_free(errMsg); + } + + rc = sqlite3_changes(db); + sqlite3_close(db); + return rc; +} + +static int create(size_t argc, const char** argv) +{ + const char* sql = "CREATE TABLE users" + "(" + "nickname CHAR NOT NULL UNIQUE," + "password CHAR NOT NULL," + "credentials CHAR NOT NULL DEFAULT 'user'," + "created TIMESTAMP DEFAULT (DATETIME('NOW'))," + "activity TIMESTAMP DEFAULT (DATETIME('NOW'))" + ");"; + + sql_execute(sql); + return 0; +} + + +static int sql_callback_list(void* ptr, int argc, char **argv, char **colName) +{ + int* found = (int*) ptr; + uhub_assert(strcmp(colName[0], "nickname") == 0 && strcmp(colName[2], "credentials") == 0); + printf("%s\t%s\n", argv[2], argv[0]); + (*found)++; + return 0; +} + +static int list(size_t argc, const char** argv) +{ + char* errMsg; + int found = 0; + int rc; + + open_database(); + + rc = sqlite3_exec(db, "SELECT * FROM users;", sql_callback_list, &found, &errMsg); + if (rc != SQLITE_OK) { +#ifdef DEBUG_SQL + fprintf(stderr, "SQL: ERROR: %s (%d)\n", errMsg, rc); +#endif + sqlite3_free(errMsg); + exit(1); + } + + sqlite3_close(db); + return 0; +} + + +static int add(size_t argc, const char** argv) +{ + char* user = NULL; + char* pass = NULL; + const char* cred = NULL; + int rc; + + if (argc < 2) + print_usage("username password [credentials = user]"); + + user = sql_escape_string(argv[0]); + pass = sql_escape_string(argv[1]); + cred = validate_cred(argv[2] ? argv[2] : "user"); + + rc = sql_execute("INSERT INTO users (nickname, password, credentials) VALUES('%s', '%s', '%s');", user, pass, cred); + + free(user); + free(pass); + + if (rc != 1) + { + fprintf(stderr, "Unable to add user \"%s\"\n", argv[0]); + return 1; + } + return 0; +} + +static int mod(size_t argc, const char** argv) +{ + char* user = NULL; + const char* cred = NULL; + int rc; + + if (argc < 2) + print_usage("username credentials"); + + user = sql_escape_string(argv[0]); + cred = validate_cred(argv[1]); + + rc = sql_execute("UPDATE users SET credentials = '%s' WHERE nickname = '%s';", cred, user); + + free(user); + + if (rc != 1) + { + fprintf(stderr, "Unable to set credentials for user \"%s\"\n", argv[0]); + return 1; + } + return 0; +} + +static int pass(size_t argc, const char** argv) +{ + char* user = NULL; + char* pass = NULL; + int rc; + + if (argc < 2) + print_usage("username password"); + + user = sql_escape_string(argv[0]); + pass = sql_escape_string(argv[1]); + + rc = sql_execute("UPDATE users SET password = '%s' WHERE nickname = '%s';", pass, user); + + free(user); + free(pass); + + if (rc != 1) + { + fprintf(stderr, "Unable to change password for user \"%s\"\n", argv[0]); + return 1; + } + + return 0; +} + + +static int del(size_t argc, const char** argv) +{ + char* user = NULL; + int rc; + + if (argc < 2) + print_usage("username"); + + user = sql_escape_string(argv[0]); + + rc = sql_execute("DELETE FROM users WHERE nickname = '%s';", user); + free(user); + + if (rc != 1) + { + fprintf(stderr, "Unable to delete user \"%s\".\n", argv[0]); + return 1; + } + + return 0; +} + +void main_usage(const char* binary) +{ + printf( + "Usage: %s filename command [...]\n" + "\n" + "Command syntax:\n" + " create\n" + " add username password [credentials = user]\n" + " del username\n" + " mod username credentials\n" + " pass username password\n" + " list\n" + "\n" + "Parameters:\n" + " 'filename' is a database file\n" + " 'username' is a nickname (UTF-8, up to 64 bytes)\n" + " 'password' is a password (UTF-8, up to 64 bytes)\n" + " 'credentials' is one of 'admin', 'super', 'op', 'user'\n" + "\n" + , binary); +} + +int main(int argc, char** argv) +{ + binary = argv[0]; + filename = argv[1]; + command = argv[2]; + size_t n = 0; + + if (argc < 3) + { + main_usage(argv[0]); + return 1; + } + + for (; n < sizeof(COMMANDS) / sizeof(COMMANDS[0]); n++) + { + if (!strcmp(command, COMMANDS[n].command)) + return COMMANDS[n].handle(argc - 2, (const char**) &argv[3]); + } + + // Unknown command! + main_usage(argv[0]); + return 1; +} + +