*/
#include <ucw/lib.h>
+#include <ucw/log.h>
#include <ucw/opt.h>
#include <ucw-json/json.h>
#include <errno.h>
#include <fcntl.h>
+#include <pwd.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <time.h>
#include <unistd.h>
#include "autoconf.h"
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 ***/
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)
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)
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");
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)
}
}
+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("");
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,
};
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)
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);
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);
{
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);
{ "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 },