From 06c7bb3232ae7377839f5cec287ccc143be5c3fa Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Wed, 19 Jul 2017 23:41:54 +0200 Subject: [PATCH] Client mostly complete --- client/subauth.c | 207 ++++++++++++++++++++++++++++++++++++++++++---- server/auth.c | 2 +- server/cmd.c | 52 ++++++++++-- server/subauthd.h | 2 +- 4 files changed, 242 insertions(+), 21 deletions(-) diff --git a/client/subauth.c b/client/subauth.c index 30dd28c..723ee4f 100644 --- a/client/subauth.c +++ b/client/subauth.c @@ -5,13 +5,16 @@ */ #include +#include #include #include #include #include +#include #include #include +#include #include #include "autoconf.h" @@ -20,9 +23,10 @@ static char *socket_path = INSTALL_RUN_DIR "/subauthd.socket"; -static char *arg_login; +static char *arg_user; static char *arg_zone; static char *arg_ident; +static char *arg_comment; static int debug; /*** Communication with the daemon ***/ @@ -33,7 +37,8 @@ static struct json_node *rp; static void set_string(struct json_node *n, const char *key, const char *val) { - json_object_set(n, key, json_new_string(js, val)); + if (val) + json_object_set(n, key, json_new_string(js, val)); } static struct json_node *get_child(struct json_node *obj, const char *key, uint type) @@ -57,6 +62,8 @@ static void op_new(const char *op) js = json_new(); rq = json_new_object(js); set_string(rq, "cmd", op); + if (arg_user) + set_string(rq, "login", arg_user); } static void op_debug(const char *msg, struct json_node *n) @@ -97,7 +104,7 @@ static void op_run(void) die("Cannot send request: %m"); bclose(rq_fb); - byte rp_buf[4096]; // FIXME + byte rp_buf[16384]; int rp_len = recv(sk, rp_buf, sizeof(rp_buf), 0); if (rp_len < 0) die("Cannot receive reply: %m"); @@ -113,22 +120,145 @@ static void op_run(void) die("Malformed reply: Top-level node is not an object"); struct json_node *err = need_child(rp, "error", JSON_STRING); if (strcmp(err->string, "")) - { - fprintf(stderr, "Error: %s\n", err->string); - exit(1); - } + die("Error: %s", err->string); } /*** Commands ***/ static void cmd_set_passwd(void) { + if (!arg_zone) + opt_failure("--zone must be given"); + + char *passwd = xstrdup(getpass("New password: ")); + char *again = xstrdup(getpass("Confirm password: ")); + if (strcmp(passwd, again)) + die("Passwords do not match"); + + op_new("set-passwd"); + set_string(rq, "zone", arg_zone); + set_string(rq, "passwd", passwd); + + op_run(); +} + +static void cmd_delete_passwd(void) +{ + if (!arg_zone) + opt_failure("--zone must be given"); + + op_new("delete-passwd"); + set_string(rq, "zone", arg_zone); + + op_run(); +} + +static void cmd_create_token(void) +{ + if (!arg_zone) + opt_failure("--zone must be given"); + + op_new("create-token"); + set_string(rq, "zone", arg_zone); + set_string(rq, "comment", arg_comment); + + op_run(); + struct json_node *jt = need_child(rp, "token", JSON_STRING); + printf("%s\n", jt->string); +} + +static void cmd_delete_token(void) +{ + if (!arg_zone || !arg_ident) + opt_failure("--zone and --ident must be given"); + + op_new("delete-token"); + set_string(rq, "zone", arg_zone); + set_string(rq, "ident", arg_ident); + + op_run(); +} + +static void cmd_create_acct(void) +{ + if (!arg_zone || !arg_user) + opt_failure("--zone and --user must be given"); + + op_new("create-acct"); + set_string(rq, "zone", arg_zone); + set_string(rq, "login", arg_user); + + op_run(); +} + +static void cmd_delete_acct(void) +{ + if (!arg_zone || !arg_user) + opt_failure("--zone and --user must be given"); + + op_new("delete-acct"); + set_string(rq, "zone", arg_zone); + set_string(rq, "login", arg_user); + + op_run(); +} + +static void cmd_delete_user(void) +{ + if (!arg_user) + opt_failure("--user must be given"); + + op_new("delete-acct"); + set_string(rq, "zone", "*"); + set_string(rq, "login", arg_user); + + op_run(); } static void cmd_list(void) { op_new("list-accts"); op_run(); + + struct json_node **jas = need_child(rp, "accounts", JSON_ARRAY)->elements; + if (!GARY_SIZE(jas)) + { + puts("No accounts defined"); + return; + } + + printf("%-16s %-6s %-5s %-19s %s\n", + "Zone", "Type", "Ident", "Last modified", "Comment"); + for (uint i=0; i < GARY_SIZE(jas); i++) + { + struct json_node *ja = jas[i]; + struct json_node *jz = need_child(ja, "zone", JSON_STRING); + struct json_node **jts = need_child(ja, "tokens", JSON_ARRAY)->elements; + + if (!GARY_SIZE(jts)) + { + printf("%-16s (no tokens defined)\n", jz->string); + continue; + } + + for (uint j=0; j < GARY_SIZE(jts); j++) + { + struct json_node *lt = jts[j]; + struct json_node *jtype = need_child(lt, "type", JSON_STRING); + struct json_node *jident = get_child(lt, "ident", JSON_STRING); + struct json_node *jcomment = get_child(lt, "comment", JSON_STRING); + struct json_node *jlastmod = need_child(lt, "lastmod", JSON_NUMBER); + time_t lm = jlastmod->number; + struct tm *tm = localtime(&lm); + printf("%-16s %-6s %-5s %04d-%02d-%02d %02d:%02d:%02d %s\n", + jz->string, + jtype->string, + (jident ? jident->string : "-"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + (jcomment ? jcomment->string : "")); + } + } } static void cmd_zones(void) @@ -153,6 +283,30 @@ static void cmd_zones(void) } } +static void cmd_login(void) +{ + if (!arg_zone) + opt_failure("--zone must be given"); + + if (!arg_user) + { + uid_t uid = getuid(); + struct passwd *pw = getpwuid(uid); + if (!pw) + die("You do not exist"); + arg_user = xstrdup(pw->pw_name); + } + + char *passwd = xstrdup(getpass("Password: ")); + + op_new("login"); + set_string(rq, "zone", arg_zone); + set_string(rq, "passwd", passwd); + + op_run(); + printf("OK\n"); +} + static void cmd_raw(void) { op_new(""); @@ -166,16 +320,30 @@ static void cmd_raw(void) enum command { CMD_SET_PASSWD, + CMD_DELETE_PASSWD, + CMD_CREATE_TOKEN, + CMD_DELETE_TOKEN, + CMD_CREATE_ACCT, + CMD_DELETE_ACCT, + CMD_DELETE_USER, CMD_LIST, CMD_ZONES, + CMD_LOGIN, CMD_RAW, CMD_MAX }; void (* const command_handlers[CMD_MAX])(void) = { [CMD_SET_PASSWD] = cmd_set_passwd, + [CMD_DELETE_PASSWD] = cmd_delete_passwd, + [CMD_CREATE_TOKEN] = cmd_create_token, + [CMD_DELETE_TOKEN] = cmd_delete_token, + [CMD_CREATE_ACCT] = cmd_create_acct, + [CMD_DELETE_ACCT] = cmd_delete_acct, + [CMD_DELETE_USER] = cmd_delete_user, [CMD_LIST] = cmd_list, [CMD_ZONES] = cmd_zones, + [CMD_LOGIN] = cmd_login, [CMD_RAW] = cmd_raw, }; @@ -193,22 +361,33 @@ static const struct opt_section options = { OPT_BOOL(0, "debug", debug, 0, "\tDump raw communication with the daemon"), OPT_HELP_OPTION, OPT_HELP(""), - OPT_HELP("Commands:"), - OPT_SWITCH(0, "passwd", command, CMD_SET_PASSWD, OPT_SINGLE, "\tSet password"), - OPT_SWITCH(0, "list", command, CMD_LIST, OPT_SINGLE, "\tList tokens for all zones"), - OPT_SWITCH(0, "zones", command, CMD_ZONES, OPT_SINGLE, "\tList all known zones"), - OPT_SWITCH(0, "raw", command, CMD_RAW, OPT_SINGLE, "\tSend raw JSON command to the daemon"), + OPT_HELP("User commands:"), + OPT_SWITCH('l', "list", command, CMD_LIST, OPT_SINGLE, "\tList tokens for all zones"), + OPT_SWITCH(0, "zones", command, CMD_ZONES, OPT_SINGLE, "\tList all known zones"), + OPT_SWITCH(0, "passwd", command, CMD_SET_PASSWD, OPT_SINGLE, "\tSet password"), + OPT_SWITCH(0, "delete-passwd", command, CMD_DELETE_PASSWD, OPT_SINGLE, "\tRemove password"), + OPT_SWITCH(0, "create-token", command, CMD_CREATE_TOKEN, OPT_SINGLE, "\tCreate a new token"), + OPT_SWITCH(0, "delete-token", command, CMD_DELETE_TOKEN, OPT_SINGLE, "\tRemove an existing token"), + OPT_SWITCH(0, "login", command, CMD_LOGIN, OPT_SINGLE, "\tTry to log in"), + OPT_HELP(""), + OPT_HELP("Administrative commands:"), + OPT_SWITCH(0, "create-acct", command, CMD_CREATE_ACCT, OPT_SINGLE, "\tCreate an account"), + OPT_SWITCH(0, "delete-acct", command, CMD_DELETE_ACCT, OPT_SINGLE, "\tDelete an account"), + OPT_SWITCH(0, "delete-user", command, CMD_DELETE_USER, OPT_SINGLE, "\tDelete a user with all his accounts"), + OPT_SWITCH(0, "raw", command, CMD_RAW, OPT_SINGLE, "\tSend raw JSON command to the daemon"), OPT_HELP(""), OPT_HELP("Command options:"), - OPT_STRING('u', "user", arg_login, OPT_REQUIRED_VALUE, "login\tUser to act on (default: whoever calls it)"), + OPT_STRING('u', "user", arg_user, OPT_REQUIRED_VALUE, "login\tUser to act on (default: whoever calls it)"), OPT_STRING('z', "zone", arg_zone, OPT_REQUIRED_VALUE, "zone\tAuthentication zone"), - OPT_STRING('i', "ident", arg_ident, OPT_REQUIRED_VALUE, "id\tToken ID"), + OPT_STRING('i', "ident", arg_ident, OPT_REQUIRED_VALUE, "id\tToken ID ('*' for all tokens in zone)"), + OPT_STRING('c', "comment", arg_comment, OPT_REQUIRED_VALUE, "string\tHuman-readable description of token"), OPT_END } }; int main(int argc UNUSED, char **argv) { + log_set_format(log_default_stream(), 0, 0); opt_parse(&options, argv+1); if (command < 0) diff --git a/server/auth.c b/server/auth.c index bafcd52..e92d5ad 100644 --- a/server/auth.c +++ b/server/auth.c @@ -77,7 +77,7 @@ struct auth_token *auth_find_token_passwd(struct auth_acct *aa) return NULL; } -struct auth_token *auth_find_token_generated(struct auth_acct *aa, char *ident) +struct auth_token *auth_find_token_generated(struct auth_acct *aa, const char *ident) { CLIST_FOR_EACH(struct auth_token *, at, aa->tokens) if (at->type == TOKEN_GENERATED && !strcmp(at->ident, ident)) diff --git a/server/cmd.c b/server/cmd.c index 4b10368..5533ddc 100644 --- a/server/cmd.c +++ b/server/cmd.c @@ -236,6 +236,20 @@ static void cmd_set_passwd(struct client *c) cmd_ok(c); } +static void cmd_delete_passwd(struct client *c) +{ + struct auth_acct *aa = cmd_need_target_acct(c); + struct auth_token *at = auth_find_token_passwd(aa); + if (!at) + cmd_error(c, "No password set"); + + msg(L_INFO, "Deleted password: login=<%s> zone=<%s>", aa->user->login, aa->zone->name); + auth_delete_token(at); + + db_write(); + cmd_ok(c); +} + static void cmd_create_token(struct client *c) { struct auth_acct *aa = cmd_need_target_acct(c); @@ -257,6 +271,29 @@ static void cmd_create_token(struct client *c) cmd_ok(c); } +static void cmd_delete_token(struct client *c) +{ + struct auth_acct *aa = cmd_need_target_acct(c); + const char *ident = cmd_need_string(c, "ident"); + bool all = !strcmp(ident, "*"); + bool matched = 0; + + struct auth_token *tmp; + CLIST_FOR_EACH_DELSAFE(struct auth_token *, at, aa->tokens, tmp) + if (at->type == TOKEN_GENERATED && (all || !strcmp(at->ident, ident))) + { + matched = 1; + msg(L_INFO, "Deleted token: login=<%s> zone=<%s> id=<%s>", aa->user->login, aa->zone->name, at->ident); + auth_delete_token(at); + } + + if (!all && !matched) + cmd_error(c, "No such token"); + + db_write(); + cmd_ok(c); +} + static void cmd_login_fake(struct client *c, const char *passwd) { auth_check_token(auth_fake_token, passwd); @@ -325,15 +362,18 @@ static void cmd_list_accts(struct client *c) { const char *login = cmd_need_target_login(c); - struct auth_user *au = auth_find_user(login, 0); - if (!au) - cmd_error(c, "No such user"); - struct json_context *js = c->json; - set_string(c, c->reply, "login", au->login); + set_string(c, c->reply, "login", login); struct json_node *jas = json_new_array(js); json_object_set(c->reply, "accounts", jas); + struct auth_user *au = auth_find_user(login, 0); + if (!au) + { + cmd_ok(c); + return; + } + CLIST_FOR_EACH(struct auth_acct *, aa, au->accounts) { struct json_node *ja = json_new_object(js); @@ -396,7 +436,9 @@ static const struct command command_table[] = { { "create-acct", cmd_create_acct }, { "delete-acct", cmd_delete_acct }, { "create-token", cmd_create_token }, + { "delete-token", cmd_delete_token }, { "set-passwd", cmd_set_passwd }, + { "delete-passwd", cmd_delete_passwd }, { "login", cmd_login }, { "list-accts", cmd_list_accts }, { "list-zones", cmd_list_zones }, diff --git a/server/subauthd.h b/server/subauthd.h index 7148bfd..4fb6650 100644 --- a/server/subauthd.h +++ b/server/subauthd.h @@ -92,7 +92,7 @@ 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); struct auth_token *auth_find_token_passwd(struct auth_acct *aa); -struct auth_token *auth_find_token_generated(struct auth_acct *aa, char *ident); +struct auth_token *auth_find_token_generated(struct auth_acct *aa, const char *ident); void auth_delete_user(struct auth_user *au); void auth_delete_acct(struct auth_acct *aa); void auth_delete_token(struct auth_token *at); -- 2.39.2