#include <errno.h>
#include <fcntl.h>
+#include <time.h>
#include <unistd.h>
#include "subauthd.h"
#define HASH_NODE struct auth_user
#define HASH_PREFIX(x) user_hash_##x
#define HASH_KEY_ENDSTRING login
-//#define HASH_WANT_FIND
+#define HASH_WANT_FIND
#define HASH_WANT_LOOKUP
-// #define HASH_WANT_REMOVE
+#define HASH_WANT_REMOVE
#define HASH_GIVE_INIT_DATA
static void user_hash_init_data(struct auth_user *au)
clist_init(&au->accounts);
}
-static struct auth_zone *find_zone_by_name(const char *name)
+#include <ucw/hashtable.h>
+
+struct auth_zone *auth_find_zone(const char *name)
{
CLIST_FOR_EACH(struct auth_zone *, z, zone_list)
if (!strcmp(z->name, name))
return NULL;
}
-#include <ucw/hashtable.h>
+struct auth_user *auth_find_user(const char *login, bool create)
+{
+ if (create)
+ return user_hash_lookup((char *) login);
+ else
+ return user_hash_find((char *) login);
+}
+
+struct auth_acct *auth_find_acct(struct auth_user *au, struct auth_zone *az, bool create)
+{
+ CLIST_FOR_EACH(struct auth_acct *, aa, au->accounts)
+ if (aa->zone == az)
+ return aa;
+
+ if (!create)
+ return NULL;
+
+ struct auth_acct *aa = xmalloc_zero(sizeof(*aa));
+ clist_add_tail(&au->accounts, &aa->n);
+ aa->user = au;
+ aa->zone = az;
+ clist_init(&aa->tokens);
+ return aa;
+}
+
+void auth_delete_user(struct auth_user *au)
+{
+ struct auth_acct *aa;
+ while (aa = clist_head(&au->accounts))
+ auth_delete_acct(aa);
+
+ user_hash_remove(au);
+}
+
+void auth_delete_acct(struct auth_acct *ac)
+{
+ struct auth_token *at;
+ while (at = clist_head(&ac->tokens))
+ auth_delete_token(at);
+ clist_remove(&ac->n);
+}
+
+void auth_delete_token(struct auth_token *at)
+{
+ clist_remove(&at->n);
+ xfree(at->salt);
+ xfree(at->hash);
+ xfree(at->comment);
+}
+
+struct auth_token *auth_create_token(struct auth_acct *aa)
+{
+ struct auth_token *at = xmalloc_zero(sizeof(*at));
+ clist_add_tail(&aa->tokens, &at->n);
+ at->acct = aa;
+ at->last_modified = time(NULL);
+ return at;
+}
+
+void auth_set_token_passwd(struct auth_token *at, const char *passwd)
+{
+ at->type = TOKEN_PASSWORD;
+ at->salt = xstrdup("???");
+ at->hash = xstrdup("???");
+}
static void NONRET db_corrupted(const char *reason)
{
if (!jas)
db_corrupted("User has no accounts");
- struct auth_user *au = user_hash_lookup((char *) login);
+ struct auth_user *au = auth_find_user(login, 1);
if (clist_head(&au->accounts))
db_corrupted("Multiple users with the same login");
struct auth_acct *aa = xmalloc_zero(sizeof(*aa));
clist_add_tail(&au->accounts, &aa->n);
- aa->zone = find_zone_by_name(zone_name);
+ aa->user = au;
+ aa->zone = auth_find_zone(zone_name);
if (!aa->zone)
die("Database defines accounts in zone %s, which is not configured", zone_name);
clist_init(&aa->tokens);
if (!get_uint(jt, "m", &lastmod))
db_corrupted("Token has invalid last modified time");
- struct auth_token *at = xmalloc(sizeof(*at));
- clist_add_tail(&aa->tokens, &at->n);
+ struct auth_token *at = auth_create_token(aa);
at->type = type;
at->salt = xstrdup(salt);
at->hash = xstrdup(hash);
*/
#include <ucw/lib.h>
+#include <ucw/trans.h>
+
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
#include "subauthd.h"
-void cmd_error(struct client *c, const char *err)
+static void NONRET cmd_error(struct client *c, const char *err, ...)
{
- json_object_set(c->reply, "error", json_new_string(c->json, err));
+ va_list args;
+ char msg[1024];
+ va_start(args, err);
+ vsnprintf(msg, sizeof(msg), err, args);
+ json_object_set(c->reply, "error", json_new_string(c->json, msg));
+ trans_throw("adhoc", NULL, "Error caught");
+ va_end(args);
}
static void cmd_ok(struct client *c)
{
- cmd_error(c, "");
+ json_object_set(c->reply, "error", json_new_string_ref(c->json, ""));
}
const char *get_string(struct json_node *n, const char *key)
return NULL;
}
+static const char *cmd_need_string(struct client *c, const char *key)
+{
+ const char *val = get_string(c->request, key);
+ if (!val)
+ cmd_error(c, "Missing %s", key);
+ return val;
+}
+
+static struct auth_zone *cmd_need_zone(struct client *c)
+{
+ const char *name = cmd_need_string(c, "zone");
+ struct auth_zone *az = auth_find_zone(name);
+ if (!az)
+ cmd_error(c, "No such zone");
+ return az;
+}
+
+static bool cmd_is_admin(struct client *c)
+{
+ // FIXME
+ (void) c;
+ return 1;
+}
+
+static void cmd_require_admin(struct client *c)
+{
+ if (!cmd_is_admin(c))
+ cmd_error(c, "Permission denied");
+}
+
+static const char *cmd_need_target_login(struct client *c)
+{
+ const char *l = get_string(c->request, "login");
+ if (l)
+ {
+ cmd_require_admin(c);
+ return l;
+ }
+ else
+ {
+ struct passwd *pw = getpwuid(c->uid);
+ if (!pw)
+ cmd_error(c, "You do not exist");
+ return json_strdup(c->json, pw->pw_name);
+ }
+}
+
+static struct auth_acct *cmd_need_target_acct(struct client *c)
+{
+ const char *login = cmd_need_target_login(c);
+ struct auth_zone *az = cmd_need_zone(c);
+
+ struct auth_user *au = auth_find_user(login, 0);
+ struct auth_acct *aa = au ? auth_find_acct(au, az, 0) : NULL;
+ if (aa)
+ return aa;
+
+ if (!az->auto_create_acct)
+ cmd_error(c, "No such account");
+
+ if (!au)
+ {
+ msg(L_INFO, "Automatically creating user: login=<%s>", login);
+ au = auth_find_user(login, 1);
+ }
+ msg(L_INFO, "Automatically creating account: login=<%s> zone=<%s>", login, az->name);
+ return auth_find_acct(au, az, 1);
+}
+
static void cmd_nop(struct client *c)
{
cmd_ok(c);
}
+static void cmd_create_acct(struct client *c)
+{
+ cmd_require_admin(c);
+
+ const char *login = get_string(c->request, "login");
+ if (!login)
+ cmd_error(c, "Login required");
+
+ struct auth_zone *az = cmd_need_zone(c);
+ struct auth_user *au = auth_find_user(login, 1);
+ if (!auth_find_acct(au, az, 0))
+ {
+ msg(L_INFO, "Creating account: login=<%s> zone=<%s>", login, az->name);
+ auth_find_acct(au, az, 1);
+ db_write();
+ }
+
+ cmd_ok(c);
+}
+
+static void cmd_delete_acct(struct client *c)
+{
+ cmd_require_admin(c);
+
+ const char *login = get_string(c->request, "login");
+ if (!login)
+ cmd_error(c, "Login required");
+ const char *zone_name = get_string(c->request, "zone");
+
+ struct auth_user *au = auth_find_user(login, 0);
+ if (!au)
+ return cmd_ok(c);
+
+ if (zone_name && !strcmp(zone_name, "*"))
+ {
+ msg(L_INFO, "Deleting user: login=<%s>", login);
+ auth_delete_user(au);
+ }
+ else
+ {
+ struct auth_zone *az = cmd_need_zone(c);
+ struct auth_acct *aa = auth_find_acct(au, az, 0);
+ if (!aa)
+ return cmd_ok(c);
+ msg(L_INFO, "Deleting account: login=<%s> zone=<%s>", login, az->name);
+ auth_delete_acct(aa);
+ if (!clist_head(&au->accounts))
+ {
+ msg(L_INFO, "Deleting user with no accounts: login=<%s>", login);
+ auth_delete_user(au);
+ }
+ }
+
+ db_write();
+ cmd_ok(c);
+}
+
+static void cmd_set_passwd(struct client *c)
+{
+ struct auth_acct *aa = cmd_need_target_acct(c);
+ const char *passwd = cmd_need_string(c, "passwd");
+
+ if (!aa->zone->allow_passwd)
+ cmd_error(c, "This zone does not allow authentication by password");
+
+ msg(L_INFO, "Set password: login=<%s> zone=<%s>", aa->user->login, aa->zone->name);
+
+ CLIST_FOR_EACH(struct auth_token *, at, aa->tokens)
+ if (at->type == TOKEN_PASSWORD)
+ {
+ auth_delete_token(at);
+ break;
+ }
+
+ struct auth_token *at = auth_create_token(aa);
+ auth_set_token_passwd(at, passwd);
+
+ // FIXME
+
+ db_write();
+ cmd_ok(c);
+}
+
+static void cmd_verify(struct client *c)
+{
+ struct auth_acct *aa = cmd_need_target_acct(c);
+ const char *passwd = cmd_need_string(c, "passwd");
+
+ // FIXME
+
+ cmd_ok(c);
+}
+
struct command {
const char *cmd;
void (*handler)(struct client *c);
static const struct command command_table[] = {
{ "nop", cmd_nop },
+ { "create-acct", cmd_create_acct },
+ { "delete-acct", cmd_delete_acct },
+ { "set-passwd", cmd_set_passwd },
+ { "verify", cmd_verify },
};
void cmd_dispatch(struct client *c)
if (rq->type != JSON_OBJECT || !(cmd = get_string(rq, "cmd")))
{
- cmd_error(c, "Malformed request");
+ json_object_set(c->reply, "error", json_new_string_ref(c->json, "Malformed request"));
return;
}
+ const struct command *command = NULL;
for (uint i=0; i < ARRAY_SIZE(command_table); i++)
if (!strcmp(cmd, command_table[i].cmd))
{
- command_table[i].handler(c);
- return;
+ command = &command_table[i];
+ break;
}
+ if (!command)
+ {
+ json_object_set(c->reply, "error", json_new_string_ref(c->json, "No such command"));
+ return;
+ }
- cmd_error(c, "No such command");
+ TRANS_TRY
+ {
+ if (command)
+ command->handler(c);
+ }
+ TRANS_CATCH(x)
+ {
+ }
+ TRANS_END;
}