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 uint cmd_need_uint(struct client *c, const char *key)
97 if (!get_uint(c->request, key, &val))
98 cmd_error(c, "Missing %s", key);
102 static struct auth_zone *cmd_need_zone(struct client *c)
104 const char *name = cmd_need_string(c, "zone");
105 struct auth_zone *az = auth_find_zone(name);
107 cmd_error(c, "No such zone");
111 static bool cmd_is_admin(struct client *c)
113 return (c->uid == 0);
116 static void cmd_require_admin(struct client *c)
118 if (!cmd_is_admin(c))
119 cmd_error(c, "Permission denied");
122 static const char *cmd_need_target_login(struct client *c)
124 const char *l = get_string(c->request, "login");
127 cmd_require_admin(c);
132 struct passwd *pw = getpwuid(c->uid);
134 cmd_error(c, "You do not exist");
135 return json_strdup(c->json, pw->pw_name);
139 static void cmd_passwd_auth_fake(struct client *c, const char *passwd)
141 auth_check_token(auth_fake_token, passwd);
142 cmd_error(c, "Invalid password");
145 static struct auth_acct *cmd_need_passwd_auth_acct(struct client *c, const char *passwd)
147 struct auth_zone *az = cmd_need_zone(c);
148 const char *login = cmd_need_string(c, "login");
149 if (!az->allow_passwd_auth || !az->allow_passwd)
151 msg(L_INFO, "Password authentication denied by zone configuration: login=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
152 cmd_error(c, "This zone does not allow password authentication for management");
155 struct auth_user *au = auth_find_user(login, 0);
158 msg(L_INFO, "Password authentication failed: No user=<%s> uid=<%u>", login, c->uid);
159 cmd_passwd_auth_fake(c, passwd);
162 struct auth_acct *aa = auth_find_acct(au, az, 0);
165 msg(L_INFO, "Password authentication failed: No account user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
166 cmd_passwd_auth_fake(c, passwd);
168 if (!aa->allow_passwd_auth)
170 msg(L_INFO, "Password authentication failed: Not allowed by user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
171 cmd_passwd_auth_fake(c, passwd);
174 struct auth_token *at = auth_find_token_passwd(aa);
177 msg(L_INFO, "Password authentication failed: No password user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
178 cmd_passwd_auth_fake(c, passwd);
181 if (!auth_check_token(at, passwd))
183 msg(L_INFO, "Password authentication failed: Wrong password for user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
184 cmd_error(c, "Invalid password");
187 msg(L_INFO, "Password authentication successful: user=<%s> zone=<%s> uid=<%u>", login, az->name, c->uid);
191 static struct auth_acct *cmd_need_target_acct(struct client *c)
193 const char *auth_passwd = get_string(c->request, "auth-passwd");
195 return cmd_need_passwd_auth_acct(c, auth_passwd);
197 const char *login = cmd_need_target_login(c);
198 struct auth_zone *az = cmd_need_zone(c);
200 struct auth_user *au = auth_find_user(login, 0);
201 struct auth_acct *aa = au ? auth_find_acct(au, az, 0) : NULL;
205 if (!az->auto_create_acct)
206 cmd_error(c, "No such account");
210 msg(L_INFO, "Automatically creating user: login=<%s>", login);
211 au = auth_find_user(login, 1);
213 msg(L_INFO, "Automatically creating account: login=<%s> zone=<%s>", login, az->name);
214 return auth_find_acct(au, az, 1);
217 static void cmd_nop(struct client *c)
222 static void cmd_create_acct(struct client *c)
224 cmd_require_admin(c);
226 const char *login = get_string(c->request, "login");
228 cmd_error(c, "Login required");
230 struct auth_zone *az = cmd_need_zone(c);
231 struct auth_user *au = auth_find_user(login, 1);
232 if (!auth_find_acct(au, az, 0))
234 msg(L_INFO, "Creating account: login=<%s> zone=<%s>", login, az->name);
235 auth_find_acct(au, az, 1);
242 static void cmd_delete_acct(struct client *c)
244 cmd_require_admin(c);
246 const char *login = get_string(c->request, "login");
248 cmd_error(c, "Login required");
249 const char *zone_name = get_string(c->request, "zone");
251 struct auth_user *au = auth_find_user(login, 0);
255 if (zone_name && !strcmp(zone_name, "*"))
257 msg(L_INFO, "Deleting user: login=<%s>", login);
258 auth_delete_user(au);
262 struct auth_zone *az = cmd_need_zone(c);
263 struct auth_acct *aa = auth_find_acct(au, az, 0);
266 msg(L_INFO, "Deleting account: login=<%s> zone=<%s>", login, az->name);
267 auth_delete_acct(aa);
268 if (!clist_head(&au->accounts))
270 msg(L_INFO, "Deleting user with no accounts: login=<%s>", login);
271 auth_delete_user(au);
279 static void cmd_set_passwd(struct client *c)
281 struct auth_acct *aa = cmd_need_target_acct(c);
282 const char *passwd = cmd_need_string(c, "passwd");
284 if (!aa->zone->allow_passwd)
285 cmd_error(c, "This zone does not allow authentication by password");
287 if (strchr(passwd, '-'))
288 cmd_error(c, "The minus sign is forbidden in passwords");
290 msg(L_INFO, "Set password: login=<%s> zone=<%s>", aa->user->login, aa->zone->name);
292 struct auth_token *at_old = auth_find_token_passwd(aa);
294 auth_delete_token(at_old);
296 struct auth_token *at = auth_create_token(aa);
297 auth_set_token_passwd(at, passwd);
303 static void cmd_delete_passwd(struct client *c)
305 struct auth_acct *aa = cmd_need_target_acct(c);
306 struct auth_token *at = auth_find_token_passwd(aa);
308 cmd_error(c, "No password set");
310 msg(L_INFO, "Deleted password: login=<%s> zone=<%s>", aa->user->login, aa->zone->name);
311 auth_delete_token(at);
317 static void cmd_create_token(struct client *c)
319 struct auth_acct *aa = cmd_need_target_acct(c);
321 if (!aa->zone->allow_tokens)
322 cmd_error(c, "This zone does not allow authentication by tokens");
324 if (clist_size(&aa->tokens) >= aa->zone->allow_tokens)
325 cmd_error(c, "Maximum number of tokens was reached");
327 const char *comment = get_string(c->request, "comment");
328 if (comment && strlen(comment) > max_comment_size)
329 cmd_error(c, "Comment too long");
331 struct auth_token *at = auth_create_token(aa);
332 char *tok = auth_set_token_generated(at, comment, c->pool);
333 set_string(c, c->reply, "token", tok);
334 set_string(c, c->reply, "ident", at->ident);
336 msg(L_INFO, "Created token: login=<%s> zone=<%s> id=<%s>", aa->user->login, aa->zone->name, at->ident);
342 static void cmd_delete_token(struct client *c)
344 struct auth_acct *aa = cmd_need_target_acct(c);
345 const char *ident = cmd_need_string(c, "ident");
346 bool all = !strcmp(ident, "*");
349 struct auth_token *tmp;
350 CLIST_FOR_EACH_DELSAFE(struct auth_token *, at, aa->tokens, tmp)
351 if (at->type == TOKEN_GENERATED && (all || !strcmp(at->ident, ident)))
354 msg(L_INFO, "Deleted token: login=<%s> zone=<%s> id=<%s>", aa->user->login, aa->zone->name, at->ident);
355 auth_delete_token(at);
358 if (!all && !matched)
359 cmd_error(c, "No such token");
365 static void cmd_change_token(struct client *c)
367 struct auth_acct *aa = cmd_need_target_acct(c);
368 const char *ident = cmd_need_string(c, "ident");
369 struct auth_token *at = auth_find_token_generated(aa, ident);
371 cmd_error(c, "No such token");
373 const char *comment = get_string(c->request, "comment");
374 if (comment && !strcmp(comment, ""))
376 auth_change_token_comment(at, comment);
378 msg(L_INFO, "Changed token: login=<%s> zone=<%s> id=<%s>", aa->user->login, aa->zone->name, at->ident);
384 static void cmd_create_temp(struct client *c)
386 struct auth_acct *aa = cmd_need_target_acct(c);
389 if (!get_uint(c->request, "validity", &validity))
390 cmd_error(c, "Validity must be given");
392 if (!aa->zone->max_temp_validity)
393 cmd_error(c, "This zone does not allow temporary tokens");
395 if (validity > aa->zone->max_temp_validity)
396 cmd_error(c, "This zone limits temporary token validity to %d seconds", aa->zone->max_temp_validity);
398 char *tok = temp_generate(aa->zone->name, aa->user->login, validity, c->pool);
399 set_string(c, c->reply, "token", tok);
401 const char *shortened = temp_shorten(tok, c->pool);
403 msg(L_INFO, "Created temp token: login=<%s> zone=<%s> temp=<%s> validity=%u", aa->user->login, aa->zone->name, shortened, validity);
408 static void cmd_login_fake(struct client *c, const char *passwd)
410 auth_check_token(auth_fake_token, passwd);
411 cmd_error(c, "Invalid password");
414 static void cmd_login_by_temp(struct client *c, struct auth_zone *az, const char *given_passwd)
416 const char *login = cmd_need_string(c, "login");
417 const char *shortened = temp_shorten(given_passwd, c->pool);
419 const char *reason = temp_check(az->name, login, given_passwd, c->pool);
422 msg(L_INFO, "Login failed: %s user=<%s> zone=<%s> temp=<%s>", reason, login, az->name, shortened);
427 * The following checks test for improbable things like user
428 * disappearing since the token has been issued.
431 if (!az->max_temp_validity)
433 msg(L_INFO, "Login failed: Temporary tokens no longer accepted for zone=<%s>", az->name);
437 struct auth_user *au = auth_find_user(login, 0);
440 msg(L_INFO, "Login failed: No user=<%s> temp=<%s>", login, shortened);
444 struct auth_acct *aa = auth_find_acct(au, az, 0);
447 msg(L_INFO, "Login failed: No account user=<%s> zone=<%s> temp=<%s>", login, az->name, shortened);
451 msg(L_INFO, "Login successful: user=<%s> zone=<%s> temp=<%s>", login, az->name, shortened);
456 cmd_error(c, "Temporary token refused");
459 static void cmd_login(struct client *c)
461 struct auth_zone *az = cmd_need_zone(c);
462 const char *given_passwd = cmd_need_string(c, "passwd");
464 // Split password string to token ident and the password proper
465 char passbuf[strlen(given_passwd) + 1];
466 strcpy(passbuf, given_passwd);
467 char *ident, *passwd;
468 char *sep = strchr(passbuf, '-');
481 if (ident && !strcmp(ident, "t"))
482 return cmd_login_by_temp(c, az, given_passwd);
484 const char *login = cmd_need_string(c, "login");
485 struct auth_user *au = auth_find_user(login, 0);
488 msg(L_INFO, "Login failed: No user=<%s>", login);
489 return cmd_login_fake(c, passwd);
492 struct auth_acct *aa = auth_find_acct(au, az, 0);
495 msg(L_INFO, "Login failed: No account user=<%s> zone=<%s>", login, az->name);
496 return cmd_login_fake(c, passwd);
499 struct auth_token *at;
501 at = auth_find_token_generated(aa, ident);
503 at = auth_find_token_passwd(aa);
506 msg(L_INFO, "Login failed: No token user=<%s> zone=<%s> ident=<%s>", login, az->name, (ident ? : ""));
507 return cmd_login_fake(c, passwd);
510 if (!auth_check_token(at, passwd))
512 msg(L_INFO, "Login failed: Wrong password for user=<%s> zone=<%s> ident=<%s>", login, az->name, (ident ? : ""));
513 cmd_error(c, "Invalid password");
516 msg(L_INFO, "Login successful: user=<%s> zone=<%s> ident=<%s>", login, az->name, (ident ? : ""));
520 static void cmd_allow_passwd_auth(struct client *c)
522 struct auth_acct *aa = cmd_need_target_acct(c);
523 uint allow = cmd_need_uint(c, "allow");
524 if (!aa->zone->allow_passwd_auth)
525 cmd_error(c, "This zone does not allow password authentication for management");
527 msg(L_INFO, "Setting account password authentication: allow-passwd-auth=<%u> login=<%s> zone=<%s>", !!allow, aa->user->login, aa->zone->name);
529 aa->allow_passwd_auth = !!allow;
535 static void cmd_list_accts(struct client *c)
537 const char *login = cmd_need_target_login(c);
539 struct json_context *js = c->json;
540 set_string(c, c->reply, "login", login);
541 struct json_node *jas = json_new_array(js);
542 json_object_set(c->reply, "accounts", jas);
544 struct auth_user *au = auth_find_user(login, 0);
551 CLIST_FOR_EACH(struct auth_acct *, aa, au->accounts)
553 struct json_node *ja = json_new_object(js);
554 json_array_append(jas, ja);
555 set_string(c, ja, "zone", aa->zone->name);
556 set_uint(c, ja, "allow-passwd-auth", aa->zone->allow_passwd_auth && aa->allow_passwd_auth);
558 struct json_node *jts = json_new_array(js);
559 json_object_set(ja, "tokens", jts);
561 CLIST_FOR_EACH(struct auth_token *, at, aa->tokens)
563 struct json_node *jt = json_new_object(js);
564 json_array_append(jts, jt);
569 case TOKEN_PASSWORD: type = "passwd"; break;
570 case TOKEN_GENERATED: type = "token"; break;
571 default: type = "unknown"; break;
573 set_string(c, jt, "type", type);
575 set_string(c, jt, "ident", at->ident);
576 set_string(c, jt, "comment", at->comment);
577 set_uint(c, jt, "lastmod", at->last_modified);
584 static void cmd_list_zones(struct client *c)
586 struct json_context *js = c->json;
588 struct json_node *jzs = json_new_array(js);
589 json_object_set(c->reply, "zones", jzs);
591 CLIST_FOR_EACH(struct auth_zone *, az, zone_list)
593 struct json_node *jz = json_new_object(js);
594 json_array_append(jzs, jz);
595 set_string(c, jz, "name", az->name);
596 set_string(c, jz, "desc", az->desc);
597 set_uint(c, jz, "allow-passwd", az->allow_passwd);
598 set_uint(c, jz, "allow-tokens", az->allow_tokens);
599 set_uint(c, jz, "allow-passwd-auth", az->allow_passwd_auth);
600 set_uint(c, jz, "max-temp-validity", az->max_temp_validity);
608 void (*handler)(struct client *c);
611 static const struct command command_table[] = {
613 { "create-acct", cmd_create_acct },
614 { "delete-acct", cmd_delete_acct },
615 { "create-token", cmd_create_token },
616 { "delete-token", cmd_delete_token },
617 { "change-token", cmd_change_token },
618 { "set-passwd", cmd_set_passwd },
619 { "delete-passwd", cmd_delete_passwd },
620 { "create-temp", cmd_create_temp },
621 { "login", cmd_login },
622 { "allow-passwd-auth", cmd_allow_passwd_auth },
623 { "list-accts", cmd_list_accts },
624 { "list-zones", cmd_list_zones },
627 void cmd_dispatch(struct client *c)
629 struct json_node *rq = c->request;
632 if (rq->type != JSON_OBJECT || !(cmd = get_string(rq, "cmd")))
634 set_string(c, c->reply, "error", "Malformed request");
638 const struct command *command = NULL;
639 for (uint i=0; i < ARRAY_SIZE(command_table); i++)
640 if (!strcmp(cmd, command_table[i].cmd))
642 command = &command_table[i];
647 set_string(c, c->reply, "error", "No such command");