message or an empty string to indicate success.
Some operations require root privileges. Other operations are
-unprivileged if no login name present, or if it matches the UID
-of the requesting user.
+unprivileged if no login name present, or if the correct "auth-passwd"
+is provided and target user has allowed administration of their
+account using a password authentication. Only regular password can be
+used for such authentication, tokens are not accepted.
# No operation (unprivileged)
{
{
"cmd": "create-token",
"login": "login name",
+ "auth-passwd": "current password",
"zone": "auth zone",
"comment": "optional comment"
}
{
"cmd": "delete-token",
"login": "login name",
+ "auth-passwd": "current password",
"zone": "auth zone",
"ident": "token id" # "*" for all tokens for the login+zone
}
{
"cmd": "change-token"
"login": "login name",
+ "auth-passwd": "current password",
"zone": "auth zone",
"ident": "token id",
"comment": "new comment" # optional
{
"cmd": "set-passwd",
"login": "login name",
+ "auth-passwd": "current password",
"zone": "auth zone",
"passwd": "new password"
}
{
"cmd": "delete-passwd",
"login": "login name",
+ "auth-passwd": "current password",
"zone": "auth zone"
}
{
"cmd": "create-temp",
"login": "login name",
+ "auth-passwd": "current password",
"zone": "auth zone",
"validity": seconds # Requested token validity
}
"passwd": "password or token"
}
+# Allow/disallow management of selected account using password
+{
+ "cmd": "allow-passwd-auth",
+ "login": "login name",
+ "auth-passwd": "current password",
+ "zone": "auth zone",
+ "allow": integer
+}
+
# List user's accounts and tokens
{
"cmd": "list-accts",
"accounts": [
{
"zone": "auth zone",
+ "allow-passwd-auth": integer, # Can anybody manage this account using its password?
"tokens": [
{
"type": "token type", # passwd/token
"desc": "human-readable description",
"allow-passwd": integer, # Does the zone support passwords?
"allow-tokens": integer, # Does the zone support auth tokens?
+ "allow-passwd-auth": integer, # Does the zone support password authentication for account management?
"max-temp-validity": seconds # Maximum validity of temp tokens
# (if no temp tokens supported)
}
in the given zone. Requires *--zone*. Optionally, you can specify a *--user*
to test somebody else's credentials.
+*--allow-passwd-auth*::
+ Allow administration of our account in a given zone to anybody, who
+ knows our password. Requires *--zone*. Depending on the local setup,
+ this may for example enable passowrd changes from webmail interface.
+
+*--disallow-passwd-auth*::
+ Prevent other users from administration of our account in a given zone
+ using password authentication. Requires *--zone*.
+
*Administrator commands*
*--create-acct*::
which is either "*h*", "*m*", or "*s*". Maximum lifetime can be limited
by zone configuration.
+*--auth-passwd*::
+ Ask for target user's account passowrd to authorize the operation.
+ Note that password authentication has to be explicitly enabled on
+ target account. Requires *--zone* and *--user*.
+
*--socket=*'path'::
Communicate with the subauth daemon through the given socket instead
of the default one.
static char *arg_comment;
static int debug;
static char *arg_expire;
+static int arg_auth_passwd;
/*** Communication with the daemon ***/
set_string(rq, "cmd", op);
if (arg_user)
set_string(rq, "login", arg_user);
+ if (arg_auth_passwd)
+ {
+ char *auth_passwd = xstrdup(getpass("Current password: "));
+ set_string(rq, "auth-passwd", auth_passwd);
+ }
}
static void op_debug(const char *msg, struct json_node *n)
if (!arg_zone)
opt_failure("--zone must be given");
+ op_new("set-passwd");
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);
return;
}
- printf("%-16s %-6s %-5s %-19s %s\n",
- "Zone", "Type", "Ident", "Last modified", "Comment");
+ printf("%-16s %-6s %-5s %-19s %-8s %s\n",
+ "Zone", "Type", "Ident", "Last modified", "PassAuth", "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;
+ struct json_node *jpassauth = get_child(ja, "allow-passwd-auth", JSON_NUMBER);
if (!GARY_SIZE(jts))
{
- printf("%-16s (no tokens defined)\n", jz->string);
+ printf("%-16s (no tokens defined%s)\n",
+ jz->string,
+ jpassauth ? (jpassauth->number ? ", password auth allowed" : ", password auth disabled") : "");
continue;
}
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",
+ printf("%-16s %-6s %-5s %04d-%02d-%02d %02d:%02d:%02d %8s %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,
+ (jpassauth && jpassauth->number ? "yes" : "-"),
(jcomment ? jcomment->string : ""));
}
}
op_run();
struct json_node **jzs = need_child(rp, "zones", JSON_ARRAY)->elements;
- printf("%-16s %6s %6s %6s %s\n", "Zone", "Passwd", "Tokens", "MaxTmp", "Description");
+ printf("%-16s %6s %6s %6s %8s %s\n", "Zone", "Passwd", "Tokens", "MaxTmp", "PassAuth", "Description");
for (uint i=0; i < GARY_SIZE(jzs); i++)
{
struct json_node *jz = jzs[i];
struct json_node *jdesc = get_child(jz, "desc", JSON_STRING);
struct json_node *jpass = get_child(jz, "allow-passwd", JSON_NUMBER);
struct json_node *jtokens = get_child(jz, "allow-tokens", JSON_NUMBER);
+ struct json_node *jpassauth = get_child(jz, "allow-passwd-auth", JSON_NUMBER);
struct json_node *jtemp = get_child(jz, "max-temp-validity", JSON_NUMBER);
- printf("%-16s %6s %6d %6d %s\n",
+ printf("%-16s %6s %6d %6d %8s %s\n",
jname->string,
(jpass && jpass->number ? "yes" : "-"),
(jtokens ? (uint) jtokens->number : 0),
(jtemp ? (uint) jtemp->number : 0),
+ (jpassauth && jpassauth->number ? "yes" : "-"),
(jdesc ? jdesc->string : "(no description)"));
}
}
printf("%s\n", jt->string);
}
+static void cmd_allow_passwd_auth(void)
+{
+ if (!arg_zone)
+ opt_failure("--zone must be given");
+
+ op_new("allow-passwd-auth");
+ set_string(rq, "zone", arg_zone);
+ set_uint(rq, "allow", 1);
+
+ op_run();
+}
+
+static void cmd_disallow_passwd_auth(void)
+{
+ if (!arg_zone)
+ opt_failure("--zone must be given");
+
+ op_new("allow-passwd-auth");
+ set_string(rq, "zone", arg_zone);
+ json_object_set(rq, "allow", json_new_number(js, 0));
+
+ op_run();
+}
+
static void cmd_raw(void)
{
op_new("");
CMD_ZONES,
CMD_LOGIN,
CMD_TEMP_TOKEN,
+ CMD_ALLOW_PASSWD_AUTH,
+ CMD_DISALLOW_PASSWD_AUTH,
CMD_RAW,
CMD_MAX
};
[CMD_ZONES] = cmd_zones,
[CMD_LOGIN] = cmd_login,
[CMD_TEMP_TOKEN] = cmd_temp_token,
+ [CMD_ALLOW_PASSWD_AUTH] = cmd_allow_passwd_auth,
+ [CMD_DISALLOW_PASSWD_AUTH] = cmd_disallow_passwd_auth,
[CMD_RAW] = cmd_raw,
};
OPT_HELP_OPTION,
OPT_HELP(""),
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, "change-token", command, CMD_CHANGE_TOKEN, OPT_SINGLE, "\tChange the comment of an existing token"),
- OPT_SWITCH(0, "login", command, CMD_LOGIN, OPT_SINGLE, "\tTry to log in"),
- OPT_SWITCH(0, "temp-token", command, CMD_TEMP_TOKEN, OPT_SINGLE, "\tCreate a temporary token"),
+ 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, "change-token", command, CMD_CHANGE_TOKEN, OPT_SINGLE, "\tChange the comment of an existing token"),
+ OPT_SWITCH(0, "login", command, CMD_LOGIN, OPT_SINGLE, "\tTry to log in"),
+ OPT_SWITCH(0, "temp-token", command, CMD_TEMP_TOKEN, OPT_SINGLE, "\tCreate a temporary token"),
+ OPT_SWITCH(0, "allow-passwd-auth", command, CMD_ALLOW_PASSWD_AUTH, OPT_SINGLE, "\tAllow management using password authentication"),
+ OPT_SWITCH(0, "disallow-passwd-auth", command, CMD_DISALLOW_PASSWD_AUTH, OPT_SINGLE, "\tDisallow account management"),
OPT_HELP(""),
OPT_HELP("Administrative commands:"),
OPT_SWITCH(0, "create-acct", command, CMD_CREATE_ACCT, OPT_SINGLE, "\tCreate an account"),
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_STRING('x', "expire", arg_expire, OPT_REQUIRED_VALUE, "n(h|m|s)\tSet expiration time of a temporary token (default: 5m)"),
+ OPT_BOOL(0, "auth-passwd", arg_auth_passwd, 0, "\tAsk for current password to authorize the operation"),
OPT_END
}
};
# Allow users to set a password
AllowPasswd 1
+ # Allow users to manage account without shell access
+ AllowPasswdAuth 0
+
# Allow users to create a token, set maximum number of tokens per user
AllowTokens 16
die("Database defines accounts in zone %s, which is not configured", zone_name);
clist_init(&aa->tokens);
+ uint allow_passwd_auth;
+ if (!get_uint(ja, "p", &allow_passwd_auth))
+ allow_passwd_auth = 0;
+ aa->allow_passwd_auth = allow_passwd_auth;
+
struct json_node **jts = get_array(ja, "t");
if (jts)
{
struct json_node *ja = json_new_object(js);
json_array_append(jas, ja);
json_object_set(ja, "z", json_new_string_ref(js, aa->zone->name));
+ json_object_set(ja, "p", json_new_number(js, aa->allow_passwd_auth));
struct json_node *jts = json_new_array(js);
CLIST_FOR_EACH(struct auth_token *, at, aa->tokens)
{
return val;
}
+static uint cmd_need_uint(struct client *c, const char *key)
+{
+ uint val = 0;
+ if (!get_uint(c->request, key, &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");
}
}
+static void cmd_passwd_auth_fake(struct client *c, const char *passwd)
+{
+ auth_check_token(auth_fake_token, passwd);
+ cmd_error(c, "Invalid password");
+}
+
+static struct auth_acct *cmd_need_passwd_auth_acct(struct client *c, const char *passwd)
+{
+ struct auth_zone *az = cmd_need_zone(c);
+ const char *login = cmd_need_string(c, "login");
+ if (!az->allow_passwd_auth || !az->allow_passwd)
+ {
+ msg(L_INFO, "Password authentication denied by zone configuration: login=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
+ cmd_error(c, "This zone does not allow password authentication for management");
+ }
+
+ struct auth_user *au = auth_find_user(login, 0);
+ if (!au)
+ {
+ msg(L_INFO, "Password authentication failed: No user=<%s> uid=<%u>", login, c->uid);
+ cmd_passwd_auth_fake(c, passwd);
+ }
+
+ struct auth_acct *aa = auth_find_acct(au, az, 0);
+ if (!aa)
+ {
+ msg(L_INFO, "Password authentication failed: No account user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
+ cmd_passwd_auth_fake(c, passwd);
+ }
+ if (!aa->allow_passwd_auth)
+ {
+ msg(L_INFO, "Password authentication failed: Not allowed by user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
+ cmd_passwd_auth_fake(c, passwd);
+ }
+
+ struct auth_token *at = auth_find_token_passwd(aa);
+ if (!at)
+ {
+ msg(L_INFO, "Password authentication failed: No password user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
+ cmd_passwd_auth_fake(c, passwd);
+ }
+
+ if (!auth_check_token(at, passwd))
+ {
+ msg(L_INFO, "Password authentication failed: Wrong password for user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
+ cmd_error(c, "Invalid password");
+ }
+
+ msg(L_INFO, "Password authentication successful: user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
+ return aa;
+}
+
static struct auth_acct *cmd_need_target_acct(struct client *c)
{
+ const char *auth_passwd = get_string(c->request, "auth-passwd");
+ if (auth_passwd)
+ return cmd_need_passwd_auth_acct(c, auth_passwd);
+
const char *login = cmd_need_target_login(c);
struct auth_zone *az = cmd_need_zone(c);
cmd_ok(c);
}
+static void cmd_allow_passwd_auth(struct client *c)
+{
+ struct auth_acct *aa = cmd_need_target_acct(c);
+ uint allow = cmd_need_uint(c, "allow");
+ if (!aa->zone->allow_passwd_auth)
+ cmd_error(c, "This zone does not allow password authentication for management");
+
+ msg(L_INFO, "Setting account password authentication: allow-passwd-auth=<%u> login=<%s> zone=<%s>", !!allow, aa->user->login, aa->zone->name);
+
+ aa->allow_passwd_auth = !!allow;
+
+ db_write();
+ cmd_ok(c);
+}
+
static void cmd_list_accts(struct client *c)
{
const char *login = cmd_need_target_login(c);
struct json_node *ja = json_new_object(js);
json_array_append(jas, ja);
set_string(c, ja, "zone", aa->zone->name);
+ set_uint(c, ja, "allow-passwd-auth", aa->zone->allow_passwd_auth && aa->allow_passwd_auth);
struct json_node *jts = json_new_array(js);
json_object_set(ja, "tokens", jts);
set_string(c, jz, "desc", az->desc);
set_uint(c, jz, "allow-passwd", az->allow_passwd);
set_uint(c, jz, "allow-tokens", az->allow_tokens);
+ set_uint(c, jz, "allow-passwd-auth", az->allow_passwd_auth);
set_uint(c, jz, "max-temp-validity", az->max_temp_validity);
}
};
static const struct command command_table[] = {
- { "nop", cmd_nop },
- { "create-acct", cmd_create_acct },
- { "delete-acct", cmd_delete_acct },
- { "create-token", cmd_create_token },
- { "delete-token", cmd_delete_token },
- { "change-token", cmd_change_token },
- { "set-passwd", cmd_set_passwd },
- { "delete-passwd", cmd_delete_passwd },
- { "create-temp", cmd_create_temp },
- { "login", cmd_login },
- { "list-accts", cmd_list_accts },
- { "list-zones", cmd_list_zones },
+ { "nop", cmd_nop },
+ { "create-acct", cmd_create_acct },
+ { "delete-acct", cmd_delete_acct },
+ { "create-token", cmd_create_token },
+ { "delete-token", cmd_delete_token },
+ { "change-token", cmd_change_token },
+ { "set-passwd", cmd_set_passwd },
+ { "delete-passwd", cmd_delete_passwd },
+ { "create-temp", cmd_create_temp },
+ { "login", cmd_login },
+ { "allow-passwd-auth", cmd_allow_passwd_auth },
+ { "list-accts", cmd_list_accts },
+ { "list-zones", cmd_list_zones },
};
void cmd_dispatch(struct client *c)
CF_STRING("Description", PTR_TO(struct auth_zone, desc)),
CF_UINT("AutoCreateAcct", PTR_TO(struct auth_zone, auto_create_acct)),
CF_UINT("AllowPasswd", PTR_TO(struct auth_zone, allow_passwd)),
+ CF_UINT("AllowPasswdAuth", PTR_TO(struct auth_zone, allow_passwd_auth)),
CF_UINT("AllowTokens", PTR_TO(struct auth_zone, allow_tokens)),
CF_UINT("MaxTempValidity", PTR_TO(struct auth_zone, max_temp_validity)),
CF_END
uint auto_create_acct;
uint allow_passwd;
uint allow_tokens;
+ uint allow_passwd_auth;
uint max_temp_validity;
};
struct auth_user *user;
struct auth_zone *zone;
clist tokens; // of struct auth_token
+ uint allow_passwd_auth;
};
enum token_type {