From 0e14c062efbd52a0f3c4ce384f01a4d887bb483c Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Wed, 19 Jul 2017 15:32:56 +0200 Subject: [PATCH] More commands --- client/subauth.c | 10 ++- server/auth.c | 83 +++++++++++++++++-- server/cmd.c | 205 ++++++++++++++++++++++++++++++++++++++++++++-- server/subauthd.c | 2 +- server/subauthd.h | 11 ++- 5 files changed, 293 insertions(+), 18 deletions(-) diff --git a/client/subauth.c b/client/subauth.c index 42b9003..91a1a1f 100644 --- a/client/subauth.c +++ b/client/subauth.c @@ -49,9 +49,17 @@ int main(int argc UNUSED, char **argv) struct fastbuf *out = bfdopen(1, 4096); struct json_context *js = json_new(); + +#if 0 struct json_node *rq = json_new_object(js); - json_object_set(rq, "cmd", json_new_string(js, "nop")); + json_object_set(rq, "cmd", json_new_string(js, "create-acct")); + json_object_set(rq, "login", json_new_string(js, "mj")); + json_object_set(rq, "zone", json_new_string(js, "mail")); +#else + struct fastbuf *in = bfdopen(0, 4096); + struct json_node *rq = json_parse(js, in); +#endif bprintf(out, ">>> Request:\n"); json_write(js, out, rq); diff --git a/server/auth.c b/server/auth.c index f34039b..0401898 100644 --- a/server/auth.c +++ b/server/auth.c @@ -10,6 +10,7 @@ #include #include +#include #include #include "subauthd.h" @@ -17,9 +18,9 @@ #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) @@ -27,7 +28,9 @@ 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 + +struct auth_zone *auth_find_zone(const char *name) { CLIST_FOR_EACH(struct auth_zone *, z, zone_list) if (!strcmp(z->name, name)) @@ -35,7 +38,71 @@ static struct auth_zone *find_zone_by_name(const char *name) return NULL; } -#include +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) { @@ -55,7 +122,7 @@ static void db_parse_user(struct json_node *ju) 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"); @@ -69,7 +136,8 @@ static void db_parse_user(struct json_node *ju) 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); @@ -97,8 +165,7 @@ static void db_parse_user(struct json_node *ju) 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); diff --git a/server/cmd.c b/server/cmd.c index 125422a..8eba618 100644 --- a/server/cmd.c +++ b/server/cmd.c @@ -5,17 +5,28 @@ */ #include +#include + +#include +#include +#include #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) @@ -61,11 +72,173 @@ struct json_node *get_object(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); @@ -73,6 +246,10 @@ struct command { 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) @@ -82,16 +259,30 @@ 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; } diff --git a/server/subauthd.c b/server/subauthd.c index d9892b4..30c25a2 100644 --- a/server/subauthd.c +++ b/server/subauthd.c @@ -112,7 +112,7 @@ static void received_packet(struct client *c, byte *pkt, int len) } TRANS_CATCH(x) { - cmd_error(c, "Parse error"); + json_object_set(c->reply, "error", json_new_string_ref(c->json, "Parse error")); send_reply(c); return; } diff --git a/server/subauthd.h b/server/subauthd.h index e807ef3..d1a1cf7 100644 --- a/server/subauthd.h +++ b/server/subauthd.h @@ -30,7 +30,6 @@ extern char *database_name; /* cmd.c */ -void cmd_error(struct client *c, const char *err); void cmd_dispatch(struct client *c); const char *get_string(struct json_node *n, const char *key); @@ -55,6 +54,7 @@ struct auth_user { struct auth_acct { cnode n; + struct auth_user *user; struct auth_zone *zone; clist tokens; // of struct auth_token }; @@ -67,6 +67,7 @@ enum token_type { struct auth_token { cnode n; + struct auth_acct *acct; enum token_type type; char *salt; char *hash; @@ -76,3 +77,11 @@ struct auth_token { void auth_init(void); void db_write(void); +struct auth_zone *auth_find_zone(const char *name); +struct auth_user *auth_find_user(const char *login, bool create); +struct auth_acct *auth_find_acct(struct auth_user *au, struct auth_zone *az, bool create); +void auth_delete_user(struct auth_user *au); +void auth_delete_acct(struct auth_acct *aa); +void auth_delete_token(struct auth_token *at); +struct auth_token *auth_create_token(struct auth_acct *aa); +void auth_set_token_passwd(struct auth_token *at, const char *passwd); -- 2.39.2