2 * Sub-authentication Daemon: Commands
4 * (c) 2017 Martin Mares <mj@ucw.cz>
16 static void NONRET cmd_error(struct client *c, const char *err, ...)
21 vsnprintf(msg, sizeof(msg), err, args);
22 json_object_set(c->reply, "error", json_new_string(c->json, msg));
23 trans_throw("adhoc", NULL, "Error caught");
27 static void cmd_ok(struct client *c)
29 json_object_set(c->reply, "error", json_new_string_ref(c->json, ""));
32 const char *get_string(struct json_node *n, const char *key)
34 struct json_node *s = json_object_get(n, key);
35 if (s && s->type == JSON_STRING)
41 bool get_uint(struct json_node *n, const char *key, uint *dest)
43 struct json_node *s = json_object_get(n, key);
44 if (s && s->type == JSON_NUMBER)
46 uint u = (uint) s->number;
47 if ((double) u == s->number)
57 struct json_node **get_array(struct json_node *n, const char *key)
59 struct json_node *s = json_object_get(n, key);
60 if (s && s->type == JSON_ARRAY)
66 struct json_node *get_object(struct json_node *n, const char *key)
68 struct json_node *s = json_object_get(n, key);
69 if (s && s->type == JSON_OBJECT)
75 static const char *cmd_need_string(struct client *c, const char *key)
77 const char *val = get_string(c->request, key);
79 cmd_error(c, "Missing %s", key);
83 static struct auth_zone *cmd_need_zone(struct client *c)
85 const char *name = cmd_need_string(c, "zone");
86 struct auth_zone *az = auth_find_zone(name);
88 cmd_error(c, "No such zone");
92 static bool cmd_is_admin(struct client *c)
97 static void cmd_require_admin(struct client *c)
100 cmd_error(c, "Permission denied");
103 static const char *cmd_need_target_login(struct client *c)
105 const char *l = get_string(c->request, "login");
108 cmd_require_admin(c);
113 struct passwd *pw = getpwuid(c->uid);
115 cmd_error(c, "You do not exist");
116 return json_strdup(c->json, pw->pw_name);
120 static struct auth_acct *cmd_need_target_acct(struct client *c)
122 const char *login = cmd_need_target_login(c);
123 struct auth_zone *az = cmd_need_zone(c);
125 struct auth_user *au = auth_find_user(login, 0);
126 struct auth_acct *aa = au ? auth_find_acct(au, az, 0) : NULL;
130 if (!az->auto_create_acct)
131 cmd_error(c, "No such account");
135 msg(L_INFO, "Automatically creating user: login=<%s>", login);
136 au = auth_find_user(login, 1);
138 msg(L_INFO, "Automatically creating account: login=<%s> zone=<%s>", login, az->name);
139 return auth_find_acct(au, az, 1);
142 static void cmd_nop(struct client *c)
147 static void cmd_create_acct(struct client *c)
149 cmd_require_admin(c);
151 const char *login = get_string(c->request, "login");
153 cmd_error(c, "Login required");
155 struct auth_zone *az = cmd_need_zone(c);
156 struct auth_user *au = auth_find_user(login, 1);
157 if (!auth_find_acct(au, az, 0))
159 msg(L_INFO, "Creating account: login=<%s> zone=<%s>", login, az->name);
160 auth_find_acct(au, az, 1);
167 static void cmd_delete_acct(struct client *c)
169 cmd_require_admin(c);
171 const char *login = get_string(c->request, "login");
173 cmd_error(c, "Login required");
174 const char *zone_name = get_string(c->request, "zone");
176 struct auth_user *au = auth_find_user(login, 0);
180 if (zone_name && !strcmp(zone_name, "*"))
182 msg(L_INFO, "Deleting user: login=<%s>", login);
183 auth_delete_user(au);
187 struct auth_zone *az = cmd_need_zone(c);
188 struct auth_acct *aa = auth_find_acct(au, az, 0);
191 msg(L_INFO, "Deleting account: login=<%s> zone=<%s>", login, az->name);
192 auth_delete_acct(aa);
193 if (!clist_head(&au->accounts))
195 msg(L_INFO, "Deleting user with no accounts: login=<%s>", login);
196 auth_delete_user(au);
204 static void cmd_set_passwd(struct client *c)
206 struct auth_acct *aa = cmd_need_target_acct(c);
207 const char *passwd = cmd_need_string(c, "passwd");
209 if (!aa->zone->allow_passwd)
210 cmd_error(c, "This zone does not allow authentication by password");
212 if (strchr(passwd, '-'))
213 cmd_error(c, "The minus sign is forbidden in passwords");
215 msg(L_INFO, "Set password: login=<%s> zone=<%s>", aa->user->login, aa->zone->name);
217 struct auth_token *at_old = auth_find_token_passwd(aa);
219 auth_delete_token(at_old);
221 struct auth_token *at = auth_create_token(aa);
222 auth_set_token_passwd(at, passwd);
228 static void cmd_create_token(struct client *c)
230 struct auth_acct *aa = cmd_need_target_acct(c);
232 if (!aa->zone->allow_tokens)
233 cmd_error(c, "This zone does not allow authentication by tokens");
235 if (clist_size(&aa->tokens) >= aa->zone->allow_tokens)
236 cmd_error(c, "Maximum number of tokens was reached");
238 struct auth_token *at = auth_create_token(aa);
239 char *tok = auth_set_token_generated(at, get_string(c->request, "comment"));
240 json_object_set(c->reply, "token", json_new_string(c->json, tok));
243 msg(L_INFO, "Created token: login=<%s> zone=<%s> id=<%s>", aa->user->login, aa->zone->name, at->ident);
249 static void cmd_login_fake(struct client *c, const char *passwd)
251 auth_check_token(auth_fake_token, passwd);
252 cmd_error(c, "Invalid password");
255 static void cmd_login(struct client *c)
257 struct auth_zone *az = cmd_need_zone(c);
258 const char *given_passwd = cmd_need_string(c, "passwd");
260 // Split password string to token ident and the password proper
261 char passbuf[strlen(given_passwd) + 1];
262 strcpy(passbuf, given_passwd);
263 char *ident, *passwd;
264 char *sep = strchr(passbuf, '-');
277 const char *login = cmd_need_string(c, "login");
278 struct auth_user *au = auth_find_user(login, 0);
281 msg(L_INFO, "Login failed: No user=<%s>", login);
282 return cmd_login_fake(c, passwd);
285 struct auth_acct *aa = auth_find_acct(au, az, 0);
288 msg(L_INFO, "Login failed: No account user=<%s> zone=<%s>", login, az->name);
289 return cmd_login_fake(c, passwd);
292 struct auth_token *at;
294 at = auth_find_token_generated(aa, ident);
296 at = auth_find_token_passwd(aa);
299 msg(L_INFO, "Login failed: No token user=<%s> zone=<%s> ident=<%s>", login, az->name, (ident ? : ""));
300 return cmd_login_fake(c, passwd);
303 if (!auth_check_token(at, passwd))
305 msg(L_INFO, "Login failed: Wrong password for user=<%s> zone=<%s> ident=<%s>", login, az->name, (ident ? : ""));
306 cmd_error(c, "Invalid password");
309 msg(L_INFO, "Login successful: user=<%s> zone=<%s> ident=<%s>", login, az->name, (ident ? : ""));
313 static void cmd_list(struct client *c)
315 const char *login = cmd_need_target_login(c);
317 struct auth_user *au = auth_find_user(login, 0);
319 cmd_error(c, "No such user");
321 struct json_context *js = c->json;
322 json_object_set(c->reply, "login", json_new_string(js, au->login));
323 struct json_node *jas = json_new_array(js);
324 json_object_set(c->reply, "accounts", jas);
326 CLIST_FOR_EACH(struct auth_acct *, aa, au->accounts)
328 struct json_node *ja = json_new_object(js);
329 json_array_append(jas, ja);
330 json_object_set(ja, "zone", json_new_string(js, aa->zone->name));
332 struct json_node *jts = json_new_array(js);
333 json_object_set(ja, "tokens", jts);
335 CLIST_FOR_EACH(struct auth_token *, at, aa->tokens)
337 struct json_node *jt = json_new_object(js);
338 json_array_append(jts, jt);
343 case TOKEN_PASSWORD: type = "passwd"; break;
344 case TOKEN_GENERATED: type = "token"; break;
345 default: type = "unknown"; break;
347 json_object_set(jt, "type", json_new_string(js, type));
350 json_object_set(jt, "ident", json_new_string(js, at->ident));
352 json_object_set(jt, "comment", json_new_string(js, at->comment));
353 json_object_set(jt, "lastmod", json_new_number(js, at->last_modified));
362 void (*handler)(struct client *c);
365 static const struct command command_table[] = {
367 { "create-acct", cmd_create_acct },
368 { "delete-acct", cmd_delete_acct },
369 { "create-token", cmd_create_token },
370 { "set-passwd", cmd_set_passwd },
371 { "login", cmd_login },
372 { "list", cmd_list },
375 void cmd_dispatch(struct client *c)
377 struct json_node *rq = c->request;
380 if (rq->type != JSON_OBJECT || !(cmd = get_string(rq, "cmd")))
382 json_object_set(c->reply, "error", json_new_string_ref(c->json, "Malformed request"));
386 const struct command *command = NULL;
387 for (uint i=0; i < ARRAY_SIZE(command_table); i++)
388 if (!strcmp(cmd, command_table[i].cmd))
390 command = &command_table[i];
395 json_object_set(c->reply, "error", json_new_string_ref(c->json, "No such command"));