405 lines
11 KiB
C
405 lines
11 KiB
C
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
|
|
#include "spp.h"
|
|
#include "json.h"
|
|
|
|
#ifndef _ACCOUNT_CLIENT
|
|
#define _ACCOUNT_CLIENT
|
|
|
|
#define REQ2(condition, err) if (!(condition)) { errno = err; return -1; }
|
|
#define REQ(condition) REQ2(condition, EPERM)
|
|
#define REQP2(condition, err) if (!(condition)) { errno = err; return NULL; }
|
|
#define REQP(condition) REQP2(condition, EPERM)
|
|
#define ITEROBJ(obj, it) for (struct json_object_element_s *it = obj->start; it != NULL; it = it->next)
|
|
#define DECLPOOL(pool, buf, size) struct account_memory_pool pool = { .ptr = buf, .len = size, .cursor = 0 }
|
|
|
|
#ifdef __linux__
|
|
static inline int account_connect() {
|
|
return spp_connect_linux("verified:org.semilabs.os/accountd");
|
|
}
|
|
#else
|
|
static inline int account_connect() {
|
|
return spp_connect_unix("/var/run/accountd.sock");
|
|
}
|
|
#endif /* __linux__ */
|
|
|
|
struct account_memory_pool {
|
|
void *ptr;
|
|
size_t cursor;
|
|
size_t len;
|
|
};
|
|
|
|
static inline void *account_allocate_pool(struct account_memory_pool *pool, size_t size) {
|
|
if (!pool || !pool->ptr || size == 0) {
|
|
return NULL;
|
|
}
|
|
if (pool->cursor + size > pool->len) {
|
|
return NULL;
|
|
}
|
|
void *result = (char *)pool->ptr + pool->cursor;
|
|
pool->cursor += size;
|
|
return result;
|
|
}
|
|
|
|
static inline int account_recordpw(
|
|
struct json_value_s *root,
|
|
struct passwd *passwd,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
passwd->pw_passwd = account_allocate_pool(pool, 2);
|
|
REQ2(passwd->pw_passwd, ERANGE);
|
|
strcpy(passwd->pw_passwd, "x");
|
|
|
|
passwd->pw_gecos = account_allocate_pool(pool, 1);
|
|
REQ2(passwd->pw_gecos, ERANGE);
|
|
strcpy(passwd->pw_gecos, "");
|
|
|
|
struct json_object_s *object = json_value_as_object(root);
|
|
REQ(object);
|
|
ITEROBJ(object, it) {
|
|
const char *name = it->name->string;
|
|
struct json_value_s *value = it->value;
|
|
|
|
if (strcmp(name, "username") == 0) {
|
|
struct json_string_s *username = json_value_as_string(value);
|
|
REQ(username);
|
|
passwd->pw_name = account_allocate_pool(pool, username->string_size + 1);
|
|
REQ2(passwd->pw_name, ERANGE);
|
|
strcpy(passwd->pw_name, username->string);
|
|
continue;
|
|
}
|
|
if (strcmp(name, "home_directory") == 0) {
|
|
struct json_string_s *homedir = json_value_as_string(value);
|
|
REQ(homedir);
|
|
passwd->pw_dir = account_allocate_pool(pool, homedir->string_size + 1);
|
|
REQ2(passwd->pw_dir, ERANGE);
|
|
strcpy(passwd->pw_dir, homedir->string);
|
|
continue;
|
|
}
|
|
if (strcmp(name, "defaults") == 0) {
|
|
struct json_object_s *defaults = json_value_as_object(value);
|
|
REQ(defaults);
|
|
ITEROBJ(defaults, def) {
|
|
name = def->name->string;
|
|
value = def->value;
|
|
if (strcmp(name, "CliLoginShell") == 0) {
|
|
struct json_string_s *shell = json_value_as_string(value);
|
|
REQ(shell);
|
|
passwd->pw_shell = account_allocate_pool(pool, shell->string_size + 1);
|
|
REQ2(passwd->pw_shell, ERANGE);
|
|
strcpy(passwd->pw_shell, shell->string);
|
|
continue;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if (strcmp(name, "host_uid") == 0) {
|
|
struct json_number_s *uid = json_value_as_number(value);
|
|
REQ(uid);
|
|
passwd->pw_uid = atoi(uid->number);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int account_recordgr(
|
|
struct json_value_s *root,
|
|
struct group *group,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
group->gr_passwd = account_allocate_pool(pool, 2);
|
|
REQ2(group->gr_passwd, ERANGE);
|
|
strcpy(group->gr_passwd, "x");
|
|
|
|
struct json_object_s *object = json_value_as_object(root);
|
|
REQ(object);
|
|
ITEROBJ(object, it) {
|
|
const char *name = it->name->string;
|
|
struct json_value_s *value = it->value;
|
|
|
|
if (strcmp(name, "groupname") == 0) {
|
|
struct json_string_s *groupname = json_value_as_string(value);
|
|
REQ(groupname);
|
|
group->gr_name = account_allocate_pool(pool, groupname->string_size + 1);
|
|
REQ2(group->gr_name, ERANGE);
|
|
strcpy(group->gr_name, groupname->string);
|
|
continue;
|
|
}
|
|
if (strcmp(name, "host_gid") == 0) {
|
|
struct json_number_s *gid = json_value_as_number(value);
|
|
REQ(gid);
|
|
group->gr_gid = atoi(gid->number);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
typedef int (*pwgr_parser_t)(struct json_value_s*, void*, struct account_memory_pool*);
|
|
|
|
static inline int account_getpwgrkey(
|
|
int fd,
|
|
const char *method,
|
|
const char *key,
|
|
const char *valueborder,
|
|
const char *name,
|
|
pwgr_parser_t parser,
|
|
void *pwgr,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
int status;
|
|
|
|
const char *request_template = "{\"method\": \"%s\", \"params\": {\"%s\": %s%s%s}}";
|
|
int request_need = snprintf(NULL, 0, request_template, method, key, valueborder, name, valueborder);
|
|
REQ(request_need > 0);
|
|
char *request_json = malloc(request_need + 1);
|
|
REQ2(request_json, ENOMEM);
|
|
snprintf(request_json, request_need + 1, request_template, method, key, valueborder, name, valueborder);
|
|
status = spp_send(fd, request_json);
|
|
free(request_json);
|
|
REQ(status == 0);
|
|
|
|
const char *reply_str = spp_recv(fd);
|
|
REQ(reply_str);
|
|
|
|
struct json_value_s *reply = json_parse(reply_str, strlen(reply_str));
|
|
spp_free((void*)reply_str);
|
|
REQ(reply);
|
|
struct json_object_s *reply_object = json_value_as_object(reply);
|
|
if (!reply_object) goto format_error;
|
|
ITEROBJ(reply_object, it) {
|
|
if (strcmp(it->name->string, "success") == 0) {
|
|
if (json_value_is_false(it->value)) goto not_found;
|
|
continue;
|
|
}
|
|
if (strcmp(it->name->string, "value") == 0) {
|
|
status = parser(it->value, pwgr, pool);
|
|
if (status != 0) goto not_found;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
free(reply);
|
|
return 0;
|
|
|
|
format_error:
|
|
errno = EPERM;
|
|
not_found:
|
|
free(reply);
|
|
return -1;
|
|
}
|
|
|
|
static inline char *account_list_uuid_next(
|
|
int fd,
|
|
const char *method,
|
|
unsigned int no
|
|
) {
|
|
int status;
|
|
|
|
const char *request_template = "{\"method\": \"%s\", \"params\": {\"start\": %u, \"len\": 1}}";
|
|
int request_need = snprintf(NULL, 0, request_template, method, no);
|
|
REQP(request_need > 0);
|
|
char *request_json = malloc(request_need + 1);
|
|
REQP2(request_json, ENOMEM);
|
|
snprintf(request_json, request_need + 1, request_template, method, no);
|
|
status = spp_send(fd, request_json);
|
|
free(request_json);
|
|
REQP(status == 0);
|
|
|
|
const char *reply_str = spp_recv(fd);
|
|
REQP(reply_str);
|
|
|
|
struct json_value_s *reply = json_parse(reply_str, strlen(reply_str));
|
|
spp_free((void*)reply_str);
|
|
REQP(reply);
|
|
struct json_object_s *reply_object = json_value_as_object(reply);
|
|
if (!reply_object) goto format_error;
|
|
ITEROBJ(reply_object, it) {
|
|
if (strcmp(it->name->string, "success") == 0) {
|
|
if (json_value_is_false(it->value)) goto not_found;
|
|
continue;
|
|
}
|
|
if (strcmp(it->name->string, "value") == 0) {
|
|
struct json_array_s *list = json_value_as_array(it->value);
|
|
if (!list) goto not_found;
|
|
struct json_array_element_s *first = list->start;
|
|
if (!first) goto not_found;
|
|
struct json_string_s *item = json_value_as_string(first->value);
|
|
if (!item) goto format_error;
|
|
return strdup(item->string);
|
|
}
|
|
}
|
|
|
|
free(reply);
|
|
return NULL;
|
|
|
|
format_error:
|
|
errno = EPERM;
|
|
not_found:
|
|
free(reply);
|
|
return NULL;
|
|
}
|
|
|
|
static inline int account_getpwuid(
|
|
int fd,
|
|
uid_t uid,
|
|
struct passwd *passwd,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
char uid_str[32];
|
|
int len = snprintf(uid_str, sizeof(uid_str), "%u", (unsigned int)uid);
|
|
REQ(len > 0 && (size_t)len < sizeof(uid_str));
|
|
|
|
return account_getpwgrkey(
|
|
fd,
|
|
"GetUserInfo",
|
|
"host_uid",
|
|
"",
|
|
uid_str,
|
|
(pwgr_parser_t)account_recordpw,
|
|
passwd,
|
|
pool
|
|
);
|
|
}
|
|
|
|
static inline int account_getpwnam(
|
|
int fd,
|
|
const char *name,
|
|
struct passwd *passwd,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
return account_getpwgrkey(
|
|
fd,
|
|
"GetUserInfo",
|
|
"username",
|
|
"\"",
|
|
name,
|
|
(pwgr_parser_t)account_recordpw,
|
|
passwd,
|
|
pool
|
|
);
|
|
}
|
|
|
|
static inline int account_getpwuuid(
|
|
int fd,
|
|
const char *uuid,
|
|
struct passwd *passwd,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
return account_getpwgrkey(
|
|
fd,
|
|
"GetUserInfo",
|
|
"uuid",
|
|
"\"",
|
|
uuid,
|
|
(pwgr_parser_t)account_recordpw,
|
|
passwd,
|
|
pool
|
|
);
|
|
}
|
|
|
|
static inline int account_getgrgid(
|
|
int fd,
|
|
gid_t gid,
|
|
struct group *group,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
char gid_str[32];
|
|
int len = snprintf(gid_str, sizeof(gid_str), "%u", (unsigned int)gid);
|
|
REQ(len > 0 && (size_t)len < sizeof(gid_str));
|
|
|
|
return account_getpwgrkey(
|
|
fd,
|
|
"GetGroupInfo",
|
|
"host_gid",
|
|
"",
|
|
gid_str,
|
|
(pwgr_parser_t)account_recordgr,
|
|
group,
|
|
pool
|
|
);
|
|
}
|
|
|
|
static inline int account_getgrnam(
|
|
int fd,
|
|
const char *name,
|
|
struct group *group,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
return account_getpwgrkey(
|
|
fd,
|
|
"GetGroupInfo",
|
|
"groupname",
|
|
"\"",
|
|
name,
|
|
(pwgr_parser_t)account_recordgr,
|
|
group,
|
|
pool
|
|
);
|
|
}
|
|
|
|
static inline int account_getgruuid(
|
|
int fd,
|
|
const char *uuid,
|
|
struct group *group,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
return account_getpwgrkey(
|
|
fd,
|
|
"GetGroupInfo",
|
|
"uuid",
|
|
"\"",
|
|
uuid,
|
|
(pwgr_parser_t)account_recordgr,
|
|
group,
|
|
pool
|
|
);
|
|
}
|
|
|
|
static inline int account_getpwent(
|
|
int fd,
|
|
unsigned int *no,
|
|
struct passwd *passwd,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
int status;
|
|
|
|
char *next_uuid = account_list_uuid_next(fd, "ListUser", *no);
|
|
REQ2(next_uuid, errno);
|
|
status = account_getpwuuid(fd, next_uuid, passwd, pool);
|
|
free(next_uuid);
|
|
REQ(status == 0);
|
|
*no += 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int account_getgrent(
|
|
int fd,
|
|
unsigned int *no,
|
|
struct group *group,
|
|
struct account_memory_pool *pool
|
|
) {
|
|
int status;
|
|
|
|
char *next_uuid = account_list_uuid_next(fd, "ListGroup", *no);
|
|
REQ2(next_uuid, errno);
|
|
status = account_getgruuid(fd, next_uuid, group, pool);
|
|
free(next_uuid);
|
|
REQ(status == 0);
|
|
*no += 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* _ACCOUNT_CLIENT */
|