2 * Sub-authentication Daemon: Commands
4 * (c) 2017 Martin Mares <mj@ucw.cz>
16 static void set_string(struct client *c, struct json_node *n, const char *key, const char *val)
19 json_object_set(n, key, json_new_string(c->json, val));
22 static void set_uint(struct client *c, struct json_node *n, const char *key, uint val)
24 json_object_set(n, key, json_new_number(c->json, val));
27 static void NONRET cmd_error(struct client *c, const char *err, ...)
32 vsnprintf(msg, sizeof(msg), err, args);
33 set_string(c, c->reply, "error", msg);
34 trans_throw("adhoc", NULL, "Error caught");
38 static void cmd_ok(struct client *c)
40 set_string(c, c->reply, "error", "");
43 const char *get_string(struct json_node *n, const char *key)
45 struct json_node *s = json_object_get(n, key);
46 if (s && s->type == JSON_STRING)
52 bool get_uint(struct json_node *n, const char *key, uint *dest)
54 struct json_node *s = json_object_get(n, key);
55 if (s && s->type == JSON_NUMBER)
57 uint u = (uint) s->number;
58 if ((double) u == s->number)
68 struct json_node **get_array(struct json_node *n, const char *key)
70 struct json_node *s = json_object_get(n, key);
71 if (s && s->type == JSON_ARRAY)
77 struct json_node *get_object(struct json_node *n, const char *key)
79 struct json_node *s = json_object_get(n, key);
80 if (s && s->type == JSON_OBJECT)
86 static const char *cmd_need_string(struct client *c, const char *key)
88 const char *val = get_string(c->request, key);
90 cmd_error(c, "Missing %s", key);
94 static struct auth_zone *cmd_need_zone(struct client *c)
96 const char *name = cmd_need_string(c, "zone");
97 struct auth_zone *az = auth_find_zone(name);
99 cmd_error(c, "No such zone");
103 static bool cmd_is_admin(struct client *c)
105 return (c->uid == 0);
108 static void cmd_require_admin(struct client *c)
110 if (!cmd_is_admin(c))
111 cmd_error(c, "Permission denied");
114 static const char *cmd_need_target_login(struct client *c)
116 const char *l = get_string(c->request, "login");
119 cmd_require_admin(c);
124 struct passwd *pw = getpwuid(c->uid);
126 cmd_error(c, "You do not exist");
127 return json_strdup(c->json, pw->pw_name);
131 static struct auth_acct *cmd_need_target_acct(struct client *c)
133 const char *login = cmd_need_target_login(c);
134 struct auth_zone *az = cmd_need_zone(c);
136 struct auth_user *au = auth_find_user(login, 0);
137 struct auth_acct *aa = au ? auth_find_acct(au, az, 0) : NULL;
141 if (!az->auto_create_acct)
142 cmd_error(c, "No such account");
146 msg(L_INFO, "Automatically creating user: login=<%s>", login);
147 au = auth_find_user(login, 1);
149 msg(L_INFO, "Automatically creating account: login=<%s> zone=<%s>", login, az->name);
150 return auth_find_acct(au, az, 1);
153 static void cmd_nop(struct client *c)
158 static void cmd_create_acct(struct client *c)
160 cmd_require_admin(c);
162 const char *login = get_string(c->request, "login");
164 cmd_error(c, "Login required");
166 struct auth_zone *az = cmd_need_zone(c);
167 struct auth_user *au = auth_find_user(login, 1);
168 if (!auth_find_acct(au, az, 0))
170 msg(L_INFO, "Creating account: login=<%s> zone=<%s>", login, az->name);
171 auth_find_acct(au, az, 1);
178 static void cmd_delete_acct(struct client *c)
180 cmd_require_admin(c);
182 const char *login = get_string(c->request, "login");
184 cmd_error(c, "Login required");
185 const char *zone_name = get_string(c->request, "zone");
187 struct auth_user *au = auth_find_user(login, 0);
191 if (zone_name && !strcmp(zone_name, "*"))
193 msg(L_INFO, "Deleting user: login=<%s>", login);
194 auth_delete_user(au);
198 struct auth_zone *az = cmd_need_zone(c);
199 struct auth_acct *aa = auth_find_acct(au, az, 0);
202 msg(L_INFO, "Deleting account: login=<%s> zone=<%s>", login, az->name);
203 auth_delete_acct(aa);
204 if (!clist_head(&au->accounts))
206 msg(L_INFO, "Deleting user with no accounts: login=<%s>", login);
207 auth_delete_user(au);
215 static void cmd_set_passwd(struct client *c)
217 struct auth_acct *aa = cmd_need_target_acct(c);
218 const char *passwd = cmd_need_string(c, "passwd");
220 if (!aa->zone->allow_passwd)
221 cmd_error(c, "This zone does not allow authentication by password");
223 if (strchr(passwd, '-'))
224 cmd_error(c, "The minus sign is forbidden in passwords");
226 msg(L_INFO, "Set password: login=<%s> zone=<%s>", aa->user->login, aa->zone->name);
228 struct auth_token *at_old = auth_find_token_passwd(aa);
230 auth_delete_token(at_old);
232 struct auth_token *at = auth_create_token(aa);
233 auth_set_token_passwd(at, passwd);
239 static void cmd_delete_passwd(struct client *c)
241 struct auth_acct *aa = cmd_need_target_acct(c);
242 struct auth_token *at = auth_find_token_passwd(aa);
244 cmd_error(c, "No password set");
246 msg(L_INFO, "Deleted password: login=<%s> zone=<%s>", aa->user->login, aa->zone->name);
247 auth_delete_token(at);
253 static void cmd_create_token(struct client *c)
255 struct auth_acct *aa = cmd_need_target_acct(c);
257 if (!aa->zone->allow_tokens)
258 cmd_error(c, "This zone does not allow authentication by tokens");
260 if (clist_size(&aa->tokens) >= aa->zone->allow_tokens)
261 cmd_error(c, "Maximum number of tokens was reached");
263 struct auth_token *at = auth_create_token(aa);
264 char *tok = auth_set_token_generated(at, get_string(c->request, "comment"));
265 set_string(c, c->reply, "token", tok);
268 msg(L_INFO, "Created token: login=<%s> zone=<%s> id=<%s>", aa->user->login, aa->zone->name, at->ident);
274 static void cmd_delete_token(struct client *c)
276 struct auth_acct *aa = cmd_need_target_acct(c);
277 const char *ident = cmd_need_string(c, "ident");
278 bool all = !strcmp(ident, "*");
281 struct auth_token *tmp;
282 CLIST_FOR_EACH_DELSAFE(struct auth_token *, at, aa->tokens, tmp)
283 if (at->type == TOKEN_GENERATED && (all || !strcmp(at->ident, ident)))
286 msg(L_INFO, "Deleted token: login=<%s> zone=<%s> id=<%s>", aa->user->login, aa->zone->name, at->ident);
287 auth_delete_token(at);
290 if (!all && !matched)
291 cmd_error(c, "No such token");
297 static void cmd_create_temp(struct client *c)
299 struct auth_acct *aa = cmd_need_target_acct(c);
302 if (get_uint(c->request, "validity", &validity))
303 cmd_error(c, "Validity must be given");
305 if (!aa->zone->max_temp_validity)
306 cmd_error(c, "This zone does not allow temporary tokens");
308 if (validity > aa->zone->max_temp_validity)
309 cmd_error(c, "This zone limits temporary token validity to %d seconds", aa->zone->max_temp_validity);
311 char *tok = temp_generate(aa->zone->name, aa->user->login, validity);
312 set_string(c, c->reply, "token", tok);
315 msg(L_INFO, "Created temp token: login=<%s> zone=<%s> validity=%u", aa->user->login, aa->zone->name, validity);
320 static void cmd_login_fake(struct client *c, const char *passwd)
322 auth_check_token(auth_fake_token, passwd);
323 cmd_error(c, "Invalid password");
326 static void cmd_login_by_temp(struct client *c, struct auth_zone *az, const char *given_passwd)
328 const char *login = cmd_need_string(c, "login");
330 const char *reason = temp_check(az->name, login, given_passwd);
333 msg(L_INFO, "Login failed: %s user=<%s> zone=<%s> type=<temp>", reason, login, az->name);
338 * The following checks test for improbable things like user
339 * disappearing since the token has been issued.
342 if (!az->max_temp_validity)
344 msg(L_INFO, "Login failed: Temporary tokens no longer accepted for zone=<%s>", az->name);
348 struct auth_user *au = auth_find_user(login, 0);
351 msg(L_INFO, "Login failed: No user=<%s> type=<temp>", login);
355 struct auth_acct *aa = auth_find_acct(au, az, 0);
358 msg(L_INFO, "Login failed: No account user=<%s> zone=<%s> type=<temp>", login, az->name);
362 msg(L_INFO, "Login successful: user=<%s> zone=<%s> type=<temp>", login, az->name);
366 cmd_error(c, "Temporary token refused");
369 static void cmd_login(struct client *c)
371 struct auth_zone *az = cmd_need_zone(c);
372 const char *given_passwd = cmd_need_string(c, "passwd");
374 // Split password string to token ident and the password proper
375 char passbuf[strlen(given_passwd) + 1];
376 strcpy(passbuf, given_passwd);
377 char *ident, *passwd;
378 char *sep = strchr(passbuf, '-');
391 if (ident && !strcmp(ident, "t"))
392 return cmd_login_by_temp(c, az, given_passwd);
394 const char *login = cmd_need_string(c, "login");
395 struct auth_user *au = auth_find_user(login, 0);
398 msg(L_INFO, "Login failed: No user=<%s>", login);
399 return cmd_login_fake(c, passwd);
402 struct auth_acct *aa = auth_find_acct(au, az, 0);
405 msg(L_INFO, "Login failed: No account user=<%s> zone=<%s>", login, az->name);
406 return cmd_login_fake(c, passwd);
409 struct auth_token *at;
411 at = auth_find_token_generated(aa, ident);
413 at = auth_find_token_passwd(aa);
416 msg(L_INFO, "Login failed: No token user=<%s> zone=<%s> ident=<%s>", login, az->name, (ident ? : ""));
417 return cmd_login_fake(c, passwd);
420 if (!auth_check_token(at, passwd))
422 msg(L_INFO, "Login failed: Wrong password for user=<%s> zone=<%s> ident=<%s>", login, az->name, (ident ? : ""));
423 cmd_error(c, "Invalid password");
426 msg(L_INFO, "Login successful: user=<%s> zone=<%s> ident=<%s>", login, az->name, (ident ? : ""));
430 static void cmd_list_accts(struct client *c)
432 const char *login = cmd_need_target_login(c);
434 struct json_context *js = c->json;
435 set_string(c, c->reply, "login", login);
436 struct json_node *jas = json_new_array(js);
437 json_object_set(c->reply, "accounts", jas);
439 struct auth_user *au = auth_find_user(login, 0);
446 CLIST_FOR_EACH(struct auth_acct *, aa, au->accounts)
448 struct json_node *ja = json_new_object(js);
449 json_array_append(jas, ja);
450 set_string(c, ja, "zone", aa->zone->name);
452 struct json_node *jts = json_new_array(js);
453 json_object_set(ja, "tokens", jts);
455 CLIST_FOR_EACH(struct auth_token *, at, aa->tokens)
457 struct json_node *jt = json_new_object(js);
458 json_array_append(jts, jt);
463 case TOKEN_PASSWORD: type = "passwd"; break;
464 case TOKEN_GENERATED: type = "token"; break;
465 default: type = "unknown"; break;
467 set_string(c, jt, "type", type);
469 set_string(c, jt, "ident", at->ident);
470 set_string(c, jt, "comment", at->comment);
471 set_uint(c, jt, "lastmod", at->last_modified);
478 static void cmd_list_zones(struct client *c)
480 struct json_context *js = c->json;
482 struct json_node *jzs = json_new_array(js);
483 json_object_set(c->reply, "zones", jzs);
485 CLIST_FOR_EACH(struct auth_zone *, az, zone_list)
487 struct json_node *jz = json_new_object(js);
488 json_array_append(jzs, jz);
489 set_string(c, jz, "name", az->name);
490 set_string(c, jz, "desc", az->desc);
491 set_uint(c, jz, "allow-passwd", az->allow_passwd);
492 set_uint(c, jz, "allow-tokens", az->allow_tokens);
500 void (*handler)(struct client *c);
503 static const struct command command_table[] = {
505 { "create-acct", cmd_create_acct },
506 { "delete-acct", cmd_delete_acct },
507 { "create-token", cmd_create_token },
508 { "delete-token", cmd_delete_token },
509 { "set-passwd", cmd_set_passwd },
510 { "delete-passwd", cmd_delete_passwd },
511 { "create-temp", cmd_create_temp },
512 { "login", cmd_login },
513 { "list-accts", cmd_list_accts },
514 { "list-zones", cmd_list_zones },
517 void cmd_dispatch(struct client *c)
519 struct json_node *rq = c->request;
522 if (rq->type != JSON_OBJECT || !(cmd = get_string(rq, "cmd")))
524 set_string(c, c->reply, "error", "Malformed request");
528 const struct command *command = NULL;
529 for (uint i=0; i < ARRAY_SIZE(command_table); i++)
530 if (!strcmp(cmd, command_table[i].cmd))
532 command = &command_table[i];
537 set_string(c, c->reply, "error", "No such command");